Ansible是近年越来越火的一款运维自动化工具,其主要功能是帮助我们实现IT工作的自动化、降低认为操作失误、提高业务自动化率、提升工作效率,常用于软件部署自动化、配置自动化、管理自动化、系统化系统任务、持续集成(CI)、零宕机平滑升级等。它丰富的内置模块(如acl、command、shell、cron、yum、copy、file、user等,多达569个)和开放的API接口,同时任何遵循GPL协议的企业或个人都可以随意修改和发布自己的版本。
ansible基础
安装ansible
安装方式主要有以下几种方式:
- pip方式安装
- Yum安装(建议使用)
- apt-get安装(建议使用)
- 源码安装
与远程服务器通信
Ansible的通信默认基于SSH,因此我们需要对主机先进行认证。Ansible认证方式有密码认证和公私钥认证两种方式,其实完全等同于SSH的认证,所以这里关于这两种认证方式不做过多介绍。Ansible默认使用公私钥认证方式(建议使用),主要是出于安全的考虑,密码不用明文存放。
ansible目录结构
我们只讲一下需要配置的目录,大家要熟练掌握。
- 配置文件目录
/etc/ansible/
,主要功能为:Inventory主机信息配置、Ansible工具功能配置等。所有Ansible的配置均存放在该目录下,我们平时的所有配置类操作也均基于此目录进行。 - 执行文件目录
/usr/bin/
,主要功能为:Ansible系列命令默认存放目录。Ansible所有的可执行文件均存放在该目录下。
在/usr/lib/pythonXXX/site-packages/
下,该目录是系统当前默认的Python路径,因为Ansible是基于Python编写的,所以Ansible的所有lib库文件和模块文件也均存放于该目录下。
ansible配置文件
Inventory用于定义Ansible的主机列表配置,Ansible的自身配置文件只有一个,即ansible.cfg,Ansible安装好后它默认存放于/etc/ansible/
目录下。ansible.cfg配置文件可以存在于多个地方,Ansible读取配置文件的顺序依次是当前命令执行目录=>用户家目录下的.ansible.cfg=>/etc/ansible.cfg
,先找到哪个配置文件就使用哪个配置文件。其中ansible.cfg配置的所有内容均可在命令行通过参数的形式传递或定义在Playbooks中。
下面详细介绍一下配置文件:
1 | # config file for ansible -- https://ansible.com/ |
ansible命令格式
Ansible的命令使用格式如下:
ansible <host-pattern> [options]
<hots-pattern>
是Inventory中定义的主机或主机组,可以为ip、hostname、Inventory中的group组名、具有“.”或“*”或“:”等特殊字符的匹配型字符串,<>表示该选项是必选项。
[options]
是Ansible的参数选项,[]表示该选项中的参数任选其一。
ansible命令
Inventory配置文件
Inventory是Ansible管理主机信息的配置文件,相当于系统HOSTS文件的功能,默认存放在/etc/ansible/hosts
。为方便批量管理主机,便携使用其中的主机分组,Ansible通过Inventory来定义其主机和组,在使用时通过-i
或--inventory-file
指定读取,与Ansible命令结合使用时组合如下:
ansible -i /etc/ansible/hosts webs -m ping
如果真有一个Inventory时可不用指定路径,默认读取/etc/ansible/hosts
。Inventory可以同时存在多个,而且支持动态生成,如AWS EC2、Cobbler等均支持。下面我们来讲一下Inventory的使用规则。
定义主机和组
Inventory配置文件遵循INI文件风格,中括号中的字符为组名。其支持将同一个主机同时归并到多个不同的组中,分组的功能为我们维护主机列表提供了非常大的便利。此外,如果目标主机使用了非默认的SSH端口,还可以在主机名称之后使用冒号加端口来标明,以行为单位分隔配置。
下面我们看一个示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15# "# "开头的行表示该行为注释行
# Inventory可以直接为IP地址
192.168.1.1
# Inventory同样支持Hostname的方式,后跟冒号加数字表示端口号,默认端口号22
ntp.golanghub.cn:2221
nfs.golanghub.cn
# 中括号内的内容表示一个分组的开始,紧随其后的主机均属于该组成员,空行后的主机也属于该组。
[webservers]
web1.golanghub.cn
web[10:20].golanghub.cn # [10:20]表示10~20之间的所有数字(包括10和20),即表示web10.golanghub.cn、web11.golanghub.cn......web20.golanghub.cn的所有主机
web2.golanghub.cn
[dbservers]
db-a.golanghub.cn
db-[b:f].golanghub.cn # [b:f]表示b到f之间的所有字母(包括b和f),即表示db-b.golanghub.cn、db-c.golanghub.cn......db-f.golanghub.cn的所有主机定义主机变量
工作中考虑到安全性问题,通常会将80端口改为其它端口号,这种非标准化的配置需求,可以直接通过修改Inventory配置来实现,在定义主机时为其添加主机变量,以便在Playbook中使用针对某一主机的个性化要求。
示例如下:
1
2[webservers]
web1.golanghub.cn http_port=8080 maxRequestsPerChild=5000 # 自定义http_port的端口号为8080,配置maxRequestsPerChild为5000Ansible其实支持多种方式修改或自定义变量,Inventory是其中的一种修改方式。
定义组变量
Ansible支持定义组变量,主要针对大量机器的变量定义需求,赋予指定组内所有主机在Playbook中可用的变量,等同于逐一给该组中的所有主机赋予同一变量。
示例如下:
1
2
3
4
5
6
7[groupservers]
web1.golanghub.cn
web2.golanghub.cn
[groupservers:vars]
ntp_server=ntp.golanghub.cn # 定义groupservers组中所有主机ntp_server值为ntp.golanghub.cn
nfs_server=nfs.golanghub.cn # 定义groupservers组中所有主机nfs_server值为nfs.golanghub.cn定义组嵌套及组变量
Inventory中,组还可以包含其它的组(嵌套),并且也可以向组中的主机指定变量。不过,这些变量只能在Ansible-playbook中使用,而Ansible不支持。组与组之间可以相互调用,并且可以向组中的主机指定变量。
示例如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14[apache]
httpd1.golanghub.cn
httpd2.golanghub.cn
[nginx]
ngx1.golanghub.cn
ngx2.golanghub.cn
[webservers:children]
apache
nginx
[webservers:vars]
ntp_server=ntp.golanghub.cn多重变量定义
变量除了可以在Inventory中一并定义,也可以独立于Inventory文件之外单独存储到YAML格式的配置文件中,这些文件通常以.yml、.yaml、.json为后缀或者无后缀。变量通常从如下4个位置检索:
Inventory配置文件(默认
/etc/ansible/hosts
)Playbook中vars定义的区域
Roles中vars目录下的文件
Roles同级目录group_vars和host_vars目录下的文件
假如foosball主机同属于raleigh和webservers组,那么其变量在如下文件中设置均有效:
1
2
3/etc/ansible/group_vars/raleigh # can optionally end in '.yml','.yaml',or '.json'
/etc/ansible/group_vars/webservers
/etc/ansible/host_vars/foosball对于变量的读取,Ansible遵循如上优先级顺序,因此大家设置变量时尽量沿用同一种方式,方便维护管理。
其它Inventory参数列表
除了支持如上的功能外,Ansible基于SSH连接Inventory中指定的远程主机时,还内置了很多其他参数,用于指定其交互方式,如下列举了部分重要参数:
1
2
3
4
5
6
7ansible_ssh_host:指定连接主机
ansible_ssh_port:指定SSH连接端口,默认22
ansible_ssh_user:指定SSH连接用户
ansible_ssh_pass:指定SSH连接密码
ansible_sudo_pass:指定SSH连接时sudo密码
ansible_ssh_private_key_file:指定特有私钥文件
...其它内置参数还有数十个,这些参数均可以直接写在命令行或playbook文件中,以覆盖配置文件中的定义。
Ad-Hoc入门
Ad-Hoc命令集介绍
Ad-HOC组管理和特定主机变更
Ad-Hoc用户与组管理
Playbook入门
YAML语法
多行缩进
数据结构可以用类似大纲的缩排方式呈现,结构通过缩进来表示,连续的项目通过减号“-”来表示,map结构里面的key/value对用冒号“:”来分隔。代码格式如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15house:
family:
name: Doe
parents:
- John
- Jane
children:
- Paul
- Mark
- Simone
address:
number: 34
street: Main Street
city: Nowheretown
zipcode: 12345- 字串不一定要用双引号标识;
- 在缩排中空白字符的数目并不重要,只要相同阶层的元素左侧对齐就可以了(不过不能使用Tab字符);
- 允许在文件中加入选择性的空行,以增加可读性;
- 选择性的符号“…”可以用来表示档案结尾(在利用串流的通信中,这非常有用,可以在不关闭串流的情况下,发送结束信号)。
单行缩写
YAML也有用来描述好几行相同结构的数据的缩写语法,数组用“[]”包括起来,hash用“{}”来包括。因此,上面的这个YAML能够缩写成这样:
1
2
3house:
family: { name: Doe, parents: [John, Jane], children: [Paul, Mark, Simone] }
address: { number: 34, street: Main Street, city: Nowheretown, zipcode: 12345 }Playbook语法
了解了普通的YAML格式文件,我们来看一下正式的Playbook内容是什么样的。Playbook代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19---
# 这个是你选择的主机
- hosts: webservers
# 这个是变量
vars:
http_port: 80
max_clients: 200
# 远端的执行权限
remote_user: root
tasks:
# 利用YUM模块来操作
- name: ensure apache is at the laste version
yum: pkg=httpd state=laste
-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总的来说,Playbook语法具有如下一些特性。
需要以“—”(3个减号)开始,且需要顶行首写。
次行开始正常些Playbook的内容,但建议写明该Playbook的功能。
使用#号注释代码。
缩进必须是统一的,不能将空格和Tab混用。
缩进的级别必须是一致的,同样的缩进代表同样的级别,程序判别配置的级别是通过缩进结合换行来实现的。
YAML文件内容和Linux系统大小写判断方式保持一致,是区分大小写的,k/v的值均大小写敏感。
k/v的值可同行写也可换行写。同行使用“:”分隔,换行写需要以“-”分隔。
一个完整的代码块功能需最少元素,需包括name:task。
一个name只能包括一个task。
Ansible-playbook的命令格式
Ansible-playbook的命令使用格式如下:
ansible-playbook playbook.yml
ansible-playbook命令后跟事先编辑好的playbook.yml文件即可。
Ansible-playbook使用技巧
限定执行范围
当Playbook指定的一批主机中个别主机需要进行变更时,我们不需要去修改Playbook文件,而是通过一些选项就可以直接限定和查看Ansible命令的执行范围。
–limit
使用ansible-playbook命令来指定主机:
ansible-playbook playbook.yml --limit webservers
这样,(假设你的inventory文件中包含webservers组),即便Playbook中设定“hosts: all”,
但也仅对webservers组有效。–list-hosts
如果想知道在执行Playbook时,哪些主机将会受影响,则使用“–list-hosts”选项。
ansible-playbook playbook.yml --list-hosts
运行结果:
1
2
3
4playbook: playbook.yml
paly # 1 (all): host count=1
webservers如上两种方式针对需要对批量操作的主机列表中的某一台主机做特定修改时非常有用。该技巧极大地增加了Ansible的使用灵活度。
用户与权限设置
–remote-user
在Playbook中,如果在hosts字段下没有定义users关键字,那么Ansible将会使用你在Inventory文件中定义的用户,如果Inventory文件中也没有定义用户,Ansible将默认使用当前系统用户身份来通过SSH连接远程主机,在远程主机中运行play内容。
我们也可以直接在ansible-playbook中使用–remote-user选项来指定用户。
ansible-playbook playbook.yml --remote-user=tom
–ask-sudo-pass
在某些情况下,我们需要传递sudo密码到远程主机,来保证sudo命令的正常运行。这时,可以使用–ask-sudo-pass(-K)选项来交互式的输入密码。
–sudo
使用–sudo选项,可以强制所有play都使用sudo用户,同时使用–sudo-user选项指定sudo可以执行哪个用户的权限,如果不指定,则默认以root身份运行。
比如,当前用户Tom想以Jerry的身份运行Playbook,命令如下:
ansible-playbook playbook.yml --sudo --sudo-user=jerry --ask-sudo-pass
执行过程中,会要求用户输入jerry的密码。
其它选项技巧
Ansible-playbook命令还有一些其它选项。
–inventory=PATH(-i PATH):指定inventory文件,默认文件是
/etc/ansible/hosts
。–verbose(-v):显示详细输出,也可以使用-vvvv显示精确到每分钟的输出。
–extra-vars=VARS(-e VARS):定义在Playbook使用的变量,格式为:“key=value, key=value”。
–forks=NUM(-f NUM):指定并发执行的任务数,默认为5,根据服务器性能,调大这个值可提高Ansible执行效率。
–connection=TYPE(-c TYPE):指定连接远程主机的方式,默认为SSH,设为local时,则只在本地执行Playbook,建议不做修改。
–check:检测模式,Playbook中定义的所有任务将在每台远程主机上进行检测,但并不真正执行。
以上这些参数可满足大部分的工作需求。
Playbook拓展
Handlers
Handlers是Playbook中一种特殊的任务类型,我们通过在任务末尾使用notify选项加Handlers的名称,来触发对应名称下Handlers中定义的任务。
Handlers以及Playbook中的任务也可以在独立的文件中进行定义,然后被当前的Playbook进行引用。
示例如下:
handlers文件内容:
1 | --- |
tasks文件内容:
1 | --- |
在某些情况下,我们可能需要同时调用多个Handlers,或者需要使用Handlers调用其它Handlers,Ansible可以很简单地实现这些功能。
示例如下:
同时调用多个Handlers,tasks文件内容:
1 | - name: Rebuild application configuration. |
使用Handlers调用其它Handlers,则直接在Handlers中使用notify选项就可以了,handlers内容如下:
1 | - name: restart apache |
使用Handlers时,有几点注意事项:
- Handlers只有在其所在的任务被执行时,才会被运行。如果一个任务中定义了notify调用Handlers,但是由于条件判断等原因,该任务未被执行,那么Handlers同样不会被执行。
- Handlers只会在Play的末尾运行一次,如果想在一个Playbook的中间运行Handlers,则需要使用meta模块来实现,例如:
-meta: flush_handlers
。 - 如果一个Play在运行到调用Handlers的语句之前失败了,那么这个Handlers将不会被执行。我们可以使用meta模块的
--force-handlers
选项来强制执行Handlers,即使是Handlers所在的Play中途运行失败也能执行。
环境变量
变量
Playbook进阶
Includes
Roles
Roles就是有相互关联功能的集合,适合在大项目中使用。Roles不仅支持Tasks的集合,同时包括var_files、tasks、handlers、meta、templates。
构建Roles
Roles主要依赖于目录的命名和摆放,默认tasks/main.yml是所有任务的入口,所以使用Roles的过程可以理解为目录规范化命名的过程。如下几个目录就可以构建Ansible Roles。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23php72/
├── defaults
│ └── main.yml
├── files
│ ├── exif.so
│ ├── imagick.so
│ ├── install.sh
│ ├── ldap.so
│ ├── memcache.so
│ ├── mongodb.so
│ ├── opcache.so
│ ├── php-7.2.6.tar.gz
│ ├── redis.so
│ ├── yac.so
│ └── yaf.so
├── handlers
│ └── main.yml
├── tasks
│ └── main.yml
└── templates
├── php-fpm
├── php-fpm.conf
└── php.ini每个目录下均由main.yml定义该功能的任务集,tasks/main.yml默认执行所有指定的任务。Roles的调用文件palybook_role.yml的内容如下:
1
2
3
4
5
6
7---
- name: Install PHP72
hosts: "{{hosts}}"
become: yes
roles:
- php72Roles执行方法如下:
ansible-playbook playbook_role.yml
Roles目录可以摆放在
/etc/ansible/ansible.cfg
中“roles_path”定义的路径,也可以和入口Playbook文件存放在同级目录,Ansible对此没有强制要求。使用Roles重构Playbooks
Roles技巧之Handlers:动态变更
Roles技巧之Files:文件传输
Roles技巧之Templates:模板替换
更多复杂的跨平台Roles
Inventory文件扩展
Inventory文件实战
独立的Inventory文件
Inventory变量
- host_vars目录
- group_vars目录