ansible安装
1,安装相关的扩展
yum install -y http://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
2,直接yum安装
yum -y install ansible
配置清单
清单的默认路径为/etc/ansible/hosts,以下格式都是受支持的
#直接写IP地址,管理麻烦
192.168.5.117
192.168.4.120
192.168.5.91
#[xxxx]分组的意思名字可以见名知意
#将主机分组
[webserver]
192.168.5.117
192.168.4.120
#指定端口号,因为ansible是基于ssh的有时候为了安全会改掉默认的22端口,这时候我们这里需要指定端口
[dbserver]
192.168.5.117:2222
192.168.4.120:2333
#基于ip段甚至域名
[staticserver]
192.168.5.117
192.168.4.12[0:2]
db-[a-z].example.com
ansible是通过ssh实现配置管理远程主机,我们可以配置基于ansible段密钥认证的方式连和各个节点建立关系,我这里做测试就一直敲回车生成了,但是为了安全建议加密
ansible <host-pattern> [-m module_name] [-a args]
ansible +被管理的主机(ALL) +模块 +参数
--version 显示版本
-m module 指定模块,默认为command
-v 详细过程 –vv -vvv更详细
--list-hosts 显示主机列表,可简写 --list
-k, --ask-pass 提示输入ssh连接密码,默认Key验证
-C, --check 检查,并不执行
-T, --timeout=TIMEOUT 执行命令的超时时间,默认10s
-u, --user=REMOTE_USER 执行远程执行的用户
-b, --become 代替旧版的sudo切换
--become-user=USERNAME 指定sudo的runas用户,默认为root
-K, --ask-become-pass 提示输入sudo时的口令
ansible主机上生成密钥对
ssh-keygen #生成密钥对
ssh-copy-id 192.168.5.117 #拷贝到各个节点
我这里的清单如下,后面没有特殊说明都是以本清单进行测试
[webserver]
192.168.5.117
192.168.4.120
[dbserver]
192.168.5.117
192.168.5.91
使用ping模块测试是否可以跳过密码进行连接
[root@localhost ~]# ansible -m ping webserver
192.168.5.117 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"ping": "pong"
}
192.168.4.120 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"ping": "pong"
}
ansible的主机模式
all :表示清单里所有主机
[root@localhost ~]# ansible all -m ping
192.168.5.91 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"ping": "pong"
}
192.168.5.117 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"ping": "pong"
}
192.168.4.120 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"ping": "pong"
}
星号 * :通配符
[root@localhost ~]# ansible *server -m ping
192.168.5.117 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"ping": "pong"
}
192.168.5.91 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"ping": "pong"
}
192.168.4.120 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"ping": "pong"
}
[root@localhost ~]# ansible 192.168.5.* -m ping
192.168.5.117 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"ping": "pong"
}
192.168.5.91 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"ping": "pong"
}
或关系,即webserver或者dbserver中存在的主机
[root@localhost ~]# ansible webserver:dbserver -m ping
192.168.4.120 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"ping": "pong"
}
192.168.5.117 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"ping": "pong"
}
192.168.5.91 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"ping": "pong"
}
逻辑与,即存在于webserver并且存在于dbserver中的主机
[root@localhost ~]# ansible "webserver:&dbserver" -m ping
192.168.5.117 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"ping": "pong"
}
逻辑非,即在webserver中但是不在dbserver中
[root@localhost ~]# ansible 'webserver:&dbserver' -m ping
192.168.5.117 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"ping": "pong"
}
甚至还支持正则表达式这里就不贴了
ansible命令执行过程
1,加载自己的配置文件默认是/etc/ansible/ansible.cfg
2,加载自己对应的模块文件
3,通过ansible将模块或命令生成对应的临时py文件,并将该文件传输到远程服务器的对应执行用户的加目录下/root/.ansible/tmp/ansible-tmp-数字/xxx.py文件
4,给文件加X权限执行
5,执行并返回结果
6,删除临时py文件,sleep 0退出
这个过程可以使用-vvv查看详细过程
执行状态的三种颜色
绿色:执行成功没有对目标主机做改变
黄色:执行成功对目标主机做了改变
红色:执行失败
ansible常用模块
- command模块,用于在远程主机上执行命令,command是默认模块,注意此模块对管道,特殊符号支持没那么友好,执行带特殊符号或者管道的命令会打印出命令不执行,对于这个问题我们可以使用另一个模块就是shell模块。
举个使用的例子如下
#查看目标主机下/data
[root@localhost ~]# ansible webserver -m command -a 'ls /data'
相关选项示例
1. removes 文件不存在就不执行
[root@localhost ~]# ansible webserver -a 'removes=/etc/fs cat /etc/fstab'
2. creates 文件存在将不执行,文件不存在将执行
[root@localhost ~]# ansible webserver -a 'creates=/etc/fs cat /etc/fstab'
3.chdir 先切换到对应文件夹再执行相关操作
[root@localhost ~]# ansible webserver -a 'chdir=/data ls '
- shell模块 此模块和command类似,但是对于带有特殊符号的命令更加友好
下面几个例子:
查看远程主机名
[root@localhost ~]# ansible webserver -m shell -a 'echo $HOSTNAME'
- script模块 用来将本机脚本在远程主机上执行
下面一个例子:
[root@localhost data]# ansible webserver -m script -a '/data/1.sh'
- Copy:从主控端复制文件到远程主机
src : 源文件 指定拷贝文件的本地路径 (如果有/ 则拷贝目录内容,比拷贝目录本身)
dest: 指定目标路径
mode: 设置权限
backup: 备份源文件
content: 代替src 指定本机文件内容,生成目标主机文件
下面举两个关于这些参数的例子
#将本地1.sh文件发送到远程主机并更改所属组和权限并对原来主机上的1.sh进行备份
[root@localhost data]# ansible webserver -m copy -a "src=/data/1.sh dest=/data/ showner=nginx mode=600 backup=yes"
#将aaaaaa直接写入1.txt并发送到远程主机
[root@localhost data]# ansible webserver -m copy -a "content='aaaaaa' dest=/tmp/1.txt"
- Fetch:从远程主机提取文件至主控端,copy相反,目前不支持目录,可以先打包,再提取文件,不过打包文件有一个指定的模块是unarchive,在本地会生成每个被管理主机不同编号的目录,不会发生文件名冲突
打包一个文件举例:
[root@localhost data]# ansible webserver -m fetch -a 'src=/var/log/cron dest=/data/'
- file:文件相关的模块,举几个例子如下
#创建一个f3文件,path是文件路径,他还有name,dest两个别名,效果是一样的
[root@localhost log]# ansible webserver -m file -a 'path=/data/f3 state=touch'
#删除f3文件,absent表示删除,否定的意思,后面好多模块都会用到
[root@localhost log]# ansible webserver -m file -a 'path=/data/f3 state=absent'
#创建一个目录
[root@localhost log]# ansible webserver -m file -a 'path=/data/dir1 state=directory'
#删除目录
[root@localhost log]# ansible webserver -m file -a 'path=/data/dir1 state=absent'
#创建一个软连接,src表示源文件,path表示软连接的路径,state=link是创建软连接
[root@localhost log]# ansible webserver -m file -a 'src=/data path=/tmp/data.link state=link'
#删除软连接
[root@localhost log]# ansible webserver -m file -a 'path=/tmp/data.link state=absent'
#修改文件的属性
ansible webserver -m file -a 'path=/tmp/data.link owner=nginx mode=755'
- Hostname:管理主机名
#修改单台主机名
ansible 192.168.5.231 -m hostname 'name=daili01'
- Cron:计划任务
#支持时间:minute(分钟),hour(小时),day(天),month(月),weekday(星期)
配置每周1,3,5每分钟执行广播
job是要执行的命令,注意命令里有空格要用双引号引起来,name取个名字
ansible webserver -m cron -a 'minute=* weekday=1,3,5 job="/usr/bin/wall FBI warning" name=warningcron'
#disabled=true注释掉定时任务
ansible webserver -m cron -a 'disabled=true job="/usr/bin/wall FBI warning" name=warningcron'
#disabled=fasle启用定时任务
ansible webserver -m cron -a 'disabled=false job="/usr/bin/wall FBI warning" name=warningcron'
#state=absent删除定时任务
ansible webserver -m cron -a 'job="/usr/bin/wall FBI warning" name=warningcron state=absent'
- Yum:管理包
#安装vsftpd,其实后面还有个state参数,这个参数默认是present,安装的意思,这里省略没写
ansible webserver -m yum -a 'name=vsftpd'
#安装多个包,用逗号隔开
ansible webserver -m yum -a 'name=vsftpd,httpd,lrzsz'
#查看安装的所有程序列表
ansible webserver -m yum -a 'list=installed'
#卸载某个软件包
ansible webserver -m yum -a 'name=vsftpd state=absent'
#卸载多个包
ansible webserver -m yum -a 'name=vsftpd,httpd,lrzsz state=absent'
#更新yum缓存
ansible webserver -m yum -a 'update_cache=yes'
- Service:管理服务
#启动nginx服务,并设置开机自启
ansible webserver -m service -a 'name=nginx state=started enabled=yes'
#停止nginx服务
ansible webserver -m service -a 'name=nginx state=stopped'
#重启nginx服务
ansible webserver -m service -a 'name=nginx state=restarted'
#重新加载nginx服务
ansible webserver -m service -a 'name=nginx state=reloaded'
- User:管理用户
#创建一个用户,
home 指定家目录路径
system 指定系统账号
uid 指定uid
group 指定主组
groups 指定辅助组
remove 清除账户
shell 指定shell类型
ansible webserver -m user -a 'name=zabbix system=yes home=/data/zabbix groups=root,bin uid=80'
#删除用户,remive 是否删除用户家目录,yes表示删除家目录
ansible webserver -m user -a 'name=zabbix state=absent remove=yes'
- Group:管理组
#创建一个组
ansible webserver -m group -a 'name=zabbix system=yes'
#删除组
ansible webserver -m group -a 'name=zabbix system=ansent'
ansible系列命令
- ansible-galaxy
连接 https://galaxy.ansible.com 下载相应的roles(角色)
#安装一个角色
ansible-galaxy install geerlingguy.nginx
#查看所有以安装的角色
ansible-galaxy list
#删除角色
ansible-galaxy remove geerlingguy.nginx
- ansible-playbook
运行一个剧本
运行一个剧本(playbook)例如
ansible-playbook hell0.yaml
- ansible-vault
功能:管理加密解密yml文件
ansible-vault [create|decrypt|edit|encrypt|rekey|view]
ansible-vault encrypt hello.yml 加密
ansible-vault decrypt hello.yml 解密
ansible-vault view hello.yml 查看
ansible-vault edit hello.yml 编辑加密文件
ansible-vault rekey hello.yml 修改口令
ansible-vault create new.yml 创建新文件
playbook
playbook是由一个或多个"play"组成的列表。但是我们一般一个yml文件里只写一个play
play的主要功能在于将预定义的一组主机,装扮成事先通过ansible中的task定义好的角色。Task实际是调用ansible的一个module,将多个play组织在一个playbook中,即可以让它们联合起来,按事先编排的机制执行预定义的动作
Playbook采用YAML语言编写
YAML介绍
YAML是一个可读性高的用来表达资料序列的格式。
YAML参考了其他多种语言,包括:XML、C语言、Python、Perl以及电子邮件格式RFC2822等。
Clark Evans在2001年在首次发表了这种语言,另外Ingy döt Net与Oren Ben-Kiki也是这语言的共同设计者
YAML Ain't Markup Language,即YAML不是XML。
不过,在开发的这种语言时,YAML的意思其实是:"Yet Another Markup Language"(仍是一种标记语言)
特性
YAML的可读性好
YAML和脚本语言的交互性好
YAML使用实现语言的数据类型
YAML有一个一致的信息模型
YAML易于实现
YAML可以基于流来处理
YAML表达能力强,扩展性好
更多的内容及规范参见:http://www.yaml.org
YAML语法简介
> 在单一档案中,可用连续三个连字号(-)区分多个档案。另外,还有选择性的连续三个点号( ... )用来表示档案结尾
> 次行开始正常写Playbook的内容,一般建议写明该Playbook的功能
> 使用#号注释代码
> 缩进必须是统一的,不能空格和tab混用
> 缩进的级别也必须是一致的,同样的缩进代表同样的级别,程序判别配置的级别是通过缩进结合换行来实现的
> YAML文件内容是区别大小写的,k/v的值均需大小写敏感
> 多个k/v可同行写也可换行写,同行使用:分隔
> v可是个字符串,也可是另一个列表[]
> 一个完整的代码块功能需最少元素需包括 name 和 task
> 一个name只能包括一个task
> YAML文件扩展名通常为yml或yaml
List:列表,其所有元素均使用“-”打头
列表代表同一类型的元素
示例:
# A list of tasty fruits
- Apple
- Orange
- Strawberry
- Mango
Dictionary:字典,通常由多个key与value构成 键值对
示例:
---
# An employee record
name: Example Developer
job: Developer
skill: Elite
也可以将key:value放置于{}中进行表示,用,分隔多个key:value
示例:
---
# An employee record
{name: Example Developer, job: Developer, skill: Elite}
YAML的语法和其他高阶语言类似,并且可以简单表达清单、散列表、标量等数据结构。
其结构(Structure)通过空格来展示,序列(Sequence)里的项用"-"来代表,Map里的键值对用":"分隔
示例
name: John Smith
age: 41
gender: Male
spouse:
name: Jane Smith
age: 37
gender: Female
children:
- name: Jimmy Smith
age: 17
gender: Male
- name: Jenny Smith
age 13
gender: Female
Playbook核心元素
Hosts 执行的远程主机列表(应用在哪些主机上)
Tasks 任务集
这里有两种格式:
(1) action: module arguments
(2) module: arguments 建议使用 模块: 参数
注意:shell和command模块后面跟命令,而非key=value
Variables 内置变量或自定义变量在playbook中调用
Templates模板 可替换模板文件中的变量并实现一些简单逻辑的文件
Handlers和notify结合使用,由特定条件触发的操作,满足条件方才执行,否则不执行
tags标签 指定某条任务执行,用于选择运行playbook中的部分代码。
ansible具有幂等性,因此会自动跳过没有变化的部分,
即便如此,有些代码为测试其确实没有发生变化的时间依然会非常地长。
此时,如果确信其没有变化,就可以通过tags跳过此些代码片断
ansible-playbook -t tagsname useradd.yml
下面来个示例,
[root@localhost ansible]# cat touch.yaml
---
- hosts: webserver #指明在哪个主机组上面执行
remote_user: root #指定远程主机上的用户
tasks:
- name: create new file
file: name=/data/newfile state=touch #创建一个文件
- name: create new user
user: name=test system=yes shell=/sbin/nologin #创建一个用户
- name: install package
yum: name=httpd #安装httpd
- name: copy html
copy: src=/var/www/html/index.php dest=/var/www/html #把本机的页面文件拷贝到远程主机
- name: start service
service: name=httpd state=started enabled=yes #启动httpd服务,并设为开机自启
对于刚写好的yml文件为了检查正确性可以使用-C参数,比如
ansible-playbook -C touch.yaml #测试,不会对远程主机做任何更改
上面是一个简单的yaml文件,我们都清楚,因为yaml是从上到下依次执行,加入上一个模块执行失败下面的就不会继续执行,为了解决这个问题我们可以用以下的方式
tasks:
- name: run this command and ignore the result
shell: /usr/bin/somecommand || /bin/true
转错为正 如果命令失败则执行 true
或者使用ignore_errors来忽略错误信息
tasks:
- name: run this command and ignore the result
shell: /usr/bin/somecommand
ignore_errors: True 忽略错误
我们做一个场景,比如下面这个例子
[root@localhost ansible]# cat touch.yaml
---
- hosts: webserver
remote_user: root
tasks:
- name: install package
yum: name=httpd
- name: copy html
copy: src=/etc/httpd/conf/httpd.conf dest=/etc/httpd/conf/ #假如远程主机的httpd服务在运行,这里我如果把源配置文件的端口修改为8080再拷贝到远程主机,那么远程主机配置文件发生改变,但是因为远程主机已经在运行了,所以并不会重新加载配置文件,也就达不到监听8080端口的目的,下面我们修改这个yml文件解决这个问题
- name: start service
service: name=httpd state=started
场景问题解决:
[root@localhost ansible]# cat touch.yaml
---
- hosts: webserver
remote_user: root
tasks:
- name: install package
yum: name=httpd
tags: inshttpd
- name: copy html
copy: src=/etc/httpd/conf/httpd.conf dest=/etc/httpd/conf/
notify: restart service #可以理解为上面的模块操作的远程主机发生改变后,notify才会调用handlers的触发器,如果没有发生变化就不调用触发器,注意这里和 handlers里name里的一一对应,支持多对多
- name: start service
service: name=httpd state=started
handlers: #可以理解为触发器,被触发后执行下面的操作
- name: restart service
serveice: name=httpd state=restarted
那么我们如果想单纯的执行一个模块进行测试呢,这里要用tags来给模块打标签,比如上面的tags标签,我可以用下面的命令来单独执行安装模块
ansible-playbook -t inshttpd touch.yaml
Playbook中变量的使用
变量名:仅能由字母、数字和下划线组成,且只能以字母开头
变量来源:
1> ansible setup facts 远程主机的所有变量都可直接调用 (系统自带变量)
setup模块可以实现系统中很多系统信息的显示
可以返回每个主机的系统信息包括:版本、主机名、cpu、内存
ansible all -m setup -a 'filter="ansible_nodename"' 查询主机名
ansible all -m setup -a 'filter="ansible_memtotal_mb"' 查询主机内存大小
ansible all -m setup -a 'filter="ansible_distribution_major_version"' 查询系统版本
ansible all -m setup -a 'filter="ansible_processor_vcpus"' 查询主机cpu个数
2> 在/etc/ansible/hosts(主机清单)中定义变量
普通变量:主机组中主机单独定义,优先级高于公共变量(单个主机 )
公共(组)变量:针对主机组中所有主机定义统一变量(一组主机的同一类别)
3> 通过命令行指定变量,优先级最高
ansible-playbook –e varname=value
4> 在playbook中定义
vars:
- var1: value1
- var2: value2
5> 在独立的变量YAML文件中定义
6> 在role中定义
我们分别对上面的几种方式做测试
我们用几种不通的变量书写方法来安装httpd服务
1. 使用-e参数在执行playbook的命令行下对yml文件中的变量赋值,优先级最高
yml文件如下:
[root@localhost ansible]# cat app.yml
---
- hosts: webserver
remote_user: root
tasks:
- name: install package
yum: name={{ package }}
- name: start service
service: name={{ package }} state=started enabled=yes
对于这种变量书写方法我们用-e参数进行复制
ansible-playbook -e package=httpd app.yml
2. 在playbook中定义,yml文件如下
[root@localhost ansible]# cat app.yml
---
- hosts: webserver
remote_user: root
vars:
- package: httpd #定义一个变量
tasks:
- name: install package
yum: name={{ package }}
- name: start service
service: name={{ package }} state=started enabled=yes
3. 在/etc/ansible/hosts(主机清单)中定义变量
相关清单变量如下
[webserver]
192.168.5.231 http_port=81 #对单台主机定义变量
192.168.4.120 http_port=82
[webserver:vars] #对整个主机组共用一套变量
nodename=www
domainname=zhangshao.com
下面我们要调用我们定义的这些变量更改我们的主机名
相关yml内容如下
[root@localhost ansible]# cat hostname.yml
---
- hosts: webserver
remote_user: root
tasks:
- name: set hostname
hostname: name={{nodename}}{{http_port}}.{{domainname}}
执行这个yml即可更改远程主机名为www81.zhangshao.com和www82.zhangshao.com
ansible-playbook hostname.yml
4. 我们还可以把变量单独的写入到一个文件中,我们在playbook中进行调用,注意,这种方式定义的变量只能在playbook中调用,下面是一个例子
变量文件如下:
[root@localhost ansible]# cat vars.yml
var1: httpd
var2: vsftpd
下面我们编写一个yml文件尝试调用这两个变量分别安装httpd服务和创建一个.log文件
[root@localhost ansible]# cat testvars.yml
---
- hosts: webserver
remote_user: root
vars_files: #这里调用我们定义的变量文件
- vars.yml
tasks:
- name: install packsge
yum: name={{ var1 }}
- name: create file
file: name=/data/{{ var2 }}.log state=touch
模板templates
文本文件,嵌套有脚本(使用模板编程语言编写) 借助模板生成真正的文件
Jinja2语言,使用字面量,有下面形式
字符串:使用单引号或双引号
数字:整数,浮点数
列表:[item1, item2, ...]
元组:(item1, item2, ...)
字典:{key1:value1, key2:value2, ...}
布尔型:true/false
算术运算:+, -, *, /, //, %, **
比较操作:==, !=, >, >=, <, <=
逻辑运算:and,or,not
流表达式:For,If,When
Jinja2相关
字面量
1> 表达式最简单的形式就是字面量。字面量表示诸如字符串和数值的 Python对象。如“Hello World”
双引号或单引号中间的一切都是字符串。
2> 无论何时你需要在模板中使用一个字符串(比如函数调用、过滤器或只是包含或继承一个模板的参数),如4242.23
3> 数值可以为整数和浮点数。如果有小数点,则为浮点数,否则为整数。在Python 里, 42 和 42.0 是不一样的
Jinja2:算术运算
算术运算
Jinja 允许你用计算值。这在模板中很少用到,但为了完整性允许其存在
支持下面的运算符
+:把两个对象加到一起。
通常对象是素质,但是如果两者是字符串或列表,你可以用这 种方式来衔接它们。
无论如何这不是首选的连接字符串的方式!连接字符串见 ~ 运算符。 {{ 1 + 1 }} 等于 2
-:用第一个数减去第二个数。 {{ 3 - 2 }} 等于 1
/:对两个数做除法。返回值会是一个浮点数。 {{ 1 / 2 }} 等于 {{ 0.5 }}
//:对两个数做除法,返回整数商。 {{ 20 // 7 }} 等于 2
%:计算整数除法的余数。 {{ 11 % 7 }} 等于 4
*:用右边的数乘左边的操作数。 {{ 2 * 2 }} 会返回 4 。
也可以用于重 复一个字符串多次。{{ ‘=’ * 80 }} 会打印 80 个等号的横条
**:取左操作数的右操作数次幂。 {{ 2**3 }} 会返回 8
Jinja2
比较操作符
== 比较两个对象是否相等
!= 比较两个对象是否不等
> 如果左边大于右边,返回 true
>= 如果左边大于等于右边,返回 true
< 如果左边小于右边,返回 true
<= 如果左边小于等于右边,返回 true
逻辑运算符
对于 if 语句,在 for 过滤或 if 表达式中,它可以用于联合多个表达式
and
如果左操作数和右操作数同为真,返回 true
or
如果左操作数和右操作数有一个为真,返回 true
not
对一个表达式取反(见下)
(expr)
表达式组
['list', 'of', 'objects']:
一对中括号括起来的东西是一个列表。列表用于存储和迭代序列化的数据。
例如 你可以容易地在 for循环中用列表和元组创建一个链接的列表
<ul>
{% for href, caption in [('index.html', 'Index'), ('about.html', 'About'), ('downloads.html',
'Downloads')] %}
<li><a href="{{ href }}">{{ caption }}</a></li>
{% endfor %}
</ul>
('tuple', 'of', 'values'):
元组与列表类似,只是你不能修改元组。
如果元组中只有一个项,你需要以逗号结尾它。
元组通常用于表示两个或更多元素的项。更多细节见上面的例子
{'dict': 'of', 'key': 'and', 'value': 'pairs'}:
Python 中的字典是一种关联键和值的结构。
键必须是唯一的,并且键必须只有一个 值。
字典在模板中很少使用,罕用于诸如 xmlattr() 过滤器之类
true / false:
true 永远是 true ,而 false 始终是 false
template 的使用
这里设想一个场景,比如我想拷贝一份nginx的配置文件,但是我想根据不同的服务器配置启用不同的nginx进程数,这个是copy做不到的,那么我们就可以使用template(模板)来实现,具体实现看下面的例子
我们复制一份nginx.conf文件到和yaml/yml平级的目录下(不是绝对这样,但是建议这样),且命名为 .j2 结尾,修改这个模板,调用系统自带的模块变量
这里为了测试只更改这个地方
下面我们来编写一个yml文件,来将nginx配置文件使用这个模板拷贝到远程主机,使nginx线程数变为cpu核心数的两倍,yml内容如下
[root@localhost ansible]# cat tempnginx.yml
---
- hosts: webserver
remote_user: root
tasks:
- name: template config to remote hosts
template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf #template功能类似于copy但是不一样
我远程主机原来核心数分别为1和2,ansible-playbook后查看内容如下
[root@localhost ansible]# ansible webserver -m shell -a 'cat /etc/nginx/nginx.conf |grep 'worker_processes''
192.168.4.120 | CHANGED | rc=0 >>
worker_processes 4;
192.168.5.231 | CHANGED | rc=0 >>
worker_processes 2;
when 实现条件判断
这里我们设想一个场景,还是上面拷贝nginx配置文件到远程主机,但是我们考虑不通版本的centos配置文件可能不同,现在我想实现如果是centos6我就拷贝centos6的配置文件,centos7我就拷贝centos7的配置文件,要怎么实现呢,when可以完美解决这个问题
我们准备centos6的配置文件为nginx.conf.c6.j2,centos7的配置文件为nginx.conf.c7.j2,并且都和yml同级目录,现在我想把centos6的配置文件拷贝到centos6的主机上,centos7的配置文件拷贝到centos7的主机上,使用when,编写如下yml文件并执行,内容如下
tasks:
- name: install conf file to centos7
template: src=nginx.conf.c7.j2 dest=/etc/nginx/nginx.conf
when: ansible_distribution_major_version == "7" #ansible_distribution_major_version查看版本的变量
- name: install conf file to centos6
template: src=nginx.conf.c6.j2 dest=/etc/nginx/nginx.conf
when: ansible_distribution_major_version == "6"
迭代:with_items
迭代:当有需要重复性执行的任务时,可以使用迭代机制,类似于循环
> 对迭代项的引用,固定变量名为"item"
> 要在task中使用with_items给定要迭代的元素列表
下面举个例子,比如我要创建三个文件,可以使用with_items来实现
[root@localhost ansible]# cat file.yml
---
- hosts: webserver
remote_user: root
tasks:
- name: create some file
file: name=/data/{{ item }} state=touch #item是一个系统变量会调用with_items里定义的列表
with_items:
- file1
- filr2
- file3
还可以嵌套子变量
比如我要创建三个用户,让他们分别属于三个不同的组
相关yml文件如下
[root@localhost ansible]# cat user.yml
---
- hosts: webserver
remote_user: root
tasks:
- name: create some groups
group: name={{ item }}
with_items:
- g1
- g2
- g3
- name: create some users
user: name={{ item.name }} group={{ item.group }} #item.name分别调用 with_items里定义的三个name
with_items:
- { name: 'user1',group: 'g1' }
- { name: 'user2',group: 'g2' }
- { name: 'user3',group: 'g3' }
Playbook中template for循环
比如我这里想要通过模板将如下内容copy到目标主机可以使用如下模板和yml实现
#要在远程主机上实现的效果
server{
listen 81
}
server{
listen 82
}
server{
listen 83
}
#yml文件如下
[root@localhost ansible]# cat testfor.yml
---
- hosts: webserver
remote_user: root
vars:
liebiao:
- 81
- 82
- 83
tasks:
- name: copy conf
template: src=testfor1.conf.j2 dest=/data/testfor1.conf
#template使用for循环内容如下
[root@localhost ansible]# cat testfor1.conf.j2
{% for prot in liebiao %} #固定开始格式,prot为自定义的变量,liebiao为yml中定义的列表名
server{
listen {{ prot }}
}
{% endfor %} #必须以此格式结束
Playbook中template if嵌套
首先yml内容如下
[root@localhost ansible]# cat for1.yml
---
- hosts: webserver
remote_user: root
vars:
ports:
- web1:
port: 81
rootdir: /data/web1
- web1:
port: 82
name: web2.zhangshao.com
rootdir: /data/web2
- web1:
port: 83
rootdir: /data/web3
tasks:
- name: copy conf
template: src=for.conf.j2 dest=/data/for.conf
#template使用if嵌套循环内容如下
[root@localhost ansible]# cat for.conf.j2
{% for p in ports %}
server{
listen {{ p.port }}
{% if p.name is defined %} #当p.name被定义时执行if代码块中的内容
servername {{ p.name }}
{% endif %}
documentroot {{ p.rootdir }}
}
{% endfor %}
roles
roles
ansible自1.2版本引入的新特性,用于层次性、结构化地组织playbook。
roles能够根据层次型结构自动装载变量文件、tasks以及handlers等。
要使用roles只需要在playbook中使用include指令即可。
简单来讲,roles就是通过分别将变量、文件、任务、模板及处理器放置于单独的目录中,
并可以便捷地include它们的一种机制。
角色一般用于基于主机构建服务的场景中,但也可以是用于构建守护进程等场景中
复杂场景:建议使用roles,代码复用度高
变更指定主机或主机组
如命名不规范维护和传承成本大
某些功能需多个Playbook,通过includes即可实现
roles目录结构
每个角色,以特定的层级目录结构进行组织
roles目录结构:
playbook.yml 调用角色
roles/
project/ (角色名称)
tasks/
files/
vars/
templates/
handlers/
default/ 不常用
meta/ 不常用
Roles各目录作用
/roles/project/ :项目名称,有以下子目录
files/ :存放由copy或script模块等调用的文件
templates/:template模块查找所需要模板文件的目录
tasks/:定义task,role的基本元素,至少应该包含一个名为main.yml的文件;
其它的文件需要在此文件中通过include进行包含
handlers/:至少应该包含一个名为main.yml的文件;
其它的文件需要在此文件中通过include进行包含
vars/:定义变量,至少应该包含一个名为main.yml的文件;
其它的文件需要在此文件中通过include进行包含
meta/:定义当前角色的特殊设定及其依赖关系,至少应该包含一个名为main.yml的文件,
其它文件需在此文件中通过include进行包含
default/:设定默认变量时使用此目录中的main.yml文件
roles/appname 目录结构
tasks目录:至少应该包含一个名为main.yml的文件,其定义了此角色的任务列表;
此文件可以使用include包含其它的位于此目录中的task文件
files目录:存放由copy或script等模块调用的文件;
templates目录:template模块会自动在此目录中寻找Jinja2模板文件
handlers目录:此目录中应当包含一个main.yml文件,用于定义此角色用到的各handler;
在handler中使用include包含的其它的handler文件也应该位于此目录中;
vars目录:应当包含一个main.yml文件,用于定义此角色用到的变量;
meta目录:应当包含一个main.yml文件,用于定义此角色的特殊设定及其依赖关系;
ansible1.3及其以后的版本才支持;
default目录:为当前角色设定默认变量时使用此目录;应当包含一个main.yml文件
roles/example_role/files/ 所有文件,都将可存放在这里
roles/example_role/templates/ 所有模板都存放在这里
roles/example_role/tasks/main.yml: 主函数,包括在其中的所有任务将被执行
roles/example_role/handlers/main.yml:所有包括其中的 handlers 将被执行
roles/example_role/vars/main.yml: 所有包括在其中的变量将在roles中生效
roles/example_role/meta/main.yml: roles所有依赖将被正常登入
创建role
创建role的步骤
(1) 创建以roles命名的目录
(2) 在roles目录中分别创建以各角色名称命名的目录,如webservers等
(3) 在每个角色命名的目录中分别创建files、handlers、meta、tasks、templates和vars目录;
用不到的目录可以创建为空目录,也可以不创建
(4) 在playbook文件中,调用各角色
下面我们举个简单例子,我现在要装nginx这个服务,大致步骤可以分为:
创建nginx组
创建nginx用户
装包
复制模板配置文件
启动nginx
我们做实现就用这几个步骤来了解roles(角色)当然生产中没那这个例子用不到角色
下面我们按照上面的步骤从头开始创建一个角色
#创建nginx组
[root@localhost tasks]# cat group.yml
- name: create group
group: name=nginx gid=80
#创建nginx用户
[root@localhost tasks]# cat user.yml
- name: create user
user: name=nginx uid=80 group=nginx system=yes shell=/sbin/nologin
#装包
[root@localhost tasks]# cat yum.yml
- name: install package
yum: name=nginx
#创建模板
[root@localhost templates]# pwd
/ansible/roles/nginx/templates
[root@localhost templates]# cat nginx.conf.j2
#调用模板文件
[root@localhost tasks]# cat templ.yml
- name: copy conf
template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf
#调用上面我们写的yml模块
[root@localhost tasks]# cat main.yml
- include: group.yml
- include: user.yml
- include: yum.yml
- include: templ.yml
- include: start.yml
#最后我们编写需要执行的yml文件,注意这里要和角色目录同级
[root@localhost ansible]# cat nginx_role.yml
- hosts: webserver
remote_user: root
roles:
- role: nginx #这里是调用我们的角色
好了 这样我们的一个简单的用角色安装nginx的步骤就完成了,下面只需要用ansible-playbook执行就ok了