Ansible

Table of Contents

1. Ansible

和 Puppet,SaltStack 等类似,Ansible 是另一个自动化运维工具。

Ansilbe 的优点在于它更加简单:Ansible 通过 SSH 协议将命令推送到被管理机器中,被管理机器中无需安装相应的 Agent;而 Puppet,SaltStack 等运维工具都采用 C/S 架构,需要在各个被管理机器中安装相应的 Agent。不过,当需要管理大量机器时,Ansible 的部署速度比其他运维工具要慢。

参考:
Ansible Documentation
Ansible Module Index
Ansible Playbooks
Ansible Best Practices

1.1. 安装 Ansible

Ansible 是基于 Python 开发的,在“控制服务器”中通过 pip 可以安装 Ansible,如:

$ sudo pip install ansible   # 在“控制服务器”中安装Ansible

一般地,“被管理机器”需要安装 Python(一般 Unix-like 系统中都会默认安装),此外,如果启用了 selinux,则被管理机器中需要安装 libselinux-python。

2. Ansible 基本使用

下面介绍 Ansible 的基本使用。

首先,把你想要管理的机器列表写入到某个文件(比如当前目录的 hosts 文件)中:

$ cat hosts
192.168.1.111    ansible_ssh_user=cig01
192.168.1.112

使用 ansible all -i hosts -m ping -k 可以测试一下所有被管理机器是否可连接,如:

$ ansible all -i hosts -m ping -k     # all表明“所有”机器;-i指定hosts文件(默认为/etc/ansible/hosts);-m指定要执行的模块;-k提示输入ssh登录密码
SSH password:
192.168.1.112 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
192.168.1.111 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}

说明 1:如果提前配置好 ssh 免密码登录(即使用证书登录),则不用使用 -k 参数,下文所有例子假定你已经配置好 ssh 免密码登录。
说明 2:如果是第一次登录被管理机器,则可能有下面提示:

The authenticity of host '192.168.1.111 (192.168.1.111)' can't be established.
RSA key fingerprint is 3f:be:d8:fd:b5:03:3e:16:cb:85:bd:b2:36:06:ab:6c.
Are you sure you want to continue connecting (yes/no)?

解决办法 1:设置环境变量 export ANSIBLE_HOST_KEY_CHECKING=False
解决办法 2:在文件/etc/ansible/ansible.cfg 或者~/.ansible.cfg 中写入下面内容:

[defaults]
host_key_checking = False

2.1. 执行基本命令

ansible 的基本用法(选项不全)如下:

Usage: ansible <host-pattern> [options]

Options:
-m MODULE_NAME, --module-name=MODULE_NAME  想要执行的模块,默认为command
-a MODULE_ARGS, --args=MODULE_ARGS         模块的参数
-u REMOTE_USER, --user=REMOTE_USER         远程机器的用户名
-k, --ask-pass                             提示输入ssh登录密码
-s, --sudo                                 以sudo运行
-i INVENTORY, --inventory-file=INVENTORY   指定hosts文件路径,默认/etc/ansible/hosts
-o, --one-line                             摘要输出
-v, --verbose                              verbose mode

下面命令会在当前目录的 hosts 文件中定义的所有机器中执行 /bin/cat /etc/issue

$ ansible all -i hosts -m command -a "/bin/cat /etc/issue"  # -m指定模块,-a指定模块参数
SSH password:
192.168.1.112 | SUCCESS | rc=0 >>
Ubuntu 16.04.1 LTS \n \l

192.168.1.111 | SUCCESS | rc=0 >>
Ubuntu 14.04.3 LTS \n \l

由于默认为 command 模块,所以下面两条命令相同:

$ ansible all -i hosts -a "/bin/cat /etc/issue"             # 同下条命令。省写了command模块
$ ansible all -i hosts -m command -a "/bin/cat /etc/issue"

注: command 模块不支持 shell 变量,也不支持管道等 shell 相关的东西,如果你想使用这些东西,请使用 shell 模块。
请注意下面两条命令输出的不同:

$ ansible all -i hosts -m shell -a 'echo -e "b\na" | sort'  # 使用了管道,必需用shell模块
192.168.1.112 | SUCCESS | rc=0 >>
a
b

192.168.1.112 | SUCCESS | rc=0 >>
a
b
$ ansible all -i hosts -a 'echo -e "b\na" | sort'   # 使用command模块(默认),把|当作了字符串
192.168.1.112 | SUCCESS | rc=0 >>
b
a | sort

