第1章: 安装
更换epel源
Yum -y install epel-release
Yum -y install ansiible
1.1: 目录
配置文件目录:/etc/ansible/
主要功能:inventory主机信息配置,Ansible工具功能配置。所有的ansible的配置均放在该目录下
执行文件目录/usr/bin/
第2章: 命令用法详解
2.0.1: ansible
ansible <host-pattern> [options]
<host-pattern>:Inventory中定义的主机或主机组,或者 all 代表所有
[options]是ansible的参数选项,例举如下:
-m NAME ,--module-name=NAME:指定执行使用的模块
-u USERNAME,--user=USERNAME:指定远程主机以USERNAME运行命令
-s,--sudo:相当于linux下的sudo命令
-U SUDO_USERNAME,--sudo-user=SUDO_USERNAME:使用sudo 相当于linux下的sudo命令
2.0.2: ansible-playbook
ansible-playbook playbook.yml
命令后跟事先编辑好的playbook.yml
参数:
--ask-vault-pass:加密playbook文件时提示输入的密码
-D,--diff:当跟新的文件以及内容较少时,改选项可显示这些文件不同的地方,改选项结合-C用会比较好
-C,--check:测试
-e EXTRA_VARS,extra-vars=EXTRA_VARS:在playbook中引入外部变量
--flush-handiers:强行运行handlers,即使在任务失败的时候
-i INVERNTORY 指定读取的Inventory(资产文件)文件
--list-tags:列出所有可用的tags
--list-tasks:列出所有即将被执行的任务
--skip-tags=SKIP_TAGS:跳过指定的tags任务。
--start-at-task=START_AT_TASK:从第几条任务开始执行
--step:逐步执行playbook定义的任务,并经过人工确认后继续执行下一步任务
--syntax-check:检查Playbook的语法
-t TAGS,--tags=TAGS:指定执行该tags的任务
2.1: ansible命令使用详解以及场景
2.1.1: ansible
日常使用非常高的命令,
场景:非固化需求
临时一次性操作
二次开发接口调用
(如:查看主机是否存活,想复制某个文件到远程主机的某个目录)
2.1.2: ansible-galaxy
可以理解为python的pip的功能,通过ansible-galaxy可以根据下载量或者关注量查找和安装优秀的roles
roles聚集地:https://galaxy.ansible.com
命令格式:
ansible-galaxy [ init | info | install | list | remove ] [--help] [options] ...
命令分为三大部分:
(1)[ init | info | install | list | remove ]
(2)init:初始化本地的roles配置,以备上传roles至galaxy
(3)info:列表指定roles的详细信息
(4)install:下载并安装galaxy指定的roles到本地
(5)list:列出本地已下载的roles
(6)ansible2.0版本中,针对ansible-galaxy增加了login import delete setup 等功能
(7)help用大显示[--help]
(8) 针对第一部分的init、info的功能
(9)例:ansible-galaxy init --help
(10)参数项[options]
(11) 该部分结合第一部分的参数完成ansible-galaxy完整的功能用法,
(12)如:ansible-galaxy init [options]reles-name
(13)即:ansible init 后跟 [-f|-h|-c|-p|--offline|-s SERVER|-v|--version]参数,后跟reles-name成为一条完整的命令
2.1.3: anisble-pull
该指令涉及ansible的另一种工作模式:pull模式(Ansible默认使用push模式)。这和通常使用的push模式工作机制刚好相反,其适用于以下场景:1你有数量巨大的机器需要配置,即使使用高并发依旧需要花费很长时间:2你要在刚启动,没有网络连接的主机上运行ansible
命令格式:
ansible-pull [options] [playbook.yml]
通过ansible-pull结合Git和crontab一并实现,其原理如下:通过crontab定期拉取指定的git版本到本地,并以指定模式自动运行预先指定好的指令
2.1.4: ansible-doc
ansible模块文档说明,针对每个模块都有详细的用法说明及应用案例介绍,功能和linux的man相似
用法:
列出支持的模块:
ansible-doc -l
模块功能说明:
ansible-doc ping
2.1.5: ansible-playbook
日常使用频率比较高的命令,其工作机制是通过预先编写好的playbook文件实现批量管理。要实现的功能于命令ansible一样,可以理解为按一定条件组成的ansible任务集。
2.1.6: ansible-vault
用于配置文件加密,如编写的playbook配置文件包含敏感信息,不希望其他人随意查看,ansible-vault可加密/解密这个配置文件,具体使用方法如下:
ansible-vault [create|decrypt|edit|encrypt|rekey|view] [--help] [options]
例子:设定如下密码,加密a.yml
ansible-vault decrypt a.yml //然后会提示你输入密码
这时查看a.yml会出现乱码,只有通过以下命令解密后方可正常查看
ansible-valut decrypt a.yml //然后会提示你输入密码
解密后方可正常查看
2.1.7: ansible-console
ansible为用户提供的一款交互式工具,用户可以在ansible-console虚拟出来的终端上像shell一样使用ansible内置的各种命令,这为习惯于使用shell交互方式的用户提供了良好的使用体验
第3章: Inventory资产文件配置及详解
3.1: 定义主机和组
inventory是ansible管理主机信息的配置文件,相当于系统HOSTS文件的功能,默认存放在/etc/ansible/hosts,通过Inventory定义主机和主机组,在使用的时候也可以通过 -i或者--inventory-file指定读取
行为单位分割配置,详细信息可参考一下代码中的注释
# ”#”开头的行为注释行,即当时行的配置不生效
# Inventory可以直接为IP地址
192.168.159.124
# Inventory同样支持Hostname的方式后跟冒号加数字表示端口号,默认22端口
ntp.server.com:2222
# 中括号的内容表示一个分组的开始,紧随其后的主机均属于改组的成员,空行后的主机就不属于改组
web2.magedu.com 这台主机也属于[webservers]
web1.server.com
web[10:20].server.com # [10:20]表示10-20之间的数字包含10、20。
web2.server.com[dbserver]
db-a.server.com
db-[b:f].server.com # [b:f]表示b-f之间的所有数字(包括b、f)
对于每个节点,可以单独设置用户名和连接类型:
[targets]
other1.example.com ansible_connection=ssh ansible_ssh_user=mpdehaan
other2.example.com ansible_connection=ssh ansible_ssh_user=mdehaan
3.2: 定义主机变量
在日常生活中,通常会遇到非标准化的需求配置,如考虑到安全性问题,业务人员通常将企业内部的web服务80端口修改为其他端口号,而该功能可以直接通过修改Inventory配置来实现,在定义主机时为其添加主机变量,以便在playbook中使用对某一主机的个性化要求
[webserver]
web1.server.com http_port=808 maxRequestsPerChild=801 # 自定义http_port的端口为808,配置 maxRequestsPerChild=801
3.3: 定义组变量
Ansible支持定义组变量,主要针对大量机器的变量定义需求,赋予指定组内所有主机在playbook中可用的变量。等同于逐一给改组下的所有主机赋予同一变量
[groupservers]
web1.magedu.com
web2.magedu.com
[groupservers:vars]
ntp_server=net.magedu.com #定义[groupservers]组中所有的主机ntp_server值为ntp.magedu.com
nfs_server=nfs.magedu.com #定义groupservers组中的所有主机nfs_server值为nfs.magedu.com
3.4: 定义组嵌套及组变量
Inventory中,组还可以包含其他组(嵌套),并且也可以向组中的主机指定变量。不过这些变量只能在ansible-playbook中使用,而ansible不支持。组于组之间可以相互调用,并且可以向组中的主机指定变量。
参考示例如下:
[apache]
httpd1.server.com
httpd2.server.com
[nginx]
nginx.server.com
nginx.server.com
[webservers:children]
apache
nginx
[werservers:vars]
ntp_server=ntp.magedu.com
ansible以简单为其核心理念,上述实现在业务日常使用并不常见,了解用法即可
3.5: 多重变量定义
变量除了可以写在Inventory中,也可以独立于Inventory文件之外的单独存储到YAML格式的配置文件中,这些文件通常以 .yml .yaml为后缀或者无后缀。变量通常从一下四个位置检索
Inventory配置文件(默认/etc/ansible/hosts)#默认位置可修改
playbook中vars定义的区域
Roles中的vars目录下的文件
Roles同级目录group_vars和hosts_vars目录下的文件
假如foosball主机同属于raleigh和webserver组,那么其变量在如下文件中均有效
/etc/ansible/group_vars/raleigh [.yml .yaml .json]
/etc/ansible/group_vars/webservers [.yml .yaml .json]
/etc/ansible/host_vars/foosball [.yml .yaml .json]
对于变量的读取,Ansible遵循如上优先级,因此大家设置变量是尽量沿用同一种方式,以便于维护人员管理
第4章: Ansible中的正则
正则表达式是各类高级语言的必定支持的方法之一,ansible也不例外。其正则功能等同于正则表达式,语法使用也和正则类同,这大大便利了运维的使用,其对于ansible的灵活性有着极大的贡献,该功能同样支持ansible-playbook,其用法也很简单
ansible <pattern_goes_here> -m <module_name> -a <arguments>
该功能主要针对Inventory的主机列表使用,我们通过一些案例可以更好的了解其功能及用法。在如下示例中主要针对webservers进行正则匹配
4.1: All(全量)匹配
匹配所有的主机,all或 * 号功能相同。如检测所有主机存活情况。
all 和 * 功能相同 但 * 需要引号引起来
ansible all -m ping
ansible “*” -m ping
检查192.168.1.0/24网段所有主机存活状态。
ansible 192.168.1.* -m ping
4.2: 逻辑或(or)匹配
如果我们希望同时对多台主机或多个组同时执行,相互之间用“:”(冒号)分割
示例:
ansible “web1:web2” -m ping
4.3: 逻辑非(!)匹配
逻辑非用感叹号(!)表示,主要针对多重条件的匹配规则,使用方法如下
//所有在webservers组但不在phoenix组中的主机
webserver:!phoenix
4.4: 逻辑与(&)匹配
和逻辑非一样,逻辑与也主要针对多重条件的匹配规则,知识逻辑上的判断不同。逻辑与使用&表示,示例
//webservers组和staging组中同时存在的主机
webservers:&staging
4.5: 多条件组合
ansible同样支持多条件的复杂组合,该情况企业用的不多,这里来个简单的小示例
//所有以.server.com结尾的主机
*.server.com
//one开头.com结尾的所有主机和dbserver组中的所有主机
one*.com:dbserver
4.6: 域切割
ansible底层基于python,因此也支持域切割,python字符串域切割的示例如下:
str = ‘123456’
print str[0:1]
通过[0:1]即可获取数值1.该功能在ansible中也支持,以如下Inventory内容为例:
[webservers]
cobweb
webbing
weber
通过截取属组下表可以获取对应变量值。
webservers[0] #==cobweb
webservers[-1] #==weber
webservers[0:1] #==webserver[0],webserver[1] == cobwed,webbing
webservers[1:] #==webbing,weber
4.7: 正则匹配
Ansible同样完整支持正则匹配功能,~ 开始表示正则匹配。
示例:
~(web|db).*\.example.com
检测bate.example.com、web.example.com、green.example.com、bate.example.org、web.example.org、green.example.org的存活,使用如下匹配模式:
ansible “~(bate|web|green)\.example\.(com|org)” -m ping
检测inventory中所有以192.168开头的服务器存活信息:
ansible ~192\.168\.[0-9]\{\2}.[0-9]\{2,} -m ping
4.8: Ansible Ad-Hoc命令集
第5章: 常用模块
5.1: setup
该模块可以获取ansible客户端的详细信息,命令:
ansible webserver(机器组名) -m setup
5.2: copy模块
该模块可以实现ansible主机向客户端传送文件的功能,类似scp
ansible webserver -m copy -a “src=/usr/local/src/test.py dest=/tmp/ owner=root group=root mode=0755 force=yes”
force:如果目标主机包含该文件,但内容不同,则设置为yes后会强制覆盖,设置为no时,只有当目标主机的目标位置不存在该文件时,才复制该文件到目标主机;默认为yes
backup:在覆盖之前备份源文件,备份文件包含时间。该参数有yes|no两个选项
注意:copy模块和rsync命令一样,如果路径使用“/”结尾,则只复制目录里的内容;如果没有“/”结尾,则包含目录在内的整个内容全部被复制
5.3: 复制文件模块synchronize
synchronize模块会调用rsync命令,因此首先要记得提前安装好rsync软件包。
synchronize模块用于将ansible机器的指定目录推送(push)到客户机的指定目录,命令如下
ansible 192.168.1.206 -m synchronize -a “src=/usr/local/src/ dest=/usr/local/src delete=yes compress=yes”
其中delete=yes用来实现使两边的内容一样(即以push为主),实现效果跟rsync --delete效果是一样的。如果客户端不存在的文件或目录则增补,如果存在着不同的文件或目录则删除,以保证内容两边一样
compress=yes用于开启压缩,默认为开启。
另外,由于synchronize模块调用的是rsync命令,因此如果使用“/”结尾,则只复制目录里的内容;如果没有“/”来结尾,则包含目录在内的整个内容全部被复制
5.4: file模块
file模块主要用于设置文件或者目录的属性。
group:定义文件或者目录的属组
mode:定义文件或目录的权限
owner:定义文件或目录的属主
path:必选项,定义文件或目录的路径。
recurse:递归设置文件的属性,只对目录有效
src:被链接的源文件路径,只用于state=link的情况。
dest:被链接到的路径,只应用于state=link的情况。
force:强制创建软链接,有两种情况,一种是源文件不存在,但之后会建立的情况;另一种是先取消已经创建的软链接,在创建新的软联俄及,有yes|no两个选项。
state:后面链接文件的各种状态,例如下面的directory,link,hard,file和,absent。
link:创建软连接
hard:创建硬链接
directory:如果目录不存在,则创建目录。
file:即使文件不存在也不会被创建
absent:删除目录,文件或链接文件。
touch:如果文件不存在,则会创建一个新文件,如果问价或者目录已经存在,则更新其最后的修改时间,这一点跟linux touch命令一样
案例1:将客户端机器192.168.1.205的/usr/local/src/test.py软连接到/tmp/test.py下
ansible 192.168.1.205 -m file -a ‘src=/usr/local/src/test.py dest=/tmp/test.py state=link ’
案例2:将刚刚建立的软连接删除
ansible 192.168.1.205 -m file -a ‘path=/tmp/test.py state=absent’
案例3:在webserver组建立/text.txt 属组和属主均为root,权限为0755,命令:
ansible webserver -m file -a ‘path /text.txt state=touch owner=root group=root mode=0755’
案例4:在webserver 组中建立test目录,属主和属组均为root权限为0755
ansible webserver -m file -a ‘path=/tmp/test state=directory ownet=root group=root mode=0755 ’
5.5: 测试连通性模块ping
用于检测被控与被控端的连通性,命令如下:
ansible webserver -m ping
5.6: 创建删除用户组模块group
group模块可以在所有节点上创建自己定义的组,比如利用此模块创建一个组名为test的,gid为2016的组
ansible webserver -m group -a ‘ gid=2016 name=test’
5.7: 创建删除用户模块user
该模块用于创建用户
案例:在指定节点上创建一个用户名为test、组为test的用户,命令如下:
ansible webserver -m user -a ”name=test group=test”
删除用户
ansible webserver -m user -a “name=test state=absent remove=yes”
5.8: shell模块
shell模块是执行被控端的shell脚本文件,跟另一个模块raw的功能类似,并且支持管道符。
案例:执行任何在linux可执行的命令
ansible webserver -m shell -a “ ls -a ”
执行脚本:
ansible webserver -m shell -a “/root/test.sh”
5.9: 执行脚本模块sript
script模块用于在远程被控端主机上执行本地ansible机器中的shell脚本文件,相当于scp+shell的组合命令,比如,要执行本地机器的/root/print_hello.sh
ansible webserver -m script -a “/root/print_hello.sh”
5.10: 下载url模块get_url
get_url模块可以实现在远程主机上下载url到本地,这个模块应该在平时的工作中用的比较多,比如,webserver组中的被控端需要下载某个文件要到/tmp目录下
ansible webserver -m get_url -a ‘url=路径 dest=/tmp’
5.11: yum模块
用来管理linux平台的软件包操作的
config_file::yum的配置文件。
disable_gpg_check:关闭gpg_check
disablerepo:不启用某个源
enablerepo:启用某个源
name:要进行操作的软件包的名字,也可以传递一个url或一个本地的rpm包的路径。
state:present|absent|latest,state状态对应了三种软件包的状态absent表示删除,剩下两个是安装
5.12: 技术任务模块cron
cron模块,就是创建计划任务,可以定义webserver组被控端机器每天凌晨1点1分ntpdate自动对时,命令:
5.13: 开启关闭服务模块service
被控端服务管理,例如开启,关闭,重启服务等。
实例1:在webserver组中开启nginx服务,
ansible webserver -m service -a ‘name=nginx state=started enabled=yes’
5.14: 添加删除公钥模块authorized_key
authorized_key是ansible官方新推出的一个模块,作用为添加或删除用户的SSH公钥
5.15: 配置文件 template:
基于模板的方式生成一个文件复制到被管理的主机,这样就可以在模板语言中判断,不同的系统复制不同的配置文件,这就是template模块区别与copy模块的地方
5.16: 拷贝解压归档文件模块unarchive
将本地的归档文件拷贝到被管控的主机上,解压归档文件
ansible all -m unarchive -a "src=/files/wordpress.tar dest=/data/dynamic/"
5.17: 挂载模块mount
挂载和卸载模块,path指定本地挂载点,src指定需要挂载的设备
ansible 192.168.23.12 -m mount -a "path=/var/www/html src=192.168.23.25:/data/static fstype=nfs state=mounted"
5.18: 文件编辑模块:lineinfile
模块参数详解:
path:指定要修改的配置文件
regexp:匹配要修改的内容
line:要增加或者修改的内容
state:
absent:表示删除,当匹配到时进行删除
present:表示增加,当匹配到时进行修改,当没有匹配到时在最后增加一行,默认为此项
backrefs:
no:表示如果没有匹配到,则增加line;如果匹配成功,则替换line;
yes:表示如果没有匹配到,则不变line;如果匹配成功,则替换line;
backup:
no:表示如果没有匹配到,则增加line;如果匹配成功,则替换line;不备份原文件
yes:表示如果没有匹配到,则增加line;如果匹配成功,则替换line;备份原文件
insertafter(匹配的是此行):
在匹配到的行之后添加一行
insertbefore(匹配的是此行):
在匹配到的行之前添加一行
修改nginx.conf测试:[root@nfs-server playbook]# cat modify_nginx.yml
---
- hosts: 192.168.2.111
tasks:
- name: "修改配置文件"
lineinfile:
dest: "/etc/nginx/conf.d/default.conf"
regexp: \'listen 80;\'
line: \'listen 8000;\'
#backup: yes
backrefs: yes
notify:
- reload nginx
handlers:
- name: reload nginx
service: name=nginx state=reloaded
注:经测试,当不添加backerfs: yes参数时,匹配到后也会进行替换,但当匹配到的内容不存在时,会在最后增加一行;所以当不增加backerfs参数时,要确定匹配到的内容存在;
替换存在的行:
#ansible oms -m lineinfile -a \'path=/etc/sudoers regexp="SYSTEM,SOFTWARE" line="STAPLES_ADMIN ALL=(ROOT) NOPASSWD:NETWORKING,LOCATE,STORAGE,DELEGATING,DRIVERS,SYSTEM,SOFTWARE,SERVICES,PROCESSES,FILE" backrefs=no\'
匹配到的行后增加一行:
#ansible oms -m lineinfile -a \'dest=/etc/sudoers insertafter="Cmnd_Alias SYSTEM = /usr/sbin/reboot, /usr/sbin/halt, /usr/bin/ansible, /usr/bin/ssh" line="Cmnd_Alias FILE = /bin/mkdir,/bin/touch,/usr/bin/vim"\'
删除匹配到的行:
#ansible oms -m lineinfile -a \'path=/etc/sudoers state=absent regexp="PROCESSES,FILE"\'
5.19: fetch从远程主机拉取文件
5.20: 20:mysql
给root用户设置密码
- name: set password for root
mysql_user: name=root password={{ mariadb_root_password }} check_implicit_admin=true
设置密码后,将密码放到~/.my.cnf文件中,mysql_db模块需要读取认证信息创建数据库:
credentials.j2文件内容如下:
[client]
user=root
password={{ mariadb_root_password }}
使用template模块,创建~/.my.cnf文件:
- name: put crendentials in ~/.my.cnf file
template: src=credentials.j2 dest=~/.my.cnf mode=600 backup=yes
创建数据库
mariadb_databases是一个列表,比如:
mariadb_databases:
- keystone
- glance
- cinder
- nova_api
- nova
- neutron
创建数据库:
- name: create databases
mysql_db: name={{ item }} state=present
with_items: "{{ mariadb_databases | default([])}}"
when: mariadb_databases is defined
第6章: Playbook
核心元素:
Hosts:指明运行在哪个主机上
Remote_user:以哪个用户的身份运行任务
Tasks:任务
Variables:变量
Templates:包含了模板语法的文本文件
Handlers:由特定条件出发的任务
Roles:
6.0.1: 测试(只检测可能会发生的改变,但不真正执行操作)
ansible-playbook --check firsh.yaml
6.0.2: 定义主机和用户:
每个 playbook文件都需要指定针对哪些主机进行运维,而hosts变量则说明这个问题,users则说明了采用那个用户执行这条命令
针对webserver主机组,这里采用root用户执行命令:
---
- host: webserver
remote_user: root
如果是AWS EC2 主机,可以采用sudo模式执行命令,
---
- host: webserver
remote_user: ec2-user
sudo: yes
6.0.3: 任务列表
每个playbook都会有一份任务列表(task list),说明究竟要按照怎样的顺序去执行这些命令(从上至下,依照顺序执行task)。
tasks:
- name: name sure apache is runing
service: name=httpd state=running
每一个小task都是ansible的模块组成的
6.0.4: handlers
当被控端主机配置文件发生变化以后,通知处理程序handlers来触发后续动作,比如重启apache服务。在没有通知出发时handlers中定义的处理程序是不会执行的,触发是通过handlers定义的name标签来识别的,比如下面的notify中的”restart apache”和handlers中的name: restart apache 内容请保持一致。
notify:
- restart apache
- name: ensure apache is running
service: name=httpd state=started
handlers
- name: restart httpd
service: name=httpd state=restarted
6.0.5: 条件语句
在when 后面使用Jinja2 表达式,结果为True则执行任务。
就是如果when 后面的结构是true 就执行 task
tasks:
- name: reboot redhat host
command: /usr/sbin/reboot
when: ansible_os_family == “RedHat”
可以对条件进行分组在比较。
tasks:
- name: "shut down CentOS 6 and Debian 7 systems"
command: /sbin/shutdown -t now
when: (ansible_distribution == "CentOS" and ansible_distribution_major_version == "6") or
(ansible_distribution == "Debian" and ansible_distribution_major_version == "7")
案例3
tasks:
- shell: echo "only on Red Hat 6, derivatives, and later"
when: ansible_os_family == "RedHat" and ansible_distribution_major_version|int >= 6
6.0.6: 循环语句
6.0.6.1: 标准循环
示例:
tasks:
- name: install LNMP
yum: name={{ item }} state=present
with_items:
- nginx
- mysql-server
- php-fpm
循环还支持列表,使用的是with_flattened语句。
变量文件的示例
6.0.6.2: 列表循环
---
packages_LNMP:
- [ ‘nginx’, ‘mysql-server’, ‘php-fpm’ ]
- name: Install LNMP
yum: name={{ item }} state=present
with_flattened:
- packeges_LNMP
6.0.6.3: 字典循环
- name: add several users
user: name={{ item.name }} state=present groups={{ item.groups }}
with_items:
- { name: \'testuser1\', groups: \'wheel\' }
- { name: \'testuser2\', groups: \'root\'}
6.0.6.4: 文件循环
with_file 是将每个文件的文件内容作为item的值
with_fileglob 是将每个文件的全路径作为item的值, 在文件目录下是非递归的, 如果是在role里面应用改循环, 默认路径是roles/role_name/files_directory
- copy: src={{ item }} dest=/etc/fooapp/ owner=root mode=600
with_fileglob:
- /playbooks/files/fooapp/*
6.1: roles
目录结构:
案例
第7章: Ansible 进阶技巧
7.1: 简介
Ansible 是一个系统自动化工具,可以用来做系统配管理,批量对远程主机执行操作指令。我自己使用 Ansible 也有一段时间了,这里总结了一些使用 Ansible 过程中使用的心得与大家分享。
7.2: Ansible 性能优化
在使用 Ansible 的过程中,当管理的服务器数量增加时,不得不面对一个无法避免的问题执行效率慢,这里列出一些解决办法。
7.2.1: 优化前的准备—收集数据
在做性能优化之前首先需要做的是收集一些统计数据,这样才能为后面做的性能优化提供数据支持,对比优化前后的结果。非常不错的是,在 github 发现一个 Ansible 任务计时插件“ansible-profile”,安装这个插件后会显示 ansible-playbook 执行每一个任务所花费的时间。Github 地址:https://github.com/jlafon/ansible-profile。 这个插件安装很简单,只需要简单的三个命令即可完成安装。在你的 playbook 文件的目录下创建一个目录,目录名 callback_plugins 然后将下载的 profile_tasks.py 文件放到该目录下。
cd /etc/ansible
mkdir callback_plugins
cd callback_plugins
wget
https://raw.githubusercontent.com/jlafon/ansible-profile/master/callback_plugins/profile_tasks.py
现在,执行 ansible-playbook 命令就会看到 playbook 中每个 tasks 的用时情况。
7.2.1.1: 图 1.ansible-playbook tasks 用时情况
在这里,我设置了 2 个 task,1 个 task sleep 10 秒,另 1 个 task sleep 15 秒,在 PLAY RECAP 处会汇总所有 task 执行消耗的时间。
7.2.2: 关闭 gathering facts
如果您观察过 ansible-playbook 的执行过程中,您会发现 ansible-playbook 的第 1 个步骤总是执行 gather facts,不论你有没有在 playbook 设定这个 tasks。如果你不需要获取被控机器的 fact 数据的话,你可以关闭获取 fact 数据功能。关闭之后,可以加快 ansible-playbook 的执行效率,尤其是你管理很大量的机器时,这非常明显。关闭获取 facts 很简单,只需要在 playbook 文件中加上“gather_facts: no”即可。如下
---
- hosts: 172.16.64.240
gather_facts: no
remote_user: liheng
sudo: yes
roles:
- {role: profile_test}
好的,来看关闭前后的执行时间变化。
7.2.2.1: 图 2. 关闭 gather_facts 前后的执行变化
关闭前后,执行时间相关 1 秒,因为我这里只有一台机器,所以时间差距并不是很明显。不过,从这个例子也可以看出,关闭 facts 获取后,执行速度是快了的。
7.2.3: SSH PIPElinING
SSH pipelining 是一个加速 Ansible 执行速度的简单方法。ssh pipelining 默认是关闭,之所以默认关闭是为了兼容不同的 sudo 配置,主要是 requiretty 选项。如果不使用 sudo,建议开启。打开此选项可以减少 ansible 执行没有传输时 ssh 在被控机器上执行任务的连接数。不过,如果使用 sudo,必须关闭 requiretty 选项。修改 /etc/ansible/ansible.cfg 文件可以开启 pipelining
将
pipelining=False
修改为
pipelining=True
修改完后,可以批量对机器执行命令试下,可以明显感受到速度的提升。
7.2.4: ControlPersist
ControlPersist 特性需要高版本的 SSH 才支持,CentOS 6 默认是不支持的,如果需要使用,需要自行升级 openssh。ControlPersist 即持久化 socket,一次验证,多次通信。并且只需要修改 ssh 客户端就行,也就是 Ansible 机器即可。
升级 openssh 的过程这里不做介绍。这里只介绍下 ControlPersist 设置的办法。
cat ~/.ssh/config
Host *
Compression yes
ServerAliveInterval 60
ServerAliveCountMax 5
ControlMaster auto
ControlPath <a href="mailto:~/.ssh/sockets/%25r@%25h-%25p"><code>~/.ssh/sockets/%r@%h-%p</code></a>
ControlPersist 4h
在开启了 ControlPersist 特性后,SSH 在建立了 sockets 之后,节省了每次验证和创建的时间。在网络状况不是特别理想,尤其是跨互联网的情况下,所带来的性能提升是非常可观的。有这边需求的,试试就知道了。
7.3: Ansible-playbook 技巧
7.3.1: 获取执行命令的输出 --Register
在刚开始使用 ansible-playbook 做应用程序部署的时候,因为在部署的过程中有使用到 command 或 shell 模块执行一些自定义的脚本,而且这些脚本都会有输出,用来表示是否执行正常或失败。如果像之前自己写脚本做应用程序部署的,这很好实现。但现在是用 Ansible 做,那么要怎么样做可以获取到 ansible playbook 中 command 模块的输出呢? Ansible 也提供的解决办法,这时我们就可以通过使用 register 关键字来实现,register 关键字可以存储指定命令的输出结果到一个自定义的变量中,我们通过访问这个自定义变量就可以获取到命令的输出结果。Register 的使用很方便,只需要在 task 声明 register 关键字,并自定义一个变量名就可以。如下:
|
1 2 3 4 5 6 7 |
- name: echo date command: date register: date_output
- name: echo date_output command: echo "30" when: date_output.stdout.split(\' \')[2] == "30" |
这里第 1 个 task 是执行了一个 date 命令,register 关键字将 date 命令的输出存储到 date_output 变量名。第 2 个 task 对输出进行分析,并使用 when 对关键字对分析后的进行判断,如果匹配,则执行这个 task,不匹配就不执行。这里要重点说下的,因为 register 获取到的输出内容都是字符串,而 ansible 又是 python 写的,你可以使用 python 字符串的方法对其做处理,比如本文中使用的 split,还可以使用 find 方法。个人觉得,真是非常灵活方便。
7.3.1.1: 图 3.register 执行结果 1
这里由于条件匹配,两个 task 都执行了。然后把第 2 个 task 中的条件改动了下,使其不匹配,执行结果如下:
7.3.1.2: 图 4.register 执行结果 2
这里第 2 个 task 条件不匹配,skipping 了。
7.3.2: Delegate_to( 任务委派功能 )
场景介绍:在对一组服务器 server_group1 执行操作过程中,需要在另外一台机器 A 上执行一个操作,比如在 A 服务器上添加一条 hosts 记录,这些操作必须要在一个 playbook 联动完成。也就是是说 A 服务器这个操作与 server_group1 组上的服务器有依赖关系。Ansible 默认只会在定义好的一组服务器上执行相同的操作,这个特性对于执行批处理是非常有用的。但如果在这过程中需要同时对另外 1 台机器执行操作时,就需要用到 Ansible 的任务委派功能(delegate_to)。使用 delegate_to 关键字可以委派任务到指定的机器上运行。在 playbook 的操作如下:
|
1 2 3 4 5 6 |
- name: add host record shell: \'echo "192.168.1.100 test.xyz.com" >> /etc/hosts\'
- name: add host record to center server shell: \'echo "192.168.1.100 test.xyz.com " >> /etc/hosts\' delegate_to: 192.168.1.1 |
任务委派功能还可以用于以下场景:
- 在部署之前将一个主机从一个负载均衡集群中删除。
- 当你要对一个主机做改变之前去掉相应 dns 的记录
- 当在一个存储设备上创建 iscsi 卷的时候
- 当使用外的主机来检测网络出口是否正常的时候
7.3.3: 本地操作功能 --local_action
Ansible 默认只会对控制机器执行操作,但如果在这个过程中需要在 Ansible 本机执行操作呢?细心的读者可能已经想到了,可以使用 delegate_to( 任务委派 ) 功能呀。没错,是可以使用任务委派功能实现。不过除了任务委派之外,还可以使用另外一外功能实现,这就是 local_action 关键字。
|
1 2 |
- name: add host record to center server local_action: shell \'echo "192.168.1.100 test.xyz.com " >> /etc/hosts\' |
当然您也可以使用 connection:local 方法,如下:
|
1 2 3 |
- name: add host record to center server shell: \'echo "192.168.1.100 test.xyz.com " >> /etc/hosts\' connection: local |
这两个操作结果是一样的。
7.3.4: Check 模式
当以— check 参数来运行 ansible-playbook 时,将不会对远程的系统作出任何修改。相对的,任何带有检测功能的模块只要支持‘检测模式’将会报告它们会做出什么改变而不是直接进行改变。其他不支持检测模式的模块将即不响应也不提出相应的报告(事实上几乎所有主要核心模块都是支持‘检测模式’)。检测模式只是一种模拟。如果你的 playbook 是以先前命令的执行结果作为条件的话,那它可能作用就不明显了。但是在正式运行前,使用 check 模式做个语法检查也是不错的。
选择性执行 task--Tag(标签)
您可能因为某些原因,会创建一个很大型的 playbook,但是你可能只想想运行其中特定部分的配置而无需要运行整个 playbook 。那么这时你可能需要用到 tag 功能。示例如下:
|
1 2 3 4 5 6 7 8 9 10 11 1 2 |
- name: yun install package yum: name={{ item }} state=installed with_items: - httpd - memcached tags: - packages
- name: configuration modity template: src=templates/src.j2 dest=/etc/foo.conf tags: - configuration |
如果你只想运行 playbook 中的”configuration”和”packages”,你可以这样做
|
1 |
ansible-playbook example.yml – tags “configuration,packages” |
如果你只想执行 playbook 中某个特定任务之外的所有任务,你可以这样做:
|
1 2 |
ansible-playbook example.yml – skip-tags “configuration” tag 特性是一个不错的功能,但如果真的是要维护一个大型的 playbook,还是建议将 playbook 按功能或应用拆分成多个 playbook,然后再在主 playbook include 其他子 playbook,这样即既利于维护也方便管理 |
7.3.5: 错误处理
Ansible 默认会检查命令和模块的返回状态,并进行相应的错误处理,默认是遇到错误就中断 playbook 的执行,这些默认行为都是可以改变的。
忽略错误
command 和 shell 模块执行的命令如果返回非零状态码则 ansible 判定这 2 个模块执行失败,可以通过 ignore_errors 忽略返回状态码(前提是要确定这 command 与 shell 执行错误不会影响后面 task 的执行)。如下:
|
1 2 3 |
- name: this will not be counted as a failure command: /bin/false ignore_errors: yes |
自定义错误判定条件
命令不依赖返回状态码来判定是否执行失败,而是要查看命令返回内容来决定,比如返回内容中包括 failed 字符串,则判定为失败。示例如下:
|
1 2 3 4 |
- 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" |
ansible 会自动判断模块执行状态,command、shell 及其它模块如果修改了远程主机状态则被判定为 change 状态,不过也可以自己决定达到 changed 状态的条件,示例如下:
|
1 2 3 4 5 6 7 |
- name: copy in nginx conf template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf
- name: validate nginx conf shell: "/data/app/nginx/sbin/nginx -t" register: command_result changed_when: command_result.stdout.find(\'successful\') |
命令返回中有“successful”字符串,则为 changed 状态,下面这个设定将永远也不会达到 changed 状态。
|
1 2 3 |
- name: validate nginx conf shell: "/data/app/nginx/sbin/nginx -t" changed_when: false |
7.4: 结束语
本文介绍了一些关于 Ansible 的执行性能优化与 playbook 使用的技巧,这些都是在我们使用 Ansible 过程中需要面对的问题,希望今天列出的这些内容对大家学习和使用 Ansible 能有所帮助。
7.4.1: 相关主题
- 参考 Ansible 官方文档,Ansible 权威的使用指南和操作手册,是学习 Ansible 第一手的权威资料。
- IBM developerWorks 中国 linux 专区:为使用 linux 的开发人员准备的技术信息和资料。这里提供产品下载、how-to 信息、支持资源以及免费技术库,包含 2000 多份技术文章、教程、最佳实践、IBM Redbook 和在线产品手册。