运维自动化之Ansible

1. 介绍

1.1. Ansible 发展和起源

Ansible 是一款开源的 IT 配置管理工具,常被 IT 界的小伙伴们用于服务部署、配置管理等工作。配置文件采用最常见的 yaml 格式,学习起来也是比较容易,并且不像 SaltStack,Ansible 并不需要也没有 agent,只有一个控制端。该工具使用简单但功能非常强大,可以解决众多工作中繁琐的服务安装、配置等问题。

Ansible 的第一个版本是 0.0.1,发布于 2012 年 3 月 9 日,其作者兼创始人是 Michael DeHaan。迄今为止已经发展到了 2.9 版本。并且它的关注度、Star 数以及 Fork 的次数都位居榜首。就连强大的 SaltStack 也只能排到第二。

Michael DeHaan 在配置管理和架构设计方面有丰富的经验,曾就职于 RedHat 公司,在 RedHat 任职期间主要开发了 Cobble。在他尝试了各种自动化工具 Puppet、Chef 之后,决定自己打造一款能够结合众多优点的自动化工具。由此,便有了 Ansible 这款易理解、易上手、受众人喜爱的自动化工具。

1.2. 为什么需要 Ansible

前面说过,ansible 通常用于自动化的场景,多用在服务部署、配置管理方面。随着时间推移和公司发展,项目越来越多,团队日益壮大,各种公司内部开发的应用、第三方开源的中间件等服务越来越多,那么管理起来就相对比较困难,人肉操作已经完全满足不了传统的运维工作,需要消耗相当多的时间来进行变更,进而阻碍了开发人员的速度,极大的降低了工作效率。显然可考又高效的部署和管理成为了公司的一大难点与挑战。那么一款高效且可靠的服务部署和管理工具就显得尤为重要。而在很长一段时间里,Docker 容器与 kubernetes 容器编排系统没有被广泛的普及之前,有很大一部分人在使用 Jenkins + Ansible 进行 CICD。

1.3. Ansible 的主要功能

批量执行远程命令:可以对任意多台主机同时进行命令的执行。

批量配置软件服务:可以进行自动化的方式部署、配置及管理服务。

编排高级的 IT 任务:Ansible 可以使用 yaml 来编写一套完整的 Playbook,用来部署维护一套完全的基础架构。

1.4. Ansible 的相关特性

Ansible 是基于每个模块进行工作,自身并没有批量部署的能力,ansible 自身只是提供了一种框架。

Ansible 由 Python 语言开发,没有 agent,不需要在被管理节点安装任何客户端;

模块化:基于模块工作,秩序调用特定的模块来完成特定工作;

基于 SSH 协议;

三大关键组成模块:Paramiko, PyYAML, Jinja2;

幂等性:一个任务执行 1 遍和执行 n 遍效果一样,不因重复执行带来意外情况;

可以使用命令行 ad-hoc 方式来执行批量任务,也可以使用 yaml 格式的文件来定制 Playbook 剧本实现批量任务;

可以使用 Role 组织批量任务

1.5. Ansible 的优点

容易学习且轻量:无需在被控制节点安装 agent,做批量操作时只需要在操作机操作即可(前提:需要配置好免密登录);

  • 操作灵活:具有众多的模块,可使用命令行 ad-hoc 方式或者 Playbook 剧本的方式来实现批量任务执行;
  • 可移植性高:可以基于 yaml 文件编写一套 Playbook,只要做好逻辑判断,就可以在多种操作系统上拿来即用;
  • 幂等性:一个任务执行 1 遍和执行 n 遍效果一样,不因重复执行带来意外情况;
  • 支持普通用户 sudo 提权。

但是任何事物都具有两面性。SSH 虽好,但如果被管理的机器数量众多的话,执行的速度就会比较慢,就需要进行一定的优化和分批任务来缓解速度问题。

1.6. Ansible 的架构

Ansible 由以下几个核心工具组成:

  • INVENTORY:Ansible 管理主机的清单/etc/anaible/hosts;
  • MODULES:Ansible 执行命令的功能模块,多数为内置核心模块,也可自定义;
  • PLUGINS:模块功能的补充,如连接类型插件、循环插件、变量插件、过滤插件等;
  • APl:供第三方程序调用的应用程序编程接口。

在日常工作中,我们大多数用到的且使用比较频繁的主要是:Inventory 和 Modules。通常我们会根据项目的需求来定制化我们的 Inventory,不会将它放在默认的/etc/anaible/hosts 文件中。

2. 运维自动化和 Ansible 架构介绍

2.1. 运维自动化发展历程及技术应用

2.1.1. 云计算运维工程师核心职能

  • 平台架构组建
    负责参与并审核架构设计的合理性和可运维性, 搭建运维平台技术架构, 通过开源解决方案,以确保在产品发布之后能高效稳定的运行,
    保障并不断提升服务的可用性, 确保用户数据安全, 提升用户体验.
  • 日常运营保障
    负责用运维技术或者运维平台确保产品可以高效的发布上线, 负责保障产品 7*24H 稳定运行, 在此期间对出现的各种问题可以快速的定位并解决; 在日常工作中不断优化系统架构和部署的合理性, 以提升系统服务的稳定性.
  • 性能, 效率优化
    用自动化的工具/平台提升软件在研发声明周期中的工作效率. 优化资源利用率支持产品的不断迭代, 需要不断的进行架构优化调整, 以确保整个产品能够在功能不断丰富和复杂的条件下, 同时保持高可用性.

2.1.2. 软件开发阶段

计划=>编码=>打包=>测试=>发布=>部署=>运维=>监控

2.1.2.1. 相关工具
  • 代码管理(SCM): GitHub, GitLab, BitBucket, SubVersion
  • 构建工具: maven, Ant, Gradle
  • 自动化部署: Capistrano, CodeDeploy
  • 持续集成(CI): jenkins, Travis
  • 配置管理: Ansible, SaltStack, Chef, Puppet
  • 容器: Docker, Podman, LXC, 第三方厂商如 AWS
  • 编排: Kubernetes, Cor, Apache Mesos
  • 服务注册与发现: Zookeeper, etcd, Consul
  • 脚本语言: python, ruby, shell
  • 日志管理: ELK, Logentries
  • 系统监控: Prometheus, Zabbix, Datadog, Graphite, Ganglia, Nagios
  • 性能监控: AppDynamics, New Relic, Splunk
  • 压力测试: JMeter, Blaze meter, loader.io
  • 应用服务器: Tomcat, jBoss, IIS
  • Web 服务器: Apache, Nginx
  • 数据库: MySQL, Oracel, PostgreSQL 等关系型数据库; mongoDB, Redis 等 NoSQL 数据库.
  • 项目管理(PM): Jira, Asana, Taiga, Trello, Basecamp, Pivotal Tracker

2.1.3. 运维工程师的发展路线

基础运维=>监控运维=>系统运维=>应用运维=>自动化运维=>架构师=>总监或 CTO

2.1.3.1. 运维的未来是什么?
  1. 一切皆自动化
    “运维的未来是, 让开发人员借助工具, 自动化和流程, 并且让他们能够在运维干预极少的情况下部署和运维服务, 从而实现自助服务, 每一个角色都应该努力使工作实现自动化.” — <<运维的未来>>
2.1.3.2. 企业实际应用场景
2.1.3.2.1. Dev 开发环境

使用者: 程序员
功能: 程序员个人的办公电脑或项目的开发测试环境, 部署开发软件, 测试个人或项目整体的 BUG 的环境
管理者: 程序员

