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:有多种取值。
  1. directory 如果目录不存在,创建目录
  2. file 即使文件不存在也不会被创建,存在则返回文件的信息
  3. link 创建软链接
  4. hard 创建硬链接
  5. touch 如果文件不存在,创建一个新的文件,如果文件或目录已存在,更新其最后访问时间和修改时间
  6. 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 支持三种格式的数据:
  1. 对象:键值对的集合,又称为映射,类似于 Python 中的字典
  2. 数组:一组按次序排列的值,又称为序列,类似于 Python 中的列表
  3. 纯量:单个的、不可再分的值,如字符串、布尔值和数字

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 文件。查找顺序如下:

  1. ANSIBLE_CONFIG 环境变量指定的配置文件
  2. 当前目录下的 ansible.cfg 文件
  3. 当前用户家目录下的 .ansible.cfg 文件
  4. 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...
    ...