1. Ansible 介绍
Ansible 是一个简单的自动化引擎,可以完成配置管理、应用部署、服务编排等需求。Ansible 是一款使用 Python 语言开发实现的开源软件,依赖 Jinja2、paramiko 和 PyYAML。
2. Ansible 的优点
① 安装部署简单:Ansible 只需在主控端部署环境,被控端无须做任何操作。
② 基于 SSH 进行配置管理,充分利用现成的机制。
③ 不需要守护进程。
④ 日志集中存储:所有操作日志都存储在 Ansible 发起服务器。
⑤ 简单易用:运行一个部署命令就可以完成应用的部署。此外,Ansible 使用 YAML 语法管理配置,YAML 本身是一种可读性非常强的标记语言。
⑥ 功能强大:通过模块来实现各种功能。
⑦ 设计优秀,便于分享:使用 role 组织 Playbook 并提供了分享 role 的平台(galaxy.ansible.com)。
⑧ 对云计算和大数据平台都有很好的支持。
3. Ansible 的安装
pip install ansible
安装完成后,控制端服务器会增加以下可执行程序:
- ansible
- ansible-doc
- ansible-playbook
- ansible-vault
- ansible-console
- ansible-galaxy
- ansible-pull
4. Ansible 的运行环境
使用 Ansible 操作远程服务器时,首先需要确定的是操作哪些服务器,然后再确定执行哪些操作。Ansible 会默认读取 /etc/ansible/hosts 文件中配置的远程服务器列表。例如:
cat /etc/ansible/hosts
[test1]
10.10.1.1
10.10.1.2
[test2]
10.10.1.3
Ansible 默认使用当前用户和默认的 22 端口号与远程服务器建立 SSH 连接,如果需要其他用户或端口号,可以自行配置:
cat /etc/ansible/hosts
[test1]
10.10.1.1 ansible_user=abc ansible_port=1234
10.10.1.2 ansible_user=abc ansible_port=1234
[test2]
10.10.1.3 ansible_user=abc ansible_port=1234
Ansible 默认使用 /etc/ansible/ansible.cfg 文件,可以在这个配置文件中设定一些默认值:
cat /etc/ansible/ansible.cfg
[defaults]
remote_port = 1234
remote_user = abc
5. Ansible 的 ad-hoc 模式
格式:
ansible {服务器组名} -m {模块名} -a "参数"
例子:
ansible test -m ping
ansible test -m command -a "hostname"
ansible test -m copy -a "src=/tmp/data.txt dest=/tmp/data.txt"
-become 参数的作用等同于 sudo
其他参数:
-k,--ask-pass 登录密码,提示输入SSH密码而不是假设基于密钥的验证
--ask-su-pass su切换密码
-K,--ask-sudo-pass 提示密码使用sudo,sudo表示提权操作
--ask-vault-pass 假设我们设定了加密的密码,则用该选项进行访问
-B SECONDS #后台运行超时时间
-C #模拟运行环境并进行预运行,可以进行查错测试
-c CONNECTION #连接类型使用
-f FORKS #并行任务数,默认为5
--list-hosts #查看有哪些主机组
-o #压缩输出,尝试将所有结果在一行输出,一般针对收集工具使用
-S #用 su 命令
-s #用 sudo 命令
-R SU_USER #指定 su 的用户,默认为 root 用户
-U SUDO_USER #指定 sudo 到哪个用户,默认为 root 用户
-T TIMEOUT #指定 ssh 默认超时时间,默认为10s,也可在配置文件中修改
-u REMOTE_USER #远程用户,默认为 root 用户
-v #查看详细信息,同时支持-vvv,-vvvv可查看更详细信息
6. Ansible 的 Inventory 管理
(1)文件位置
Inventory 指的是可管理的服务器的集合。
在 Ansible 中,有三种指定 hosts 文件的方式:
① 默认读取 /etc/ansible/hosts 文件
② 通过命令行参数的 -i 指定 hosts 文件
③ 通过 ansible.cfg 文件中的 inventory 选项指定。
ansible test -i hosts --list-hosts
这里的 --list-hosts 的功能是列出匹配的服务器列表。
也可以在 ansible.cfg 中指定:
cat /etc/ansible/ansible.cfg
[defaults]
remote_port = 1234
remote_user = abc
inventory = /home/abc/hosts
(2)灵活定义和匹配 hosts 文件内容
可以将服务器分组:
test.example.com
[webservers]
foo.example.com
bar.example.com
[dbservers]
one.example.com
two.example.com
分组后,可以使用组的名称匹配改组下的所有服务器:
$ ansible webservers --list-hosts
hosts (2):
foo.example.com
bar.example.com
all 或 * 代表所有服务器:
$ ansible '*' --list-hosts
$ ansible all --list-hosts
hosts (5):
test.example.com
foo.example.com
bar.example.com
one.example.com
two.example.com
也可以定义一个包含服务器组名称的组,其中的“:children”用来声明这是一个特殊的组:
[common:children]
webservers
dbservers
定义格式也可以更简单:
[webservers]
web[1:3].example.com
[dbservers]
db[a:b].example.com
$ ansible all --list-hosts
hosts (5):
web1.example.com
web2.example.com
web3.example.com
dba.example.com
dbb.example.com
服务器匹配规则:
规则 | 含义 |
192.168.0.1 或 web.example.com | 匹配目标 IP 地址或服务器名,如果有多个 IP 或服务器,使用“:”分隔。 |
webservers | 匹配目标组为 webservers,多个组使用“:”分隔。 |
all 或 '*' | 匹配所有服务器 |
webservers:!dbservers | 匹配在 webservers 中,不在 dbservers 中的服务器 |
webservers:&dbservers | 匹配同时在 webservers 和 dbservers 中的服务器 |
*.example.com 或 192.168.* | 使用通配符进行匹配 |
webservers[0],webservers[1:],webservers[-1] | 使用索引或切片操作的方式匹配组中的服务器 |
~ (web|db).*.example.com | 以 ~ 开头的匹配表示使用正则表达式匹配 |
(3)Inventory 行为参数
行为参数指的是在指定服务器地址时,可以同时指定的一些参数:
名称 | 默认值 | 描述 |
ansible_host | 主机的名称 | ssh 目的主机名或 IP |
ansible_user | 当前用户 | ssh 连接的主机名 |
ansible_port | 22 | ssh 连接的端口号 |
ansible_ssh_private_key_file | none | ssh 连接使用的私钥 |
ansible_connection | smart | Ansible 使用的连接模式,取值为 smart、ssh 或 paramiko |
ansible_become | none | 类似于 linux 下的 sudo |
ansible_become_user | none | 切换到哪个用户执行命令 |
ansible_shell_type | sh | 执行命令所用的 shell |
ansible_python_interpreter | /usr/bin/python | 使用的 python 的解释器 |
ansible_*_interpreter | none | 指定任意语言的解释器 |
可以通过 ansible.cfg 更改默认值。
(4)定义普通变量
在 hosts 文件中也可以定义普通变量。例如:
[test]
10.10.1.1 mysql_port=3306
10.10.1.2 mysql_port=3307
可以查看这个普通变量的值:
ansible all -a 'echo {{mysql_port}}'
10.10.1.1 | SUCCESS | rc=0 >>
3306
10.10.1.2 | SUCCESS | rc=0 >>
3307
如果一个组里边同一个变量的取值相同,也可以定义组变量:
[test]
10.10.1.1
10.10.1.2
[test:vars]
mysql_port=3306
Ansible 提供了更好的方法来管理服务器与群组的变量,即为每个服务器和群组创建独立的变量文件。将组的变量存放在一个名为 group_vars 目录下,目录下的文件名与组的名称相同,文件的扩展名可以是 .yml 或 .yaml,也可以没有扩展名。服务器的变量存放在一个名为 host_vars 的目录下,该目录下的文件名为服务器的名称。
Ansible 将依次在 playbook 所在的目录、hosts 文件所在的目录和 /etc/ansible 目录寻找 group_vars 目录和 host_vars 目录。这里假设位于 /etc/ansible 目录下:
$ cd /etc/ansible
$ tree
.
|---- ansible.cfg
└---- group_vars
| └---- test.yaml
|---- hosts
└---- host_vars
└---- 127.0.0.1.yaml
$ cat group_vars/test.yaml
mysql_port: 3306
7. Ansible 模块
(1)介绍
Ansible 对远程服务器的操作实际上是通过模块完成的,其工作原理如下:
1)将模块拷贝到远程服务器
2)执行模块定义的操作,完成对服务器的修改
3)在远程服务器中删除模块
Ansible 中的模块是幂等的,多次执行的操作,只有第一次生效。
ansible-doc -l # 查看模块列表
ansible-doc {模块名} # 获取模块的帮助信息
ansible-doc -l {模块名} # 获取模块的帮助信息
(2)常用模块
① ping 模块
ping 模块用来测试现有的 ssh 参数是否能够顺利连通远程服务器。
ansible test -m ping
② 远程命令模块
command 是默认模块,可以不指定模块名称直接运行 linux 命令。command 模块的重要选项有:
- chdir:在执行指令之前,先切换到指定的目录
- executable:切换 shell 来执行命令
command 模块在执行 linux 命令时不能使用管道。
ansible test -a 'hostname'
ansible test -m command -a 'hostname'
如果执行的命令需要使用管道,可以使用 raw 模块,raw 模块相当于使用 ssh 直接执行 linux 命令,不会进入到 Ansible 的模块子系统中。也可以使用 shell 模块,shell 模块还可以执行远程服务器上的 shell 脚本文件,脚本文件需要使用绝对路径。
ansible test -m raw -a 'cat /etc/passwd | wc -l'
ansible test -m shell -a 'cat /etc/passwd | wc -l'
ansible test -m shell -a '/home/abc/test.sh'
script 模块可以在远程服务器上执行主控节点中的脚本文件。
ansible test -m script -a 'test.sh'
③ file 模块
file 模块主要用于对远程服务器上的文件(包括链接和目录)进行操作,包括修改文件的权限、修改文件的所有者、创建文件、删除文件等。
重要选项有:
- path:指定文件/目录的路径
- recurse:递归设置文件属性,只对目录有效
- group:定义文件/目录的组
- mode:定义文件/目录的权限
- owner:定义文件/目录的所有者
- src:被链接的源文件路径,只应用于 state 为 link 的情况
- dest:被链接到的路径,只应用于 state 为 link 的情况
- force:在两种情况下会强制创建软链接,一种是源文件不存在但之后会建立的情况;另一种是目标软链接已存在,需要先取消之前的软链接,然后创建新的软链接,默认取值为 no
- state:有多种取值。
- directory 如果目录不存在,创建目录
- file 即使文件不存在也不会被创建,存在则返回文件的信息
- link 创建软链接
- hard 创建硬链接
- touch 如果文件不存在,创建一个新的文件,如果文件或目录已存在,更新其最后访问时间和修改时间
- absent 删除目录、文件或链接
# 创建一个目录
ansible test -m file -a 'path=/tmp/aaa state=directory mode=0755'
# 修改文件的权限
ansible test -m file -a 'path=/tmp/aaa state=touch mode="u=rw,g=r,o=r"'
# 创建软链接
ansible test -m file -a 'src=/tmp/aaa dest=/tmp/bbb owner=abc group=abc state=link'
# 修改文件的所有者
ansible test -m file -a 'path=/tmp/aaa owner=root group=root mode=0644' -become
④ copy 模块
将主控节点的文件或目录拷贝到远程服务器上,同时也可以设置文件在远程服务器的权限和所有者。
- src:要复制到远程服务器的文件地址,可以是绝对路径或相对路径,如果路径是目录,将递归复制。如果路径以 “/” 结尾,只复制目录里的内容,否则复制包含目录在内的整个内容。
- dest:文件复制的目的地,必须是绝对路径,如果源文件是一个目录,dest 指向也必须是一个目录。
- force:默认值为 yes,表示目标服务器包含该文件,但内容不同时会强制覆盖。如果该选项设置为 no,则只有当目标服务器的目标位置不存在于该文件时,才会进行复制。
- backup:默认值为 no,如果配置为 yes,在覆盖之前将原文件进行备份。
- directory_mode:递归设定目录权限,默认为系统默认权限。
- 所有 file 模块的选项都可以在这里使用
# 拷贝文件到远程服务器
ansible test -m copy -a "src=test.sh dest=/tmp/test.sh"
# 拷贝文件到远程服务器,如果文件已经存在,备份该文件
ansible test -m copy -a "src=test.sh dest=/tmp/test.sh backup=yes force=yes"
# 拷贝文件到远程服务器,并且修改文件的所有者和权限
ansible test -m copy -a "src=test.sh dest=/tmp/test.sh owner=root group=root mode=644 force=yes" -become
⑤ user 和 group 模块
user 模块请求的是 useradd、userdel、usermod 三个指令,group 模块请求的是 groupadd、groupdel、groupmod 三个指令。
- name:需要操作的用户名(或组名)
- comment:用户的详细描述
- createhome:创建用户时,是否创建家目录,默认为 yes
- home:指定用户的家目录,需要与 createhome 选项配合使用
- groups:指定用户的属组
- uid:设置用户的 uid
- gid:设置组的 gid
- password:设置用户的密码
- update_password:假如设置的密码不同于原密码,会更新密码
- state:是创建用户(组)还是删除用户(组),取值包括 present 和 absent
- expires:用户的过期时间
- shell:指定用户的 shell 环境
- generate_ssh_key:设置为yes将会为用户生成密钥,这不会覆盖原来的密钥
- ssh_key_type:指定用户的密钥类型,默认rsa,具体类型取决于被管理节点
- remove:当与state=absent一起使用时,删除一个用户及关联的目录,比如家目录, 邮箱目录,yes|no
# 创建一个用户
ansible test -m user -a 'name=abc comment="test" uid=1234 group=root' -become
# 删除一个用户
ansible test -m user -a 'name=abc state=absent' -become
# 创建一个用户,并产生一对秘钥
ansible test -m user -a 'name=abc comment="test" generate_ssh_key=yes ssh_key_bits=2048' -become
# 创建组
ansible test -m group -a "name=ansible state=present gid=1234" -become
# 删除组
ansible test -m group -a "name=ansible state=absent" -become
⑥ apt 模块
在 Debian/Ubuntu 系统中安装、删除软件
- name:软件包的名称
- state:软件包的状态,可以取值为 latest、absent、present、build-dep,默认为 present
- autoremove:默认值为 no,如果值为 yes,移除不需要的软件包
- force:强制安装或删除软件包
- update_cache:相当于 apt-get update
- deb:deb 文件的路径
# 安装软件包
ansible test -m apt -a "name=git state=present" -become
# 卸载软件包
ansible test -m apt -a "name=git state=absent" -become
# 更新源
ansible test -m apt -a "update_cache=yes" -become
⑦ yum 模块
在 Redhat 或 centOS 系统中安装、删除软件
- name:软件包名
- state:软件包的状态,可以取值为 present、installed、latest、absent 和 removed
- disable_gpg_check:禁用 rpm 包的公钥 gpg 验证
- enable_repo:指定安装软件包时临时启用的yum源
- disable_repo:指定安装软件包时临时禁用的yum源
# 安装软件包
ansible test -m yum -a "name=git state=present" -become
# 卸载软件包
ansible test -m yum -a "name=git state=absent" -become
⑧ get_url 模块
从互联网下载数据到本地,可以控制下载后的数据所有者、权限以及检查下载数据的 checksum 等
- url:文件的下载地址
- dest:文件保存的绝对路径
- mode:文件的权限
- checksum:文件的校验码
- headers:传递给下载服务器的 HTTP Headers
- backup:如果本地已经存在同名文件,则备份文件
- timeout:下载的超时时间
- 所有 file 模块的选项都可以在这里使用
⑨ unarchive 模块
用于解压文件,将控制节点的压缩包拷贝到远程服务器
- remote_src:yes 表示解压的文件存在远程服务器中,no 表示解压的文件存在控制节点所在的服务器中,默认值为 no,表示解压之前先把文件拷贝到远程主机中。
- src:制定压缩文件的路径,所在服务器取决于 remote_src 的取值。
- dest:远程服务器上的绝对路径,表示压缩文件解压的路径。
- list_files:默认 no。取 yes 时,在解压的之后的返回值中列出压缩包里的文件。
- exclude:解压文件时排除的文件或目录
- keep_newer:默认 False。如果为 True,当目标地址中存在同名的文件且文件比压缩包中的文件更新时,不进行覆盖。
- owner:解压后的所有者
- group:解压后的所属的组
- mode:解压后的权限
# 解压本地文件
ansible test -m unarchive -a "src=data.tar.gz dest=/tmp/data list_files=yes"
# 解压远程的文件
ansible test -m unarchive -a "src=/tmp/data.tar.bz2 dest=/tmp remote_src=yes"
⑩ git 模块
在远程服务器执行 git 相关的操作
- repo:远程 git 库的地址,可以是一个 git 协议、ssh 协议或 http 协议 的 git 库地址
- dest:git 库 clone 到本地服务器后保存的绝对路径
- version:git 库的版本,可以取 HEAD、分支的名称、tag 的名称,也可以是一个 commit 的 hash 值
- force:默认 no。如果 yes,如果本地 git 库有修改,会抛弃本地的修改
- accept_hostkey:如果 git 库的服务器不在 know_hosts 中,则添加到 know_hosts 中,key_file 指定克隆远程 git 库地址时使用的私钥
ansible test -m git -a "repo=https://github.com/kennethreitz/requests.git dest=/tmp/requests version=HEAD"
⑪ stat 模块
用于获取远程服务器上的文件信息,可以获取 atime、ctime、mtime、checksum、size、uid、gid 等信息
ansible test -m stat -a "path=/etc/passwd"
⑫ cron 模块
管理计划任务
- backup:默认 no,表示修改之前先备份
- state:present 或 absent,表示创建还是删除该计划任务
- name:计划任务的描述
- job:添加或删除任务,取决于 state 的取值
- user:指定 crontab 所属用户
- cron_file:如果指定该选项,用该文件替换远程服务器上的 cron.d 目录下的用户任务计划
- month weekday day minute hour:指定 crontab 的时间参数
ansible test -m cron -a 'backup=yes name="test cron" minute=*/2 hour=* job="ls /tmp > /dev/null"'
⑬ service 模块
相当于 service 命令,用来启动、停止、重启服务
- name:服务的名称
- state:可以取值为 started、stopped、restarted 和 reloaded。started 和 stopped 是幂等的。如果服务已经启动了,执行 started 不会执行任何操作。
- sleep:重启的过程中,先停止服务然后 sleep 几秒再启动
- pattern:定义一个模式,Ansible 首先通过 status 命令查看服务的状态,判断服务是否在运行。如果通过 status 查看服务状态时没有响应,Ansible 会尝试匹配 ps 命令的输出,当匹配到相应的模式时,认为服务已经启动,否则认为没有启动。
- enabled:设置服务是否开机启动
# 停止 Apache
ansible test -m service -a "name=apache2 state=stopped"
# 重启 Apache
ansible test -m service -a "name=apache2 state=restarted"
⑭ systemd 模块
相当于 systemctl 命令,用来启动、停止、重启服务
- name:服务名称
- pattern:定义一个模式,Ansible 首先通过 status 命令查看服务的状态,判断服务是否在运行。如果通过 status 查看服务状态时没有响应,Ansible 会尝试匹配 ps 命令的输出,当匹配到相应的模式时,认为服务已经启动,否则认为没有启动。
- sleep:重启的过程中,先停止服务然后 sleep 几秒再启动
- state:可取值为 started、stopped、restarted、reloaded
- enabled:设置服务是否开机启动
- daemon_reload:重新载入 systemd
⑮ sysctl 模块
用于控制内核参数
- name:需要设置的参数
- value:需要设置的值
- sysctl_file:sysctl.conf 文件的绝对路径,默认路径是 /etc/sysctl.conf
- reload:默认yes,表示设置完成后是否需要执行 sysctl -p
# 设置 overcommit_memory 参数的值为 1
ansible test -m sysctl -a "name=vm.overcommit_memory value=1" -become
⑯ mount 模块
在远程服务器上挂载磁盘,如果挂载点指定的路径不存在,将创建该路径
- name:挂载点的路径
- state:可取值为 present、absent、mounted、unmounted,其中,mounted 和 unmounted 用来处理磁盘的挂载和卸载,且正确配置 fstab 文件,present 和 absent 只会设置 fstab 文件,不会操作磁盘
- fstype:指定文件系统类型,当 state 取值为 present 或 mounted 时,此为必选项
- src:挂载的设备
ansible test -m mount -a "name=/mnt/data src=/dev/vda fstype=ext4 state=mounted"
⑰ syncronize 模块
对 rsync 的封装
- src:需要同步的文件或目录
- dest:远程服务器保存数据的路径
- archive:默认值为 yes,相当于同时开启 recursive、links、perms、times、owner、group、-D 等选项
- compress:在同步过程中是否启用压缩
- delete:默认为 no,取值为 yes 时,表示删除 dest 中存在而 src 中不存在的文件
ansible test -m syncronize -a "src=test dest=/tmp"
(3)模块的返回值
执行模块操作后,Ansible 会根据不同的需要返回不同的结果。
返回值 | 含义 |
changed | 几乎所有 Ansible 模块都会返回该变量,表示模块是否对远程主机执行了修改操作 |
failed | 如果模块未能执行完成,返回 failed 为 true |
msg | 模块执行失败的原因,常见的错误如 ssh 连接失败,没有权限执行模块等 |
rc | 与命令行相关的模块会返回 rc,表示执行 linux 命令的返回码 |
stdout | 与 rc 类似,返回的是标准输出的结果 |
stderr | 与 rc 类似,返回的是标准错误的结果 |
backup_file | 所有存在 backup 选项的模块,用来返回备份文件的路径 |
results | 应用在 Playbook 中存在循环的情况,返回多个结果 |
8. YAML 语法
- YAML 文件的第一行为 “---”,表示这是一个 YAML 文件
- YAML 中的字段大小写敏感
- YAML 与 Python 一样,使用缩进表示层级关系
- YAML 的缩进不允许使用 Tab 键,只允许使用空格,且空格的数目不重要,只要相同层级的元素左侧对齐即可
- “#” 表示注释,从这个字符一直到行尾都会被解析器忽略
- YAML 支持三种格式的数据:
- 对象:键值对的集合,又称为映射,类似于 Python 中的字典
- 数组:一组按次序排列的值,又称为序列,类似于 Python 中的列表
- 纯量:单个的、不可再分的值,如字符串、布尔值和数字
9. Playbook
(1)定义
Playbook 的功能强大,可以实现各种高级功能,如指定任务的执行顺序,委派其他主机来执行某一个任务,与监控服务器和负载均衡组件进行交互等。Ansible 中的模块类似 linux 下的命令,Playbook 类似于 linux 下的 Shell 脚本文件。Playbook 将各个模块组合起来实现复杂的部署功能。
在 Ansible 中,一个 Play 必须包含以下两项:
- hosts:需要对哪些远程服务器执行操作
- tasks:需要在这些服务器上执行的任务列表
举个例子:
---
- hosts: dbservers
become: yes
become_method: sudo
tasks:
- name: install mongodb
apt: name=mongodb-server state=present
- hosts: webservers
tasks:
- name: copy file
copy: src=/tmp/data.txt dest=/tmp/data.txt
- name: change mode
file: dest=/tmp/data.txt mode=655 owner=abc group=abc
tasks 的格式是:
tasks:
- name: task1
module1: args1
- name: task2
module2: args2
...
也可以折叠换行:
- name: install apache
apt: >
name=apache2
update_cache=yes
state=present
- name: install apache
apt:
name: apache2
update_cache: yes
state: present
name 是可选的,所以可以不写。建议写上,方便使用者知道当前执行到哪一步。
在上边的例子中展示的 Playbook,可以拆分成两个 Playbook(db.yml 和 web.yml)。可以编写一个 all.yml:
---
- include: db.yml
- include: web.yml
在执行 all.yml 时,db.yml 和 web.yml 会依次执行。
(2)使用 ansible-playbook 执行 Playbook
ansible-playbook 的命令行选项:
- -T --timeout:建立 ssh 连接的超时时间
- --key-file --private-key:建立 ssh 连接的私钥文件
- -i --inventory-file:指定 Inventory 文件,默认是 /etc/ansible/hosts
- -f --forks:并发执行的进程数,默认为 5
- --list-hosts:匹配的服务器列表
- --list-tasks:列出任务列表
- --step:每执行一个任务后停止,等待用户确认
- --syntax-check:检查 Playbook 的语法
- -C --check:预测 Playbook 的执行结果
(3)详细语法
① 权限
在 Ansible 中,默认以当前用户连接远程服务器执行操作。可以在 ansible.cfg 文件中配置默认用户,也可以在 Play 定义中配置:
---
- hosts: webservers
remote_user: root
可以细分 task 对应的用户:
---
- hosts: webservers
remote_user: root
tasks:
- name: test connection
ping:
remote_user: abc
可以使用管理员身份执行操作:
---
- hosts: webservers
remote_user: abc
tasks:
- service: name=nginx state=started
become: yes
become_method: sudo
② 通知
通过 notify 和 handler 机制来实现。handler 是 Ansible 提供的条件机制,与 task 类似。但是 handler 只有在被 notify 触发后才会执行:
---
- hosts: webservers
tasks:
- name: ensure apache is at the latest version
yum: name=httpd state=latest
- name: write the apache config file
template: src=/srv/httpd.j2 dest=/etc/httpd.conf
notify:
- restart apache
- name: ensure apache is running
service: name=httpd state=started
handlers:
- name: restart apache
service: name=httpd state=restarted
handler 只会在 task 执行完之后执行,即使一个 handler 被触发多次,也只会执行一次。handler 是按照定义顺序执行。官方文档提到 handler 的唯一用途就是重启服务与服务器。
③ 变量
最直接的定义变量的方式是定义在 Playbook 的 vars 选项中:
- hosts: dbservers
vars:
mysql_port: 3306
在 Playbook 中定义的变量,可以在模板渲染时使用,例如,官方例子:
[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
user=mysql
port={{ mysql_port }}
当变量比较多时,可以将变量保存在一个独立的文件中,通过 vars_files 引用该文件:
---
- hosts: all
vars:
favcolor: blue
vars_files:
- /vars/external_vars.yml
tasks:
- name: this is just a placeholder
command: /bin/echo foo
保存变量的文件是一个 YAML 格式的字典:
---
somevar: somevalue
password: magic
在 Ansible 中,可以获取任务的执行结果,将执行结果保存在一个变量中。这样的变量使用 register 获取,也称为注册变量。
- hosts: webservers
tasks:
- shell: /usr/bin/foo
register: foo_result
ignore_errors: True
- shell: /usr/bin/bar
when: foo_result.rc == 5
ignore_errors 表示忽略当前 task 中的错误,when 是一个条件语句,条件为真时才会执行这个 task。
④ Facts 变量
在 Ansible 中,有一些特殊的变量,这些变量不需要进行任何设置就可以直接使用,这样的变量称为 Facts 变量。Facts 变量是 Ansible 执行远程部署之前从远程服务器中获取的系统信息。在 Playbook 中,可以通过 gather_facts 选项控制是否收集远程服务器的信息,默认值为yes。
---
- hosts: all
tasks:
- shell: echo {{ansible_os_family}}
...
when: ansible_os_family == "Debian"
要访问复杂变量的子属性时,需要使用嵌套结构:
ansible_eth0["ipv4"]["address"]
ansible_eth0.ipv4.address
⑤ 循环
举例:
- name: Install Mysql package
yum: name={{ item }} state=installed
with_items:
- mysql-server
- MySQL-python
- libselinux-python
- libsemanage-python
⑥ 条件
使用 when
tasks:
- name: shut down Debian flavored systems
command: /sbin/shutdown -t now
when: ansible_os_family == "Debian"
多个条件语句:
...
when:
- ansible_distribution == "CentOS"
- ansible_distribution_major_version == "6"
使用 and or:
...
when: (ansible_distribution == "CentOS" and ansible_distribution_major_version == "6") or
(ansible_distribution == "CentOS" and ansible_distribution_major_version == "7")
使用 jinja2 的过滤器:
...
when: result|failed
可以读取变量的取值:
...
epic: true
...
when: epic
可以和循环一起使用:
tasks:
- command: echo {{ item }}
with_items: [ 0, 2, 4, 6, 8, 10 ]
when: item > 5
⑦ 任务执行策略
从 Ansible 2.0 开始,支持 free 的任务执行策略,允许执行较快的远程服务器提前完成 Play 的部署:
- hosts: all
strategy: free
...
(4)高级语法
① 线性更新服务器
使用 Ansible 的 serial 选项,可以取值为一个数字,表示一次更新多少台服务器,也可以取值为一个百分比,例如:
- name: test play
hosts: webservers
serial: 1
- name: test play
hosts: webservers
serial: "30%"
下面的配置表示先更新 1 台,再更新 5 台,最后更新 10 台服务器:
- name: test play
hosts: webservers
serial:
- 1
- 5
- 10
② 使用 delegate_to 实现任务委派功能
- name: take out of load balancer pool
command: /usr/bin/take_out_of_pool {{ inventory_hostname }}
delegate_to: 127.0.0.1
③ 使用 local_action 在控制服务器执行操作
tasks:
- name: take out of load balancer pool
local_action: command /usr/bin/take_out_of_pool {{ inventory_hostname }}
④ 使用 run_once 保证任务只执行一次
...
run_once: true
⑤ 高级循环结构
除了 with_items 之外,还包含以下循环:
- with_lines
- with_fileglob
- with_first_found
- with_dict
- with_flattened
- with_indexed_items
- with_nested
- with_random_choice
- with_sequence
- with_together
- with_subelements
- with_file
with_items 的每一项都可以是字典,通过 item.key 的方式引用字典:
- name: Add several users
ansible.builtin.user:
name: "{{ item.name }}"
state: present
groups: "{{ item.groups }}"
with_items:
- { name: 'testuser1', groups: 'wheel' }
- { name: 'testuser2', groups: 'root' }
如果循环的元素是一个嵌套字典,需要使用 with_dict 遍历元素:
---
users:
alice:
name: Alice Appleworth
telephone: 123-456-7890
bob:
name: Bob Bananarama
telephone: 987-654-3210
tasks:
- name: Print phone records
debug:
msg: "User {{ item.key }} is {{ item.value.name }} ({{ item.value.telephone }})"
with_dict: "{{ users }}"
如果循环的每一项是列表,可以使用 with_nested 遍历,通过下标的方式访问列表中的元素:
- name: give users access to multiple databases
mysql_user:
name: "{{ item[0] }}"
priv: "{{ item[1] }}.*:ALL"
append_privs: yes
password: "foo"
with_nested:
- [ 'alice', 'bob' ]
- [ 'clientdb', 'employeedb', 'providerdb' ]
可以使用 with_sequence 产生数字列表,指定起点、终点和步长:
- user:
name: "{{ item }}"
state: present
groups: "evens"
with_sequence: start=0, end=32, format=testuser%02x
with_random_choice 随机选择一项:
- debug:
msg: "{{ item }}"
with_random_choice:
- "go through the door"
- "drink from the goblet"
- "press the red button"
- "do nothing"
⑥ 使用标签灵活控制执行
- name: install packages
yum: name={{ item }} state=installed
with_items:
- httpd
- memcached
tags:
- packages
- name: uploading config file
template: src=templates/src.j2 dest=/etc/foo.conf
tags:
- configuration
- name: be sure ntpd is running and enabled
service: name=ntpd state=started enabled=yes
tags: ntp
在这个例子中,用 tags 为每个任务打上了标签,使用下边的方法可以控制标签执行:
ansible-playbook example.yml --tags "configuration,packages"
ansible-playbook example.yml --skip-tags "ntp"
⑦ 使用 changed_when 控制对 changed 字段的定义
在使用 shell 模块时,往往会根据自己的判断报告是否对远程服务器进行了修改。
tasks:
- shell: /usr/bin/billybass --mode="take me to the river"
register: bass_result
changed_when: "bass_result.rc != 2"
⑧ 使用 failed_when 控制对 failed 字段的定义
对于一些特殊的命令,无法通过返回码判断命令是否执行成功,可以使用 failed_when 自定义命令执行失败的标准:
- name: this command prints FAILED when it fails
command: /usr/bin/example-command -x -y -z
register: command_result
failed_when: "'FAILED' in command_result.stderr"
10. role 的定义与使用
(1)role 的概念
role 是一种将复杂的 Playbook 分割成多个文件的机制,简化了 Playbook 的编写,使 Playbook 的复用变得简单。每个 role 都会有一个名字,比如 mongodb,与 mongodb role 相关的文件都存放在 /etc/ansible/roles/mongodb 目录中:
$ tree mongodb
mongodb
|-----defaults
| └----main.yml
|-----files
|-----handlers
| └----main.yml
|-----meta
| └----main.yml
|-----README.md
|-----tasks
| └----main.yml
|-----templates
└-----vars
└----main.yml
- defaults/main.yml:可以被覆盖的默认变量
- files:目录,保存了需要同步到远程服务器的文件
- handlers/main.yml:与 Playbook 中的 handlers 选项类似,包含了所有 handler
- meta/main.yml:role 的依赖信息
- README.md:role 的说明文件
- tasks/main.yml:包含了任务列表
- templates:目录,保存了 jinja2 模板文件
- vars/main.yml:不应该被覆盖的变量,与 Playbook 中的 vars 或 vars_file 类似
注意:这里的文件都是可选的。templates 和 files 目录中的文件都是相对引用,不需要写路径。
(2)使用 ansible-galaxy 管理 role
ansible-galaxy 的用法:
① 初始化一个 roles 的目录结构:
ansible-galaxy init /etc/ansible/roles/role_name
② 安装别人写好的 role:
ansible-galaxy install -p /etc/ansible/roles role_name
# 安装到指定目录
ansible-galaxy -p ./roles install role_name
Ansible 中,默认将 role 下载到 /etc/ansible/roles 目录,也可以在 ansible.cfg 中配置 role_path。
③ 列出已安装的 roles:
ansible-galaxy list
④ 查看已安装的 role 的信息:
ansible-galaxy info role_name
⑤ 卸载 role
ansible-galaxy remove role_name
(3)使用 role
在使用 role 之前需要编写一个 Playbook,例如:
---
- hosts: webservers
become: yes
become_method: sudo
roles:
- role: role_name
执行时使用以下格式:
ansible-playbook -i hosts role_name.yml
11. Ansible 的配置文件
在 Ansible 中,有多种方式使用 ansible.cfg 文件。查找顺序如下:
- ANSIBLE_CONFIG 环境变量指定的配置文件
- 当前目录下的 ansible.cfg 文件
- 当前用户家目录下的 .ansible.cfg 文件
- Ansible 默认的 /etc/ansible/ansible.cfg 文件
ansible.cfg 中常用的配置项:
1)默认配置
- inventory:指定 Inventory 文件的路径
- remote_user:ssh 连接时使用的用户名
- remote_port:ssh 连接时使用的端口号
- private_key_file:ssh 连接时使用的私钥文件
- roles_path:查找 role 的路径,可以指定多个查找路径,路径之间用冒号分隔
- log_path:Ansible 的日志文件路径
- host_key_checking:类似于 ssh 命令中的 StrictHostKeyChecking,当该选项设置为 False 时,不检查远程服务器是否存在于 know_hosts 文件中
- forks:并行进程的数量
- gathering:控制收集 Facts 变量的策略
2)ssh 连接配置
- ssh_args:控制 ssh 连接
- pipelining:多个 task 共享 ssh 连接
- control_path:保存 ControlPath socket 的路径
3)权限提升配置
- become:是否进行权限提升
- become_method:权限提升的方式,默认为 sudo
- become_user:提升为哪个用户的权限,默认为 root
- become_ask_pass:默认为 False,表示权限提升时不需要密码
12. Ansible 的文件组织
production # 生产环境服务器
staging # 测试环境服务器
group_vars/
group1
group2
host_vars/
hostname1
hostname2
library/ # 如果有自定义的模块,放到这里
filter_plugins # 如果有自定义的过滤插件,放到这里
site.yml # 主要的 playbook
webservers.yml # webserver 服务器的 playbook
dbservers.yml # dbserver 服务器的 playbook
roles/
common/ # 代表 role
tasks/
main.yml
handlers/
main.yml
templates/
ntp.conf.j2
files/
bar.txt
foo.sh
vars/
main.yml
defaults/
main.yml
meta/
main.yml
library/
lookup_plugins/
...
other roles...
...