2.1.3.2.2. 测试环境

使用者: QA 测试工程师
功能: 测试经过 Dev 环境测试通过的软件的功能和性能, 判断是否达到项目的预期目标, 生成测试报告.
管理者: 运维
说明: 测试环境往往有多套, 测试环境满足测试功能即可, 不易过多

  1. 测试人员希望测试环境多套, 公司的产品多产品线并发, 即多个版本, 意味着多个版本同步测试.
  2. 通常测试环境有多套和产品线数量保持一样
2.1.3.2.3. 预发布环境

使用者: 运维
功能: 使用和生产环境一样的数据库, 缓存服务等配置, 测试是否正常

2.1.3.2.4. 发布环境

包括代码发布机, 有些公司为堡垒机(安全屏障)

使用者: 运维
功能: 发布代码至生产环境
管理者: 运维
发布机: 往往需要有 2 台(主备)

2.1.3.2.5. 生产环境

使用者: 运维, 少数情况开放权限给核心开发人员, 极少数公司将权限完全开放给开发人员并维护
功能: 对用户提供公司产品的服务

2.1.3.2.6. 灰度环境

属于生产环境的一部分
使用者: 运维
功能: 在全量发布代码前将代码的功能面向少量精准用户发布的环境, 可基于主机或用户执行灰度发布
案例: 供 100 台生产服务器, 先发布其中 10 台服务器, 这 10 台服务器就是灰度服务器
管理者: 运维
灰度环境: 往往该版本功能变更较大, 为了保险起见特意先让一部分用户优化体验该功能, 待这部分用户使用没有重大问题的时候, 再全量发布至所有服务器

2.1.3.3. 程序发布

程序发布要求:

不能导致系统故障或造成系统完全不可用
不能影响用户体验

预发布验证:

新版本的代码发布到服务器(跟线上环境配置完全相同, 只是未接入到调度器)

灰度发布:

基于主机, 用户, 业务

正式发布

发布过程:

  1. 在调度器上下线一批主机标记为 mainttenance 状态
  2. 关闭服务
  3. 部署新版本的应用程序
  4. 启动服务
  5. 在调度器上启用这一批服务器

自动化灰度发布:

脚本
发布平台

2.1.3.3.1. 自动化运维应用场景

文件传输
应用部署
配置管理
任务流编排

2.1.3.4. 常用自动化运维工具
  • Ansible: python, Agentless, 中小型应用环境
  • Saltstack: python, 一般需要部署 Agent, 执行效率更高
  • Puppet: Ruby, 功能强大, 配置复杂, 重型, 适合大型环境
  • Fabric: python, agentless
  • Chef: ruby, 国内应用少
  • Cfengine
  • func

2.1.4. Ansible 介绍和架构

2.1.4.1. Ansible 发展史

作者: Michael DeHaan(Cobbler 与 Func 作者)
ansible 的名字来自<<安德的游戏>>中跨越时空的即时通信工具
2012-03-09, 发布 0.0.1 版, 2015-10-17, Red Hat 宣布 1.5 亿美金收购

官网: http://www.ansible.com/
官方文档: https://docs.ansible.com/

2.1.4.2. Ansible 特性
  • 模块化: 调用特定的模块, 完成特定任务
  • Paramiko(python 对 ssh 的实现), PyYAML, jinja2(模板语言) 三个关键模块
  • 支持自定义模块, 可使用任何编程语言写模块
  • 基于 Python 语言实现
  • 部署简单, 基于 python 和 SSH(默认已安装), agentless, 无需代理不依赖 PKI(无需 ssl)
  • 安全, 基于 OpenSSH
  • 幂等性: 一个任务执行 1 遍和 n 遍效果一样, 不因重复执行带来意外情况
  • 支持 playbook 编排任务, YAML 格式, 支持丰富的数据结构
  • 较强的多层解决方案 role
2.1.4.3. Ansible 架构
2.1.4.3.1. Ansible 组成

Inventory, API, Modules, Plugin 的绿框, 可以理解为是 Ansible 命令工具,
其为核心执行工具.

  • Inventory: Ansible 管理主机的清单/etc/ansible/hosts
  • Modules: Ansible 执行命令的功能模块, 多数为内置核心模块, 也可自定义
  • Plugins: 模块功能的补充, 如连接类型插件, 循环插件, 变量插件, 过滤插件等, 该功能不常用
  • API: 供第三方程序调用的应用程序接口
2.1.4.3.2. Ansible 命令执行来源
  • User 普通用户, 即 System Administrator
  • Playbooks: 任务剧本(任务集), 编排定义 Ansible 任务集的配置文件, 由 Ansible 顺序依次执行, 通常是 Json 格式的 YML 文件
  • CMDB(配置管理数据库) API 调用
  • PUBLIC/PRIVATE Cloud API 调用
  • User => Ansible Playbook => Ansible
2.1.4.3.3. 注意事项
  • 执行 ansible 的主机一般称为主控端, 中控, master 或堡垒机
  • 主控端 Python 版本需要 2.6 或以上
  • 被控端 Python 版本小于 2.4 需要安装 python-simplejson
  • 被控端如开启 SELinux 需要安装 libselinux-python
  • Windows 不能作为主控端

3. Ansible 的安装和使用

3.1. Ansbile 安装

ansible 的安装方法有多种

3.1.1. EPEL yum 源安装

1
yum install ansible

3.1.2. 编译安装

1
2
3
4
5
6
7
8
yum -y install python-jinja2 PyYAML python-paramiko python-babel \
python-crypto tar xf ansible-1.5.4.tar.gz

