运维自动化之ANSIBLE
1. 应用概述
1.1 一些术语
-
云计算的三个分层
基础设施在最下端,平台在中间,软件在顶端,分别是分别是Infrastructure-as-a-Service(IaaS),Platform-as-a-Service(PaaS),Software-as-a-Service(SaaS),别的一些“软”的层可以在这些层上面添加- 基础设施(infrastructure)
网络系统(Networking)、存储设备(storage)、服务器(servers)、虚拟化技术(virtualization) - 平台(platform)
操作系统(OS)、中间件(middleware)、运行库(runtime) - 软件(software)
数据(date)、应用(application)
- 基础设施(infrastructure)
-
本地部署 On-Premises
购买服务器,搭机房,构建系统,开发应用程序,所有东西都要自己准备,也就是云计算的三层都自己做- 优点:为定制提供了最灵活的选择,基本上不需要厂商的介入,日常维护费低,全面控制环境,实施监管比较容易
- 缺点:进入成本、运营、升级成本很高
-
iaas (Infrastructure as a Service)
基础设施即服务:需要购买和维护的硬件设备现在都可以租用了,企业可以将所有硬件采购和人员的维护服务都交由其他专业公司管理,直接采取租用的方式进行使用- 优点:硬件成本大幅缩减,维护人员和费用降低,实现硬件的弹性扩展
- 缺点:交由第三方公司管理,增加了沟通环节和沟通成本,应急响应和数据安全难以保障
-
Paas (Platform as a Service)
平台即服务:它是指基于硬件系统,企业可以在PaaS平台上研发SaaS应用,让原本散落在不同地方的资源变得更加容易整合及利用,使得不同开发团队的协作能力有效提升,大大缩短软件应用的研发成本和周期。- 优点:更便捷的开发环境和集成系统支持,开发效率可以得到显著提升
- 缺点:平台闭源,服务商提供指定的语言和服务,开发者自由度较低
-
SaaS (Software as a Service)
软件即服务平台:所有东西都准备好了,你需要使用其服务- 优点:SaaS的最大优点在于应用的便捷性、即时性和移动化三大特征,它对于传统设备或者客户端的依赖较小。
- 缺点:各服务商的开发质量参差不齐,实际项目交付问题频繁甚至烂尾,选择靠谱的供应商非常重要
1.2 运维工程师的核心职能
- 平台架构组建
- 负责参与并审核架构设计的合理性和可运维性,搭建运维平台技术架构
- 通过开源解决方案,以确保在产品发布之后能高效稳定的运行
- 保障并不断提升服务的可用性,确保用户数据安全,提升用户体验
- 日常运营保障
- 负责用运维技术或者运维平台确保产品可以高效的发布上线,
- 负责保障产品7*24H稳定运行,在此期间对出现的各种问题可以快速定位并解决;
- 在日常工作中不断优化系统架构和部署的合理性,以提升系统服务的稳定性
- 性能、效率优化
- 用自动化的工具/平台提升软件在研发生命周期中的工程效率
- 不断优化系统架构、提升部署效率、优化资源利用率支持产品的不断迭代
- 需要不断的运行架构优化调整,以确保整个产品能够在功能不断丰富和复杂的条件下,同时保持高可用性
1.3 产品上线发布的策略
-
蓝绿发布(Blue/Green Deployment)
蓝绿部署是最常见的一种0 downtime部署的方式,是一种以可预测的方式发布应用的技术,目的是减少发布过程中服务停止的时间。- 原理
通常生产环境需要两组配置(蓝绿配置),一组是active的生产环境的配置(绿配置),一组是inactive的配置(蓝配置)。用户访问的时候,只会让用户访问active的服务器集群。在绿色环境(active)运行当前生产环境中的应用,也就是旧版本应用version1。当你想要升级到version2 ,在蓝色环境(inactive)中进行操作,即部署新版本应用,并进行测试。如果测试没问题,就可以把负载均衡器/反向代理/路由指向蓝色环境了。随后需要监测新版本应用,也就是version2 是否有故障和异常。如果运行良好,就可以删除version1 使用的资源。如果运行出现了问题,可以通过负载均衡器指向快速回滚到绿色环境 - 优点
- 部署过程,应用始终在线,不需要停机
- 没有修改老版本任何内容,部署期间老版本不收影响
- 原理
-
灰度发布(金丝雀发布)
通过在线上运行的服务中,新加入少量的新版本的服务,然后从这少量的新版本中快速获得反馈,根据反馈决定最后的交付形态。- 金丝雀发布要解决的问题主要是缩短反馈周期,以及弥补巨大产品环境下无法进行有效容量测试所可能导致的问题的一种手段。是一个能大大降低新版本发布风险的方法。
- 灰度发布
灰度发布是通过切换线上并存版本之间的路由权重,逐步从一个版本切换为另一个版本的过程,金丝雀发布更倾向于获取快速的反馈,而灰度发布更倾向于从一个版本到另一个版本平稳的切换。
可以通过逐渐增加负载,记录并衡量应用程序响应时间,CPU使用率,I/O,内存使用率以及日志中是否有异常报告这种方式来检查应用程序是否满足容量需求,降低容量测试不理想带来的风险。
- 金丝雀发布要解决的问题主要是缩短反馈周期,以及弥补巨大产品环境下无法进行有效容量测试所可能导致的问题的一种手段。是一个能大大降低新版本发布风险的方法。
1.4 Linux运维工程师职能划分
-
Dev开发环境
- 使用者:程序员
- 功能:程序员开发软件,测试BUG的环境
- 管理者:程序员(一般不需要运维人员维护,因为开发习惯不同)
-
测试环境
- 使用者:QA测试工程师
- 功能:测试经过Dev环境测试通过的软件的功能
- 管理者:运维
- 说明:测试环境往往有多套,测试环境满足测试功能即可,不宜过多
- 测试人员希望测试环境有多套,公司的产品多产品线并发,即多个版本,意味着多个版本同步测试
- 通常测试环境有多少套和产品线数量保持一样
-
发布环境:代码发布机,有些公司为堡垒机(安全屏障)
- 使用者:运维
- 功能:发布代码至生产环境
- 管理者:运维(有经验)
- 发布机:往往需要有2台(主备)
-
生产环境
- 使用者:运维,少数情况开放权限给核心开发人员,极少数公司将权限完全开放给开发人员并其维护
- 功能:对用户提供公司产品的服务
- 管理者:只能是运维,生产环境服务器数量一般比较多,且应用非常重要。往往需要自动工具协助部署配置应用
-
灰度环境(生产环境的一部分)
- 使用者:运维
- 功能:在全量发布代码前将代码的功能面向少量精准用户发布的环境,可基于主机或用户执行灰度发布
- 管理者:运维
-
自动化运维应用场景
- 文件传输
- 应用部署
- 配置管理
- 任务流编排
2. 自动化工具Ansible
2.1 Ansible概述
创始人,Michael DeHaan( Cobbler 与 Func 的作者)
2015-10-17,Red Hat宣布收购
-
特性
- 模块化:调用特定的模块,完成特定任务,支持自定义模块
有Paramiko,PyYAML,Jinja2(模板语言)三个关键模块 - 基于Python语言实现
- 部署简单,基于python和SSH(默认已安装),agentless
- 安全,基于OpenSSH
- 支持playbook编排任务
- 幂等性:一个任务执行1遍和执行n遍效果一样,不因重复执行带来意外情况
- 无需代理不依赖PKI(无需ssl)
- 可使用任何编程语言写模块
- YAML格式,编排任务,支持丰富的数据结构
- 较强大的多层解决方案
- 模块化:调用特定的模块,完成特定任务,支持自定义模块
-
Ansible主要组成部分
- ANSIBLE PLAYBOOKS:任务剧本(任务集),编排定义Ansible任务集的配置文件,由Ansible顺序依次执行,通常是JSON格式的YML文件
- INVENTORY:Ansible管理主机的清单/etc/anaible/hosts
- MODULES:Ansible执行命令的功能模块,多数为内置核心模块,也可自定义
- PLUGINS:模块功能的补充,如连接类型插件、循环插件、变量插件、过滤插件等,该功能不常用
- API:供第三方程序调用的应用程序编程接口
- ANSIBLE:组合INVENTORY、API、MODULES、PLUGINS的绿框,可以理解为是ansible命令工具,其为核心执行工具
-
利用ansible实现管理的方式:
- Ad-Hoc
即执行单条的ansible命令,主要用于临时命令使用场景 - Ansible-playbook
主要用于长期规划好的,大型项目的场景,需要有前提的规划
执行过程:- 将已有编排好的任务集写入Ansible-Playbook
- 通过ansible-playbook命令分拆任务集至逐条ansible命令,按预定规则逐条执行
- Ad-Hoc
-
Ansible主要操作对象:
- HOSTS主机
- NETWORKING网络设备
-
注意事项
- 执行ansible的主机一般称为主控端,中控,master或堡垒机
- 主控端Python版本需要2.6或以上
- 被控端Python版本小于2.4需要安装python-simplejson
- 被控端如开启SELinux需要安装libselinux-python
- windows不能做为主控端
2.2 安装
- rpm包安装: EPEL源
yum install ansible - 编译安装:
- 准备编译环境,安装一下工具
[root@localhost ~]$yum -y install python-jinja2 PyYAML python-paramiko python-babel python-crypto
- 下载并解压下载的ansible 包
[root@localhost ~]$tar xf ansible-1.5.4.tar.gz
- 进入解压后生成的目录,依次执行一下内容
[root@localhost ~]$cd ansible-1.5.4 [root@localhost ansible]$python setup.py build [root@localhost ansible]$python setup.py install [root@localhost ansible]$mkdir /etc/ansible [root@localhost ansible]$cp -r examples/* /etc/ansible
- 准备编译环境,安装一下工具
- 确认安装: ansible --version
2.3 ansible相关文件
- 配置文件:/etc/ansible/hosts
ansible的主要功用在于批量主机操作,想要完成主机管理,第一步要编辑主机清单(inventory),为了便捷地使用其中的部分主机,可以在inventory file中将其分组命名- 分组命名遵循INI文件风格,也就是中括号中的字符为组名,如[web]
[web] 192.168.169.128 192.168.169.129
- 可以将同一个主机同时归并到多个不同的组中,如192.168.169.129同时存在于[web]与[app]中
[web] 192.168.169.128 192.168.169.129 [app] 192.168.169.130 192.168.169.129
- 支持主机名写法,并且支持通配符
www[01:50].example.com <==支持通配符匹配www01 www02 ...www50
- 当如若目标主机使用了非默认的SSH端口,还可以在主机名称之后使用冒号加端口号来标明
www1.magedu.com:2222
- 分组命名遵循INI文件风格,也就是中括号中的字符为组名,如[web]
- 配置文件
- 主配置文件,配置ansible工作特性
/etc/ansible/ansible.cfg - 主机清单,配置被管理端主机
/etc/ansible/hosts - 存放角色的目录
/etc/ansible/roles/
- 主配置文件,配置ansible工作特性
- 程序
- 主程序,临时命令执行工具
/usr/bin/ansible - 查看配置文档,模块功能查看工具
/usr/bin/ansible-doc - 下载/上传优秀代码或Roles模块的官网平台
/usr/bin/ansible-galaxy - 定制自动化任务,编排剧本工具
/usr/bin/ansible-playbook - 远程执行命令的工具
/usr/bin/ansible-pull - 文件加密工具
/usr/bin/ansible-vault - 基于Console界面与用户交互的执行工具
/usr/bin/ansible-console
- 主程序,临时命令执行工具
2.3.1 ansible 主配置文件说明
Ansible 主配置文件/etc/ansible/ansible.cfg (一般保持默认)
ansible通过ssh实现配置管理、应用部署、任务执行等功能,建议配置ansible端能基于key验证的方式联系各被管理节点
[defaults]
#inventory = /etc/ansible/hosts #
`主机清单配置文件`
#library = /usr/share/my_modules/ #
`库文件存放目录,存放模块`
#module_utils = /usr/share/my_module_utils/
`用到的utils工具存放目录`
#remote_tmp = $HOME/.ansible/tmp #
`临时py命令文件存放在远程主机目录,先把ansible执行的指令,先生成py程序,然后放在本地的目录里,然后再用ssh协议,将生成的py程序复制到被管理的机器remote_tmp目录下,复制过去后,拿出来执行,执行完毕后会将该程序删除`
#local_tmp = $HOME/.ansible/tmp #
`本机的临时命令执行目录`
#forks = 5 #
`默认并发数`
#poll_interval = 15
`每隔15秒去查看对方的状态`
#sudo_user = root #
`默认sudo 用户 `
#ask_sudo_pass = True
#ask_pass = True
`每次执行ansible命令是否询问ssh密码`
#remote_port = 22
#host_key_checking = False #
`检查对应服务器的host_key,建议取消注释,用于取消第一次连接问yes|no`
#log_path=/var/log/ansible.log
`日志文件,建议取消注释,可以记录日志`
2.4 ansible系列命令
- Ansible系列命令
- ansible
- ansible-doc
- ansible-playbook
- ansible-vault
- ansible-console
- ansible-galaxy
- ansible-pull
- ansible命令执行过程
- 加载自己的配置文件 默认/etc/ansible/ansible.cfg
- 加载自己对应的模块文件,如command
- 通过ansible将模块或命令生成对应的临时py文件,并将该文件传输至远程服务器的对应执行用户$HOME/.ansible/tmp/ansible-tmp-数字/XXX.PY文件
- 给文件+x执行
- 执行并返回结果
- 删除临时py文件,sleep 0退出
- 常见执行状态:
- 绿色:执行成功并且不需要做改变的操作
- 黄色:执行成功并且对目标主机做变更
- 红色:执行失败
2.4.1 显示模块帮助ansible-doc
- 格式
ansible-doc [options] [module…] - 选项
-a:显示所有模块的文档
-l, --list:列出可用模块
-s, --snippet:显示指定模块的playbook片段 - 示例:
- 示例1:列出所有模块,目前1852个
[root@localhost ~]$ansible-doc -l |wc -l 1852
- 示例2:查看指定模块帮助用法,较详细说明
[root@localhost ~]$ansible-doc ping
- 示例3:查看指定模块帮助用法,简要说明
[root@localhost ~]$ansible-doc -s ping
- 示例1:列出所有模块,目前1852个
2.4.2 主程序,临时执行任务命令ansible
-
格式
ansible <host-pattern> [-m module_name] [-a args] -
选项
<host-pattern>定义要管理的在主机清单里的一部分主机,可以是分组名如[app],all表示清单里的所有主机,也可以是一台主机选项 功能 -a args 模块参数,没有参数可忽略 --version
显示版本 -m module 指定模块,默认为command -v 详细过程 –vv 、-vvv更详细 --list-hosts
显示主机列表,可简写 --list -k, --ask-pass 提示输入ssh连接密码,默认Key验证 -K, --ask-become-pass 提示输入sudo时的口令 -C, --check 检查,并不执行 -T, --timeout=TIMEOUT 执行命令的超时时间,默认10s -U, --user=REMOTE_USER 远程执行的用户 -b, --become 代替旧版的sudo 切换 -
Host-pattern匹配主机的列表的表示方法
- All :表示所有Inventory中的所有主机
[root@localhost ~]$ansible all -m ping
- * :通配符
匹配所有主机同all [root@localhost ~]$ansible '*' -m ping 匹配192.168.1网段的所有主机 [root@localhost ~]$ansible 192.168.1.* -m ping 匹配所有以srvs结尾的项 [root@localhost ~]$ansible '*srvs' -m ping
- 或关系,两部分都会执行,中间以冒号隔开
[root@localhost ~]$ansible 'web:app' -m ping [root@localhost ~]$ansible '192.168.1.10:192.168.1.20' -m ping
- 逻辑与,交集关系
在web组并且在app组中的主机 [root@localhost ~]$ansible 'web:&app' -m ping
- 逻辑非,排除关系
[root@localhost ~]$vim /etc/ansible/hosts [web] 192.168.169.128 192.168.169.129 [app] 192.168.169.130 192.168.169.129 [root@localhost ~]$ansible 'web:!app' -m ping 192.168.169.128 | SUCCESS => { <==只显示128主机
- 综合逻辑
属于web或db但不属于app排除ftp组内的主机 [root@localhost ~]$ansible 'web:db:&app:!ftp' -m ping
- 正则表达式
匹配以web或者db服务支持的所有magedu.com域名 [root@localhost ~]$ansible '~(web|db).*\.magedu\.com' -m ping ~表示后面跟正则表达式
- All :表示所有Inventory中的所有主机
-
示例:
- 示例1:以ping模块为例,判断被管理主机是否可以正常连接。这里的ping走的是ssh协议
[root@localhost ~]$ansible 192.168.169.128 -m ping -k SSH password: <==提供对方的口令 192.168.169.128 | SUCCESS => { "changed": false, "ping": "pong" <==可以正常连接,返回pong } 生成key,然后通过脚本推送到管理的主机上,使用基于key验证 [root@localhost ~]$ssh-keygen 推送成功后,再次执行相关命令,不需要加-k,基于key为默认方式 [root@localhost ~]$ansible all -m ping
- 示例2:以wang用户执行ping存活检测
[root@localhost ~]$ansible all -m ping -u wang -k
- 示例3:以v9 用户sudo至root执行ping存活检测
[root@localhost ~]$ansible 192.168.169.128 -m ping -u v9 -k
- 示例1:以ping模块为例,判断被管理主机是否可以正常连接。这里的ping走的是ssh协议
2.4.3 ansible-galaxy
-
功能
管理从 https://galaxy.ansible.com 下载的各种roles -
列出所有已安装的galaxy
ansible-galaxy list
-
安装galaxy
ansible-galaxy install geerlingguy.redis
例如:从网站获取apache的角色模板[root@localhost ~]$ansible-galaxy install geerlingguy.apache
-
获取galaxy
从 https://galaxy.ansible.com 获取,选取相应roles,复制下载命令 -
删除galaxy
ansible-galaxy remove geerlingguy.redis[root@localhost ~]$ansible-galaxy remove geerlingguy.apache
2.4.4 ansible-pull
推送命令至远程,效率无限提升,对运维要求较高,没有实践数据
2.4.5 Ansible-playbook
- 功能:用于执行配置好的剧本,后文有详细说明
- 示例
[root@localhost /data]$ansible-playbook hello.yml [root@localhost /data]$cat hello.yml - hosts: websrvs remote_user: root tasks: - name: hello world command: /usr/bin/wall hello world
2.4.6 Ansible-vault
- 功能
管理加密解密yml文件 - 格式:
ansible-vault [create|decrypt|edit|encrypt|rekey|view] - 示例:
为角色hello.yml加密,参数encrypt
加密后的角色也可以查看,添加view参数[root@localhost ~]$ansible-vault encrypt hello.yml New Vault password: Confirm New Vault password: <==输入两次密码 Encryption successful 查看加密后的角色 [root@localhost ~]$cat hello.yml $ANSIBLE_VAULT;1.1;AES256 <==使用的加密算法 32323939376538383839613533383463653962653739633834653730366235393 436386232303436
加密后的角色也可以直接编辑,参数为edit[root@localhost ~]$ansible-vault view hello.yml Vault password: # world yml file
加密后不满意原密码,可以修改口令,参数rekey[root@localhost ~]$ansible-vault edit hello.yml
加密后的角色是不能执行的,需要先执行解密命令,参数decrypt[root@localhost ~]$ansible-vault rekey hello.yml Vault password: 原密码 New Vault password: 新密码 Confirm New Vault password: 重复新密码 Rekey successfu
直接创建加密的新文件,参数create[root@localhost ~]$ansible-vault decrypt hello.yml Vault password: <==输入一次密码 Decryption successful
[root@localhost ~]$ansible-vault create newhello.yml
2.4.7 Ansible-console
Ansible-console:2.0+新增,可交互执行命令
[root@localhost ~]$ansible-console
Welcome to the ansible console.
Type help or ? to list commands.
| | | | | |
root @ all (3) [f:5]$ ? <==敲[?]或者[help]获得帮助,会列出很多命令
|执行用户|@|当前操作的主机数| (当前组的主机数量)|[f:并发数]|$
- 常用命令
- 设置并发数: forks n
例如: forks 10
- 切换组: cd 主机组
例如: cd web
- 列出当前组主机列表: list
- 列出所有的内置命令: ?或help
- 设置并发数: forks n
- 示例
安装httpd服务,直接输入模块和服务名,不需要加-m和-a[root@localhost ~]$ansible-console Welcome to the ansible console. Type help or ? to list commands. root@all (3)[f:5]$ yum name=httpd
3. ansible常用模块介绍
3.1 Command模块
- 作用:在远程主机执行命令,默认模块,可忽略-m选项
在所有被管理主机上执行ls
[root@localhost ~]$ansible all -m command -a 'ls /data'
在所有被管理主机上增加用户test
[root@localhost ~]$ansible all -m command -a 'usradd test'
- 常用参数
chdir:在运行该命令之前,切换到该目录
creates:指定的文件存在时,不执行对应命令[root@localhost /var/log]$ansible 192.168.169.129 -a 'chdir=/root ls'
remove:与creates相反,指定的文件不存在,则不执行对应命令[root@localhost /data]$ansible app -a 'creates=/etc/passwd ls' 192.168.169.130 | SUCCESS | rc=0 >> skipped, since /etc/passwd exists <==跳过不执行
[root@localhost /data]$ansible app -a 'removes=/etc/passwd ls' 192.168.169.129 | SUCCESS | rc=0 >> anaconda-ks.cfg Desktop
- 此命令在远程主机中执行命令时,不会经过远程主机的shell处理,不支持$VARNAME < > | ; & 等
[root@localhost ~]$ansible all -m command -a 'echo magedu |passwd --stdin v9' 192.168.169.130 | SUCCESS | rc=0 >> magedu |passwd --stdin wang <==只是将字符串打印出来,没有执行
3.2 Shell模块与Script模块
- Shell模块
- 作用
和command相似,会用远程shell执行命令,支持$VARNAME < > | ; & 等 - 参数:
executable:调用远程主机的其他shell类型,如executable=/bin/csh
[root@localhost ~]$ansible all -m shell -a 'echo free7801336|passwd --stdin v9' 192.168.169.129 | SUCCESS | rc=0 >> Changing password for user v9. passwd: all authentication tokens updated successfully.
- 作用
- 将shell修改为默认模块
[root@localhost ~]$vim /etc/ansible/ansible.cfg #default module name for /usr/bin/ansible #module_name = command <==修改为常用模块即可
调用bash执行命令 类似cat /tmp/stanley.md | awk -F'|' '{print $1,$2}' &> /tmp/example.txt
这些复杂命令,即使使用shell也可能会失败,解决办法:写到脚本中,copy到远程,执行,再把需要的结果拉回执行命令的机器
- Script模块
- 作用
运行本地的脚本 - 调用格式
-m script -a "/PATH/TO/SCRIPT_FILE"
[root@localhost /data]$ansible app -m script -a '/data/f1.sh'
- 参数
creates:如果其后跟的文件存在,则不执行脚本[root@localhost /data]$ansible app -m script -a 'creates=/etc/fstab /data/f1.sh' 192.168.169.130 | SKIPPED <==跳过,不执行 192.168.169.129 | SKIPPED
3.3 Copy模块
- 作用
从服务器复制文件到客户端 - 参数
- backup:如果目标主机已经有源文件,会事先备份,防止覆盖
- src:源文件路径
- dest:目标绝对路径,如果源是文件夹,目标也必须是文件夹,不存在将创建
- mode:设置读写权限
- ower:复制时候带所有者
- content:将目标文件的内容,指定为content所带的字符串
- 示例
例如:将源文件f1复制到被管理组srv的所有主机上,目标路径为/tmp/并改名为f2,指定所有者为v9,设置权限为666
如果再次执行此命令[root@localhost /data]$ansible app -m copy -a 'src=/data/f1.sh dest=/data/f2.sh owner=v9 mode=666 ' 192.168.169.129 | SUCCESS => { "changed": true, 192.168.169.130 | SUCCESS => { "changed": true,
换一个文件拷贝到目标主机,路径和文件名依然是dest=/data/f2.sh,加backup=yes备份文件[root@localhost /data]$ansible app -m copy -a 'src=/data/f1 dest=/data/f2 owner=v9 mode=666 backup=yes' 192.168.169.129 | SUCCESS => { <==显示执行成功,但是目标没有发生变化,不会因为目标存在而报错,这就是ansible的幂等性 "changed": false,
编辑内容,在被控制主机上生成文件[root@localhost /data]$ansible app ansible app -m copy -a 'src=/data/text dest=/data/f2 owner=v9 mode=666 backup=yes' 到目标主机上查看,这里需要注意的是模块shell执行命令时,是不支持别名的,例如ll执行会失败,可以ls -l来执行。 [root@localhost ~]$ansible app -a 'ls -l /data' 192.168.169.130 | SUCCESS | rc=0 >> -rw-rw-rw- 1 v9 root 0 Sep 27 07:43 f2 -rw-rw-rw- 1 v9 root 0 Sep 27 07:43 f2.3185.2018-09-26@09:09:47~ <== backup的备份效果
[root@localhost ~]$ansible app -m copy -a 'content="SELINUX=disable\nSELINUXTYPE" dest=/data/f1.txt' [root@localhost ~]$ansible app -a 'cat /data/f1.txt' 192.168.169.129 | SUCCESS | rc=0 >> SELINUX=disable SELINUXTYPE
3.4 Fetch模块
- 作用
从客户端取文件(只能是文件)至服务器端的目录里,与copy相反,如果一定要拉取目录,可以先将目录tar,再拉取 - 参数
src:源文件路径,这里必须为文件 - 示例
将远程主机的/data/f1.txt文件,拉取到本机上[root@localhost ~]$ansible app -m fetch -a 'src=/data/f1.txt dest=/data/' 将目标主机拉取过来的文件,自动建立以ip地址命名的目录分别存放 [root@localhost /data]$tree . ├── 192.168.169.129 │ └── data │ └── f1.txt ├── 192.168.169.130 │ └── data │ └── f1.txt
3.4 File模块
- 作用
设置远程主机的文件属性 - 参数
- path
指定要管理的文件,也可以写成‘dest’,‘name’ - state
状态,可以将值设定为directory表示创建目录,设定为touch表示创建文件,设定为link表示创建软连接,设定为hard表示创建硬连接,设定为absent表示删除目录文件或链接 - mode:指定权限
- owner:指定所有者
- group:指定所属组
- recurese:递归修改,用于文件
- path
- 示例、
远程主机root下文件a.sh的所有者改为wang,权限改为755
创建软连接,首先创建一个空文件data/file1,再创建软连接文件/data/file1-link[root@localhost ~]$ansible srv -m file -a "path=/root/a.sh owner=wang mode=755“
创建目录,state=directory[root@localhost /data]$ansible app -m file -a 'dest=/data/file1 state=touch mode=600' [root@localhost /data]$ansible app -a 'ls /data' 192.168.169.129 | SUCCESS | rc=0 >> file1 创建软连接,需要用src指定要创建软连接的文件路径 [root@localhost /data]$ansible app -m file -a 'src=/data/file1 dest=/data/file1-line state=link' [root@localhost /data]$ansible app -a 'ls -l /data' 192.168.169.129 | SUCCESS | rc=0 >> -rw------- 1 root root 0 Sep 27 08:28 file1 lrwxrwxrwx 1 root root 11 Sep 27 08:30 file1-line -> /data/file1
删除目录或文件,state=absent[root@localhost /data]$ansible app -m file -a 'dest=/data/dir state=directory'
也可以删除一级目录,会报错,但是可以清楚数据[root@localhost /data]$ansible app -m file -a 'dest=/data/dir state=absent'
[root@localhost /data]$ansible app -m file -a 'dest=/data/ state=absent' 192.168.169.130 | FAILED! => { "changed": false, <==报错 [root@localhost /data]$ansible app -a 'ls -l /data/' 192.168.169.129 | SUCCESS | rc=0 >> total 0 <==数据已经清空
3.5 Hostname模块
- 作用
管理主机名 - 示例
更改192.168.169.129的主机名,会更改目标主机的/etc/sysconfig/network文件,永久生效[root@localhost /data]$ansible 192.168.169.129 -m hostname -a 'name=hai7-6'
3.6 Cron模块
- 作用
制定远程主机计划任务 - 支持时间参数
minute,hour,day,month,weekday - 参数
创建计划任务,每10分钟执行一次同步时间,将此计划任务命名为synctime
禁用计划任务disabled=true[root@localhost ~]$ansible app -m cron -a 'minute=*/10 job="ntpdate 172.0.0.1 &> /dev/null" name=synctime' [root@localhost /data]$ansible app -a 'crontab -l' 192.168.169.129 | SUCCESS | rc=0 >> #Ansible: synctime <==计划任务的命名 */10 * * * * ntpdate 172.0.0.1 &> /dev/null <==没有指定其他时间,默认为' * '
启用计划任务disabled=no[root@localhost /data]$ansible app -m cron -a 'disabled=true minute=*/10 job="ntpdate 172.0.0.1 &> /dev/nll" name=synctime'
删除计划任务state=absent[root@localhost ~]$ansible app -m cron -a 'disabled=no minute=*/10 job="ntpdate 172.0.0.1 &> /dev/nll" name=synctime'
[root@localhost ~]$ansible app -m cron -a 'state=absent minute=*/10 job="ntpdate 172.0.0.1 &> /dev/nll" name=synctime'
3.6 Yum模块
- 作用
管理包(确认被管理端为红帽系列的) - 参数
安装,state=latest默认选项,可省略,支持多程序一起安装,用逗号隔开
删除,state=absent,支持多程序一起执行[root@localhost ~]$ansible app -m yum -a 'name=httpd,samba '
[root@localhost ~]$ansible app -m yum -a 'name=httpd,samba state=absent '
3.6 Service模块
- 作用
管理远程设备服务状态 - 支持参数
state:其值可以为stopped、started、reloaded、restarted、running
name:指定需要控制的服务
enabled:指定服务是否为开机启动,yes为启动,no为不启动[root@localhost ~]$ansible app -m service -a 'name=httpd state=stopped' [root@localhost ~]$ansible app -m service -a 'name=httpd state=started' [root@localhost ~]$ansible app -m service -a 'name=httpd state=reloaded' [root@localhost ~]$ansible app -m service -a 'name=httpd state=restarted'
3.7 User模块
- 作用
用于管理远程主机用户设置 - 参数
system=yes 表示添加系统用户
remove=yes表示删除家目录 - 示例
添加一个系统用户,用户名为test1,uid=222,家目录,组为root,附加组为bin,默认shell为nologin
删除用户[root@localhost ~]$ansible app -m user -a 'name=test1 uid=222 home=/data/test1 system=yes group=root groups=bin shell=/sbin/nologin'
[root@localhost ~]$ansible app -m user -a 'name=test1 state=absent remove=yes'
3.7 Group模块
- 作用
管理远程主机组设置 - 示例
创建系统组testgroup
删除组testgroup[root@localhost ~]$ansible srv -m group -a 'name=testgroup system=yes'
[root@localhost ~]$ansible srv -m group -a 'name=testgroup state=absent'
4 playbook
play的主要功能在于将事先归并为一组的主机装扮成事先通过ansible中的task定义好的角色。从根本上来讲,所谓task无非是调用ansible的一个module。将多个play组织在一个playbook中,即可以让它们联同起来按事先编排的机制同唱一台大戏,Playbook采用YAML语言编写
4.1 YAML介绍
YAML是一个可读性高的用来表达资料序列的格式。YAML参考了其他多种语言,包括:XML、C语言、Python、Perl以及电子邮件格式RFC2822等。Clark Evans在2001年在首次发表了这种语言,另外Ingy döt Net与Oren Ben-Kiki也是这语言的共同设计者
YAML Ain’t Markup Language,即YAML不是XML。不过,在开发的这种语言时,YAML的意思其实是:“Yet Another Markup Language”(仍是一种标记语言)
-
特性
- YAML的可读性好
- YAML和脚本语言的交互性好
- YAML使用实现语言的数据类型
- YAML有一个一致的信息模型
- YAML易于实现
- YAML可以基于流来处理
- YAML表达能力强,扩展性好
-
参考资料
更多的内容及规范参见http://www.yaml.org -
YAML语法简介
- 在单一档案中,可用连续三个连字号(—)区分多个档案,也就是所可以在一个文件里可以写多个playbook。另外,还有选择性的连续三个点号( … )用来表示档案结尾,当文件只有一个playbook时,写上比较规范,不写也不会报错
- 次行开始正常写Playbook的内容,一般建议写明该Playbook的功能,使用#号注释代码
- 缩进必须是统一的,不能空格和tab混用
- 缩进的级别也必须是一致的,同样的缩进代表同样的级别,程序判别配置的级别是通过缩进结合换行来实现的
- YAML文件内容和Linux系统大小写判断方式保持一致,是区别大小写的,k/v的值均需大小写敏感
- k/v的值可同行写也可换行写。同行使用:分隔
- v可是个字符串,也可是另一个列表
- 一个完整的代码块功能需最少元素需包括 name: task
- 一个name只能包括一个task,name表示描述性语句,task表示要做那些事
- YAML文件扩展名通常为yml或yaml
-
YAML语法示例
-
List:列表,其所有元素均使用“-”打头,表示平级关系
# A list of tasty fruits - Apple - Orange - Strawberry - Mango
-
Dictionary:字典,通常由多个key与value构成,类似于变量赋值,key相当于变量,value为值,只不过连接用冒号
--- # An employee record name: Example Developer job: Developer skill: Elite
也可以将key:value放置于{}中进行表示,用,分隔多个key:value
--- # An employee record {name: Example Developer,job: Developer,skill: Elite}
-
YAML语法
YAML的语法和其他高阶语言类似,并且可以简单表达清单、散列表、标量等数据结构。其结构(Structure)通过空格来展示,序列(Sequence)里的项用"-“来代表,Map里的键值对用”:"分隔name: John Smith age: 41 gender: Male spouse: <==字典嵌套,spouse:表示key name: Jane Smith <==name/age/gender三个字典为值 age: 37 gender: Female children: <==字典与列表嵌套 - name: Jimmy Smith <==被嵌套的列表 age: 17 <==列表内的字典 gender: Male - name: Jenny Smith age: 13 gender: Female
-
4.2 Playbook核心元素与基础组件
-
Playbook核心元素
- Hosts
执行的远程主机列表,Hosts为key,其值为将来要执行的主机列表 - Tasks
任务集 - Varniables
内置变量或自定义变量在playbook中调用 - Templates (模板)
可替换模板文件中的变量并实现一些简单逻辑的文件 - Handlers 和notity结合使用
由特定条件触发的操作,满足条件方才执行,否则不执行 - tags (标签)
指定某条任务执行,用于选择运行playbook中的部分代码。ansible具有幂等性,因此会自动跳过没有变化的部分,即便如此,有些代码为测试其确实没有发生变化的时间依然会非常地长。此时,如果确信其没有变化,就可以通过tags跳过此些代码片断
- Hosts
-
playbook基础组件
- Hosts
playbook中的每一个play的目的都是为了让某个或某些主机以某个指定的用户身份执行任务。hosts用于指定要执行指定任务的主机,须事先定义在主机清单中 - remote_user
可用于Host和task中。表示在远程的主机上以谁的身份去进行后面的任务。也可以通过指定其通过sudo的方式在远程主机上执行任务,其可用于play全局或某任务;此外,甚至可以在sudo时使用sudo_user指定sudo时切换的用户- hosts: websrvs remote_user: root <==指定全局为root身份 tasks: - name: test connection ping: remote_user: magedu <==表示针对name这个模块使用magedu sudo: yes <==默认sudo为root sudo_user:wang <==指定sudo切换时,使用wang
- task列表和action
- play的主体部分是task list。
task list中的各任务按次序逐个在hosts中指定的所有主机上执行,即在所有主机上完成第一个任务后,再开始第二个任务 - task的目的是使用指定的参数执行模块,而在模块参数中可以使用变量。模块执行是幂等的,这意味着多次执行是安全的,因为其结果均一致
- 每个task都应该有其name,用于playbook的执行结果输出,建议其内容尽可能清晰地描述任务执行步骤。如果未提供name,则action的结果将用于输出
- play的主体部分是task list。
- Hosts
-
tasks:任务列表
-
格式:
-
action: module arguments
action: command /sbin/setenforce 0
-
module: arguments 建议使用
command: /sbin/setenforce 0
-
注意:
- shell和command模块后面跟命令,而非key=value
- 某任务的状态在运行后为changed时,可通过“notify”通知给相应的handlers(触发任务)
- 任务可以通过"tags“打标签,而后可在ansible-playbook命令上使用-t指定进行调用
-
-
确保返回结果为0
- 如果命令或脚本的退出码不为零,任务列表就会中断
可以使用如下方式替代,使其返回值为0,以可以继续执行后续任务
举个例子tasks: - name: run this command and ignore the result shell: /usr/bin/somecommand || /bin/true <==在命令后加[|| /bin/true]
[root@localhost ~]$ls /ls || /bin/true <==查看不存在的文件,报错 ls: cannot access /ls: No such file or directory [root@localhost ~]$echo $? <==返回结果为0 0
- 或者使用ignore_errors来忽略错误信息:
tasks: - name: run this command and ignore the result shell: /usr/bin/somecommand ignore_errors: True <==忽略错误
- 如果命令或脚本的退出码不为零,任务列表就会中断
4.3 playbook执行
-
运行playbook的方式
ansible-playbook <filename.yml> ... [options]
-
常见选项
选项 作用 -C,--check 只检测可能会发生的改变,但不真正执行操作 --list-hosts 列出运行任务的主机 --limit 主机列表 只针对主机列表中的主机执行 -v 显示过程 -vv -vvv 更详细 -
示例
ansible-playbook file.yml --check
只检测
ansible-playbook file.yml
执行剧本
ansible-playbook file.yml --limit websrvs
执行剧本,只针对hosts中的websrvs组
4.4 编写剧本
- 编写剧本
内容为创建文件test.conf,创建用户appuser,安装httpd
执行检测,查看有没有语法错误--- #test yaml file - hosts: app <==针对的主机清单,需要注意空格-后和:后都有1个空格 remote_user: root <==以哪个用户身份在远程执行 tasks: <==将要执行的动作列表 - name: create config file <==action的名字 file: name=/data/test.conf state=touch <==action的模块 - name: create app user <==action的名字 user: name=appuser shell=/sbin/nologin uid=1009 <==action的模块 - name: install app package <==action的名字 yum: name=httpd <==action的模块
-C
查看剧本执行影响的主机[root@localhost ~]$ansible-playbook -C juben1.yml
--list-hosts
如果想要在列表中挑一个主机出来执行[root@localhost ~]$ansible-playbook juben1.yml --list-hosts playbook: juben1.yml play #1 (app): app TAGS: [] pattern: [u'app'] hosts (2): 192.168.169.130 192.168.169.129
--limit
[root@localhost ~]$ansible-playbook --limit 192.168.169.129 juben1.yml
- Playbook 与 ShellScripts比较
SHELL脚本 Playbook定义 #!/bin/bash --- # 安装Apache - hosts: all yum install --quiet -y httpd tasks: # 复制配置文件 - name: "安装Apache" cp /tmp/httpd.conf /etc/httpd/conf/httpd.conf yum: name=httpd cp/tmp/vhosts.conf /etc/httpd/conf.d/ - name: "复制配置文件" # 启动Apache,并设置开机启动 copy: src=/tmp/httpd.conf dest=/etc/httpd/conf/ service httpd start - name: "复制配置文件" chkconfig httpd on copy: src=/tmp/vhosts.conf dest=/etc/httpd/conf.cd/ - name: "启动Apache,并设置开机启动" service: name=httpd state=started enabled=yes
- Playbook示例:安装httpd.yml,创建系统账号,并启动
- hosts: websrvs remote_user: root tasks: <==需要注意缩进和空格[-]和[:]后都有空格 - name: create group group: name=apache gid=80 system=yes - name: create user user: name=apache uid=80 system=yes group=apache shell=/sbin/nologin home=/data/www - name: Install httpd yum: name=httpd state=present - name: copy config file copy: src=data/httpd.conf dest=/etc/httpd/conf/httpd.conf 拷贝管理机上修改过 的配置文件到被管理端上 - name: start service service: name=httpd state=started enabled=yes
在实际工作中,我们很可能会经常修改配置文件,但是ansible的幂等性决定了,已经执行的操作不会重复发生,也就是说,上例中如果配置文件发生改变,服务不会重启,配置文件也就生效不了,这时就可以用到handlers了
4.4.1 handlers和notify结合使用触发条件
-
Handlers
也是task列表,这些task与前述的task并没有本质上的不同,用于当关注的资源发生变化时,才会采取一定的操作 -
Notify
此action可用于在每个play的最后被触发,这样可避免多次有改变发生时每次都执行指定的操作,仅在所有的变化发生完成后一次性地执行指定操作。在notify中列出的操作称为handler,也即notify中调用handler中定义的操作 -
示例
将上例拷贝执行文件部分修改如下- name: copy config file copy: src=data/httpd.conf dest=/etc/httpd/conf/httpd.conf notify: restart service <==触发动作,也就是在什么发生改变时触发,就放在其下,后面跟触发后要执行的handlers的name - name: copy another config file copy: src=data/httpd2.conf dest=/etc/httpd/conf/httpd2.conf notify: restart service <==可以有多个notify,对应一个handlers - name: start service service: name=httpd state=started enabled=yes handlers: <==当notify作用时,就触发handlers执行以下动作 - name: restart service service: name=httpd state=restarted
也可以一个notify,去触发多个handlers
- hosts: websrvs remote_user: root tasks: - name: add group nginx tags: user user: name=nginx state=present - name: add user nginx user: name=nginx state=present group=nginx - name: Install Nginx yum: name=nginx state=present - name: config copy: src=/root/config.txt dest=/etc/nginx/nginx.conf notify: - Restart Nginx - Check Nginx Process handlers: - name: Restart Nginx service: name=nginx state=restarted enabled=yes - name: Check Nginx process shell: killall -0 nginx > /tmp/nginx.log <==信号0表示对进程错误检查,返回非0表示进程故障
4.4.2 Playbook中tags(标签、标记)使用
- hosts: websrvs
remote_user: root
tasks:
- name: create group
group: name=apache gid=80 system=yes
- name: create user
user: name=apache uid=80 system=yes group=apache shell=/sbin/nologin home=/data/www
- name: Install httpd
yum: name=httpd state=present
- name: copy config file
copy: src=data/httpd.conf dest=/etc/httpd/conf/httpd.conf
notify: restart service
tags: conf <==标记标签
- name: copy another config file
copy: src=data/httpd2.conf dest=/etc/httpd/conf/httpd2.conf
notify: restart service
tags: conf <==可以标记不同任务为相同标签
- name: start service
service: name=httpd state=started enabled=yes
handlers:
- name: restart service
service: name=httpd state=restarted
执行如下指令-t conf
,表示只执行标签项
[root@localhost ~]$ansible-playbook -t conf juben1.yml
TASK [copy config file] <==执行name=copy config file
TASK [copy another config file] <==执行name=copy another config file
RUNNING HANDLER [restart service] <==触发handlers
如果上例中的两个标签名不同,假设分别为conf1和conf2,同时执行使用逗号分隔
[root@localhost ~]$ansible-playbook -t conf1,conf2 juben1.yml
4.5 Playbook中变量使用
-
变量名
仅能由字母、数字和下划线组成,且只能以字母开头 -
术语
Facts
Facts 是发现远端节点的信息。当它们被用在模板的时候, facts只能被引用,而不能被设置。Facts是当运行 plays 时候执行内部的’setup’模块自动收集的。你不需要明确的调用 setup 模块,它自己运行,但是当你想节省时间的时候你可以禁止它。为了方便用户转向其他系统配置工具, fact 模块可以拉取 facts 从 Chef的’ohai’ 和 Puppet的’facter’工具 -
获取系统变量:
-
模块setup:收集被管理主机的各种信息,其中有很多变量,可以拿来使用
[root@localhost ~]$ansible all -m setup |grep name "ansible_nodename": "hai7-8" <==facts [root@localhost ~]$ansible all -m setup |grep fqdn "ansible_fqdn": "hai7-8" [root@localhost ~]$ansible all -m setup |grep version "ansible_distribution_major_version": "7" [root@localhost ~]$ansible all -m setup |grep cpu "ansible_processor_vcpus": 2, [root@localhost ~]$ansible all -m setup |grep mem "ansible_memtotal_mb": 1478, [root@localhost ~]$ansible all -m setup |grep -A 3 ipv4 "ansible_all_ipv4_addresses": [ "192.168.169.128"
-
常用参数:filter 过滤
[root@localhost ~]$ansible all -m setup -a 'filter=ansible_nodename' 192.168.169.128 | SUCCESS => { "ansible_facts": { "ansible_nodename": "hai6" <==过滤得到的主机名 }, "changed": false
支持模糊匹配
[root@localhost ~]$ansible all -m setup -a 'filter=*_nodename'
-
使用setup变量,创建一个带主机名的文件,可以直接调用,不需要声明
--- - hosts: app remote_user: root tasks: - name: create log file file: name=/var/log/ {{ ansible_fqdn }} state=touch <==调用系统变量主机全名 ansible-playbook var.yml
-
-
在/etc/ansible/hosts中定义变量
- 普通变量(主机变量):主机组中主机单独定义,优先级高于公共变量
例如,定义web组中的主机,变量hostname
直接在剧本中引用,执行结果[root@localhost /data]$vim /etc/ansible/hosts ## db-[99:101]-node.example.com [web] 192.168.169.128 hostname=hai6.centos <==hostname为定义的变量 192.168.169.129 hostname=hai7.centos
执行test.yml后,查看被管理主机生成的文件,如下所示[root@localhost ~]$vim test.yml --- - hosts: web remote_user: root tasks: - name: create log file file: name=/data/{{ hostname }} state=touch <==引用普通变量
[root@hai7-6 /data]$ls hai7.centos
- 公共(组)变量:针对主机组中所有主机定义统一变量
将上例中的剧本任务修改为[root@localhost /data]$vim /etc/ansible/hosts # db-[99:101]-node.example.com [web] 192.168.169.128 hostname=hai6.centos <==hostname为定义的普通变量 192.168.169.129 hostname=hai7.centos [web:vars] <==在中括号中写入[组名:vars],表示要定义公共变量 mark="-" <==公共变量
执行效果,在被管理主机上生成文件file: name=/data/log{{ mark }}{{ hostname }} state=touch mark为公共变量,hostname为普通变量
[root@hai7-6 /data]$ls log-hai7.centos
- 在文件中定义的变量也可以在命令行重新指定,优先级最高
[root@localhost /data]$ansible-playbook -e mark=@ text.yml '在被管理端查看生成效果' [root@hai6 /data]#ls log-hai6.centos [email protected]
- 在playbook中定义,优先级高于主机清单hosts
格式:
示例:vars: - var1: value1 - var2: value2
[root@localhost ~]$vim test.yml --- - hosts: web remote_user: root vars: <==声明是剧本中定义的变量 - hostname: hai6.centos <==变量键值对 tasks: - name: create log file file: name=/data/{{ hostname }} state=touch <==调用变量
- 在独立的变量YAML文件中定义,优先级高于hosts变量
变量yuml文件格式:
调用格式:vars_files:[root@localhost /data]#vim var.yml hostname: hai6.centos mark: @
[root@localhost ~]$vim test.yml --- - hosts: web remote_user: root vars_files: <==调用变量文件声明 - var.yml <==被调用的文件 tasks: - name: create log file file: name=/data/{{ mark }}{{ hostname }} state=touch <==在动作中调用变量
- 在role中定义,后文角色中介绍
- 普通变量(主机变量):主机组中主机单独定义,优先级高于公共变量
-
变量调用方式:
- 通过{{ variable_name }} 调用变量,且变量名前后建议有空格,有时用“{{ variable_name }}”才生效
file: name=/var/log/ {{ ansible_fqdn }}
- ansible-playbook -e 选项指定变量内容
ansible-playbook test.yml -e " ansible_fqdn=moli"
指定多个变量,用引号将多个变量括起来
ansible-playbook test.yml -e "hosts=www user=magedu"
- 通过{{ variable_name }} 调用变量,且变量名前后建议有空格,有时用“{{ variable_name }}”才生效
4.6 模板templates
-
templates功能
根据模块文件动态生成对应的配置文件
templates文件必须存放于templates目录下,且命名为 .j2 结尾 -
特征
- 文本文件,嵌套有脚本(使用模板编程语言编写)
- Jinja2语言
Jinja2官方文档http://jinjia.pocoo.org/docs/,不能在ansible命令中直接使用的模块,只能用在playbook中,有下面形式- 字符串:使用单引号或双引号
- 数字:整数,浮点数
- 列表:[item1, item2, …]
- 元组:(item1, item2, …)
- 字典:{key1:value1, key2:value2, …}
- 布尔型:true/false
- 算术运算:+, -, *, /, //(整数), %(余数), **(指数)
- 比较操作:==, !=, >, >=, <, <=
- 逻辑运算:and, or, not
- 流表达式:For If When
-
Playbook中调用template变更替换示例
示例1:这里以nginx为例,nginx生成的worker进程,worker_processes的进程数是可以和cpu个数设置关联的
-
拷贝一份配置文件到/data下
[root@localhost /data]$cp /etc/nginx/nginx.conf .
-
修改文件nginx.conf 如下内容,变量ansible_processor_vcpus来自模块setup获取
worker_processes {{ ansible_processor_vcpus*2}}; <==将进程数定义为cpu的2倍
-
要作为模板,必须存放于templates目录下,且命名为 .j2 结尾
[root@localhost /data]$mkdir templates [root@localhost /data]$mv nginx.conf templates/nginx.conf.j2
templates目录其与剧本(yaml/yml)是平级的,结构如下
[root@localhost /data]$tree . ├── templates │ └── nginx.conf.j2 └── text.yml <==此文件在下一步编制,这里职位说明,提到这里
-
编制一个剧本text.yml,这里需要注意的是,nginx使用的也是80端口,做实验时要错开端口,或者先卸载httpd
--- - hosts: web remote_user: root tasks: - name: install nginx yum: name=nginx - name: template template: src=nginx.conf.j2 dest=/etc/nginx.conf <==调用模板文件,拷贝到目标主机 - name: start service service: name=nginx state=started
-
执行剧本,查看是否已经打开80端口
[root@localhost /data]$ansible-playbook text.yml [root@localhost /data]$ansible web -a 'ss -ntl' 192.168.169.128 | SUCCESS | rc=0 >> LISTEN 0 128 :::80 :::* 192.168.169.129 | SUCCESS | rc=0 >> LISTEN 0 128 *:80 *:*
-
查看nginx进程
[root@localhost /data]$ansible web -a 'ps aux|grep nginx' 192.168.169.128 | SUCCESS | rc=0 >> 单核cpu生成2倍进程 nginx 20471 0.0 0.2 109360 2712 ? S 07:23 0:00 nginx: worker process nginx 20472 0.0 0.2 109360 2764 ? S 07:23 0:00 nginx: worker process 192.168.169.129 | SUCCESS | rc=0 >> 双核cpu生成2倍进程 nginx 28223 0.0 0.2 123264 3552 ? S 10:19 0:00 nginx: worker process nginx 28224 0.0 0.2 123264 3552 ? S 10:19 0:00 nginx: worker process nginx 28225 0.0 0.2 123264 3552 ? S 10:19 0:00 nginx: worker process nginx 28226 0.0 0.2 123264 3552 ? S 10:19 0:00 nginx: worker process
示例2:上例中也可以将端口定义为变量,放到模板配置文件中
- 在
/etc/ansible/hosts
中定义普通变量[root@localhost /data]$vim /etc/ansible/hosts [web] 192.168.169.128 http_port=81 <==定义变量 192.168.169.129 http_port=82
- 修改模板,监听端口项为变量
[root@localhost /data]$vim templates/nginx.conf.j2 server { listen {{http_port}} default_server; <==修改此项,ipv4监听端口 listen [::]:80 default_server; <==这是ipv6监听端口
- 修改剧本text.ml,加上handlers和notify,触发重启服务
--- - hosts: web remote_user: root tasks: - name: install nginx yum: name=nginx - name: template template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf notify: restart service <==加入触发动作 - name: start service service: name=nginx state=started handlers: <==加入触发动作激活的handlers - name: restart service service: name=nginx state=restarted
- 执行剧本,查看实验结果
[root@localhost /data]$ansible-playbook text.yml [root@localhost /data]$ansible web -a 'ss -ntl' 192.168.169.128 | SUCCESS | rc=0 >> LISTEN 0 128 :::81 :::* <==端口变为设定值 192.168.169.129 | SUCCESS | rc=0 >> LISTEN 0 128 *:82 *:*
-
4.6 when 条件测试语句
如果需要根据变量、facts或此前任务的执行结果来做为某task执行与否的前提是要用到条件测试,通过when语句实现,在task中使用,jinja2的语法格式
- 用法
在task后添加when子句即可使用条件测试;when语句支持Jinja2表达式语法 - 示例
centos6和centos7中的httpd服务,版本不同,配置文件不兼容,将模板选择性的发送到目标主机- 在主机列表中定义变量http_port
[web] 192.168.169.128 http_port=86 192.168.169.129 http_port=87
- 分别拷贝centos6和centos7的配置文件到模板专用目录data/templates下,并加后缀.j2,并修改端口号为变量
[root@localhost /data/templates]$ls httpd_6.conf.j2 httpd_7.conf.j2 <==模板文件 [root@localhost /data/templates]$vim httpd_6.conf.j2 #Listen 12.34.56.78:80 Listen {{http_port}} <==修改端口为变量
- 编制剧本文件text2.yml,加入判断语句when,变量来源setup收集
--- - hosts: web remote_user: root tasks: - name: install package yum: name=httpd - name: template 6 template: src=httpd_6.conf.j2 dest=/etc/httpd/conf/httpd.conf notify: restart service when: ansible_distribution_major_version == "6" <==判断版本语句 - name: template 7 template: src=httpd_7.conf.j2 dest=/etc/httpd/conf/httpd.conf notify: restart service when: ansible_distribution_major_version == "7" <==判断版本语句 - name: start service service: name=httpd state=started handlers: - name: restart service service: name=httpd state=restarted ~
- 执行,并查看实验结果
[root@localhost /data]$ansible web -a 'ss -ntl' 192.168.169.128 | SUCCESS | rc=0 >> State Recv-Q Send-Q Local Address:Port Peer Address:Port LISTEN 0 128 :::86 :::* 192.168.169.129 | SUCCESS | rc=0 >> State Recv-Q Send-Q Local Address:Port Peer Address:Port LISTEN 0 128 :::87 :::*
- 在主机列表中定义变量http_port
4.6 迭代:with_items ,类似于for循环
- 作用
当有需要重复性执行的任务时,可以使用迭代机制 - 引用格式
- 对迭代项的引用,固定变量名为”item“
- 要在task中使用with_items给定要迭代的元素列表
- 列表格式
字符串、字典 - 示例1:引用格式
上面语句的功能等同于下面的语句- hosts: web remote_user: root tasks: - name: config fiel copy: src=/data/{{item}} dest=/data/ <==此变量名固定为item with_items: <==声明为迭代的元素列表,类似于for,将元素一个个写在列表中 - file1 - file2
- name: config fiel copy: src=/data/file1 dest=/data/ - name: config fiel copy: src=/data/file2 dest=/data/
- 示例2:迭代嵌套子变量
- hosts:websrvs remote_user: root tasks: - name: add some groups <==先创建组 group: name={{ item }} state=present with_items: - group1 - group2 - group3 - name: add some users <==创建用户,要将用户加入到指定的组中 user: name={{ item.name }} group={{ item.group }} state=present with_items: - { name: 'user1', group: 'group1' } <==字典,两个键值对分别匹配,user1,对应group1 - { name: 'user2', group: 'group2' } - { name: 'user3', group: 'group3' }
4.6 Playbook中template for if
在模板中调用for循环以及if,将来在配置文件中重复的写一些相似的代码,就可以利用templat结合for循环来生成以下这种配置文件
-
for循环格式
{% for vhost in nginx_vhosts %} <==for语句开头固定格式,vhost为变量,nginx_vhosts为变量列表 server_name {{ vhost.server_name }}; <==将来要生成的内容 {% endif %} <==for语句结尾固定格式
-
示例,利用for循环生成目标文件
-
设计生成的目标
{ server www.magedu.com; listen 80 } { server app.magedu.com; listen 81 } { server mobi.magedu.com; listen 82 }
-
首先要定义一个模板文件,在模板文件中来调用这些值,例如上例中的sever名字各不相同,需要通过定义变量将不同的值区分出来
首先来解决端口的问题- hosts: web remote_user: root vars: 定义一个变量 ports: 引用一个变量值,其值是由一个列表组合而成 - 81 - 82 - 83 tasks: 定义任务 - name: test for template: src=for.conf.j2 dest=/data/for.conf 调用模板文件,
for.conf.j2模板,用来调用变量中的端口,for模板的写法为
{% for p in ports %_} <==调用for语句的特殊格式,需要个题头内容 {% for in %},for 后面跟变量, in后面跟的是列表,即调用剧本中的列表作为模板的值,与剧本中的列表要同名 server { listen {{p}} <==前面的目标文件格式 } {%endfor%} <==for循环的介绍语句
生成结果
server { listen 81 } server { listen 82 } server { listen 83 }
另一种写法为,调用字典,生成的结果是一样的
- hosts: web remote_user: root vars: ports: - listen: 81 - listen: 82 - listen: 83 tasks: <==定义任务 - name: test for template: src=for.conf.j2 dest=/data/for.conf
for模板文件
{% for p in ports %_} server { listen {{p.listen}} <==p的值来自剧本的键值对,要取哪个键值对的值,要写成[.key] } {%endfor%}
-
了解了基本用法后,我们就可以来实现最开始要实现的结果
编制剧本- hosts: web remote_user: root vars: host: - web1: listen: 81 server: www.magedu.com - web2: listen: 82 server: mobi.magedu.com - web3: listen: 83 server: app.magedu.com tasks: - name: test for template: src=for.conf.j2 dest=/data/for.conf
for模板文件
{% for p in host %_} server { listen {{p.listen}} servername {{p.server}} } {%endfor%}
生成结果
server { listen 81 servername www.magedu.com } server { listen 82 servername mobi.magedu.com } server { listen 83 servername app.magedu.com }
-
-
模板中的if格式
{% if vhost.server_name is defined %} <==if语句的开头,vhost.server_name为变量列表内容 server_name {{ vhost.server_name }} <==需要检测的列表内容 {% endif %} <==if语句结尾,与开头成对出现
以上例中的模板文件,加入判断变量是否有值,再去执行
{% for p in host %_} server { listen {{p.listen}} {% if p.server is defined %} <==if判断语句开头 servername {{p.server}} <==需要检测的列表内容 {%endif%} <==if判断语句结尾,与开头是成对出现的 } {%endfor%}
将上例中的剧本文件注释掉一行
- web2: listen: 82 #server: mobi.magedu.com <==注释掉此行,相当于没有此值
执行结果
server { listen 81 servername www.magedu.com } server { listen 82 <==注释掉后,没有相应值,判断生效 } server { listen 83 servername app.magedu.com }
5. roles
ansilbe自1.2版本引入的新特性,用于层次性、结构化地组织playbook。roles能够根据层次型结构自动装载变量文件、tasks以及handlers等。要使用roles只需要在playbook中使用include指令即可。简单来讲,roles就是通过分别将变量、文件、任务、模板及处理器放置于单独的目录中,并可以便捷地include它们的一种机制。角色一般用于基于主机构建服务的场景中,但也可以是用于构建守护进程等场景中
-
roles目录结构
每个角色,以特定的层级目录结构进行组织[root@hai7-6 /data]$tree . ├── playbooks.yml <==执行剧本 └── roles <==角色必须与执行剧本在同一级目录中 ├── project <==项目名称 │ ├── default │ ├── files │ ├── handlers │ ├── meta │ ├── tasks │ ├── templates │ └── vars └── project1 <==另一个项目
- playbooks.yml:调用角色的剧本文件
- roles:角色目录
- /roles/project/ :项目名称,有以下子目录
- files/ :存放由copy或script模块等调用的文件
- templates/:template模块查找所需要模板文件的目录
- tasks/:定义task,role的基本元素,至少应该包含一个名为main.yml的文件;其它的文件需要在此文件中通过include进行包含
- handlers/:至少应该包含一个名为main.yml的文件;其它的文件需要在此文件中通过include进行包含
- vars/:定义变量,至少应该包含一个名为main.yml的文件;其它的文件需要在此文件中通过include进行包含
- meta/:定义当前角色的特殊设定及其依赖关系,至少应该包含一个名为main.yml的文件,其它文件需在此文件中通过include进行包含
- default/:设定默认变量时使用此目录中的main.yml文件
-
创建role的步骤
这里以安装nginx为例-
创建以roles命名的目录
[root@localhost /data]$mkdir roles
-
在roles目录中分别创建以各角色名称命名的目录,如nginx
[root@localhost /data/roles]$mkdir nginx
-
在每个角色命名的目录中分别创建files、handlers、meta、tasks、templates和vars目录;用不到的目录可以创建为空目录,也可以不创建,用到时再创建也可以
[root@localhost /data/roles]$mkdir nginx/{tasks,templates,handlers,files} [root@localhost /data/roles]$tree . └── nginx ├── handlers ├── files ├── tasks └── templates
-
拷贝一个nginx配置文件到files中,此目录专门用来存放拷贝的文件
[root@localhost /data/roles/nginx]$cp /etc/nginx/nginx.conf files/
-
规划中,安装nginx需要做的事情有group、user、package、config、service,先将需要做的事情拆分,进入tasks目录中分别编辑
[root@localhost /data/roles/nginx]$cd tasks '编辑创建组动作,单独放一个文件' [root@localhost /data/roles/nginx/tasks]$vim group.yml - name: create group group: name=nginx system=yes gid=80 '编辑创建用户动作,单独放一个文件' [root@localhost /data/roles/nginx/tasks]$vim user.yml - name: create user user: name=nginx group=nginx uid=80 system=yes shell=/sbin/nologin home/data/www '编辑安装服务动作,单独放一个文件' [root@localhost /data/roles/nginx/tasks]$vim package.yml - name: install package yum: name=nginx '编辑导入配置文件动作,单独放一个文件' [root@localhost /data/roles/nginx]$vim copyconf.yml - name: copy config copy: src=nginx.conf dest=/etc/nginx/ <==按规范写的话,源文件地址不需要写绝对路径 '编辑启动服务动作,单独放一个文件 ' [root@localhost /data/roles/nginx]$vim service.yml - name: start service service: name=nginx state=started enabled=yes '编辑控制tasks目录个动作执行顺序文件' [root@localhost /data/roles/nginx/tasks]$vim main.yml <==文件名必须为main - include: group.yml <==按执行顺序由上至下写入 - include: user.yml - include: package.yml - include: copyconf.yml - include: service.yml
-
在与roles平级的目录中,编辑一个playbook.yml文件,来调用角色
[root@localhost /data]$vim nginx_role.yml --- - hosts: web remote_user: root roles: <==声明调用角色必须格式 -role: nginx <==具体需要调用的角色,可以有多个角色,换行重复即可 -role: db <==可以有多个角色,这里没有此项,只是为了说明
-
执行剧本文件nginx_role.yml,即可执行tasks目录中的动作
[root@localhost /data]$ansible-playbook nginx_role.yml
-
-
也可以调用模板
- 进入templates/目录,将拷贝到files目录下的配置文件nginx.conf复制过来,并重命名为.j2后缀
[root@localhost /data/roles/nginx]$cd templates/ [root@localhost templates]$cp ../files/nginx.conf nginx.conf.j2
- 修改模板,将变量写入模板文件,找到worker_processers行,修改变量内容如下
worker_processes {{ ansible_processor_vcpus+2}};
- 在/data/roles/nginx/tasks目录下,增加调用模板动作
[root@localhost /data/roles/nginx/tasks]$vim tmplconf.yml - name: template template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf
- 修改/data/roles/nginx/tasks/main.yml文件,修改调用
[root@localhost /data/roles/nginx/tasks]$vim main.yml - include: group.yml - include: user.yml - include: package.yml - include: templconf.yml - include: service.yml
- 执行/data/nginx_role.yml,按顺序执行tasks下定义的各动作
- 进入templates/目录,将拷贝到files目录下的配置文件nginx.conf复制过来,并重命名为.j2后缀
-
在角色中增加,条件触发功能handler
- 在handlers目录中编辑重启动作
[root@localhost /data/roles/nginx/handlers]$vim handler.yml - name: restart service service: name=nginx start=restarted
- 修改tasks下,需要激活handler动作的文件
[root@localhost /data/roles/nginx/tasks]$vim tmplconf.yml - name: template template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf notify: restart service <==配置文件发生改变时,触发notify来激活handler
- 在handlers目录中编辑重启动作
-
nginx服务管理的页面信息,在角色中也可以修改
- 建立页面文件,放在角色下的相应服务的文件目录,如/data/roles/nginx/files
[root@localhost /data/roles/nginx/files]$vim insex.html <h1>welcome to shan</h1>
- 在tasks目录下增加拷贝动作html.yml
[root@localhost /data/roles/nginx/tasks]$vim html.yml - name: html file copy: src=insex.html dest=/usr/share/nginx/html/
- 修改调用文件
[root@localhost /data/roles/nginx/tasks]$vim main.yml - include: group.yml - include: user.yml - include: package.yml - include: templconf.yml - include: html.yml - include: service.ym
- 建立页面文件,放在角色下的相应服务的文件目录,如/data/roles/nginx/files
-
roles中的各项目之间的文件是可以跨项目互相调用的
- roles目录下再设置一个apache服务项目
[root@localhost /data/roles]$mkdir apache/{tasks,files} -pv [root@localhost /data/roles]$vim apache/tasks/package.yml <==安装动作 [root@localhost /data/roles]$vim apache/tasks/html.yml <==页面配置 - name: html copy: src=roles/nginx/files/insex.html dest=/var/www/nginx/html/ <==跨项目调用路径要写roles [root@localhost /data/roles]$vim apache/tasks/service.yml <==启动动作 [root@localhost /data/roles]$vim apache/tasks/main.yml <==管理动作执行顺序
- 在roles平级目录建立调用剧本,httpd_role.yml,
[root@localhost /data/roles]$vim httpd_role.yml - hosts: app remote_user: root roles: -role: apache
- roles目录下再设置一个apache服务项目
-
传递变量给角色
- 修改/data/roles/nginx/files目录中的nginx配置文件,找到user行,将其改为变量
[root@localhost /data/roles/nginx/files]$vim nginx.conf user nginx; <==修改前 user {{username}} <==修改后
- 修改nginx剧本文件nginx_role.yml,传递变量要写成字典形式
[root@localhost /data]$vim nginx_role.yml - hosts: web remote_user: root roles: - {role: nginx,username: daemon} 'role: 用于指定角色nginx;变量(k)username用于传递(值)daemon给角色'
- 执行剧本文件,nginx使用者修改为daemon
[root@localhost /data]$ansible-playbook nginx_role.yml [root@localhost /data]$ansible app -a 'ps aux|grep nginx' daemon 28225 0.0 0.2 123264 3552 ? S 10:19 0:00 nginx: worker process | 这里 |
- 修改/data/roles/nginx/files目录中的nginx配置文件,找到user行,将其改为变量
-
tags(标签)使用
- 剧本文件.yml中可能会调用多个角色,可以为每个角色贴标签,每个角色可以贴多个
[root@localhost /data]$vim nginx_role.yml - hosts: web remote_user: root roles: - {role: nginx,username: daemon,tags: ['nginx','web']} <==中括号中的为标签 - {role: apache,tags: ['apache','web']} <==标签为'apache'或者'web',冒号后有空格
- 有了标签,就可以使用-t选项来选择性执行
[root@localhost /data]$ansible-playbook -t apache nginx_role.yml
- 剧本文件.yml中可能会调用多个角色,可以为每个角色贴标签,每个角色可以贴多个
-
在角色中,自动判断版本,执行相应动作
- 分别准备centos6和centos7的hpptd配置文件,都放在/data/roles/apache/files下
[root@localhost /data]$ls roles/apache/files/ httpd6.conf httpd7.conf
- 方便区分实验效果,将监听端口修改为86(centos6)和87(centos7),再建立一个拷贝配置文件的动作
[root@localhost /data/roles/apache/tasks]$vim copyconf.yml - name: copy config 6 copy: src=httpd6.conf dest=/etc/httpd/conf/httpd.conf when: ansible_distribution_major_version == "6" <==判断版本语句 - name: copy config 7 copy: src=httpd6.conf dest=/etc/httpd/conf/httpd.conf when: ansible_distribution_major_version == "7"
- 修改执行顺序main文件,如果httpd已经在运行,参考上面的例子handler,编制重启动作
[root@localhost /data/roles/apache/tasks]$vim main.yml - include: package.yml - include: html.yml - include: conf.yml - include: service.yml
- 也可以写在调用角色的剧本中,来判断版本
[root@localhost /data]$vim apache-role.yml - hosts: testweb remote_user: root roles: - { role: nginx ,tags: [ 'nginx', 'web' ] ,when: ansible_distribution_major_version == "6"}
- 分别准备centos6和centos7的hpptd配置文件,都放在/data/roles/apache/files下
-
一个修改缓存的例子
- 安装memcached
[root@localhost /data/roles]$yum -y install memcached
- 在roles中再建立一个服务memcached
[root@localhost /data/roles]$mkdir memcached [root@localhost /data/roles]$mkdir memcached/{tasks,templates} [root@localhost /data/roles]$tree memcached/ memcached/ ├── tasks └── templates
- 拷贝其配置文件作为模板文件
[root@localhost /data/roles]$cp /etc/sysconfig/memcached memcached/templates/memcached.j2
- 修改配置文件为变量
[root@localhost /data/roles]$vim memcached/templates/memcached.j2 PORT="11211" USER="memcached" MAXCONN="1024" CACHESIZE="64" <==修改前,指定的是缓存的大小 CACHESIZE={{ansible_memtotal_mb//4}} <==修改后,总内存的1/4,ansible_memtotal_mb为系统总内存变量,//表示整除,不要余数 OPTIONS=""
- 建立tasks相关动作,编制角色调用文件memcached_role.yml
[root@localhost /data/roles/nginx/tasks]$ls main.yml package.yml service.yml templconf.yml
- 运行角色,查看效果
[root@localhost /data]$ansible-playbook memcached_role.yml
- 安装memcached
另外:在剧本目录中可能会生成[.retry]为后缀的文件,这些文件是执行剧本时报错生成的
推荐资料