192.168.1.112 | SUCCESS | rc=0 >>
b
a | sort

2.2. Inventory 文件

Inventory 文件( -i 选项指定)保存着被管理机器列表,它默认采用 INI 类似格式(也支持 YAML 格式)。下面是一个例子:

mail.example.com

[webservers]
foo.example.com
bar.example.com

[dbservers]
one.example.com
two.example.com
three.example.com

可以一次指定一个范围,如:

[webservers]
www[01:50].example.com    # 相当于 www01.example.com, ..., www50.example.com

[databases]
db-[a:f].example.com      # 相当于 db-a.example.com, ..., db-f.example.com

可以为机器指定一个名字,如下面例子中指定机器名的 jumper:

jumper ansible_host=foo.example.com   ansible_user=cig01    ansible_port=2222

可以为每一个机器指定不同的连接类型和连接用户,如:

[targets]
localhost               ansible_connection=local
other1.example.com      ansible_connection=ssh     ansible_user=cig01    ansible_port=2222
other2.example.com      ansible_connection=ssh     ansible_user=cig01

Inventory 文件中还支持其它一些参数,如:

aws_host        ansible_ssh_private_key_file=/home/example/.ssh/aws.pem
freebsd_host    ansible_python_interpreter=/usr/local/bin/python

完整参数的列表可参考:http://docs.ansible.com/ansible/latest/intro_inventory.html

2.2.1. Host 变量和 Group 变量

可以为每个主机单独指定一些变量(称为 Host 变量),这些变量可以在 playbooks(后文将介绍)中使用:

[atlanta]
host1 http_port=80  maxRequestsPerChild=808
host2 http_port=303 maxRequestsPerChild=909

也可以使用 :vars 为一个组指定变量(称为 Group 变量),组内的每个主机都可以使用它们:

[atlanta]
host1
host2

[atlanta:vars]
ntp_server=ntp.atlanta.example.com
proxy=proxy.atlanta.example.com

使用 :children 可以让一个组包含其他的组:

[atlanta]
host1
host2

[raleigh]
host2
host3

[southeast:children]
atlanta
raleigh
2.2.1.1. 单独存放 Host 变量和 Group 变量

为了避免 Inventory 文件变得太复杂,可以把 Host 变量和 Group 变量放入单独的 YAML 文件(后缀可以是.yml/.yaml,或者直接省略后缀)中,需要符合下面的目录结构:

./hosts                        # Inventory文件
./group_vars/raleigh.yml       # 其中定义的变量可以在Group raleigh中使用
./group_vars/webservers.yml    # 其中定义的变量可以在Group webservers中使用
./host_vars/jumper.yml         # 其中定义的变量可以在Host jumper中使用

也可以进一步把同一个 Group/Host 的 Group/Host 变量放入多个文件中,如:

./group_vars/raleigh/db_settings.yml       # 其中定义的变量可以在Group raleigh中使用
./group_vars/raleigh/cluster_settings.yml  # 其中定义的变量可以在Group raleigh中使用

2.3. Patterns

前面介绍过 Ansible 基本用法: ansible <pattern> -m <module_name> -a <arguments> ,Ansible 中使用 Pattern 来指定哪些机器会被管理。

Table 1: Ansible Patterns
Pattern 实例 说明
all (or *) all Inventory 文件中所有机器
groupname webservers groupname 下所有机器
hostname foo.example.com hostname 对应机器
: webservers:dbserver 在组 webservers 或者组 dbserver 下的所有机器
:! webservers:!phoenix 在组 webservers 下,但不在组 phoenix 下的机器
:& webservers:&staging 同时在组 webservers 和组 dbserver 下的机器
wildcard *.example.com 用通配符指定以.example.com 结尾的所有机器
subscript webservers[0] 组 webservers 中第一台机器
  webservers[-1] 组 webservers 中最后一台机器
  webservers[0:1] 组 webservers 中从第一台和第二台机器
  webservers[1:] 组 webservers 中从第二台开始直到最后一台机器
~ ~(web | db).*\.example\.com 以 ~ 开始的 pattern 为正则表达式

说明 1:可以组合上面的多个 pattern,如 webservers:dbservers:&staging:!phoenix 表示:在组“webservers”或者组“dbservers”中,但同时必须在组“staging”中,且不能在组“phoenix”中的机器。

