一、ansible
1.1、ansible
ansible基于Python开发,集合了众多运维工具(puppet、chef、func、fabric)的优点,实现了批量系统配置、批量程序部署、批量运行命令等功能。ansible是基于 paramiko 开发的,并且基于模块化工作,本身没有批量部署的能力。真正具有批量部署的是ansible所运行的模块,ansible只是提供一种框架。ansible不需要在远程主机上安装client/agents,因为它们是基于ssh来和远程主机通讯的。ansible目前已经已经被红帽官方收购,是自动化运维工具中大家认可度最高的,并且上手容易,学习简单。是每位运维工程师必须掌握的技能之一。
1.2、ansible特点
部署简单,只需在主控端部署Ansible环境,被控端无需做任何操作;
默认使用SSH协议对设备进行管理;
有大量常规运维操作模块,可实现日常绝大部分操作;
配置简单、功能强大、扩展性强;
支持API及自定义模块,可通过Python轻松扩展;
通过Playbooks来定制强大的配置、状态管理;
轻量级,无需在客户端安装agent,更新时,只需在操作机上进行一次更新即可;
提供一个功能强大、操作性强的Web管理界面和REST API接口——AWX平台。
1.3、ansible架构图
Ansible:Ansible核心程序。
HostInventory:记录由Ansible管理的主机信息,包括端口、密码、ip等。
Playbooks:“剧本”YAML格式文件,多个任务定义在一个文件中,定义主机需要调用哪些模块来完成的功能。
CoreModules:核心模块,主要操作是通过调用核心模块来完成管理任务。
CustomModules:自定义模块,完成核心模块无法完成的功能,支持多种语言。
ConnectionPlugins:连接插件,Ansible和Host通信使用
1.4、ansible任务执行
# 1、ad-hoc模式(点对点模式)
使用单个模块,支持批量执行单条命令。ad-hoc 命令是一种可以快速输入的命令,而且不需要保存起来的命令。就相当于bash中的一句话shell。
# playbook模式(剧本模式)
是Ansible主要管理方式,也是Ansible功能强大的关键所在。playbook通过多个task集合完成一类功能,如Web服务的安装部署、数据库服务器的批量备份等。可以简单地把playbook理解为通过组合多条ad-hoc操作的配置文件。
简单理解就是Ansible在运行时, 首先读取ansible.cfg中的配置, 根据规则获取Inventory中的管理主机列表, 并行的在这些主机中执行配置的任务, 最后等待执行返回的结果。
二、ansible搭建
2.1、环境
ip | hostname | 备注 |
---|---|---|
192.168.13.211 | vm-master-01 | ansible控制端 |
192.168.13.12 | vm-master-02 | ansible被控制端 |
192.168.13.225 | vm-master-03 | ansible被控制端 |
2.2、安装
# 官方文档
https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html#installing-ansible-on-rhel-centos-or-fedora
yum install epel-release
yum install ansible
# ansible 帮助信息
ansible-doc -a # 显示所有模块文档
ansible-doc -l;--list # 列出可用模块
ansible-doc -s # 显示指定模块的playbook片段
eg:
ansible-doc -l
ansible-doc ping
ansible-doc -s ping
2.3、ansible配置文件
inventory = /etc/ansible/hosts #这个参数表示资源清单inventory文件的位置
library = /usr/share/ansible #指向存放Ansible模块的目录,支持多个目录方式,只要用冒号(:)隔开就可以
forks = 5 #并发连接数,默认为5
sudo_user = root #设置默认执行命令的用户
remote_port = 22 #指定连接被管节点的管理端口,默认为22端口,建议修改,能够更加安全
host_key_checking = False #设置是否检查SSH主机的密钥,值为True/False。关闭后第一次连接不会提示配置实例
timeout = 60 #设置SSH连接的超时时间,单位为秒
log_path = /var/log/ansible.log #指定一个存储ansible日志的文件(默认不记录日志)
2.4、ansible常用命令
/usr/bin/ansible Ansibe AD-Hoc 临时命令执行工具,常用于临时命令的执行
/usr/bin/ansible-doc Ansible 模块功能查看工具
/usr/bin/ansible-galaxy 下载/上传优秀代码或Roles模块 的官网平台,基于网络的
/usr/bin/ansible-playbook Ansible 定制自动化的任务集编排工具
/usr/bin/ansible-pull Ansible远程执行命令的工具,拉取配置而非推送配置(使用较少,海量机器时使用,对运维的架构能力要求较高)
/usr/bin/ansible-vault Ansible 文件加密工具
/usr/bin/ansible-console Ansible基于Linux Consoble界面可与用户交互的命令执行工具
2.5、ansible控制端环境配置
# ansible控制端上进行操作
# 1、生成私钥
ssh-keygen
# 2、向主机分发私钥
ssh-copy-id root@vm-master-01
ssh-copy-id root@vm-master-02
ssh-copy-id root@vm-master-03
# 3、配置hosts
[root@vm-master-01 ansible]# cat /etc/ansible/hosts
[test]
vm-master-01 ansible_ssh_host=192.168.13.211 ansible_ssh_port=22
vm-master-02 ansible_ssh_host=192.168.13.12 ansible_ssh_port=22
vm-master-03 ansible_ssh_host=192.168.13.225 ansible_ssh_port=22
三、ansible基本使用
ansible通过ssh实现配置管理、应用部署、任务执行等功能,建议配置ansible端能基于密钥认证的方式联系各个管理节点:
ansible [-m module_name] [-a args]
--version 显示版本
-m module 指定模块,默认为command
-v 详细过程 -vv -vvv更为详细
--list-hosts 显示主机列表,可简写为--list
-k,--ask-pass tishi 提示输入ssh链接密码,默认key验证
-C,--check 检查,并不执行
-T,-timeout=TIMEOUT 执行命令的超时时间,默认10s
-u,--user=REMOTE_USER 远程执行的用户
# ansible的host-pattern匹配主机的列表
# 1、All: 表示所有Inventory中的所有主机
ansible all -m ping
#2、*: 通配符
ansible "*" -m ping
ansible 10.10.0.* -m ping
ansible "*srs" -m ping
# 3、或
ansible "web1:web2" -m ping
ansible "192.168.1.10:192.168.1.11" -m ping
# 4、逻辑与
ansible "web1:&web2" -m ping # 在web1并且在web2组中的主机
# 5、逻辑非
ansible 'web1:!web2' -m ping # 在web1组中但不在web2组中
# 6、正则
ansible "~(wa|we).*\.baidu\.com" -m ping
ansible命令执行过程:
3.1、command模块
# 远程执行命令,command模块是默认情况下的模块,这个模块不支持shell变量和管道
ansible vm-master-01 -m command -a "ls /" <可自行>
ansible vm-master-01 -m command -a "ls / | grep data" <有管道、不可执行>
3.2、shell模块
# 支持shell变量和管道
ansible vm-master-01 -m shell -a "ls /"
ansible vm-master-01 -m shell -a "ls / | grep data"
3.3、yum模块
ansible PROD -m yum -a 'name=vsftpd state=present'
常用参数介绍:
name= #所安装的包的名称
state= #present--->安装, latest--->安装最新的, absent---> 卸载软件。
update_cache #强制更新yum的缓存
conf_file #指定远程yum安装时所依赖的配置文件(安装本地已有的包)。
disable_pgp_check #是否禁止GPG checking,只用于presentor latest。
disablerepo #临时禁止使用yum库。 只用于安装或更新时。
enablerepo #临时使用的yum库。只用于安装或更新时。
3.4、service模块
ansible vm-master-02 -m service -a "name=vsftpd state=started enabled=yes"
常用参数介绍:
arguments #命令行提供额外的参数
enabled #设置开机启动。
name= #服务名称
runlevel #开机启动的级别,一般不用指定。
sleep #在重启服务的过程中,是否等待。如在服务关闭以后等待2秒再启动。(定义在剧本中。)
state #有四种状态,分别为:started--->启动服务, stopped--->停止服务, restarted--->重启服务, reloaded--->重载配置
3.5、file模块
# file模块用于查看文件的属性,修改文件属性,查询文件是否被修改等
ansible vm-master-03 -m file -a 'path=/etc/passwd' -------------->查看文件属性
ansible vm-master-02 -m file -a 'path=/tmp/fzh state=touch mode=755 owner=fzh' --------------->创建文件
ansible vm-master-02 -m file -a 'dest=/tmp/fzh state=absent' ------------------->删除文件。
ansible vm-master-02 -m file -a 'dest=/tmp/test.sh mode=777' ------------------->修改远程服务器上文件的属性。
ansible vm-master-02 -m file -a 'dest=/tmp/test mode=755 owner=fzh group=fzh state=directory' ------->在客户端相应位置下创建目录:fzh。(make -p)
ansible vm-master-02 -m file -a 'dest=/tmp/fzh state=absent' --------------->删除远程服务器上的文件,这里是目录。
3.6、copy模块
# 从控制节点上拷贝文件到“受控节点”上
ansible vm-master-02 -m copy -a 'src=/root/test.sh dest=/tmp/ mode=600 owner=fzh group=fzh'
(把本地/root/test文件cp到PROD主机下的/tmp目录下。)
常用参数介绍:
src #被复制到远程主机的本地文件。可以是绝对路径,也可以是相对路径。如果路径是一个目录,则会递归复制,用法类似于"rsync"
content #用于替换"src",可以直接指定文件的值
dest #必选项,将源文件复制到的远程主机的绝对路径
backup #当文件内容发生改变后,在覆盖之前把源文件备份,备份文件包含时间信息
directory_mode #递归设定目录的权限,默认为系统默认权限
force #当目标主机包含该文件,但内容不同时,设为"yes",表示强制覆盖;设为"no",表示目标主机的目标位置不存在该文件才复制。默认为"yes"
others #所有的 file 模块中的选项可以在这里使用
3.7、script模块
# 远程节点执行本地脚本,脚本位于控制节点,相当于scp+shell
ansible test -m script -a "/root/hostname.sh"
3.8、synchronize模块
# 将控制节点的某个目录推送到受控制节点目录下
ansible test -m synchronize -a 'src=/root/test dest=/tmp/ compress=yes'
3.9、fetch模块
# 将vm-master-02的/root/vm-master-02.txt文件cp到本机的/root目录下
[root@vm-master-01 ~]# ansible vm-master-02 -m fetch -a "src=/root/vm-master-02.txt dest=/root/ flat=yes"
ansible还有很多模块,在此不一一介绍,具体可以参考中文指南:
http://www.ansible.com.cn/docs/intro_adhoc.html#managing-packages
四、ansible-playbook
4.1、ansible-playbook
playbook 是 ansible 用于配置,部署,和管理被控节点的剧本。通过 playbook 的详细描述,执行其中的一系列 tasks ,可以让远端主机达到预期的状态。执行一些简单的任务,使用ad-hoc命令可以方便的解决问题,但是有时一个设施过于复杂,需要大量的操作时候,执行的ad-hoc命令是不适合的,这时最好使用playbook。playbook由YMAL语言编写。YAML( /ˈjæməl/ )参考了其他多种语言,包括:XML、C语言、Python、Perl以及电子邮件格式RFC2822。
4.2、ansible-playbook yaml文件格式
---
- hosts: vm-03
remote_user: root
tasks:
- name: exec shell
shell: hostname
- name: create dir
file:
path: /root/dir
state: directory
# 解析
host:使用 hosts 指示使用哪个主机或主机组来运行下面的 tasks ,每个 playbook 都必须指定 hosts,hosts也可以使用通配符格式。主机或主机组在 inventory 清单中指定,可以使用系统默认的/etc/ansible/hosts,也可以自己编辑,在运行的时候加上-i选项,指定清单的位置即可。
remote_user:指定远端主机中的哪个用户来登录远端系统,在远端系统执行 task 的用户,可以任意指定,也可以使用 sudo,但是用户必须要有执行相应 task 的权限。
tasks:指定远端主机将要执行的一系列动作,相当于任务列表。tasks 的核心为 ansible 的模块,前面已经提到模块的用法。tasks 包含 name 和要执行的模块,name 是可选的,只是为了便于用户阅读,不过还是建议加上去,模块是必须的,同时也要给予模块相应的参数。
name:每个task都有对应的name,当我们省略name时,默认以当前任务调用的模块的名称作为任务的名称,不过建议不要省略name,因为当任务存在name时,可读性比较高。
# 执行上述任务
# 1、–list-hosts: 显示执行任务的主机列表,实际并不执行
ansible-playbook -i /etc/ansible/hosts --list-hosts /etc/ansible/test.yaml
# 2、--syntax-check: 语法检测,实际并不执行。执行结果显示playbook名称即表示没有语法错误。
ansible-playbook -i /etc/ansible/hosts /etc/ansible/test.yaml --syntax-check
# 3、--check: 任务预演,实际并不执行
ansible-playbook -i /etc/ansible/hosts /etc/ansible/test.yaml --check
# 执行结果:
绿色代表执行成功,系统保持原样
黄色代表系统代表系统状态发生改变
红色代表执行失败,显示错误输出
执行过程有三个步骤:
1、收集facts (收集对应目标主机的信息)
2、执行tasks
3、报告结果
执行结果如下图:
4.3、handlers
4.3.1、handlers初识
handlers是和tasks平级的另一种任务。但handlers中的任务会被tasks中的任务调用。只有当tasks中的任务真正执行(黄色,有改变),handlers中被调用的任务才会被执行。当服务的配置文件发生改变,需要重启的时候,handlers就能解决此类问题。格式见下:
[root@vm-master-01 ansible]# cat handlers.yaml
---
- hosts: vm-03
remote_user: root
tasks:
- name: create dir1
file: path=/root/dir1
state=directory
notify: handler1
- name: create dir2
file: path=/root/dir2
state=directory
notify: handler2
handlers:
- name: handler1
file: path=/root/dir1/file1
state=touch
- name: handler2
file: path=/root/dir2/file2
state=touch
# 解释:
通过notify关键字关联tasks和handlers,handlers被执行的顺序与在playbook中定义的顺序是相同的,与被调用执行的顺序无关。当所有task执行完毕后,才会执行handlers。如果想改变这种顺序需要meta模块。
4.3.2、handlers meta
[root@vm-master-01 ansible]# cat handlers.yaml
---
- hosts: vm-03
remote_user: root
tasks:
- name: create dir1
file: path=/root/dir1
state=directory
notify: handler1
- name: create dir2
file: path=/root/dir2
state=directory
notify: handler2
- meta: flush_handlers
- name: create dir3
file: path=/root/dir3
state=directory
notify: handler3
handlers:
- name: handler1
file: path=/root/dir1/file1
state=touch
- name: handler2
file: path=/root/dir2/file2
state=touch
- name: handler3
file: path=/root/dir2/file3
state=touch
ansible-playbook -i /etc/ansible/hosts /etc/ansible/handlers.yaml执行结果见下:
#从上面结果可以看出:
meta是一种特殊的任务,可以影响ansible内部的工作顺序,"meta: flush_handlers"表示立即执行之前的task所对应的handler。当对应的handler执行完毕后,接着执行meta下面后续的task,这些task执行完毕后,在执行对应的handlers。
4.3.3、一个task对应多个handlers
[root@vm-master-01 ansible]# cat handlers2.yaml
---
- hosts: vm-03
remote_user: root
tasks:
- name: create dir1
file: path=/root/dir1
state=directory
notify: handler-group
handlers:
- name: handler1
listen: handler-group
file: path=/root/dir1/file1
state=touch
- name: handler2
listen: handler-group
file: path=/root/dir1/file2
state=touch
- name: handler3
listen: handler-group
file: path=/root/dir1/file3
state=touch
ansible-playbook -i /etc/ansible/hosts /etc/ansible/handlers2.yaml执行结果如下:
可以将"listen"和“notify”后面的handler-group看成组,命中组的handler就可以被调用执行。
注意:如果不采用上面listen的方式,当定义多个具有相同name的handlers时,只会有一个handler会被执行。
4.4、tags
tags: 为任务列表中的指定任务打上标签,然后可以选择性的执行某一部分或者某一类的任务。
4.4.1、tags定义
[root@vm-master-01 ansible]# cat cat tags.yaml
cat: cat: No such file or directory
---
- hosts: vm-03
remote_user: root
tasks:
- name: create dir1
file: path=/root/dir1
state=directory
notify: handler1
tags: t1
- name: create dir2
file: path=/root/dir2
state=directory
notify: handler2
tags: t2
- name: create dir3
file: path=/root/dir3
state=directory
notify: handler3
tags: t3
handlers:
- name: handler1
file: path=/root/dir1/file1
state=touch
- name: handler2
file: path=/root/dir2/file2
state=touch
- name: handler3
file: path=/root/dir3/file3
state=touch
ansible-playbook -i /etc/ansible/hosts --tags=t3 /etc/ansible/tags.yaml执行结果如下:
从结果可以看出:只有tags: t3的task和其handler被执行了,也可以一次性指定多个标签
# t2、t3执行
ansible-playbook -i /etc/ansible/hosts --tags t2,t3 /etc/ansible/tags.yaml
# --skip-tags指定不执行的task<下面:t2不执行,t1、t3执行>
ansible-playbook -i /etc/ansible/hosts --skip-tags="t2" /etc/ansible/tags.yaml
# 定义标签的方式:
[root@vm-master-01 ansible]# cat tags2.yaml
---
- hosts: vm-03
remote_user: root
tasks:
- name: create dir1
file: path=/root/dir1
state=directory
notify: handler1
tags:
- t1
- name: create dir2
file: path=/root/dir2
state=directory
notify: handler2
tags: ['t2']
- name: create dir3
file: path=/root/dir3
state=directory
notify: handler3
tags:
- t3
- t4
- name: create dir4
file: path=/root/dir4
state=directory
tags: t5,t6
- name: create dir4
file: path=/root/dir4
state=directory
tags: ['t7', 't8']
handlers:
- name: handler1
file: path=/root/dir1/file1
state=touch
- name: handler2
file: path=/root/dir2/file2
state=touch
- name: handler3
file: path=/root/dir3/file3
state=touch
4.4.2、tags查看
ansible-playbook -i /etc/ansible/hosts --list-tags /etc/ansible/tags.yaml
4.4.3、相同标签
[root@vm-master-01 ansible]# cat tags3.yaml
---
- hosts: vm-03
remote_user: root
tasks:
- name: create dir1
file: path=/root/dir1
state=directory
tags:
- t1
- dir
- name: create dir2
file: path=/root/dir2
state=directory
tags: ['t2', 'dir']
上面两个task都有标签:dir,我们可以以另一种方式表示:
[root@vm-master-01 ansible]# cat tags4.yaml
---
- hosts: vm-03
remote_user: root
tags: dir
tasks:
- name: create dir1
file: path=/root/dir1
state=directory
tags:
- t1
- name: create dir2
file: path=/root/dir2
state=directory
tags: ['t2']
当我们执行:ansible-playbook -i /etc/ansible/hosts --tags=dir /etc/ansible/tags4.yaml,两个tasks都会被执行,结果见下:
4.4.4、内置tags
4.4.4.1、always
当tags=always时,我们用ansible-playbook执行任务,即便我们没有指定--tags=always,这个任务也会被执行,除非使用了'--skip-tags'。
4.4.4.2、never
和always相反,我们执行所有任务的时候,加了never标签的任务不执行。
4.4.4.3、tagged
# 下面命令表示只执行有标签的任务,没有任何标签的任务不会被执行。这里加了never标签的不会执行
ansible-playbook --tags tagged tags.yaml
# 下面命令表示跳过包含标签的任务,即使对应的任务包含always标签,也会被跳过
ansible-playbook --skip-tags tagged tags.yaml
4.4.4.4、untagged
# 下面命令表示只执行没有标签的任务,但是如果某些任务包含always标签,那么这些任务也会被执行
ansible-playbook --tags untagged tags.yaml
# 下面命令表示跳过没有标签的任务。意思是只执行有标签的任务。但是加了never标签的任务不会被执行
ansible-playbook --skip-tags untagged tags.yaml
4.4.4.5、all
特殊标签all表示所有任务会被执行,不用指定,默认情况下就是使用这个标签。
4.5、ignore_errors
# 假如ansible-playbook中的某个task出错,则按照顺序执行的后续任务都不会再执行,假如是handler报错,则不会影响剩下的流程,如果需要忽略此错误,可以用ignore_errors
[root@vm-master-01 ansible]# cat ignore_errors.yaml
---
- hosts: vm-03
remote_user: root
tasks:
- name: create dir1
file: path=/root/dir1
state=directory
notify: handler1
- name: create dir2
command: hostnameeeeee
notify: handler2
ignore_errors: True
- name: create dir3
file: path=/root/dir3
state=directory
notify: handler3
handlers:
- name: handler1
file: path=/root/dir1/file1
state=touch
- name: handler2
file: path=/root/dir2/file2
state=touch
- name: handler3
file: path=/root/dir3/file3
state=touch
执行命令: ansible-playbook -i /etc/ansible/hosts /etc/ansible/ignore_errors.yaml 结果如下:
在第二个任务,人为制造一个报错,由于目录/root/dir2/不存在,所以handler2也会出错,但只在第二个任务处添加"ignore_errors: True"后,后续的步骤都没背中断,即便handler2有报错,也没有影响后续的步骤。
4.6、limit
# 当有多台主机被命中时,限制其中的某一台执行playbook,但必须在命中的主机列表中:
[root@vm-master-01 ansible]# cat limit.yaml
---
- hosts: test
remote_user: root
tasks:
- name: limit test
command: hostname
执行命令:ansible-playbook -i /etc/ansible/hosts /etc/ansible/limit.yaml --limit vm-master-02的结果如下:
虽然test命中了三台主机,但使用limit可以限制playbook在指定的主机上执行。
4.7、playbook变量
# 1、变量名:
仅能由字母、数字、下划线组成,且只能以字母开头
# 2、变量来源:
1.1、 ansible setup facts 远程主机的所有变量都可直接调用
1.2、/etc/ansible/hosts定义
1.2.1、普通变量:主机组中主机单行定义,优先级高于公共变量
1.2.2、公共(组)变量:针对主机组中所有主机定义的统一变量
1.3、命令行指定的变量,优先级最高
1.4、playbook中定义
1.5、role中定义
4.7.1、命令行变量
# 1、var_command_line.yaml文件内容
[root@vm-master-01 ansible]# cat var_command_line.yaml
---
- hosts: test
remote_user: root
tasks:
- name: exec shell echo {{ var }}
shell: echo {{ prefix }}_{{ var }}_{{ suffix }} > /tmp/var.txt
执行命令:ansible-playbook -i /etc/ansible/hosts -e "prefix=pre1" -e "var=command-line-var" -e "suffix=suf1" /etc/ansible/var_command_line.yaml结果如下:
4.7.2、playbook变量
# 1、var_playbook.yaml文件内容
[root@vm-master-01 ansible]# cat var_playbook.yaml
---
- hosts: test
remote_user: root
vars:
- prefix: pre2
- var: playbook-var
- suffix: suf2
tasks:
- name: exec shell echo {{ var }}
shell: echo {{ prefix }}_{{ var }}_{{ suffix }} > /tmp/var.txt
执行命令:ansible-playbook -i /etc/ansible/hosts /etc/ansible/var_playbook.yaml结果如下:
4.7.3、普通变量
# 1、/etc/ansible/hosts文件内容
[root@vm-master-01 ansible]# head -n 4 /etc/ansible/hosts
[test]
vm-master-01 ansible_ssh_host=192.168.13.211 ansible_ssh_port=22 prefix=vm01_pre3 var=common-var suffix=vm01_suf3
vm-master-03 ansible_ssh_host=192.168.13.225 ansible_ssh_port=22 prefix=vm03_pre3 var=common-var suffix=vm03_suf3
# 2、var_common.yaml文件内容
[root@vm-master-01 ansible]# cat var_common.yaml
---
- hosts: test
remote_user: root
tasks:
- name: exec shell echo {{ var }}
shell: echo {{ prefix }}_{{ var }}_{{ suffix }} > /tmp/var.txt
执行命令:ansible-playbook -i /etc/ansible/hosts /etc/ansible/var_common.yaml结果如下:
4.7.4、公共(组)变量
# 1、hosts文件内容
[root@vm-master-01 ansible]# head -n 7 hosts
[test]
vm-master-01 ansible_ssh_host=192.168.13.211 ansible_ssh_port=22
vm-master-03 ansible_ssh_host=192.168.13.225 ansible_ssh_port=22
[test:vars]
prefix=test_pre4
var=group-var
suffix=test_suf4
# 2、var_group.yaml文件内容
[root@vm-master-01 ansible]# cat var_group.yaml
---
- hosts: test
remote_user: root
tasks:
- name: exec shell echo {{ var }}
shell: echo {{ prefix }}_{{ var }}_{{ suffix }} > /tmp/var.txt
执行命令:ansible-playbook -i /etc/ansible/hosts /etc/ansible/var_group.yaml结果若下:
4.7.5、变量优先级比较
# 1、hosts文件内容
[root@vm-master-01 ansible]# head -n 8 /etc/ansible/hosts
[test]
vm-master-01 ansible_ssh_host=192.168.13.211 ansible_ssh_port=22 prefix=vm01_pre3 var=common-var suffix=vm01_suf3
vm-master-03 ansible_ssh_host=192.168.13.225 ansible_ssh_port=22 prefix=vm03_pre3 var=common-var suffix=vm03_suf3
[test:vars]
prefix=test_pre4
var=group-var
suffix=test_suf4
# 2、var_priority.yaml文件内容
[root@vm-master-01 ansible]# cat /etc/ansible/var_priority.yaml
---
- hosts: test
remote_user: root
vars:
- prefix: pre2
- var: playbook-var
- suffix: suf2
tasks:
- name: exec shell echo {{ var }}
shell: echo {{ prefix }}_{{ var }}_{{ suffix }} > /tmp/var.txt
命令行传入变量执行命令:ansible-playbook -i /etc/ansible/hosts -e "prefix=pre1" -e "var=command-line-var" -e "suffix=suf1" /etc/ansible/var_priority.yaml结果如下:
命令行不传入变量执行命令:ansible-playbook -i /etc/ansible/hosts /etc/ansible/var_priority.yaml 结果如下:
# 只修改/etc/ansible/var_priority.yaml将playbook变量删掉:
[root@vm-master-01 ansible]# cat /etc/ansible/var_priority.yaml
---
- hosts: test
remote_user: root
tasks:
- name: exec shell echo {{ var }}
shell: echo {{ prefix }}_{{ var }}_{{ suffix }} > /tmp/var.txt
命令行不传入变量数执行命令:ansible-playbook -i /etc/ansible/hosts /etc/ansible/var_priority.yaml 结果如下:
补充:对于"ansible setup facts"中的变量,我们可以借助于:ansible vm-01 -m setup命令查看,我做了实验,但没有截图在这里,他的优先级要高于普通变量低于playbook变量
由以上例子可证明优先级排序为:命令行变量>playbook变量>ansible setup facts中的变量>普通变量>公共(组)变量,在执行playbook时会使用优先级高的变量。
4.7.6、将变量写入单独的yaml文件中
[root@vm-master-01 ansible]# cat vars.yaml
prefix: file_pre5
var: var-file
suffix: file_suf5
[root@vm-master-01 ansible]# cat var_file.yaml
---
- hosts: test
remote_user: root
vars_files:
- vars.yaml
tasks:
- name: exec shell echo {{ var }}
shell: echo {{ prefix }}_{{ var }}_{{ suffix }} > /tmp/var.txt
# 此时的变量相当于playbook的变量,优先级也一样,变量文件和var_file.yaml放在同级目录。
4.8、templates
4.8.1、templates语法
文本文件,嵌套有脚本(使用模版编程语言编写)
jinja2语言,使用字面量,有下面形式:
字符串:使用单引号或双引号
数字:整数,浮点数
列表:[item1,item2,...]
元组:{item1,item2,...}
字典:{key1:value1,key2:value2,...}
布尔型:true、false
算术运算:+,-,*,/,//,%,**
比较操作:==,!=,>,>=,<,<=
逻辑运算:and,or,not
流表达式:for if when
template文件必须以.j2结尾
4.8.2、一个例子
需要在vm-03上安装nginx,并将其开启的worker进程数量在auto(cpu的核心数)的基础上+2
# 1、ansible setup模块确定auto的变量名称
[root@vm-master-01 ansible]# ansible vm-03 -m setup | grep ansible_processor_vcpus
[WARNING]: Invalid characters were found in group names but not replaced, use -vvvv to see details
"ansible_processor_vcpus": 8, # vm-03有8个core
# 2、nginx.conf.j2配置<次文件放在templates目录下,templates目录和nginx.yaml文件平级,执行playbook时会自动寻找到对应名称的模版文件>
[root@vm-master-01 ansible]# head -n 6 templates/nginx.conf.j2
# For more information on configuration, see:
# * Official English Documentation: http://nginx.org/en/docs/
# * Official Russian Documentation: http://nginx.org/ru/docs/
user nginx;
worker_processes {{ ansible_processor_vcpus + 2 }}; # ansible_processor_vcpus由"1"获取
# 3、nginx.yaml
[root@vm-master-01 ansible]# cat nginx.yaml
---
- hosts: vm-03
remote_user: root
tasks:
- name: install nginx
yum: name=nginx
- name: copy templates nginx.conf.j2
template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf
- name: start nginx
service: name=nginx state=started enabled=true
执行命令:ansible-playbook -i /etc/ansible/hosts /etc/ansible/nginx.yaml结果如下:
执行命令:ansible vm-03 -m shell -a "grep worker_processes /etc/nginx/nginx.conf"查看vm-03上nginx的配置文件:
4.9、when
1、条件测试:如果需要根据变量、facts或者此前任务的执行结果来做为某个task执行与否的前提时要用到条件测试,通过when语句实现,在task中使用jinja2的语法格式
2、使用:在task后添加when子句即可使用条件测试;when语句支持jinja2表达式语法
3、例子:
name: "if RedHat exec command"
command: hostname
when: ansible_os_family == "RedHat"
# 1、需求:我们修改上面nginx配置文件的需求,假如vm-03中"ansible_os_family!=RedHat",我们就不传递vm-01上修改过的nginx.conf文件,而使用vm-03上原有的配置文件
# 2、说明:vm-03上ansible_os_family为"RedHat",可以通过setup模块获取,作为演示:我已经卸载了vm-03上面已经安装过的nginx
# 2、nginx.yaml
[root@vm-master-01 ansible]# cat nginx.yaml
---
- hosts: vm-03
remote_user: root
tasks:
- name: install nginx
yum: name=nginx
- name: copy templates nginx.conf.j2
template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf
when: ansible_os_family != "RedHat"
- name: start nginx
service: name=nginx state=started enabled=true
执行相关命令结果如下:
从上面例子可以看出:由于when条件不成立,并没有执行template操作。
4.10、with_items
with_items表迭代:当有重复执行的任务时,可以使用迭代机制
使用固定变量"item"作为迭代项的引用
使用with_items给定迭代的元素列表:
字符串
字典等
# 下面yaml文件将在vm-03主机的/tmp目录下创建三个文件:f1、f2、f3
[root@vm-master-01 ansible]# cat with_items.yaml
---
- hosts: vm-03
remote_user: root
tasks:
- name: create files
file: name=/tmp/{{ item }} state=touch
when: ansible_os_family == "RedHat"
with_items:
- f1
- f2
- f3
执行命令:ansible-playbook -i /etc/ansible/hosts /etc/ansible/with_items.yaml得到的结果如下:
4.11、for流程控制
# 1、for.yaml文件内容
[root@vm-master-01 ansible]# cat for.yaml
---
- hosts: vm-03
remote_user: root
vars:
people:
- name: zhangsan
age: 23
high: 1.68
- name: lisi
high: 1.70
- name: wangwu
age: 25
high: 1.71
tasks:
- name: copy file
template: src=for1.txt.j2 dest=/tmp/for1.txt
# 2、for1.txt.j2文件内容:
[root@vm-master-01 ansible]# cat templates/for1.txt.j2
{% for p in people %}
info of people {
name: {{ p.name }}
{% if p.age is defined %}
age: {{ p.age }}
{% endif %}
hige: {{ p.high }}
}
{% endfor %}
执行yaml文件并查看vm-03下的文件/tmp/for1.txt的内容:
4.12、register
假如有这样一个需求,使用shell命令检查nginx进程是否存在,当不存在时就重启。我们可以通过register将使用ps命令抓取的进程数赋值给info,然后通过info的相应字段(从下面截图中应该可以看出相应的字段为info.stdout)来判断进程数是否为0,为0则重启。
[root@vm-master-01 ansible]# cat sum_process_nginx.yml
---
- hosts: vm-master-03
gather_facts: no
tasks:
- name: get nginx process
shell: ps -ef | grep nginx | grep -v grep | wc -l
register: info
- name: display vars
debug:
var: info
- name: if sum(nginx process) < 1 then start it
service: name=nginx
state=started
when: info.stdout == "0"
- name: check if nginx started success or not
shell: systemctl status nginx
执行命令:ansible-playbook sum_process_nginx.yml结果如下:
五、ansible-playbook生产实践
5.1、说明
基于上面介绍,对于nginx的安装,做一个生产级别的介绍
5.2、项目结构
[root@vm-master-01 ansible]# pwd
/etc/ansible
[root@vm-master-01 ansible]# tree ./
./
├── ansible.cfg
├── hosts
├── nginx_role.yml
└── roles
├── mysql
├── nginx
│ ├── files
│ │ └── index.html
│ ├── handlers
│ │ ├── main.yml
│ │ └── restart.yml
│ ├── tasks
│ │ ├── copyfile.yml
│ │ ├── group.yml
│ │ ├── main.yml
│ │ ├── start.yml
│ │ ├── template.yml
│ │ ├── user.yml
│ │ └── yum.yml
│ ├── templates
│ │ └── nginx.conf.j2
│ └── vars
│ └── main.yml
├── redis
└── supervisor
├── files
├── handlers
├── tasks
├── templates
└── vars
15 directories, 15 files
下面我分别贴下没个文件的内容。
5.2.1、role
关于role的各个目录做下简要的概述
files:存放由copy、script模块等调用的文件
templates:template模块查找所需要模块文件的目录
tasks:定义task,至少包含一个名为main.yml的文件,其他文件需要再次文件中通过include进行包含
handlers:至少应该包含一个名为main.yml的文件,其他文件需要在此文件中通过include进行包含
vars:定义变量,至少包含一个名为main.yml的文件,其他文件需要在此文件中通过include进行包含
meta:定义当前角色的特殊设定以及其依赖关系,至少应该包含一个名为main.yml的文件,其它文件需要在此文件中通过include进行包含
default:设定默认变量时使用此目录中的main.yml文件
[root@vm-master-01 ansible]# cat nginx_role.yml
- hosts: vm-03
remote_user: root
roles:
- { role: nginx, tags: ['app', 'ops'], when: ansible_os_family == 'RedHat' }
- { role: mysql, tags: 'app' }
5.2.2、files目录
[root@vm-master-01 files]# ls
index.html
[root@vm-master-01 files]# cat index.html
nginx playbook
5.2.3、handlers目录
[root@vm-master-01 handlers]# ls
main.yml restart.yml
[root@vm-master-01 handlers]# cat main.yml
- include: restart.yml
[root@vm-master-01 handlers]# cat restart.yml
- name: nginx restart
service: name=nginx state=restarted
5.2.4、tasks目录
[root@vm-master-01 tasks]# ls
copyfile.yml group.yml main.yml start.yml template.yml user.yml yum.yml
[root@vm-master-01 tasks]# cat copyfile.yml
- name: copy index.html
copy: src=index.html dest=/usr/share/nginx/html/
notify: nginx restart
[root@vm-master-01 tasks]# cat group.yml
- name: create group
group: name={{ username }} gid=88
[root@vm-master-01 tasks]# cat main.yml
- include: group.yml
- include: user.yml
- include: yum.yml
- include: template.yml
- include: start.yml
- include: copyfile.yml
[root@vm-master-01 tasks]# cat start.yml
- name: start service
service: name=nginx state=started enabled=yes
[root@vm-master-01 tasks]# cat template.yml
- name: copy file
template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf
[root@vm-master-01 tasks]# cat user.yml
- name: create user
user: name={{ username }} group={{ groupname }} system=yes shell=/sbin/nologin
[root@vm-master-01 tasks]# cat yum.yml
- name: install nginx
yum: name=nginx
5.2.5、templates目录
[root@vm-master-01 templates]# ls
nginx.conf.j2
[root@vm-master-01 templates]# cat nginx.conf.j2
# For more information on configuration, see:
# * Official English Documentation: http://nginx.org/en/docs/
# * Official Russian Documentation: http://nginx.org/ru/docs/
user {{ username }};
worker_processes {{ ansible_processor_vcpus + 2 }};
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;
# Load dynamic modules. See /usr/share/doc/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;
events {
worker_connections 1024;
}
http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 4096;
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Load modular configuration files from the /etc/nginx/conf.d directory.
# See http://nginx.org/en/docs/ngx_core_module.html#include
# for more information.
include /etc/nginx/conf.d/*.conf;
server {
listen 80;
listen [::]:80;
server_name _;
root /usr/share/nginx/html;
# Load configuration files for the default server block.
include /etc/nginx/default.d/*.conf;
error_page 404 /404.html;
location = /404.html {
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
}
# Settings for a TLS enabled server.
#
# server {
# listen 443 ssl http2;
# listen [::]:443 ssl http2;
# server_name _;
# root /usr/share/nginx/html;
#
# ssl_certificate "/etc/pki/nginx/server.crt";
# ssl_certificate_key "/etc/pki/nginx/private/server.key";
# ssl_session_cache shared:SSL:1m;
# ssl_session_timeout 10m;
# ssl_ciphers HIGH:!aNULL:!MD5;
# ssl_prefer_server_ciphers on;
#
# # Load configuration files for the default server block.
# include /etc/nginx/default.d/*.conf;
#
# error_page 404 /404.html;
# location = /40x.html {
# }
#
# error_page 500 502 503 504 /50x.html;
# location = /50x.html {
# }
# }
}
5.2.6、vars目录
[root@vm-master-01 vars]# ls
main.yml
[root@vm-master-01 vars]# cat main.yml
groupname: nginx
username: nginx
5.3、执行playbook
执行命令:ansible-playbook -i /etc/ansible/hosts --tags=app nginx_role.yml结果如下:
5.4、浏览器访问
六、注意
1、我上面执行ansible或ansible-playbook时,结果中多处有提示:[WARNING]: Invalid characters were found in group names but not replaced, use -vvvv to see details
是因为我hosts中资源清单的组中有使用符号“-",这不符合相关语法规范,修改掉即可。