Ansible 自动化运维工具简单入门(三)
四、Ansible-playbook
1、YAML介绍
1)基础知识
-
YAML是一个可读性高的用来表达资料序列的格式。YAML参考了很多语言,包括C语言、Python、Perl德国,Clark Evens在2001年首次发表了这种语言,另外Ingy dot 和Oren Ben-Kiki也是共同设计者
-
YAML Ain’t Mrakup Language ,即YAML不是XML,不过在开发语言的时候,YAML的愿意是:"Yet Another Markup Language"(仍是一种标记语言)
-
特性
YAML的可读性好
YAML和脚本语言的交互性好
YAML使用实现语言的数据类型
YAML有一个一致的信息模型
YAML易于实现
YAML可以基于流来处理
YAML表达能力强,扩展性好
-
更多内容及规范参见http://www.yaml.org
2)语法简介
- 在单一档案中,可用连续三个---区分多个档案(习惯首行---)。另外,可以选择性的连续...来表示档案结尾
- 慈航开始正常些Playbook内容,一般建议写明该Playbook的功能
- 使用#号注释代码
- 缩进必须是统一的,空格和tab不能混用
- 缩进的级别必须是一致的,同样的缩进代表同样的级别,程序判别配置的级别是通过缩进结合换行来实现的
- YAML文件内容和Linux系统大小写判断方式保持一致,是区别大小写的,k/v的值均需大小写敏感
- k/v的值可同行业可以换行,同行使用:分割
- v可是字符串,也可以是另外一个表
- 一个完整的代码块所需最小的元素包括:name:task
- 一个name只能包括一个task
- YAML文件扩展名通常为yml或yaml
3)基础用法
-
List:列表,其所有元素均使用"-"开头
# A list of tasty fruits
- apple
- orange
- strawberry
- mango
-后面有空格
-
Dictonary:字典,通常由多个key和value构成
# An emplyoyee record
name: example developer
job: developer
skill: elite
也可以将key:value放置于{}内进行表示,用,分开多个
# An emplyoyee record
-
YAML语法和其他告诫语言类似,并且可以简单表达清单,散列表,标量等数据结构。其结构通过空格来展示,序列里面的项用-来代表,mao理的键值对用:隔开
name: John Smith
age: 40
gender: male
spourse:
name: Tom Smith
age: 37
gender: female
children:
- name: jimmy Smith
- age: 17
- name: kitty Smith
- age: 12
2、Playbook基础理论
1)简介
- playbook 是由一个或多个play组成的 列表
- play的主要功能在于将直线归并为一组的主机装扮实现通过ansible中的task定义好的角色。从根本来讲,所谓的task无非是调用ansible的一个module。将多个play组织在一个playbook内,即可以让它们联动起来按实现编排的机制唱一台大戏
- playbook采用YAML语言编写
2)工作流程
3 、Playbook核心元素
-
Hosts 执行的远程主机列表
-
Tasks 任务集
-
Varniables 内置变量或自定义变量在playbook中调用
-
Templates 模板,可替换模板中的变量并实现一些简单的逻辑的文件
-
Hanglers和notify结合使用,由特定条件触发的操作,满足条件方才执行,否则不执行
-
Tags 标签 制定某条任务执行,用户选择运行playbook中的部分代码,ansible具有幂等性,因此会自动跳过没有辩护的部分,即便如此,有的代码为测试其确实没有发生变化的时间依然会非常的长,此时确信其没有变化,就可以通过tags跳过这些代码片段
ansible-playbook -t tagsname useradd.yml
4、Hosts
-
playbook中的每一个play 的目的都是为了让某个或某些主机以某个特定身份执行任务,hosts用于制定要执行执行任务的主机,须事先定义在主机清单内
-
可以是如下形式:
one.exaple.com
one.exaple.com:two.exaple.com
172.16.111.7
172.16.111.*
-
web:db 两个组的并集
-
web:&app 两个组的交集
-
app:!db 在app且不在db组内
- hosts: web:db
实例:
[root@ansible ansible]# vim ./test.yml
---
- hosts: web
remote_user: root
tasks:
- name: create new file #创建文件
file: name=/data/file123 state=touch
- name: create new user #创建用户
user: name=test222 system=yes shell=/sbin/nologin
- name: install httpd #安装httpd
yum: name=httpd
- name: copy html #复制html
copy: src=/usr/share/httpd/noindex/index.html dest=/var/www/html/
- name: start service #开启httpd服务
service: name=httpd state=started enabled=yes
#执行前检查剧本是否正常
[root@ansible ansible]# ansible-playbook -C ./test.yml
PLAY [web] *********************************************************************
TASK [Gathering Facts] *********************************************************
ok: [172.16.111.8]
ok: [172.16.111.7]
TASK [create new file] *********************************************************
changed: [172.16.111.8]
changed: [172.16.111.7]
TASK [create new user] *********************************************************
changed: [172.16.111.7]
changed: [172.16.111.8]
TASK [remove httpd] ************************************************************
ok: [172.16.111.8]
ok: [172.16.111.7]
TASK [copy html] ***************************************************************
changed: [172.16.111.7]
changed: [172.16.111.8]
TASK [start service] ***********************************************************
ok: [172.16.111.7]
ok: [172.16.111.8]
PLAY RECAP *********************************************************************
172.16.111.7 : ok=6 changed=3 unreachable=0 failed=0
172.16.111.8 : ok=6 changed=3 unreachable=0 failed=0
测试没问题后执行,然后去被控端检查上述剧本中各项任务是否执行成功,验证。
5、Remote_user
remote_user:可用于Host和task中,也可以通过指定其通过sudo的方式在远程主机上执行任务,其可用于play全局或某服务;次在,甚至可以在sudo时使用sudo_user制定sudo时切换的用户
- hosts: web
remote_user: root
tasks:
- name: test connection
ping:
remote_user: white
sudo: yes #默认为root
sudo_user: wang #sudo为wang
6、Task
-
play的主题部分是task list。task list中的个任务按次序诸葛在hosts制定的所有主机下执行,即在所有主机上完成第一个任务后开始第二个。在运行紫霞而下某playbook时,如果中途发生错误,所有已执行任务都将回滚,因此,在更正playbook后重新执行一次即可。
-
task的目的是使用指定的参数执行模块,而在模块参数中可以使用变量。模块执行的是幂等的,这意味着多次之心是安全的,因为其结果均一致。
-
每个task都应该有其name,用于playbook的执行结果输出,建议内容尽可能的清晰描述任务步骤,如果未提供name,则action的结果将用于输出
-
task:任务列表
格式:action: module arguments
module: arguments 建议使用
注意:shell和command模块后面跟命令二不是key=value
某个任务的状态在运行后为changed时,可通过notify通知给相应的handles
任务可以通过tags打标签,而后可在ansible-playbook命令上使用-t制定进行调用
task:
- name: disable selinux
command: /sbin/setenforce 0
如果命令或脚本的退出吗不为0(命令执行失败),可以用如下方式替代:
tasks:
- name: run this command and ignore the result
shell: /usr/bin/command || /bin/true
或者使用ignore_errors来忽略错误信息:
tasks:
- name: run this command and ignore the result
shell: /usr/bin/command
ignore_errors: True
7、Playbook
- 运行playbook的方式
ansible-playbook <filename.yml> ...[options]
-
常见选项
-- check只检车可能发生的改变,不真正执行等于-C
--list-hosts 列出运行任务的主机
--limit 主机列表指着对主机列表中的主机执行
-v 显示过程 -vv -vvv更详细
-
实例
ansible-playbook file.yml --check
ansible-playbook file.yml
ansible-playbook file.yml --limit web
ansible-playbook file.yml --list-tasks
[root@ansible ~]# ansible-playbook ./ansible/test.yml --list-tasks playbook: ./ansible/test.yml play #1 (web): web TAGS: [] tasks: create new file TAGS: [] create new user TAGS: [] remove httpd TAGS: [] copy html TAGS: [] start service TAGS: [] [root@ansible ~]# ansible-playbook ./ansible/test.yml --list-hosts playbook: ./ansible/test.yml play #1 (web): web TAGS: [] pattern: [u'web'] hosts (2): 172.16.111.7 172.16.111.
实例:
httpd.tml
- hosts: web
remote_user: root
tasks:
- name: install httpd
yum: name=httpd state=present
- name: install configure file
copy: src=files/httpd.conf dest=/etc/httpd.conf
- name: start service
service: name-httpd state=started enable=yes
注意此时files使用的是相对路径,默认是当前用户家目录/下,没有files目录新建即可,把需要复制的文件复制到改文件夹内,YML脚本内即可使用相对路径
8、Handers
是task任务列表,这些task与前述的task没有本质的不同,用于当关注的资源发生变化时,才会采取一定的操作
9、Notify
此action可用于在每个play的最后被处罚,这样可以避免多次有改变每次都执行制定的操作,仅在所有的变化发生完成后一次性执行制定操作,在notify中累出的操作称为handler,也即notify中调用hander中定义的操作
[root@ansible ansible]# mkdir files
[root@ansible ansible]# cp /etc/httpd/conf/httpd.conf ./files
[root@ansible ansible]# vim httpd.yml
---
- hosts: all
remote_user: root
tasks:
- name: install httpd package
yum: name=httpd
- name: copy conf file
copy: src=files/httpd.conf dest=/etc/httpd/conf/ backup=yes
- name: start service
service: name=httpd state=started enabled=yes
#提前修改conf文件内容 用于验证 例如端口 修改为8008
[root@ansible ansible]# ansible-playbook -C httpd.yml
PLAY [all] *****************************************************************************************************************************************************************
TASK [Gathering Facts] *****************************************************************************************************************************************************
ok: [172.16.111.9]
ok: [172.16.111.7]
ok: [172.16.111.8]
TASK [install httpd package] ***********************************************************************************************************************************************
changed: [172.16.111.9]
changed: [172.16.111.7]
changed: [172.16.111.8]
TASK [copy conf file] ******************************************************************************************************************************************************
changed: [172.16.111.9]
changed: [172.16.111.7]
changed: [172.16.111.8]
TASK [start service] *******************************************************************************************************************************************************
changed: [172.16.111.9]
changed: [172.16.111.8]
changed: [172.16.111.7]
PLAY RECAP *****************************************************************************************************************************************************************
172.16.111.7 : ok=4 changed=3 unreachable=0 failed=0
172.16.111.8 : ok=4 changed=3 unreachable=0 failed=0
172.16.111.9 : ok=4 changed=3 unreachable=0 failed=0
#测试正常
执行后验证结果,查询被控端8008端口和80端口是否启动
[root@ansible ansible]# ansible all -m shell -a 'ss -lnt|grep :8080'
172.16.111.9 | FAILED | rc=1 >>
non-zero return code
172.16.111.8 | FAILED | rc=1 >>
non-zero return code
172.16.111.7 | FAILED | rc=1 >>
non-zero return code
[root@ansible ansible]# ansible all -m shell -a 'ss -lnt|grep :80'
172.16.111.9 | SUCCESS | rc=0 >>
LISTEN 0 128 :::8008 :::*
172.16.111.8 | SUCCESS | rc=0 >>
LISTEN 0 128 :::8008 :::*
172.16.111.7 | SUCCESS | rc=0 >>
LISTEN 0 128 :::8008 :::*
#查看可知80端口端口依然启动8008未启动,但是被控端都生成了httpd.conf的备份文件
此时我们需要做到的是,检测到conf文件有变化,服务自动重启,此时就需要handers介入
[root@ansible ansible]# vim httpd.yml
---
- hosts: all
remote_user: root
tasks:
- name: install httpd package
yum: name=httpd
- name: copy conf file
copy: src=files/httpd.conf dest=/etc/httpd/conf/ backup=yes
notify: restart service #触发器,放在需要监控的触发动作后,: 后面是执行的东走
- name: start service
service: name=httpd state=started enabled=yes
handlers:
- name: restart service
service: name=httpd state=restarted
此时最好再次修改httpd.conf 因为前次操作已经把httpd。conf文件已经修改了 ,修改端口为9527 ,由于安装过httpd,本次执行默认跳过安装,再次执行后查看端口
[root@ansible ansible]# ansible-playbook httpd.yml -C
PLAY [all] *****************************************************************************************************************************************************************
TASK [Gathering Facts] *****************************************************************************************************************************************************
ok: [172.16.111.9]
ok: [172.16.111.8]
ok: [172.16.111.7]
TASK [install httpd package] ***********************************************************************************************************************************************
ok: [172.16.111.8]
ok: [172.16.111.9]
ok: [172.16.111.7]
TASK [copy conf file] ******************************************************************************************************************************************************
changed: [172.16.111.9]
changed: [172.16.111.8]
changed: [172.16.111.7]
TASK [start service] *******************************************************************************************************************************************************
ok: [172.16.111.8]
ok: [172.16.111.9]
ok: [172.16.111.7]
RUNNING HANDLER [restart service] ******************************************************************************************************************************************
changed: [172.16.111.9]
changed: [172.16.111.7]
changed: [172.16.111.8]
PLAY RECAP *****************************************************************************************************************************************************************
172.16.111.7 : ok=5 changed=2 unreachable=0 failed=0
172.16.111.8 : ok=5 changed=2 unreachable=0 failed=0
172.16.111.9 : ok=5 changed=2 unreachable=0 failed=0
[root@ansible ansible]# ansible all -m shell -a 'ss -lnt|grep 80'
172.16.111.9 | FAILED | rc=1 >>
non-zero return code
172.16.111.8 | FAILED | rc=1 >>
non-zero return code
172.16.111.7 | FAILED | rc=1 >>
non-zero return code
[root@ansible ansible]# ansible all -m shell -a 'ss -lnt|grep 9527'
172.16.111.9 | SUCCESS | rc=0 >>
LISTEN 0 128 :::9527 :::*
172.16.111.7 | SUCCESS | rc=0 >>
LISTEN 0 128 :::9527 :::*
172.16.111.8 | SUCCESS | rc=0 >>
LISTEN 0 128 :::9527 :::*
可以看出,handlers和notify配合使用,类似于linux系统的inotify,监控行为
10、Targs
可以使用tags增加标签,在执行YML的时候可以指定标签使用
[root@ansible ansible]# vim httpd.yml
---
- hosts: all
remote_user: root
tasks:
- name: install httpd package
yum: name=httpd
tags: inhttpd
- name: copy conf file
copy: src=files/httpd.conf dest=/etc/httpd/conf/ backup=yes
notify: restart service
- name: start service
service: name=httpd state=started enabled=yes
tages: rshttpd
handlers:
- name: restart service
service: name=httpd state=restarted
[root@ansible ansible]# ansible-playbook -t rshttpd httpd.yml #-t 指定标签
[WARNING]: Ignoring invalid attribute: tages
PLAY [all] *****************************************************************************************************************************************************************
TASK [Gathering Facts] *****************************************************************************************************************************************************
ok: [172.16.111.9]
ok: [172.16.111.8]
ok: [172.16.111.7]
PLAY RECAP *****************************************************************************************************************************************************************
172.16.111.7 : ok=1 changed=0 unreachable=0 failed=0
172.16.111.8 : ok=1 changed=0 unreachable=0 failed=0
172.16.111.9 : ok=1 changed=0 unreachable=0 failed=0
ansible-playbook -t rshttpd,inhttpd httpd.yml #多个标签逗号隔开
多个动作可以共用一个标签(个人感觉类似索引,关键词)
11、vars
以上操作都是所有任务依次操作,所有主机都执行,不够灵活,需要引入变量,让不同的主机可以进行不同的操作,可以做条件供以后判断等操作
变量名:只能由字母、数字和下划线组成,且只能字母开头
变量来源:
1、ansible setup facts远程主机的所有变量都可以直接调用,支持通配符
ansible web -m setup -a 'filter=*address*'
2、在/etc/ansible/hosts中定义
普通变量:主机组中主机单独定义,优先级高于公共变量
公共变量:针对主机组中所有主机定义统一变量
#对所有主机的hostname可以修改 根据名字区分
[root@ansible ansible]# vim httpd1.yml
---
- hosts: web
remote_user: root
tasks:
- name: set hostname
hostname: name=www{{http_port}}.white.com
#此时这样执行,hostname均可以被修改,我们发现该hostname内 www和.white.com都是web内左右主机共用的 那就可以用在hosts内设置变量
[root@ansible ansible]# vim /etc/ansible/hosts
[web]
172.16.111.7
172.16.111.8
[web:vars] #新增web组变量
nodename=www
damainname=white.com
[root@ansible ansible]# vim httpd1.yml
#根据设置配置剧本
---
- hosts: all
remote_user: root
tasks:
- name: set hostname
hostname: name={{nodename}}{{http_port}}.{{domainname}}
~ [root@ansible ansible]# ansible-playbook httpd1.yml
3、通过命令行制定变量,优先级最高
ansible-playbook -e varname=value
[root@ansible ansible]# vim app.yml
---
- hosts: app
remote_user: root
tasks:
- name: install package
yum: name={{ pkname}} #设置变量名
- name: start service
service: name={{ pkname}} state=started enabled=yes #调用变量
[root@ansible ansible]# ansible-playbook -e 'pkname=vsftpd' app.yml #执行时需要 -e给变量赋值
#结果如下
PLAY [app] **********************************************************************************
TASK [Gathering Facts] **********************************************************************
ok: [172.16.111.8]
ok: [172.16.111.7]
ok: [172.16.111.9]
TASK [install package] *********************************************************************
ok: [172.16.111.9]
ok: [172.16.111.7]
ok: [172.16.111.8]
TASK [start service] ************************************************************************
ok: [172.16.111.9]
ok: [172.16.111.8]
ok: [172.16.111.7]
PLAY RECAP **********************************************************************************
172.16.111.7 : ok=3 changed=0 unreachable=0 failed=0
172.16.111.8 : ok=3 changed=0 unreachable=0 failed=0
172.16.111.9 : ok=3 changed=0 unreachable=0 failed=0
[root@ansible ansible]# ansible app -m shell -a 'ss -lnt|grep 21' #检查验证
172.16.111.9 | SUCCESS | rc=0 >>
LISTEN 0 32 :::21 :::*
172.16.111.7 | SUCCESS | rc=0 >>
LISTEN 0 32 :::21 :::*
172.16.111.8 | SUCCESS | rc=0 >>
LISTEN 0 32 :::21 :::*
装多个安装包可以设置多个变量
tasks:
- name: install package
yum: name={{ pkname1}} #设置变量名1
- name: install package
yum: name={{ pkname2}} #设置变量名2
[root@ansible ansible]# ansible-playbook -e 'pkname1=vsftpd pkname2=httpd' app.yml #多个变量
4、在playbook内定义
vars:
- var1: value1
- var2: value2
[root@ansible ansible]# vim app1.yml
---
- hosts: app
remote_user: root
vars:
- pkname1: httpd
- pkname2: vsftp
tasks:
- name: install package
yum: name={{ pkname1}}
yum: name={{ pkname2}}
- name: start service
service: name={{ pkname1}} state=started enabled=yes
service: name={{ pkname2}} state=started enabled=yes
[root@ansible ansible]# ansible-playbook app1.yml
#由于在脚本内已经定义了变量,直接执行即可
5、在role内定义
后面介绍
6、在独立的YML文件内定义
对于变量管理,由于变量有多重方式可以定义,不同人习惯会导致变量混乱等,故可以考虑吧变量放在同一文件内,使用的时候在文件内修改,剧本中调用该文件
[root@ansible ansible]# vim var.yml #配置变量文件
var1: httpd
var2: vsftpd
[root@ansible ansible]# vim testvars.yml
---
- hosts: web
remote_user: root
vars_files: #调用变量文件
- var.yml
tasks:
- name: install packages
yum: name={{ var1}}
- name: create file
file : name=/tmp/{{ var2}}.log state=touch
[root@ansible ansible]# ansible-playbook testvars.yml
[root@ansible ansible]# ansible web -m shell -a 'rpm -q httpd' #验证结果
[WARNING]: Consider using yum, dnf or zypper module rather than running rpm
172.16.111.8 | SUCCESS | rc=0 >>
httpd-2.4.6-80.el7.centos.1.x86_64
172.16.111.7 | SUCCESS | rc=0 >>
httpd-2.4.6-80.el7.centos.1.x86_64
[root@ansible ansible]# ansible web -m shell -a 'ls /tmp/vsftpd.log'
172.16.111.7 | SUCCESS | rc=0 >>
/tmp/vsftpd.log
172.16.111.8 | SUCCESS | rc=0 >>
/tmp/vsftpd.log
以上可以用来解决较为简单的命令
12、Templates
-
文本文件,嵌套有脚本(使用模板变成语言编写)
-
jinja2语言,使用字面量,有下面形式
字符串:使用单引号或双信号
数字:整数,浮点数
列表:[item1,item2,...]
元组:(item1,item2,...)
字典:{key1:value1,key2:value2,...}
布尔型:true/false
-
算数运算:+、-、*、/、//、%、**
//整除,不留小数和余数 **指数
-
比较运算:==,!=,>,>=,<,<=
-
逻辑运算:and,or,not
-
流表达式:For If When
配置文一般防止在yml文件同级的目录的templates下 配置文件以.js结尾
以下使用nginx做为案例
准备环境 nginx.conf为模板文件
#创建模板文件
[root@ansible ansible]# cp /etc/nginx/nginx.conf ~/ansible/templates/nginx.conf.j2
#编写脚本
[root@ansible ansible]# vim testtemplated.yml
---
- hosts: web
remote_user: root
tasks:
- name: install package
yum: name=nginx
- name: copy templated
template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf #调用模板
- name: start service
service: name=nginx state=started enabled=yes
#-C测试
[root@ansible ansible]# ansible-playbook testtemplated.yml -C
#测试正常后取消-C 执行
[root@ansible ansible]# ansible web -m shell -a 'ps -ef|grep nginx'
172.16.111.7 | SUCCESS | rc=0 >>
root 24383 1 0 23:22 ? 00:00:00 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf
nginx 24384 24383 0 23:22 ? 00:00:00 nginx: worker process
root 24435 24430 0 23:23 pts/0 00:00:00 /bin/sh -c ps -ef|grep nginx
root 24437 24435 0 23:23 pts/0 00:00:00 grep nginx
172.16.111.8 | SUCCESS | rc=0 >>
root 28866 1 0 23:22 ? 00:00:00 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf
nginx 28867 28866 0 23:22 ? 00:00:00 nginx: worker process
root 28918 28913 0 23:23 pts/0 00:00:00 /bin/sh -c ps -ef|grep nginx
root 28920 28918 0 23:23 pts/0 00:00:00 grep nginx
#查看结果,被控端nginx已经开启,注意进程数
修改nginx.conf后再继续测试,(默认nginx.conf的worker_processes 1;和CPU核心数相等,有的版本是auto,本次测试nginx版本为1.14,默认是1)
#查询setup自带的CPU变量
[root@ansible ~]# ansible web -m setup|grep "cpu"
"ansible_processor_vcpus": 2,
"ansible_processor_vcpus": 2,
#修改nginx.conf.j2文件
[root@ansible ansible]# vim templates/nginx.conf.j2
worker_processes {{ansible_processor_vcpus*2}};
#由于此前已经安装并启动了nginx,此时需要更改配置文件后重启服务,可以借助前面使用的handlers+notify来完成
[root@ansible ansible]# vim testtemplated.yml
---
- hosts: web
remote_user: root
tasks:
- name: install package
yum: name=nginx
- name: copy templated
template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf
notify: restart service
- name: start service
service: name=nginx state=started enabled=yes
handlers:
- name: restart service
service: name=nginx state=restarted
#-C测试正常后执行
[root@ansible ansible]# ansible-playbook testtemplated.yml
#再次查看nginx进程数
[root@ansible ansible]# ansible web -m shell -a 'ps -ef|grep nginx'
172.16.111.8 | SUCCESS | rc=0 >>
root 29793 1 0 23:34 ? 00:00:00 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf
nginx 29794 29793 0 23:34 ? 00:00:00 nginx: worker process
nginx 29795 29793 0 23:34 ? 00:00:00 nginx: worker process
nginx 29796 29793 0 23:34 ? 00:00:00 nginx: worker process
nginx 29797 29793 0 23:34 ? 00:00:00 nginx: worker process
root 29862 29857 0 23:35 pts/0 00:00:00 /bin/sh -c ps -ef|grep nginx
root 29864 29862 0 23:35 pts/0 00:00:00 grep nginx
172.16.111.7 | SUCCESS | rc=0 >>
root 25398 1 0 23:34 ? 00:00:00 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf
nginx 25399 25398 0 23:34 ? 00:00:00 nginx: worker process
nginx 25400 25398 0 23:34 ? 00:00:00 nginx: worker process
nginx 25401 25398 0 23:34 ? 00:00:00 nginx: worker process
nginx 25402 25398 0 23:34 ? 00:00:00 nginx: worker process
root 25463 25458 0 23:35 pts/0 00:00:00 /bin/sh -c ps -ef|grep nginx
root 25465 25463 0 23:35 pts/0 00:00:00 grep nginx
#进程数都有所增加
扩展:此时被控端的web组内(node1,node2)的nginx的服务都是80端口,如果有需求更换不同的端口(node1使用8080,node2使用9080),可以为hosts内进行配置
[root@ansible ansible]# vim /etc/ansible/hosts
[web]
172.16.111.7 http_port=8080 #配置变量
172.16.111.8 http_port=9080 #配置变量
[root@ansible ansible]# vim templates/nginx.conf.j2
server {
listen {{ http_port}};
# listen [::]:{{ http_port}} default_server
server_name _;
#charset koi8-r;
#access_log /var/log/nginx/host.access.log main;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
}
#注意 默认nginx1.14内么有上述server 需要自己添加或者从conf.d/default.conf内自己复制,可能是作者希望使用include进行管理
[root@ansible ansible]# ansible-playbook testtemplated.yml
#执行脚本后查看结果(主要是查看端口变量是否成功 使用 ss -lnt)
[root@ansible ansible]# ansible web -m shell -a 'ss -lnt'
172.16.111.8 | SUCCESS | rc=0 >>
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 128 *:9080 *:*
LISTEN 0 100 127.0.0.1:25 *:*
LISTEN 0 128 *:80 *:*
LISTEN 0 128 *:22 *:*
LISTEN 0 100 ::1:25 :::*
LISTEN 0 128 :::22 :::*
172.16.111.7 | SUCCESS | rc=0 >>
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 100 127.0.0.1:25 *:*
LISTEN 0 128 *:8080 *:*
LISTEN 0 128 *:80 *:*
LISTEN 0 128 *:22 *:*
LISTEN 0 100 ::1:25 :::*
LISTEN 0 128 :::22 :::*
#查看结果可知 8080 9080端口已经开启
此外,还可以在playbook内写入变量
[root@ansible ansible]# vim testtemplated.yml
---
- hosts: web
remote_user: root
vars: #配置变量
- http_port: 88
tasks:
- name: install package
yum: name=nginx
- name: copy templated
template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf
notify: restart service
- name: start service
service: name=nginx state=started enabled=yes
handlers:
- name: restart service
service: name=nginx state=restarted
#保存修改 执行 -C测试 执行(执行信息省略)
[root@ansible ansible]# ansible-playbook testtemplated.yml
#检验结果
[root@ansible ansible]# ansible web -m shell -a 'ss -lnt'
172.16.111.8 | SUCCESS | rc=0 >>
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 128 *:88 *:*
LISTEN 0 100 127.0.0.1:25 *:*
LISTEN 0 128 *:80 *:*
LISTEN 0 128 *:22 *:*
LISTEN 0 100 ::1:25 :::*
LISTEN 0 128 :::22 :::*
172.16.111.7 | SUCCESS | rc=0 >>
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 128 *:88 *:*
LISTEN 0 100 127.0.0.1:25 *:*
LISTEN 0 128 *:80 *:*
LISTEN 0 128 *:22 *:*
LISTEN 0 100 ::1:25 :::*
LISTEN 0 128 :::22 :::*
#端口都被修改为88
验证了最开始的优先级的概念
命令行-e>playbook剧本>hosts内普通变量>hosts内的公共(组)变量
13、When
-
条件测试:如果需要根据变量,facts或此前任务的执行结果来作为某task执行与否的提前提时需要用到条件测试,通过when语句实现,在task中使用,jinja2语法格式
-
在task后增加when子句即可实现条件测试;when语句支持jinja2表达式语法
-
示例:
tasks:
- name: "shutdown RedHat"
command: /sbin/shutdown -h now
when: ansible_os_family == "RedHat"
#ansible_os_family 是setup自带变量
[root@ansible ~]# ansible 172.16.111.7 -m setup -a "filter=ansible_os_family" #查看系列
172.16.111.7 | SUCCESS => {
"ansible_facts": {
"ansible_os_family": "RedHat"
},
"changed": false
}
#" "内的内容就是匹配到的内容,以后可根据此进行判断等操作
举例:在上次操作texttemplated.yml中,加入when判断,判断被控端hostname是node2的时候才复制配置文件并重启服务
[root@ansible ansible]# cp testtemplated.yml testwhen.yml
#修改脚本
[root@ansible ansible]# vim testwhen.yml
---
- hosts: web
remote_user: root
vars:
- http_port: 8888
tasks:
- name: install package
yum: name=nginx
- name: copy templated
template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf
when: ansible_hostname == "node2" #判断条件 可以多个task多个判断
notify: restart service
- name: start service
service: name=nginx state=started enabled=yes
handlers:
- name: restart service
service: name=nginx state=restarted
#检测后执行
[root@ansible ansible]# ansible-playbook testwhen.yml
#查看端口是否修改
[root@ansible ansible]# ansible all -m shell -a 'ss -lnt'
172.16.111.8 | SUCCESS | rc=0 >>
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 128 *:8888 *:*
LISTEN 0 100 127.0.0.1:25 *:*
LISTEN 0 128 *:80 *:*
LISTEN 0 128 *:22 *:*
LISTEN 0 100 ::1:25 :::*
LISTEN 0 128 :::22 :::*
172.16.111.7 | SUCCESS | rc=0 >>
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 128 *:88 *:*
LISTEN 0 100 127.0.0.1:25 *:*
LISTEN 0 128 *:80 *:*
LISTEN 0 128 *:22 *:*
LISTEN 0 100 ::1:25 :::*
LISTEN 0 128 :::22 :::*
172.16.111.9 | SUCCESS | rc=0 >>
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 100 127.0.0.1:25 *:*
LISTEN 0 128 *:22 *:*
LISTEN 0 100 ::1:25 :::*
LISTEN 0 128 :::22 :::*
#查看nginx服务是否开启
[root@ansible ansible]# ansible all -m shell -a 'ps -ef|grep nginx'
172.16.111.9 | SUCCESS | rc=0 >>
root 8463 8462 0 17:11 pts/0 00:00:00 /bin/sh -c ps -ef|grep nginx
root 8465 8463 0 17:11 pts/0 00:00:00 grep nginx
172.16.111.7 | SUCCESS | rc=0 >>
root 28374 1 0 00:18 ? 00:00:00 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf
nginx 28375 28374 0 00:18 ? 00:00:00 nginx: worker process
nginx 28376 28374 0 00:18 ? 00:00:00 nginx: worker process
nginx 28377 28374 0 00:18 ? 00:00:00 nginx: worker process
nginx 28378 28374 0 00:18 ? 00:00:00 nginx: worker process
root 29899 29894 0 00:55 pts/0 00:00:00 /bin/sh -c ps -ef|grep nginx
root 29901 29899 0 00:55 pts/0 00:00:00 grep nginx
172.16.111.8 | SUCCESS | rc=0 >>
root 33658 1 0 00:47 ? 00:00:00 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf
nginx 33659 33658 0 00:47 ? 00:00:00 nginx: worker process
nginx 33660 33658 0 00:47 ? 00:00:00 nginx: worker process
nginx 33661 33658 0 00:47 ? 00:00:00 nginx: worker process
nginx 33662 33658 0 00:47 ? 00:00:00 nginx: worker process
root 33852 33843 0 00:55 pts/0 00:00:00 /bin/sh -c ps -ef|grep nginx
root 33854 33852 0 00:55 pts/0 00:00:00 grep nginx
#结果:node1(172.16.111.7)的端口未发生变化,node2(172.16.111.8)的nginx端口被修改为了8888,node3(172.16.111.9)不在web组内,没有任何操作,测试成功
14、Item
可以使用循环迭代来操作多个重复动作,比如批量创建文件等
[root@ansible ansible]# vim testitem.yml
---
- hosts: all
remote_user: root
tasks:
- name: create files
file: name=/data/{{ item}} state=touch
when: ansible_hostname == "node3"
with_items:
- file1
- file2
- file3
- name: install some software
yum: name={{ item}}
with_items:
- sysstat
- lrzsz
- dstat
#测试后执行
[root@ansible ansible]# ansible-playbook testitem.yml
#验证
[root@ansible ansible]# ansible all -m shell -a 'rpm -q sysstat dstat lrzsz'
#所有被控端都安装了sysstat dstat lrzsz三个软件
[root@ansible ansible]# ansible all -m command -a 'ls -l /data'
172.16.111.9 | SUCCESS | rc=0 >>
total 0
-rw-r--r--. 1 root root 0 Oct 25 18:27 file1
-rw-r--r--. 1 root root 0 Oct 25 18:27 file2
-rw-r--r--. 1 root root 0 Oct 25 18:27 file3
172.16.111.8 | SUCCESS | rc=0 >>
total 0
-rw-r--r--. 1 root root 0 Oct 25 15:22 file123
drwxr-xr-x. 3 root root 21 Oct 22 16:33 svndata
172.16.111.7 | SUCCESS | rc=0 >>
total 0
-rw-r--r--. 1 root root 0 Oct 25 15:22 file123
drwxr-xr-x. 3 root root 21 Oct 22 17:10 svndata
#只有node3创建了file1 file2 file3文件
需求:创建三个组,创建三个用户,并且把每个用户分别放入一个组
(user1属于g1,user2属于g2,user3属于g3)
[root@ansible ansible]# vim testgroup.yml
---
- hosts: all
remote_user: root
tasks:
- name: add some groups
group: name={{ item}} state=present
with_items:
- g1
- g2
- g3
- name: add some user
user: name={{ item.name}} group={{ item.group}} state=present
with_items:
- { name: 'user1', group: 'g1'} #类似于键值对一个K对应一个V
- { name: 'user2', group: 'g2'}
- { name: 'user3', group: 'g3'}
#item.name语法记住
#执行结果验证
[root@ansible ansible]# ansible all -m shell -a 'getent passwd#该命令结果过长
[root@ansible ansible]# ansible all -m shell -a 'tail -3 /etc/passwd'
172.16.111.9 | SUCCESS | rc=0 >>
user1:x:1002:1002::/home/user1:/bin/bash
user2:x:1003:1003::/home/user2:/bin/bash
user3:x:1004:1004::/home/user3:/bin/bash
172.16.111.8 | SUCCESS | rc=0 >>
user1:x:1002:1002::/home/user1:/bin/bash
user2:x:1003:1003::/home/user2:/bin/bash
user3:x:1004:1004::/home/user3:/bin/bash
172.16.111.7 | SUCCESS | rc=0 >>
user1:x:1003:1003::/home/user1:/bin/bash
user2:x:1004:1004::/home/user2:/bin/bash
user3:x:1005:1005::/home/user3:/bin/bash
15、For
类似shell等的shell循环
语法
{% for vhost in nginx/-vhosts %}
server {
listen {{ vhost.listen| default('80 default_server') }}
{% endfor %}
需求:生成nginx.conf的server标签 listen端口不同
#创建conf文件
[root@ansible ansible]# vim templates/for1.conf.j2
{% for port in ports%} #调用变量
server {
listen {{ port}}
}
{% endfor %}
#创建执行脚本
[root@ansible ansible]# vim testfor.yml
---
- hosts: web
remote_user: root
vars:
ports:
- 81
- 82
- 83
tasks:
- name: copy conf
template: src=for1.conf.j2 dest=/data/for1.conf
#调用模板,模板内调用YML脚本内定义的变量
[root@ansible ansible]# ansible-playbook testfor.yml
[root@ansible ansible]# ansible web -m shell -a 'ls -l /data'
172.16.111.8 | SUCCESS | rc=0 >>
total 4
-rw-r--r--. 1 root root 0 Oct 25 15:22 file123
-rw-r--r--. 1 root root 66 Oct 28 20:17 for1.conf
drwxr-xr-x. 3 root root 21 Oct 22 16:33 svndata
172.16.111.7 | SUCCESS | rc=0 >>
total 4
-rw-r--r--. 1 root root 0 Oct 25 15:22 file123
-rw-r--r--. 1 root root 66 Oct 28 20:17 for1.conf
drwxr-xr-x. 3 root root 21 Oct 22 17:10 svndata
[root@ansible ansible]# ansible web -m shell -a 'less /data/for1.conf'
172.16.111.8 | SUCCESS | rc=0 >>
server {
listen 81
}
server {
listen 82
}
server {
listen 83
}
172.16.111.7 | SUCCESS | rc=0 >>
server {
listen 81
}
server {
listen 82
}
server {
listen 83
}
#使用字典的方式来修改
[root@ansible ansible]# vim testfor.yml
---
- hosts: web
remote_user: root
vars:
ports:
- listen_port: 81
- listen_port: 82
- listen_port: 83
tasks:
- name: copy conf
template: src=for1.conf.j2 dest=/data/for1.conf
[root@ansible ansible]# vim templates/for1.conf.j2
{% for port in ports %}
server {
listen {{ port.listen_port}}
}
{% endfor %}
#执行效果与上面相同,如果是多键值对的时候建议使用此方法,如果只是单一的值,推荐上面方法
多键值对示例
#配置剧本文件
[root@ansible ansible]# vim testfor2.yml
---
- hosts: web
remote_user: root
vars:
ports:
- web1:
port: 86
name: web1.white.com
rootdir: /data/website1
- web2:
port: 87
name: web2.white.com
rootdir: /data/website2
- web3:
port: 88
name: web3.white.com
rootdir: /data/website3
tasks:
- name: copy conf
template: src=for2.conf.j2 dest=/data/for2.conf
#配置conf文件
[root@ansible ansible]# vim templates/for2.conf.j2
{% for p in ports %}
server {
listen {{ p.port }}
servername {{p.name }}
documentroot {{ p.rootdir }}
}
{% endfor %}
#验证后执行
[root@ansible ansible]# ansible-playbook testfor2.yml
#验证执行结果
[root@ansible ansible]# ansible web -m shell -a 'ls /data'
172.16.111.8 | SUCCESS | rc=0 >>
file123
for1.conf
for2.conf
svndata
172.16.111.7 | SUCCESS | rc=0 >>
file123
for1.conf
for2.conf
svndata
[root@ansible ansible]# ansible 172.16.111.7 -m shell -a 'less /data/for2.conf'
172.16.111.7 | SUCCESS | rc=0 >>
server {
listen 86
servername web1.white.com
documentroot /data/website1
}
server {
listen 87
servername web2.white.com
documentroot /data/website2
}
server {
listen 88
servername web3.white.com
documentroot /data/website3
}
16、If
判断语句,与shell内相同,不多做介绍
语法
{% if vhost.server_name is defined %}
server_name {{vhost.server_name }};
{% endif %}
{% if vhost.root is defined 80 %}
root {{ vhost.root }};
{% endif %}
[root@ansible ansible]# vim testif1.yml
---
- hosts: web
remote_user: root
vars:
ports:
- web1:
port: 86
#name: web1.white.com #注释
rootdir: /data/website1
- web2:
port: 87
name: web2.white.com
rootdir: /data/website2
- web3:
port: 88
#name: web3.white.com #注释
rootdir: /data/website3
tasks:
- name: copy conf
template: src=for3.conf.j2 dest=/data/for3.conf
#对p.name进行判断,没有则不生成servername
[root@ansible ansible]# vim templates/for3.conf.j2
{% for p in ports %}
server {
listen {{ p.port }}
{% if p.name is defined %}
servername {{ p.name }}
{% endif %}
documentroot {{ p.rootdir }}
}
{% endfor %}
#验证执行
[root@ansible ansible]# ansible-playbook testif1.yml
[root@ansible ansible]# ansible web -m shell -a 'less /data/for3.conf'
172.16.111.8 | SUCCESS | rc=0 >>
server {
listen 86
documentroot /data/website1
}
server {
listen 87
servername web2.white.com
documentroot /data/website2
}
server {
listen 88
documentroot /data/website3
}
172.16.111.7 | SUCCESS | rc=0 >>
server {
listen 86
documentroot /data/website1
}
server {
listen 87
servername web2.white.com
documentroot /data/website2
}
server {
listen 88
documentroot /data/website3
}
#只有87端口的server有servername 别的由于被注释,if判断不存在 就不创建servername