Ansible使用jinja2管理配置文件以及jinja2语法简介

1、Jinja2介绍

Jinja2是基于python的模板引擎,功能比较类似于PHP的smarty,J2ee的Freemarker和velocity。它能完全支持unicode,并具有集成的沙箱执行环境,应用广泛。jinja2使用BSD授权

Jinja2的语法是由variables(变量)和statement(语句)组成,如下;

1、variables:可以输出数据

 my_variables 

{{ some_dudes_name | capitalize }}

2、statements: 可以用来创建条件和循环等

if语句:
{% if my_conditional %} 
...
{% endif %}
for 语句:
{% for item in all_items %}
`item` 
……
{% endfor %}

从上面第二个variables的例子中可以看出,jinja2支持使用带过滤器的Unix型管道操作符,有很多的内置过滤器可供使用。我们可以仅仅用一堆简单if和for就可以建立几乎任何的常规配置文件,不过如果你有意更进一步,jinja2 documentation (http://jinja.pocoo.org/docs/dev/)包含了很多有趣的东西可供了解。我们可以看到ansible允许在模板中使用诸如绘制时间此类的一些额外的模板变量

第一个例子:引用变量

#cd roles/template/
.
├── meta
│   └── main.yml
├── tasks
│   ├── template.yml  
│   └── main.yml
├── templates
│   ├── order.j2
└── vars
    └── main.yml

总调度yml文件:

#cat templates.yml
---
- hosts: 10.0.90.27
  user: root
  gather_facts: false
  roles:
   - role: template

注意:这里 - role: template 和 - template 是一样的!

其他yml文件,如下:

#cat tasks/main.yml
- include: template.yml

#cat tasks/template.yml
- name: create {{ PROJECT }} directory
  file: dest=/data/{{ PROJECT }} state=directory
- name: template transfor java dir
  template: src=order.j2 dest=/data/{{ PROJECT }}/order.conf
  
#cat templates/order.j2
project: {{ PROJECT }}
switch: {{ SWITCH }}
dbport: {{ DBPORT }}

#cat vars/main.yml
PROJECT: "JAVA"
SWITCH: "ON"
DBPORT: "8080"

测试:
# ansible-playbook templates.yml --syntax-check
playbook: templates.yml

执行:
# ansible-playbook templates.yml 
PLAY [10.0.90.27] **************************************************************
TASK [template : include] ***************************************************
included: /etc/ansible/roles/template/tasks/template.yml for 10.0.90.27
TASK [template : create JAVA directory] *************************************
changed: [10.0.90.27]
TASK [template : template transfor java dir] ********************************
changed: [10.0.90.27]
PLAY RECAP *********************************************************************
10.0.90.27                 : ok=3    changed=2    unreachable=0    failed=0   
#到10.0.90.27查看结果

#cat /data/JAVA/order.conf
project: JAVA
switch: ON
dbport: 8080
```### 1、Jinja2介绍

Jinja2是基于python的模板引擎,功能比较类似于PHP的smarty,J2ee的Freemarker和velocity。它能完全支持unicode,并具有集成的沙箱执行环境,应用广泛。jinja2使用BSD授权

Jinja2的语法是由variables(变量)和statement(语句)组成,如下;

#### 1、variables:可以输出数据

my_variables


{{ some_dudes_name | capitalize }}

#### 2、statements: 可以用来创建条件和循环等

```bash
if语句:
{% if my_conditional %} 
...
{% endif %}
for 语句:
{% for item in all_items %}
`item` 
……
{% endfor %}

从上面第二个variables的例子中可以看出,jinja2支持使用带过滤器的Unix型管道操作符,有很多的内置过滤器可供使用。我们可以仅仅用一堆简单if和for就可以建立几乎任何的常规配置文件,不过如果你有意更进一步,jinja2 documentation (http://jinja.pocoo.org/docs/dev/)包含了很多有趣的东西可供了解。我们可以看到ansible允许在模板中使用诸如绘制时间此类的一些额外的模板变量

第一个例子:引用变量

#cd roles/template/
.
├── meta
│   └── main.yml
├── tasks
│   ├── template.yml  
│   └── main.yml
├── templates
│   ├── order.j2
└── vars
    └── main.yml

总调度yml文件:

#cat templates.yml
---
- hosts: 10.0.90.27
  user: root
  gather_facts: false
  roles:
   - role: template

注意:这里 - role: template 和 - template 是一样的!

其他yml文件,如下:

#cat tasks/main.yml
- include: template.yml

#cat tasks/template.yml
- name: create {{ PROJECT }} directory
  file: dest=/data/{{ PROJECT }} state=directory
- name: template transfor java dir
  template: src=order.j2 dest=/data/{{ PROJECT }}/order.conf
  
#cat templates/order.j2
project: {{ PROJECT }}
switch: {{ SWITCH }}
dbport: {{ DBPORT }}

#cat vars/main.yml
PROJECT: "JAVA"
SWITCH: "ON"
DBPORT: "8080"

测试:
# ansible-playbook templates.yml --syntax-check
playbook: templates.yml

执行:

第二个例子:for 语句

为远程主机生成服务器列表,加入该列表从192.168.13.201 web01.test.com 到192.168.13.211 web11.test.com 结束,如果手动添加就很不科学了,这里需要使用jinja2语法的for循环通过模板批量生成对应的配置文件,如下:

ansible目录结构:

```bash
#cd /etc/ansible/roles/test_hosts
.
├── meta
│   └── main.yml
├── tasks
│   ├── file1.yml
│   └── main.yml
├── templates
│   └── test1.j2
└── vars
    └── main.yml

各个目录下yml文件内容:

# cat tasks/file1.yml 
- name: ansible jinja2 template for hosts config
  template: src=test1.j2 dest=/etc/httpd/conf/httpd.conf.test
  
# cat tasks/main.yml 
- include: file1.yml

# cat templates/test1.j2 
{% for id in range(201,212) %}
192.168.13.{{ id }} web{{ "%03d" |format(id-200) }}.test.com
{% endfor %}
解释:
{{ id }} 提取for循环中对应的变量id值
"%02d"   调用的是python内置的字符串格式化输出(%d格式化整数)因为是01,02这种格式,所以是保留2位,故用02
然后将结果通过管道符 “|” 传递给format 函数做二次处理。

执行结果:

#cat httpd.conf.test
192.168.13.201 web01.test.com
192.168.13.202 web02.test.com
192.168.13.203 web03.test.com
192.168.13.204 web04.test.com
192.168.13.205 web05.test.com
192.168.13.206 web06.test.com
192.168.13.207 web07.test.com
192.168.13.208 web08.test.com
192.168.13.209 web09.test.com
192.168.13.210 web10.test.com
192.168.13.211 web11.test.com

第三个例子:if语句

说明:如果定义端口号,就绑定定义的端口号,如果不定义端口号,就绑定默认端口号

ansible目录结果
#cd /etc/ansible/roles/mysql_cnf
#tree
.
├── meta
│   └── main.yml
├── tasks
│   └── main.yml
├── templates
│   └── test3.j2
└── vars

主要的yml文件是templates目录下面的test3.j2

# cat templates/test3.j2 
{% if PORT %}
bind_address=10.0.90.27:{{ PORT }}
{% else %}
bind_address=10.0.90.27:3306
{% endif %}

playbook主文件

# cat jinj2_test.yml 
---
- hosts: 10.0.90.27
  user: root
  gather_facts: false
  vars:
    PORT: 3136
  tasks:
    - name: copy file to client
      template: src=/etc/ansible/roles/mysql_cnf/templates/test3.j2 dest=/root/my.cnf

执行:

# ansible-playbook jinj2_test.yml
PLAY [10.0.90.27] **************************************************************
TASK [copy file to client] *****************************************************
changed: [10.0.90.27]
PLAY RECAP *********************************************************************
10.0.90.27                 : ok=1    changed=1    unreachable=0    failed=0

查看

# cat my.cnf 
bind_address=10.0.90.27:3136

如果将vars变量去掉,执行结果:

# cat jinj2_test.yml 
---
- hosts: 10.0.90.27
  user: root
  gather_facts: false
  vars:
    PORT: false
  tasks:
    - name: copy file to client
      template: src=/etc/ansible/roles/mysql_cnf/templates/test3.j2 dest=/root/my.cnf

查看:

# cat my.cnf 
bind_address=10.0.90.27:3306

3、Jinja default()设定

精通程序编码的朋友皆知,default()默认值的设定有助于程序的健壮性和简洁性。所幸Jinja也支持该功能,上面的例子中生成Mysql配置文件中的端口定义,如果指定则PORT=3136,否则PORT=3306,我们将该案例改造为使用default()试试

编辑/etc/ansible/roles/mysql_cnf/templates/test3.j2内容如下,这种方法更简介。

bind_address=10.0.90.27:{{ PORT | default(3306) }}

2、ansible使用jiaja2生成apache多主机配置

1、创建目录,创建好之后如下:

#cd /etc/ansible/roles/apache_conf
# tree ./
./
├── meta
│   └── main.yml
├── tasks
│   ├── file.yml
│   └── main.yml
├── templates
│   └── apache.config.j2
└── vars
    └── main.yml

4 directories, 5 files

2、创建tasks调度文件,如下:

#cat file.yml 
- name: ansible jinja2 template for apache config
  template: src=apache.config.j2 dest=/etc/httpd/conf/httpd.conf.template
  
#cat main.yml 
- include: file.yml

3、创建apache的jinja2模板文件,如下:

#cat apache.config.j2 
NameVirtualHost *:80
{% for vhost in apache_vhost %}
<VirtualHost *:80>
ServerName {{ vhost.servername }}
DocumentRoot {{ vhost.documentroot }}
{% if vhost.serveradmin is defined %}
ServerAdmin {{ vhost.serveradmin }}
{% endif %}
<Directory "{{ vhost.documentroot }}">
AllowOverride All
Options -Indexes FollowSymLinks
Order allow,deny
Allow from all
</Directory>
</VirtualHost>
{% endfor %}

4、创建变量,如下:

#cat vars/main.yml
apache_vhost:
- {servername: "apache.test1.com", documentroot: "/data/test1/"}
- {servername: "apache.test2.com", documentroot: "/data/test2/"}

5、创建总调度yml文件,如下:

#cat /etc/ansible/apache_test.yml 
---
- hosts: 10.0.90.27
  user: root
  gather_facts: no
  roles:
   - { role: apache_conf }

6、测试:

#ansible-playbook apache_test.yml --syntax-check

playbook: apache_test.yml

7、执行测试

#ansible-playbook apache_test.yml 
PLAY [10.0.90.27] **************************************************************
TASK [apache_conf : include] ***************************************************
included: /etc/ansible/roles/apache_conf/tasks/file.yml for 10.0.90.27
TASK [apache_conf : ansible jinja2 template for apache config] *****************
changed: [10.0.90.27]
PLAY RECAP *********************************************************************
10.0.90.27                 : ok=2    changed=1    unreachable=0    failed=0

8、到客户端查看

#cat httpd.conf.template 
NameVirtualHost *:80
<VirtualHost *:80>
ServerName apache.test1.com
DocumentRoot /data/test1/
<Directory "/data/test1/">
AllowOverride All
Options -Indexes FollowSymLinks
Order allow,deny
Allow from all
</Directory>
</VirtualHost>
<VirtualHost *:80>
ServerName apache.test2.com
DocumentRoot /data/test2/
<Directory "/data/test2/">
AllowOverride All
Options -Indexes FollowSymLinks
Order allow,deny
Allow from all
</Directory>
</VirtualHost>

3、ansible使用jiaja2生成nginx一个模板多种不同配置

说明:为2台Nginx Proxy,1台Nginx Web通过一套模板生成对应的配置

1、ansible目录结构:

# cd roles/nginx_conf/
#tree
.
├── files
├── meta
│   └── main.yml
├── tasks
│   ├── file.yml
│   └── main.yml
├── templates
│   └── nginx.conf.j2
└── vars
    └── main.yml

2、tasks目录下文件内容:

#cat tasks/file.yml 
- name: nginx.j2 template transfer example 
  template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf.template
  
#cat tasks/main.yml 
- include: file.yml

3、nginx模板文件

#cat templates/nginx.conf.j2 
{% if nginx_use_proxy %}
{% for proxy in nginx_proxies %}
upstream {{ proxy.name }}
   #server 127.0.0.1:{{ proxy.port }};
   server {{ ansible_eth0.ipv4.address }}:{{ proxy.port }};
}
{% endfor %}
{% endif%}
server {
    listen 80;
    servername {{ nginx_server_name }};
    access_log off;
    error_log /etc/nginx/nginx_error.log;
    rewrite ^ https://$server_name$request_uri? permanent;
}
server {
    listen 443 ssl;
    server_name {{ nginx_server_name }};
    ssl_certificate /etc/nginx/ssl/{{ nginx_ssl_cert_name }};
    ssl_certificate_key /etc/nginx/ssl/{{ nginx_ssl_cert_key }};
    root {{ nginx_web_root }};
    index index.html index.html;
{% if nginx_use_auth %}
   auth_basic  "Restricted";
   auth_basic_user_file /etc/nginx/{{ project_name }}.htpasswd;
{% endif %}
{% if nginx_use_proxy %}
{% for proxy in nginx_proxies %}
   location {{ proxy.location }} {
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-Proto http;
      proxy_set_header X-Url-Scheme $scheme;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $http_host;
      proxy_set_header X-NginX-Proxy true;
      proxy_redirect off;
      proxy_pass http://{{ proxy.name }};
      break;
}
{% endfor %}
{% endif %}
{% if nginx_server_static %}
   location / {
       try_files $url $url/ =404;
}
{% endif %}
}

4、ansible变量文件

cat vars/main.yml 
nginx_server_name: www.testnginx.com
nginx_web_root: /data/html/
nginx_proxies:
- name: suspicious
  location: /
  port: 1234
- name: suspicious-api
  location: /api
  port: 4567

5、ansible主playbook文件

#cat nginx_test.yml 
##The first roles
- name: Nginx Proxy Server's Config Dynamic Create
  hosts: "10.0.90.25:10.0.90.26"
  remote_user: root
  vars:
    nginx_use_proxy: true
    nginx_ssl_cert_name: ifa.crt
    nginx_ssl_cert_key: ifa.key
    nginx_use_auth: true
    project_name: suspicious
    nginx_server_static: true
  gather_facts: true
  roles:
     -  role: nginx_conf
##The second roles
- name: Nginx WebServer's Config Dynamic Create
  hosts: 10.0.90.27
  remote_user: root
  vars:
    nginx_use_proxy: false
    nginx_ssl_cert_name: ifa.crt
    nginx_ssl_cert_key: ifa.crt
    nginx_use_auth: false
    project_name: suspicious
    nginx_server_static: false
  gather_facts: false
  roles:
     -  role: nginx_conf

6、测试并执行:

#ansible-playbook nginx_test.yml --syntax-check
playbook: nginx_test.yml

执行:
# ansible-playbook nginx_test.yml
PLAY [Nginx Proxy Server's Config Dynamic Create] ******************************

TASK [setup] *******************************************************************
ok: [10.0.90.25]
ok: [10.0.90.26]

TASK [nginx_conf : include] ****************************************************
included: /etc/ansible/roles/nginx_conf/tasks/file.yml for 10.0.90.25, 10.0.90.26

TASK [nginx_conf : nginx.j2 template transfer example] *************************
changed: [10.0.90.26]
changed: [10.0.90.25]

PLAY [Nginx WebServer's Config Dynamic Create] *********************************

TASK [nginx_conf : include] ****************************************************
included: /etc/ansible/roles/nginx_conf/tasks/file.yml for 10.0.90.27

TASK [nginx_conf : nginx.j2 template transfer example] *************************
changed: [10.0.90.27]

PLAY RECAP *********************************************************************
10.0.90.25                 : ok=3    changed=1    unreachable=0    failed=0   
10.0.90.26                 : ok=3    changed=1    unreachable=0    failed=0   
10.0.90.27                 : ok=2    changed=1    unreachable=0    failed=0

7、查看检测执行结果

到Nginx Proxy 服务器查看配置文件

#cat nginx.conf.template 
upstream suspicious
   #server 127.0.0.1:1234;
   server 10.0.90.26:1234;
}
upstream suspicious-api
   #server 127.0.0.1:4567;
   server 10.0.90.26:4567;
}
server {
    listen 80;
    servername www.testnginx.com;
    access_log off;
    error_log /etc/nginx/nginx_error.log;
    rewrite ^ https://$server_name$request_uri? permanent;
}
server {
    listen 443 ssl;
    server_name www.testnginx.com;
    ssl_certificate /etc/nginx/ssl/ifa.crt;
    ssl_certificate_key /etc/nginx/ssl/ifa.key;
    root /data/html/;
    index index.html index.html;
   auth_basic  "Restricted";
   auth_basic_user_file /etc/nginx/suspicious.htpasswd;
   location / {
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-Proto http;
      proxy_set_header X-Url-Scheme $scheme;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $http_host;
      proxy_set_header X-NginX-Proxy true;
      proxy_redirect off;
      proxy_pass http://suspicious;
      break;
}
   location /api {
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-Proto http;
      proxy_set_header X-Url-Scheme $scheme;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $http_host;
      proxy_set_header X-NginX-Proxy true;
      proxy_redirect off;
      proxy_pass http://suspicious-api;
      break;
}
   location / {
       try_files $url $url/ =404;
}
}

到Nginx Web 服务器上查看配置文件

#cat nginx.conf.template 
server {
    listen 80;
    servername www.testnginx.com;
    access_log off;
    error_log /etc/nginx/nginx_error.log;
    rewrite ^ https://$server_name$request_uri? permanent;
}
server {
    listen 443 ssl;
    server_name www.testnginx.com;
    ssl_certificate /etc/nginx/ssl/ifa.crt;
    ssl_certificate_key /etc/nginx/ssl/ifa.crt;
    root /data/html/;
    index index.html index.html;
}

到这里,就结束了。用同样的模板通过简单的if和变量设置就可以完成不同类型主机的Nginx conf配置,所以一方面在了解Ansible强大的模板功能的同时,也需要看到模板质量的重要性。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容

  • 本文作者: wuXing QQ: 1226032602 E-mail: 1226032602@qq.com ht...
    五行哥阅读 2,394评论 0 0
  • 一.ansible (1) ansible: ansible是一款新出现的自动化运维系统,基于python开发并集...
    楠人帮阅读 1,937评论 0 8
  • 运维核心工作: 操作系统安装(物理机、虚拟机)--> 应用程序包部署(安装、配置、服务启动 )--> 批量操作 -...
    Net夜风阅读 1,508评论 0 4
  • ansible介绍ansible常用模块使用playbooktemplates,模板条件测试和循环迭代roles,...
    哈喽别样阅读 1,405评论 0 3
  • 1)安装2)常用模块3)inventory4)playbook(role\tag\template)5) yaml...
    秦记阅读 4,137评论 2 5