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 来指定哪些机器会被管理。
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
执行。
3.1. 文件相关
下面是使用 copy
和 file
模块的一些例子:
$ 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' # 收集内存相关信息
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。
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