说明 2:命令 ansible-playbook 中的参数 --limit 可以进一行对 pattern 进行过滤。

3. 常用 Ad-Hoc 命令

所谓 Ad-Hoc 命令就是用 ansible 执行的命令,它很方便,但不会被保存下来;Playbooks 中定义的命令用 ansible-playbook 执行。

参考:http://docs.ansible.com/ansible/latest/intro_adhoc.html

3.1. 文件相关

下面是使用 copyfile 模块的一些例子:

$ ansible atlanta -m copy -a "src=/etc/hosts dest=/tmp/hosts"    # 复制文件
$ ansible webservers -m file -a "dest=/srv/foo/a.txt mode=600"   # 创建文件
$ ansible webservers -m file -a "dest=/path/to/c mode=755 state=directory" # 创建目录
$ ansible webservers -m file -a "dest=/path/to/c state=absent"   # 删除文件或者目录(递归地)

3.2. 软件包管理

Ansible 使用 yum (Redhat 系列)和 apt (Debian 系列)模块管理软件包,下面是一些例子:

$ ansible webservers -m yum -a "name=acme state=present"      # 安装软件(若已安装不会更新它)
$ ansible webservers -m yum -a "name=acme-1.5 state=present"  # 安装指定版本软件
$ ansible webservers -m yum -a "name=acme state=latest"       # 安装最新版本软件
$ ansible webservers -m yum -a "name=acme state=absent"       # 删除软件

3.3. 用户管理

下面是使用 user 模块的一些例子:

$ ansible all -m user -a "name=foo password=<crypted password here>" # 创建用户foo
$ ansible all -m user -a "name=foo state=absent"                     # 删除用户foo

3.4. 从源码部署

下面是使用 git 模块部署应用的例子:

$ ansible webservers -m git -a "repo=https://foo.example.org/repo.git dest=/srv/myapp version=HEAD"

3.5. 服务管理

下面是使用 service 模块的例子:

$ ansible webservers -m service -a "name=httpd state=started"     # 启动httpd服务
$ ansible webservers -m service -a "name=httpd state=restarted"   # 重启httpd服务
$ ansible webservers -m service -a "name=httpd state=stopped"     # 关闭httpd服务

3.6. 收集系统信息

下面是使用 setup 模块收集系统信息的例子:

$ ansible all -m setup                           # 收集所有信息
$ ansible all -m setup -a 'filter=ansible_*_mb'  # 收集内存相关信息

参考:http://docs.ansible.com/ansible/latest/setup_module.html

4. Playbooks

对于一些简单的任务,可以直接使用 Ad-Hoc 完成;有时我们的任务相对复杂,这时推荐使用 Playbook,它记录着相关配置及处理过程,便于重复使用。

Playbook 采用 YAML 格式,下面是一个例子(假设文件名为 playbook.yml):

---
- hosts: webservers
  vars:
    http_port: 80
    max_clients: 200
  remote_user: root
  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 (and enable it at boot)
    service: name=httpd state=started enabled=yes
  handlers:
    - name: restart apache
      service: name=httpd state=restarted

运行命令 ansible-playbook -i hosts playbook.yml 可以执行上面 Playbook。

参考:http://docs.ansible.com/ansible/latest/playbooks.html

4.1. Roles

为了更好的书写和组织 Playbooks,Ansible 1.2 中引入了 Roles。

Roles 放在当前工程目录的 roles 文件夹中,其目录结构为:

roles/
    common/
        files/
        templates/
        tasks/
        handlers/
        vars/
        defaults/
        meta/
    webservers/
        files/
        templates/
        tasks/
        handlers/
        vars/
        defaults/
        meta/

现在,你可以把 Playbook 简写为下面形式:

---
- hosts: webservers
  roles:
     - common
     - webservers

这相当于,对于每一个 role(记为 x,在上面例子是 common 和 webservers)实现下面动作:
1、如果文件 roles/x/tasks/main.yml 存在,则把它作为 tasks 添加到 playbook 中;
2、如果文件 roles/x/handlers/main.yml 存在,则把它作为 handlers 添加到 playbook 中;
3、如果文件 roles/x/vars/main.yml 存在,则把它作为 vars 添加到 playbook 中;
4、等等。

参考:http://docs.ansible.com/ansible/latest/playbooks_reuse_roles.html

Author: cig01

Created: <2017-04-15 Sat>

Last updated: <2018-01-26 Fri>

Creator: Emacs 27.1 (Org mode 9.4)