cd ansible-1.5.4
python setup.py build
python setup.py install
mkdir /etc/ansible
cp -r examples/* /etc/ansible

3.1.3. Git 方式

1
2
3
4
5

git clone git://github.com/ansible/ansible.git --recursive
cd ./ansible
source ./hacking/env-setup

3.1.4. pip 安装

pip 是安装 Python 包的管理器, 类似 yum

1
2
3
4
5
6

yum install python-pip python-devel
yum install gcc glibc-devel zibl-devel rpm-build openssl-devel
pip install --upgrade pip
pip install ansible --upgrade

3.1.5. 确认安装

1
ansible --version

3.1.6. Ansible 相关文件

3.1.6.1. 配置文件

/etc/ansible/ansible.cfg 主配置文件, 配置 Ansible 工作特性
/etc/ansible/hosts 主机清单
/etc/ansible/roles/ 存放角色的目录

3.1.6.2. Ansbile 主配置文件

Ansible 的配置文件/etc/ansible/ansbile.cfg 其中大部分的配置内容无需进行修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[defaults]
# inventory = /etc/ansbile/hosts # 主机列表配置文件
# libaray = /usr/share/my_modules # 库文件存放位置
# remote_tmp = $HOME/.ansbile/tmp # 临时py命令文件存放在远程主机目录
# local_tmp = $HOME/.ansible/tmp # 本机的临时命令执行目录
# forks = 5 # 默认并发数
# sudo_user = root # 默认sudo用户
# ask_sudo_pass = True # 每次执行ansible命令是否询问ssh密码
# ssk_pass = True
# remote_port = 22
# host_key_checking = False # 检查对应服务器的host_key, 建议取消注释
# log_path=/var/log/ansible.log # 日志文件, 建议启用
# module_name = command # 默认模块, 可以修改为shell模块

3.1.6.3. ansible.cfg 文件的优先级顺序

ansible 获取 ansible.cfg 文件的优先级顺序

ansible 运行时会先检查 ansible 命令的目录中是否有 ansible.cfg 文件,如果不存在该文件,则检查用户的主目录(~/)中是否有 ansible.cfg 文件,在找不到其他配置文件时,使用全局/etc/ansible/ansible.cfg 文件,如果都不存在,ansible 包含它使用的默认值。

使用 ANSIBLE_CONFIG 环境变量指定配置文件位置,而此时指定的任何文件将覆盖所有其他配置文件

3.1.6.4. Inventory 主机清单

ansible 的主要功用在于批量主机操作, 为了便捷地使用其中的部分主机, 可以在 inventory file 中将其分组命名默认的 inventory file 为/etc/ansible/hosts
inventory file 可以有多个, 且也可以通过 Dynamic Inventory 来动态生成

3.1.6.4.1. 主机清单文件格式

inventory 文件遵循 INI 文件风格, 中括号中的字符为组名, 可以将同一个主机
同时归并到多个不同的组中,
此外, 当如若目标主机使用了非默认的 SSH 端口, 还可以在主机名称之后使用冒号加端口号来标明如果主机名称遵循相似的命名模式, 还可以使用列表的方式标识各主机

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

# Ex 1: ungrouped hosts, specify before any group headers

## green.example.com
## bluce.example.com
## 192.168.100.1
## 192.168.100.10

# Ex 2: A collection of hosts belonging to the 'webservers' group

## [webservers]
## alpha.example.org
## beta.example.org
## 192.168.1.100
## 192.168.1.110

# If you have multiple hosts following a pattern you can specify
# them lik this:

[dbservers]
## db-[99:101]-node.example.com
## db-[a:f].example.com

[appservers]
10.0.0.[1:100]

# 如何主机没有使用默认的22端口作为ssh的监听端口
# 定义host时可以指定其端口号
www.example.com:2222

3.1.6.5. Ansible 相关工具

/usr/bin/ansible 主程序, 临时命令执行工具
/usr/bin/ansible-doc 查看配置文档, 模块功能查看工具
/usr/bin/ansible-galaxy 下载/上传优秀代码或 Roles 模块的官网平台
/usr/bin/ansible-playbook 定制自动化任务, 编排剧本工具
/usr/bin/ansible-pull 远程执行命令的工具
/usr/bin/ansible-vault 文件加密工具
/usr/bin/ansible-console 基于 Console 界面与用户交互的执行工具

3.1.6.6. 利用 ansible 实现管理的主要方式

Ad-Hoc 即利用 Ansible 命令, 主要用于临时命令使用场景
Ansible-playbook 主要用于长期规划好的, 大型项目的场景, 需要有前期的规划过程

3.1.6.7. ansible-doc

此工具用来显示模块帮助
格式

1
2
3
ansible-doc [options] [module...]
-l, --list # 列出可用模块
-s, --snippet # 显示指定模块的playbook片段

范例:

1
2
3
4
5
6
# 列出所有模块
ansible-doc -l
# 查看指定模块帮助用法
ansible-doc ping
# 查看指定模块帮助用法
ansible-doc -s ping
3.1.6.8. ansible 命令

此工具通过 ssh 协议, 实现对远程主机的配置管理, 应用部署, 任务执行等功能
建议: 使用此工具前, 先配置 ansible 主控端能基于密钥认证的方式联系各个被管理节点

范例: 利用 sshpass 批量实现基于 key 验证

1
2
3
4
5
6
ssh-keygen -f /root/.ssh/id_rsa -P ''
NET=192.168.100
export SSHPASS=test
for IP in (1..200); do
sshpass -e ssh-copy-id $NET.$IP
done

格式:

1
2
3

ansible <host-pattern> [-m module_name] [-a args]

选项说明

1
2
3
4
5
6
7
8
9
10
11
12
13

--version # 显示版本号
-m module # 指定模块, 默认为command
-v # 详细过程 -vv -vvv更详细
--list-hosts # 显示主机列表, 可简写 --list
-k, --ask-pass # 提升输入ssh连接密码, 默认是key验证
-C, --check # 检查, 并不执行
-T --timeout=TIMEOUT #执行命令的超时时间, 默认10s
-u --user=REMOT_USER # 执行远程执行的用户
-b, --become # 代替旧版的sudo切换
--become-user=USERNAME #指定sudo的runas用户, 默认root
-K, --ask-become-pass # 提示输入sudo时的口令

3.1.6.9. ansible-pull

此工具会推送 ansible 的命令至远程, 效率无限提升, 对运维要求较高.

3.1.6.10. ansible-playbook

此工具用于执行编写好的 playbook 任务

范例:

1
2
3
4
5
6
7
8
9
10
11
ansible-playbook hello.yml

cat hello.yml
---
#hello world from yml file
- hosts: websrvs
remote_user: root
tasks:
- name: hello world
command: /usr/bin/wall hello world

3.1.6.11. ansible-valt

此工具可用于加密解密 yml 文件

格式:

1
ansible-vault [create|decrypt|edit|encrypt|rekey|view]

范例

1
2
3
4
5
6
7
8

ansible-vault encrypt hello.yml # 加密
ansible-vault decrypt hello.yml # 解密
ansible-vault view hello.yml
ansible-vault edit hello.yml
ansible-vault rekey hello.yml
ansible-vault create hello.yml

3.1.6.12. ansible-console

此工具是可交互执行命令, 支持 tab, ansible 2.0+新增

提示符格式:

执行用户@当前操作的主机组(当前的主机数量)[f:并发数]$

常用子命令:

设置并发数: forks n 例如: forks 10
切换组: cd 主机组 例如: cd web
列出当前组主机列表: list
列出所有的内置命令: ?或 help

范例:

1
2
3
4
5
6
7
8

root@all (2)[f:5]$ list
root@all (2)[f:5]$ cd appsrvs
root@all (2)[f:5]$ list
root@all (2)[f:5]$ yum name=httpd state=present
root@all (2)[f:5]$ service name=httpd state=started
root@all (2)[f:5]$ forks 10

3.1.7. Ansible 常用模块

2015 年底 270 多个模块, 2016 年达到 540 个, 2018 年 01 月 12 日 1378 个模块, 2018 年 07 月 15 日 1852 个模块, 2019 年 5 月 25 日(ansible 2.7.10)时 2080 个, 2020 年 0302 日有 3387 个模块

虽然模块众多, 但常用的模块也就 2,30 个而已, 针对特定业务只有 10 几个模块

常用模块帮助文档参考:

https://docs.ansible.com/ansible/latest/modules/modules_by_category.html

3.1.7.1. Command 模块

功能: 在远程主机执行命令, 此为默认模块, 可忽略-m 选项
注意: 此命令不支持 $VARNAME <> | ; & 等, 用 shell 模块实现

范例:

1
2
3
4
5
6
7
8

ansible srvs -m command -a 'service vsftpd start'
ansible srvs -m command -a 'echo hello world | passwd -stdin user_name'

# 查看操作系统版本
ansible srvs -m command -a 'cat /etc/centos-release'
ansible srvs -m command -a 'chdir=/etc cat centos-release'

3.1.7.2. shell 模块

功能: 和 command 相似, 用 shell 执行命令
范例:

1
2
3

ansible srv -m shell -a 'echo test | passwd -stdin wang'

注意: 调用 bash 执行命令, 类似 cat /tmp/test/md | awk -F ‘|’ ‘{print 1,2}’ &> /tmp/example.txt 这些复杂命令, 即使使用 shell 也可能会失效, 解决办法: 写到脚本时, copy 到远程, 执行, 再把需要的结果拉回执行命令的机器

范例: 将 shell 模块代替 command, 设为默认模块

1
2
3
vim /etc/ansible/ansible.cfg
#修改下面一行
module_name = shell
3.1.7.3. script 模块

功能: 再远程主机上运行 ansible 服务器上的脚本

范例:

1
ansible websrvs -m script -a /data/test.sh
3.1.7.4. copy 模块

功能: 从 ansible 服务器主控端复制文件到远程主机

1
2
3
4
5
6
7
8
9
# 如目标存在, 默认覆盖, 此处指定先备份
ansible srv -m copy -a "src=/root/test1.sh dest=/tmp/test2.sh owner=test mode=600 backup=yes"

# 指定内容, 直接生成目标文件
ansible srv -m copy -a "content='test content\n' dest=/tmp/test.txt"

# 复制/etc/下的文件, 不包括/etc/目录本身
ansible srv -m copy -a "src=/etc/ dest=/backup"

3.1.7.5. fetch 模块

功能: 从远程主机拷贝文件到 ansible 的主控端, 与 copy 相反, 目前不支持目录

范例:

1
ansible srv -m fetch -a "src=/root/test.sh dest=/data/scripts"
3.1.7.6. File 模块

功能: 设置文件属性

范例:

1
2
3
4
5
6
7
8
9
10
11
# 创建空文件

ansible srv -m file -a 'path=/data/test.txt state=touch'
ansible srv -m file -a 'path=/data/test.txt state=absent'
ansible srv -m file -a 'path=/data/test.txt owner=wang mode=755'

# 创建目录
ansible srv -m file -a 'path=/data/mysql state=directory owner=mysql group=mysql'
# 创建软连接
ansible srv -m file -a 'src=/data/testfile dest=/data/testfile-link state=link'

3.1.7.7. unarchieve 模块

功能: 解包解压缩

实现有两种用法:

  1. 将 ansible 主机上的压缩包传到远程主机后解压缩至特定目录, 设置 copy=yes
  2. 将远程主机上的某个压缩包解压缩到指定路径下, 设置 copy=no

常见参数:

copy: 默认为 yes, 当 copy=yes, 拷贝的文件是从 ansible 主机复制到远程主机上, 如果没有设置为 copy=no, 会在主机上寻找 src 源文件
remote_src: 和 copy 功能一样且互斥, yes 表示在远程主机, 不在 ansible 主机, no 表示文件在 ansible 主机上
src: 源路径, 可以是 ansible 主机上的路径, 也可以是远程主机上的路径, 如果是远程主机上的路径, 则需要设置 copy=no

范例:

1
ansible srv -m unarchive -a ""
3.1.7.8. archive 模块

功能: 打包压缩

范例:

1
2
3

ansible websrvs -m archive -a "path=/var/log/ dest=/data/log.tar.bz2 format=bz2 owner=wang mode=0600"

3.1.7.9. Hostname 模块

功能: 管理主机名

范例:

1
2
3
4

ansible node1 -m hostname -a "name=websrv"
ansible 192.168.100.18 -m hostname -a 'name=node18'

3.1.7.10. Cron 模块

功能: 计划任务
支持时间: minute, hour, day, month, weekday

范例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 备份数据库脚本
#cat mysql_backup.sh
mysqldump -A -F --single-transaction --master-data=2 -q -uroot | gzip >mysql_`date +%F_%T`.sql.gz

# 创建任务
ansible dbsrv -m cron -a 'hour=2 minute=30 weekday=1-5 name="backup mysql" job=/root/mysql_backup.sh'

ansible srv -m cron -a 'minute=/5 job=/usr/sbin/ntupdate 172.20.0.1 &>/dev/null name=Synctime'
# 禁用计划任务
ansible srv -m cron -a 'minute=/5 job=/usr/sbin/ntupdate 172.20.0.1 &>/dev/null name=Synctime disabled=yes'
# 启用计划任务
ansible srv -m cron -a 'minute=/5 job=/usr/sbin/ntupdate 172.20.0.1 &>/dev/null name=Synctime disabled=no'
# 删除计划任务
ansible srv -m cron -a 'name=Synctime state=absent'
ansible srv -m cron -a 'name="backup mysql" state=absent'
3.1.7.11. Yum 模块

功能: 管理软件包

范例:

1
2
3
4

ansible srv -m yum -a 'name=httpd state=present' # 安装
ansible srv -m yum -a 'name=httpd state=absent' # 删除

3.1.7.12. Service 模块

功能: 管理服务

范例:

1
2
3
4
5
ansible srv -m service -a 'name=httpd state=started enable=yes'
ansible srv -m service -a 'name=httpd state=stopped'
ansible srv -m service -a 'name=httpd state=reloaded'
ansible srv -m shell -a "sed -i 's/^Listen 80/Listen 8080/' /etc/httpd/conf/httpd.conf"
ansible srv -m service -a 'name=httpd state=restarted'
3.1.7.13. User 模块

功能: 管理用户
范例:

1
2
3
4
5
6
7
8

# 创建用户
ansible srv -m user -a 'name=user1 comment="test user" uid=2048 home=/app/user1 group=root'
ansible srv -m user -a 'name=nginx comment=nginx uid=88 group="root, deamon" shell=/sbin/nologin system=yes create_home=no home=/data/nginx non_unique=yes'

# 删除用户及家目录等数据
ansible srv -m user -a 'name=nginx state=absent remove=yes'

3.1.7.14. Group 模块

功能: 管理组

范例:

1
2
3
4
5

# 创建组
ansible websrvs -m group -a 'name=nginx gid=88 system=yes'
# 删除组
ansible websrvs -a group -a 'name=nginx state=absent'
3.1.7.15. Lineinfile 模块

ansible 在使用 sed 进行替换时, 经常会遇到需要转义的问题, 而且 ansible 在遇到特殊符号进行替换时, 存在问题, 无法正常进行替换, 其实在 ansible 自身提供了两个模块: lineinfile 模块和 replace 模块, 可以方便地进行替换

功能: 相当于 sed, 可以修改文件内容

范例:

1
2
ansible all -m lineinfile -a "path=/etc/selinux/config regexp='^SELINUX=' line='SELINUX=enforcing'"
ansible all -m lineinfile -a 'dest=/etc/fstab state=absent regexp="^#"'
3.1.7.16. Replace 模块

该模块有点类似于 sed 命令, 主要也是基于正则进行匹配和替换

范例:

1
2
3
4
5
6

# 将以UUID开头的行注释掉, \1表示正则表达式匹配的内容
ansible all -m replace -a "path=/etc/fstab regexp='^(UUID.*)' replace='#\1'"

# 恢复以上操作
ansible all -m replace -a "path=/etc/fstab regexp='^#(UUID.*)' replace='\1'"
3.1.7.17. Setup 模块

功能: setup 模块来收集主机的系统信息, 这些 facts 信息可以直接以变量的形式使用, 但是如果主机较多, 会影响执行速度, 可以使用 gather_facts: no 来禁止收集 facts 信息

范例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

ansible srv -m setup
ansible srv -m setup -a "filter=ansible_nodename"
ansible srv -m setup -a "filter=ansible_hostname"
ansible srv -m setup -a "filter=ansible_domain"
ansible srv -m setup -a "filter=ansible_memtotal_mb"
ansible srv -m setup -a "filter=ansible_memory_mb"
ansible srv -m setup -a "filter=ansible_memfree_mb"
ansible srv -m setup -a "filter=ansible_os_family"
ansible srv -m setup -a "filter=ansible_distribution_major_version"
ansible srv -m setup -a "filter=ansible_distribution_version"
ansible srv -m setup -a "filter=ansible_processor_vcpus"
ansible srv -m setup -a "filter=ansible_all_ipv4_addresses"

# 使用通配符
ansible srv -m setup -a "filter=ansible_processor_*"

import_tasks 模块

Ansible 支持将任务分解到不同的文件中,这样可以提高 playbook 的可读性和可维护性。通过将相关的任务组织到同一个文件中,可以实现任务的重用。

import_tasks 是静态地包含任务文件,这会在 playbook 解析阶段就将任务文件加载进来。由于这种加载方式,import_tasks 不支持变量在文件名中的使用,且所有任务都会在 playbook 的解析阶段被加载,这可能导致一些变量在条件判断时已经被覆盖,从而影响任务的执行。

范例:

1
2
3
4
5
6
7
8
9
10
- hosts: all
tasks:
- name: Import task list from a file
import_tasks: other_tasks.yml

- name: Import task list from a file with apply
import_tasks: other_tasks.yml
apply:
tags:
- always

在这个例子中,other_tasks.yml 是一个包含任务的文件,它在 playbook 解析时就被加载。apply 部分可以用来为包含的任务应用特定的标签。

include_tasks 模块

include_tasks 模块允许动态地包含任务列表,这意味着它在运行时解析包含的文件。这使得可以根据变量或条件来决定包含哪些任务。

范例:

1
2
3
4
5
6
7
8
9
10
11

- hosts: all
tasks:
- name: Include task list from a file
include_tasks: some_tasks.yml
when: some_condition

- name: Include task list from a variable file
include_tasks: "{{ task_file_to_include }}"
when: task_file_to_include is defined

在这个例子中,some_tasks.yml 是一个包含任务的文件,而 task_file_to_include 是一个变量,它在运行时被解析,从而动态地决定包含哪个任务文件。

assert 模块

断言给定的表达式为真。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

- assert: { that: "ansible_os_family != 'RedHat'" }

- assert:
that:
- "'foo' in some_command_result.stdout"
- number_of_the_counting == 3

- name: After version 2.7 both 'msg' and 'fail_msg' can customize failing assertion message
assert:
that:
- my_param <= 100
- my_param >= 0
fail_msg: "'my_param' must be between 0 and 100"
success_msg: "'my_param' is between 0 and 100"

- name: Please use 'msg' when ansible version is smaller than 2.7
assert:
that:
- my_param <= 100
- my_param >= 0
msg: "'my_param' must be between 0 and 100"

- name: use quiet to avoid verbose output
assert:
that:
- my_param <= 100
- my_param >= 0
quiet: true

that: 可以传递给’when’语句的相同形式的字符串表达式列表。
fail_msg: 用于失败断言的自定义消息。这个参数在Ansible 2.7之前被称为’msg’,现在它被重命名为’fail_msg’,别名’msg’
success_msg: 用于成功断言的自定义消息。
quiet: 将此设置为yes以避免冗长的输出。

4. Playbook

4.1. playbook 介绍

playbook 剧本是由一个或多个 play 组成的列表

play 的主要功能在于将预定义的一组主机, 装扮成事先通过 ansible 中的 task 定义好的角色. Task 实际是用 ansible 的一个 module, 将多个 play 组织在一个 playbook 中, 即可以让它们联合起来, 按事先编排的机制执行预定义的动作

playbook 文件是采用 YAML 语言编写的

4.2. YAML 语言

4.2.1. YAML 语言介绍

YAML 是一个可读性高的用来表达资料序列的格式. YAML 参考了其他多种语言,包括: XML, C 语言, Python, Perl 以及电子邮件格式 RFC2822 等. Clark Evans 在 2001 年首次发表了这种语言, 另外 Ingy dot net 与 Oren Ben-kiki 是这种语言的共同设计者, 目前很多软件中采用此格式的文件, 如 ubuntu, ansible, docker, k8s 等.
YAML: YAML aint markup language, 即 YAML 不是标记语言, 不过, 在开发这种语言时, YAML 的意思其实是: “Yet Another Markup Language” (仍是一种标记语言)

YAML 官方网站: http://www.yaml.org

4.2.2. YAML 语言特性

YAML 的可读性好
YAML 和脚本语言的交互性好
YAML 使用实现语言的数据类型
YAML 有一个一致的信息模型
YAML 易于实现
YAML 可以基于流来处理
YAML 表达能力强, 扩展性好

4.2.3. YAML 语法简介

在单一文件第一行, 用连续三个连字号”-“开始, 还有选择性的连续三个点号(…)用来表示文件的结束
次行开始正常写 playbook 的内容, 一般建议写明该 playbook 的功能
使用#号注释代码
缩进必须是统一的, 不能空格和 tab 混合使用
缩进的级别也必须是一致的, 同样的缩进代表同样的级别, 程序判别配置的级别是通过缩进结合换行来实现的, YAML 文件内容是区分大小写的, key/value 的值均需要大小写敏感
多个 k/v 可同行写也可换行写, 同行使用,分割
v 可是个字符串, 也可是另一个列表
一个完整的代码块功能需最少元素需包括 name 和 task
一个 name 只能包括一个 task
YAML 文件扩展名通常为 yml 或 yaml

YAML 的语法和其他高阶语言类似, 并且可以简单表达清单, 散列表, 标量等数据结构, 其结构(structure)通过空格来展示, 序列(sequence)里的项用”-“来代表, Map 里的键值对用”:”分割, 下面介绍常用的数据结构.

4.2.3.1. List 列表

列表由多个元素组成, 且所有元素前均使用”-“打头

范例:

1
2
3
4
5
6
7
8
# A list of tasty fruits
- Apple
- Orange
- Strawberry
- Mango

[Apply, Orange, Strawberry, Mango]

4.2.3.2. Dictionary 字典

字典通常由多个 key 与 value 构成

范例:

1
2
3
4
5
6
7
8
9
# An example record
name: Example Developer
job: Developer
skill: Elite

# 也可以将key:value置于{}中进行表示, 用,分割多个key:value
# An example record
{name: "Example developer", job: "developer", skill: "Elite"}

4.3. Playbook 核心元素

Hosts 执行的远程主机列表
Tasks 任务集
Variable 内置变量或自定义变量在 playbook 中调用
Templates 模板, 可替换模板文件中的变量并实现一些简单逻辑的文件
Handler 和 Notify 结合使用, 由特定条件触发的操作, 满足条件方才执行, 否则不执行
tags 标签, 指定某条任务执行, 用于选择运行 playbook 中的部分代码, ansible 具有幂等性, 因此会自动跳过没有变化的部分, 即便如此, 有些代码为测试其确实没有发生变化的时间依然会非常地长, 此时如果确信没有变化, 就可以通过 tags 跳过此些代码片段

4.3.1. hosts 组件

Hosts: playbook 中的每一个 play 的目的都是为了让特定主机以某个指定的用户身份执行任务. hosts 用于指定要执行指定任务的主机, 须事先定义在主机清单中

案例

1
- hosts: websrvs:appsrvs

4.3.2. remote_user 组件

remote_user: 可用于 host 和 task 中, 也可以通过指定其通过 sudo 的方式在远程主机上执行任务, 其可用于 play 全局或某个任务; 此外, 甚至可以在 sudo 时使用 sudo_user 指定 sudo 时切换的用户

1
2
3
4
5
6
7
8
9
- hosts: websrvs
remote_user: root

tasks:
- name: test connection
ping:
remote_user: test_user
sudo: yes # 默认sudo为root
sudo_user: wang # sudo为wang

4.3.3. task 列表和 action 组件

play 的主体部分是 task list, task list 中有一个或多个 task, 各个 task 按次序逐个在 hosts 中指定的所有主机上执行, 即在所有主机上完成第一个 task 后, 再开始第二个 task.
task 的目的是使用指定的参数执行模块, 而再模块参数中可以使用变量. 模块执行是幂等的, 这意味着多次执行是安全的, 因为其结果均一致.
每个 task 都应该有其 name, 用于 playbook 的执行结果输出, 建议其内容能清晰地描述任务执行步骤. 如果未提供 name, 则 action 的结果将用于输出

task 两种格式

(1) action: moudule arguments
(2) module: arguments 建议使用

注意: shell 和 command 模块后面跟命令, 而非 key=value

范例:

1
2
3
4
5
6
7
8
---
- hosts: websrvs
remote_user: root
tasks:
- name: install httpd
yum: name=httpd
- name: start httpd
service: name=httpd start=started enabled=yes

4.3.4. 其它组件

某任务的状态在运行后为 changed 时, 可以通过 notify 通知给相应的 handlers 任务可以通过 tags 打标签, 可在 ansible-playbook 命令上使用-t 指定进行调用

4.3.5. ShellScript VS Playbook 案例

1
2
3
4
5
6
7
8
9
10
# SHELL脚本实现

#!/bin/bash
# 安装apache
yum install --quiet -y httpd
# 复制配置文件
cp /tmp/httpd.conf /etc/httpd/conf/httpd.conf
cp /tmp/vhosts.conf /etc/httpd/conf.d/
systemctl enable --now httpd

playbook 实现

1
2
3
4
5
6
7
8
9
10
11
12
---
- hosts: websrvs
remote_user: root
tasks:
- name: "安装Apache"
yum: name=httpd
- name: "复制配置文件"
copy: src=/tmp/httpd.conf dest=/etc/httpd/conf/
- name: "复制配置文件"
copy: src=/tmp/vhosts.conf dest=/etc/httpd/conf.d/
- name: "启动Apache, 并设置开机启动"
service: name=httpd state=started enabled=yes

4.4. playbook 命令

格式

1
ansible-playbook <filename.yml> ... [options]

常见选项:

1
2
3
4
5
6
7
8

--check -C # 只检查可能发生的改变, 但不真正执行操作
--list-hosts # 列出运行任务的主机
--list-tags # 列出tag
--list-tasks # 列出task
--limit 主机列表 # 只针对列表中的主机执行
-v -vv -vvv # 显示过程

范例

1
2
3
4
5

ansible-playbook file.yml --check #只检测
ansible-playbook file.yml
ansible-playbook file.yml --limit websrvs

4.5. Playbook 初步

4.5.1. 利用 playbook 创建 mysql 用户

范例: mysql_user.yml

1
2
3
4
5
6
7
- hosts: dbsrvs
remote_user: root
gather_facts: no # 不收集主机信息
tasks:
- { name: create group, group: name=mysql system=yes gid=306 }
- name: create user
user: name=mysql shell=/sbin/nologin system=yes group=mysql uid=306 home=/data/mysql create_home=no

4.5.2. 利用 playbook 安装 nginx

范例: install_nginx.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# install nginx
- hosts: websrvs
remote_user: root
gater_facts: no
tasks:
- name: add group nginx
group: name=nginx state=present
- name: add user nginx
user: name=nginx state=present
- name: install Nginx
yum: name=nginx state=present
- name: html page
copy: src=files/index.html dest=/usr/share/nginx/html/index.html
- name: start nginx
service: name=nginx state=started enabled=yes

4.5.3. 利用 playbook 安装和卸载 httpd

范例: install_httpd.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
---
# install httpd
- hosts: websrvs
remote_user: root
gather_facts: no

tasks:
- name: install httpd
yum: name=httpd state=present
- name: install configure file
copy: src=files/httpd.conf dest=/etc/httpd/conf/
- name: start service
service: name=httpd state=started enabled=yes

范例: remove_httpd.yml

1
2
3
4
5
6
7
8
9
10
---
- hosts: websrvs
remote_user: root
tasks:
- name: remove httpd package
yum: name=httpd state=absent
- name: remove apache user
user: name=apache state=absent
- name: remove data file
file: name=/etc/httpd state=absent

4.5.4. 利用 playbook 安装 mysql

范例: 安装 mysql-5.6.46-linux-glibc2.12

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
- hosts: websrvs
remote_user: root

tasks:
- name: install packages
yum: name=libaio,perl-Data-Dumper,per-Getopt-Long
- name: create mysql group
group: name=mysql gid=306
- name: create mysql user
user: name=mysql uid=306 group=mysql shell=/sbin/nologin system=yes create_home=no home=/data/mysql
- name: copy tar to remote host and file mode
unarchive: src=/data/ansible/files/mysql-5.6.46-linux-glibc2.12-x86_64.tar.gz dest=/usr/local/ owner=root group=root
- name: crate link /usr/local/mysql
file: src=/usr/local/mysql-5.6.46-linux-glibc2.12-x86_64 dest=/usr/local/mysql state=link
- name: data dir
shell: chdir=/usr/local/mysql/ ./script/mysql_install_db --datadir=/data/mysql -- user=mysql
tags: data
- name: config my.conf
copy: src=/data/ansible/files/my.conf dest=/etc/my.cnf
- name: service script
shell: /bin/cp /usr/local/mysql/support-files/mysql.server /etc/init.d/mysqld
- name: enable service
shell: /etc/init.d/mysqld start: chkconfig --add mysqld; chkconfig mysql on
tags: service
- name: PATH variable
copy: content='PATH=/usr/local/mysql/bin:$PATH' dest=/etc/profile.d/mysql.sh
- name: secure script
script: /data/ansible/files/secure_mysql.sh
tags: script

4.6. Playbook 中使用 handlers 和 notify

Handlers 本质是 task list, 类似于 mysql 中的触发器触发行为, 其中的 task 与前述的 task 并没有本质上的不同, 主要用于当关注的资源发生变化时, 才会采取一定的操作, 而 Notify 对应的 action 可用于在每个 play 的最后触发, 这样可以避免多次有改变发生时每次都执行指定的操作, 尽在所有的变化发生完成后一次性地执行指定操作. 在 notify 中列出的操作称为 handler, 也即 notify 中调用 handler 中定义的操作

案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
---
- hosts: websrvs
remote_user: root
tasks:
- name: install httpd
yum: name=httpd state=present
- name: install configure file
copy: src=files/httpd.conf dest=/etc/httpd/conf
notify: restart httpd

handlers:
- name: restart httpd
service: name=httpd state=restarted

4.7. playbook 中使用 tags 组件

在 playbook 文件中, 可以利用 tags 组件, 为特定 task 指定标签, 当在执行 playbook 时, 可以只执行特定 tags 的 task, 而非整个 playbook 文件

案例:
httpd.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
---
# tags example
- hosts: websrvs
remote_user: root
gater_facts: no
tasks:
- name: install httpd
yum: name=httpd state=present
- name: install configure file
copy: src=files/httpd.conf dest=/etc/httpd/conf/
tags: conf
- name: start httpd service
tags: service
service: name=httpd state=started enabled=yes

执行指定的 tags

1
2
3

ansible-playbook -t conf,service httpd.yml

4.8. playbook 中使用变量

变量名: 仅能由字母, 数字和下划线, 且只能以字母开头

变量定义

1
2
3

key=value

范例:

1
http_port=80

变量调用方式:

通过 调用变量, 且变量名前后建议加空格, 有时用”“ 才生效

变量来源:

  1. ansible 的 setup facts 远程主机的所有变量都可直接调用
  2. 通过命令行指定变量, 优先级最高

4.8.1. 在 playbook 文件中定义变量

范例: var2.yml

1
2
3
4
5
6
7
8
9
10
11
12
---
- host: websrvs
remote_user: root
vars:
- username: user1
- groupname: group1

tasks:
- name: create group
group: name= {{groupname}} state=present
- name: create user
user: name= {{ username }} state=present

4.8.2. 在 playbook 文件中定义变量

范例:

1
2
3
4
5
6
7
8
9
10
- hosts: websrvs
remote_user: root
vars:
- username: user1
- groupname: group1
tasks:
- name: create group
group: name={{groupname}} state=present
- name: create user
user: name={{username}} group={{ groupname }} state=present
1
2
3

ansible-playbook -e "username=user2 groupname=group2" var3.yml

4.8.3. 使用变量文件

可以在一个独立的 playbook 文件中定义变量, 在另一个 playbook 文件中引用变量文件中的变量,比 playbook 中定义的变量优先级更高

vars.yml

1
2
package_name: vsfpd
service_name: vsfpd

install_app.yml

1
2
3
4
5
6
7
8
9
# install app and configure
- hosts: appsrvs
remote_user: root
var_files:
- vars.yml
tasks:
- name: install package
yum: name={{package_name}}
tags: install

4.8.4. 主机清单中定义变量

主机变量

在 inventory 主机清单文件中为指定的主机定义变量以便于在 playbook 中使用

范例:

1
2
3
4
5

[websrvs]
www1.example.com http_port=80 maxRequestPerChild=808
www2.example.com http_port=8080 maxRequestPerChild=909

组(公共)变量

在 inventory 主机清单文件中赋予给指定组内所有主机上的在 playbook 中可用的变量

范例:

1
2
3
4
5
6
7
[websrvs]
www1.example.com
www2.example.com

[websrvs:vars]
domain=example.com

4.9. template 模板

模板是一个文本文件, 可以作为生成文件的模板, 并且模板文件中可以嵌套 jinja2 语法

4.9.1. jinja2 语法

jinja2 语言使用字面量, 有下面形式

字符串: 使用单引号或双引号

数字: 整数, 浮点数
列表: [item1, item2, …]
元组: (item1, item2, …)
字典: {key1:value1, key2:value2}
布尔型: true/false
算术运算: +,-,*,/,//,%,**
比较操作: ==, != >, >=, <,>=
逻辑运算: and, or, not
流表达式: For, IF, When

字面量:

表达式最简单的形式是字面量, 字面量表示诸如字符串和数值的 python 对象, 如”hello world”, 双引号或单引号中间的一切都是字符串. 无论何时你需要在模板中使用一个字符串(比如函数调用,过滤器或只是包含或继承一个模板的参数), 如 42, 42.23
数值可以为整数和浮点数, 如果有小数点, 则为浮点数, 否则为整数, 在 python 里, 42 和 42.0 是一样的

jinja 语法详细语法可参考https://doc.yonyoucloud.com/doc/jinja2-docs-cn/index.html

4.9.2. template

template 功能: 可以根据和参考模块文件, 动态生成相似的配置文件
template 文件必须存放于 template 目录下, 且命名为.j2 结尾
yaml/yml 文件需和 template 目录平级, 目录结构如下:

./
|—temnginx.yml
|—templates
|___nginx.conf.j2

范例: 利用 template 同步 nginx 配置文件

1
2
3
4
5
6
7
8
9
准备templates/nginx.conf.j2文件
vim temnginx.yam
---
- hosts: websrvs
remote_user: root

tasks:
- name: template config to remote hosts
template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf

template 模块只能在 playbook 中使用,不能在 ansible 命令中单独使用
template 的作用根据模板生成文件并拷贝到目标位置

执行:

1
ansible-playbook temnginx.yml

template 变更替换

范例:

修改文件 nginx.conf.j2

vim templates/nginx.conf.j2

1
2
3

worker_processes {{ ansible_processor_vcpus }}

变量来源:

  1. setup 模块的变量(系统变量)
  2. 主机清单中的变量
  3. 变量文件中的变量
  4. yaml 文件中定义的变量
  5. ansible-playbook 中使用-e 选项传递的变量
  6. 角色中定义的变量

vim temnginx2.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
---
- hosts: websrvs
remote_user: root
tasks:
- name: install nginx
yum: name=nginx
- name: template config to remote hosts
template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf
notify: restart nginx
- name: start service
service: name=nginx state=started enabled=yes
handlers:
- name: restart nginx
service: name=nginx state=restarted
1
ansible-playbook temnginx2.yml

4.10. playbook 使用 when

when 语句,可以实现条件测试,如果需要根据变量,facts 或此前任务的执行结果来作为某 task 执行与否的前提时,

要用到条件测试,通过在 task 后添加 when 子局即可使用条件测试,jinja2 的语法格式

范例:

1
2
3
4
5
6
7
---
- hosts: websrvs
remote_user: root
tasks:
- name: "shutdown RedHat flavored systems"
command: /sbin/shutdown -h now
when: ansible_os_family == "RedHat"

范例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
---
- 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: restart Nginx
service: name=nginx state=restarted
when: ansible_distribution_major_version == "6"

4.11. playbook 使用迭代 with_items

迭代:当有需要重复性执行的任务时,可以使用迭代机制
对迭代项的引用,固定变量名为”item”
要在 task 中使用 with items 给定要迭代的元素列表

列表元素格式:

  • 字符串
  • 字典

范例:

1
2
3
4
5
6
7
8
9
10
---
- hosts: websrvs
remote_user: root

tasks:
- name: add serveral users
user: name={{ item }} state=present group=wheel
with_items:
- testuser1
- testuser2

范例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
---
- hosts: websrvs
remote_user: root

tasks:
- name: add some groups
group: name={{ item }} state=present
with_items:
- nginx
- mysql
- apache
- name: add some users
user: name={{ item.name }} group={{ item.group }} state=present
with_items:
- { name: "nginx", group: "nginx" }
- { name: "mysql", group: "mysql" }
- { name: "apache", group: "apache" }

5. Roles 角色

角色是 Ansible 自 1.2 版本引入的新特性, 用于层次性, 结构化地组织 playbook, roles 能够根据层次型结构自动装载变量文件, tasks 以及 handlers 等, 要使用 roles 只需要在 playbook 中使用 include 指令即可, 简单来讲, roles 就是通过分别将变量, 文件, 任务, 模板以及处理器放置于单独的目录中, 并可以便捷地 include 它们的一种机制, 角色一般用于基于主机构建服务的场景中, 但也可以用于构建守护进程等场景中

运维复杂的场景: 建议使用 roles, 代码复用度高

roles: 多个角色的集合, 可以将多个的 role, 分别放至 roles 目录下的独立目录中

roles/
mysql/
httpd/
nginx/
redis/

5.1. Ansible Roles 目录编排

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 文件, 比 vars 的优先级低

5.2. 创建 role

创建 role 的步骤

(1) 创建以 role 命名的目录
(2) 在 roles 目录中分别创建以各角色名称命名的目录, 如 webservers 等
(3) 在每个角色命名的目录中分别创建 files, handlers, met, tasks, templates 和 vars 目录; 用不到的目录可以创建为空目录, 也可以不创建
(4) 在 playbook 文件中, 调用各角色

针对大型项目使用 Roles 进行编排
范例: roles 的目录结构

1
2
3
4
5
6
7
8
9
10
11
12
13
nginx-role.yml
roles/
|----nginx
|----files
| |--main.yml
|----tasks
| |--groupadd.yml
| |--install.yml
| |--main.yml
| |--restart.yml
| |--useradd.yml
|----vars
|--main.yml

5.3. playbook 调用角色

调用角色方法 1:

1
2
3
4
5
6
7
---
- hosts: websrvs
remote_user: root
roles:
- mysql
- memcached
- nginx

调用角色方法 2:

键 role 用于指定角色名称, 后续的 k/v 用于传递变量给角色

1
2
3
4
5
6
7
---
- hosts: all
remote_user: root
roles:
- mysql
- (role: nginx, username: nginx)

调用角色方法 3:

1
2
3
4
5
6
---
- hosts: all
remote_user: root
roles:
- (role: nginx, username: nginx, when: ansible_distribution_major_version == '7')

5.4. roles 中的 tags 使用

1
2
3
4
5
6
7
8
9
10
11
- hosts: websrvs
remote_user: root
roles:
- {
role: nginx,
tags: ["nginx", "web"],
when: ansible_distribution_major_version == '6',
}
- { role: httpd, tags: ["httpd", "web"] }
- { role: mysql, tags: ["mysql", "db"] }
- { role: mariadb, tags: ["mariadb", "db"] }
1
2
3
# ansible-playbook可以挑选择标签执行
ansible-playbook --tags="nginx,httpd,mysql" nginx-role.yml

5.5. 实战案例

5.5.1. 案例 1: 实现 httpd 角色

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

# 创建角色相关的目录
mkdir -pv /data/ansible/roles/httpd/{tasks, files, handlers}

# 创建角色相关的文件
cd /data/ansible/roles/httpd/
vim tasks/main.yml
- include: install.yml
- include: config.yml
- include: index.yml
- include: service.yml

vim tasks/install.yml
- name: install httpd package
yum: name=httpd

vim tasks/config.yml
- name: config file
copy: src=httpd.conf dest=/etc/httpd/conf/ backup=yes
notify: restart

vim tasks/index.yml
- name: index.html
copy: src=index.html dest=/var/www/html/

vim tasks/service.yml
- name: start service
service: name=httpd state=started enabled=yes

vim handlers/main.yml
- name: restart
service: name=httpd state=restarted

# 在files目录下创建两个文件
httpd.conf index.html

在 playbook 中调用 httpd 角色

vim /data/ansible/role_httpd.yml

1
2
3
4
5
6
---
# httpd role
- hosts: all
remote_user: root
roles:
- httpd

5.5.2. 案例 2:实现 nginx 角色

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

# 创建角色相关的目录
mkdir -pv /data/ansible/roles/nginx/{tasks, files, templates, handlers, vars}

# 创建角色相关的文件
cd /data/ansible/roles/nginx/
vim tasks/main.yml
- include: install.yml
- include: config.yml
- include: index.yml
- include: service.yml


vim tasks/install.yml
- name: install nginx package
yum: name=nginx

vim tasks/config.yml
- name: config file for centos7
copy: src=nginx7.conf.j2 dest=/etc/nginx/nginx.conf
when: ansible_distribution_major_version=="7"
notify: restart
- name: config file for centos8
copy: src=nginx8.conf.j2 dest=/etc/nginx/nginx.conf
when: ansible_distribution_major_version=="8"
notify: restart

vim tasks/index.yml
- name: index.html
# 跨角色调用文件
copy: src=roles/httpd/files/index.yml dest=/usr/share/nginx/html/

vim tasks/service.yml
- name: start service
service: name=nginx state=started enabled=yes

vim handlers/main.yml
- name: restart
service: name=nginx state=restarted

vim vars/main.yml
user: daemon

vim templates/nginx7.conf.j2
......
user: {{ user }}
.......

vim templates/nginx8.conf.j2
......
user: {{ user }}
.......

在 playbook 中调用 nginx 角色

vim /data/ansible/role_nginx.yml

1
2
3
4
5
6
---
# httpd role
- hosts: all
remote_user: root
roles:
- role_nginx

5.5.3. 案例 3:实现 mysql 角色

40”51’

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
cat /data/ansible/roles/mysql/files/my.conf

[mysqld]
socket=/tmp/mysql.sock
user=mysql
symbolic-links=0
datadir=/data/mysql
innodb_file_per_table=1
log-bin
pid-file=/data/mysql/mysqld.pid

[client]
port=3306
socket=/tmp/mysqld.log

[mysqld_safe]
log-error=/var/log/mysqld.log

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
cat /data/ansible/roles/mysql/files/secure_mysql.sh
#!/bin/bash
/usr/local/mysql/bin/mysql_secure_installation <<EOF
y
philoenglish
philoenglish
y
y
y
y
EOF

ls /data/ansible/roles/mysql/files/
my.conf mysql-5.6.46-linux-glibc2.12-x86_64.tar.gz secure_mysql.sh

cat /data/ansible/roles/mysql/tasks/main.yml
- include: install.yml
- include: group.yml
- include: user.yml
- include: unarchive.yml
- include: link.yml
- include: data.yml
- include: config.yml
- include: service.yml
- include: path.yml
- include: secure.yml


cat /data/ansible/roles/mysql/tasks/install.yml
- name: install packages
yum: name=libaio, perl-Data-Dumper, perl-Getopt-Long

cat /data/ansible/roles/mysql/tasks/group.yml
- name: create mysql group
group: name=mysql gid=306

cat /data/ansible/roles/mysql/tasks/user.yml
- name: create mysql user
user: name=mysql uid=306 group=mysql shell=/sbin/nologin system=yes create_home=no home=/data/mysql

cat /data/ansible/roles/mysql/tasks/unarchive.yml
- name: copy tar to remote host and file mode
unarchive: src=mysql-5.6.46-linux-glibc2.12-x86_64.tar.gz dest=/usr/local owner=root group=root

cat /data/ansible/roles/mysql/tasks/link.yml
- name: mkdir /usr/local/mysql
file: src=/usr/local/mysql-5.6.46-linux-glibc2.12-x86_64.tar.gz dest=/usr/local/mysql state=link

cat /data/ansible/roles/mysql/tasks/data.yml
- name: data dir
shell: chdir=/usr/local/mysql ./scripts/mysql_install_db --datadir=/data/mysql --user=mysql

cat /data/ansible/roles/mysql/tasks/conf.yml
- name: conf my.cnf
copy: src=my.cnf dest=/etc/my.cnf

cat /data/ansible/roles/mysql/tasks/service.yml
- name: service script
shell: /bin/cp /usr/local/mysql/support-files/mysql.server /etc/init.d/mysqld;/etc/init.d/mysqld;/etc/init.d/mysqld start; chkconfig mysqld on

cat /data/ansible/roles/mysql/tasks/path.yml
- name: PATH variable
copy: content='PATH=/usr/local/mysql/bin:$PATH' dest=/etc/profile.d/mysql.sh

cat /data/ansible/roles/mysql/tasks/secure.yml
- name: secure script
script: secure_mysql.sh

6. 参考教程

ansible 入门到精通及企业实战

Ansible 中文权威指南

Ansible 介绍

Ansible 指南

作者

鹏叔

发布于

2021-10-28

更新于

2024-10-14

许可协议

评论