目录

一、场景说明

1. 任务背景

2. 任务要求

3. 任务拆解

4. 学习目标

二、Ansible 环境搭建

5. 自动化运维

6. 自动化运维需要关注内容

7. 常见运维工具对比

8. Ansible 概述

9. Ansible 环境搭建

10. 服务器分组(重点理解)

三、Ansible 模块(重点理解)

1. 求帮助

2. hostname 模块

3. file 模块(重点)

4. copy 模块(重点)

5. fetch 模块

6. user 模块

7. group 模块

8. cron 模块

9. yum/dnf 模块(重点)

10. service 模块(重点)

11. script 模块

12. command 与 shell 模块

四、Ansible Playbook(重点)

1. Playbook 概述

2. YAML 格式

3. Playbook 入门案例

4. Playbook 常见语法

5. Playbook 案例

五、Ansible Roles(仅供了解)

1. roles 介绍

2. 创建 roles 的目录结构

3. 通过 roles 实现 lamp

拓展案例:通过 roles 实现 lamp 并安装 discuz


作用: 掌握 Ansible 配置自动化工具,能实现多服务器批量管理!

一、场景说明

1. 任务背景

公司的服务器越来越多,维护一些简单的事情都会变得很繁琐。用 Shell 脚本来管理少量服务器效率还行,服务器多了之后 Shell 脚本无法实现高效率运维。这种情况下,我们需要引入自动化运维工具,对多台服务器实现高效运维。

2. 任务要求

通过管理服务器能够按照需求灵活高效地管理所有应用服务器的运维操作。

3. 任务拆解

  1. 需要一台服务器做管理端,来连接管理所有的应用服务器。

  2. 考虑如果只针对一部分应用服务器进行运维操作如何实现(服务器分组)。

  3. 学会将平台烂熟于心的 Linux 操作命令转化为自动化运维的方式(常见模块的学习)。

  4. 如果操作非常的冗长,学会使用 playbook 和 roles 的方式来管理(自动化编排)。

4. 学习目标

  1. 能够安装 Ansible 服务器和客户端。

  2. 能够定义 Ansible 主机清单进行服务器分组。

  3. 掌握常用模块使用。

  4. 了解 Playbook 的编写。

  5. 能够使用 Playbook 实现软件安装部署。

  6. 扩展 Ansible Roles 功能。

二、Ansible 环境搭建

5. 自动化运维

问题: 假设我要去 1000 台服务上做一个操作(如 MySQL 数据库服务器修改配置文件里的某一个参数),下面两种方法缺点明显:

  • 第一种方案: 按传统的方法,一台连着一台服务器的 SSH 上去手动操作。

    • 缺点: 效率太低。

  • 第二种方案: 写个 Shell 脚本来做。

    • 缺点: 管理的机器平台不一致,脚本可能不具备通用性。传密码麻烦(在非免密登录的环境下,需要 expect 来传密码),效率较低,循环 1000 次也需要一个一个的完成,如果用 & 符放到后台执行,则会产生 1000 个进程。

  • 第三种方案:自动化运维

    • 将日常 IT 运维中大量的重复性工作,小到简单的日常检查、配置变更和软件安装,大到整个变更流程的组织调度,由过去的手工执行转为自动化操作,从而减少乃至消除运维中的延迟,实现"零延时"的 IT 运维。

6. 自动化运维需要关注内容

假如管理很多台服务器,主要关注以下几个方面:

  • 管理机与被管理机的连接: 管理机如何将管理指令发送给被管理机。

  • 服务器信息收集: 如果被管理的服务器有 CentOS 外还有其它 Linux 发行版,如 SUSE, Ubuntu 等。当你要做的事情在不同 OS 上有所不同,你需要收集信息,并将其分开处理。

  • 服务器分组: 因为有些时候我要做的事情不是针对所有服务器,可能只针对某一个分组。

  • 管理内容的主要分类:

    • 文件目录管理(包括文件的创建,删除,修改,查看状态,远程拷贝等)

    • 用户和组管理

    • Cron 时间任务管理

    • Yum 源配置与通过 Yum 管理软件包

    • 服务管理

    • 远程执行脚本

    • 远程执行命令

7. 常见运维工具对比

  • Puppet: 基于 Ruby 语言,成熟稳定。适合于大型架构,相对于 Ansible 和 SaltStack 会复杂些。

  • SaltStack: 基于 Python 语言,简单、并发能力比 Ansible 要好,需要维护被管理端的服务。如果服务断开,连接就会出问题。

  • Ansible: 基于 Python 语言。简单快捷,被管理端不需要启服务。直接走 SSH 协议,需要验证所以机器多的话速度会较慢。

8. Ansible 概述

image

Ansible 是一种由 Python 开发的自动化运维工具,集合了众多运维工具(Puppet、CFEngine、Chef、Func、Fabric)的优点,实现了批量系统配置、批量程序部署、批量运行命令等功能。

特点:

  • 部署简单

  • 默认使用 SSH 进行管理,基于 Python 里的 Paramiko 模块开发

  • 管理端和被管理端不需要启动服务

  • 配置简单,功能强大,扩展性强

  • 能够通过 Playbook(剧本)进行多个任务的编排

image

9. Ansible 环境搭建

实验准备: 三台机器,一台管理,两台被管理

编号 IP 角色
node1 192.168.88.101 管理机 master
node2 192.168.88.102 被管理机 1
node3 192.168.88.103 被管理机 2

配置说明:

  • 静态 IP

  • 主机名及 IP 互相绑定

  • 关闭防火墙,SELinux

  • 时间同步

  • 确认和配置 Yum 源

全服务器时间同步:

dnf install epel-release -y  
# dnf install ntpsec -y  
# ntpdate cn.ntp.org.cn 

第 1 步:管理机上安装 Ansible,被管理节点必须打开 SSH 服务 注意:Ansible 不需要在每一台机器上安装,只需要在 node1 主节点安装即可。

# yum install ansible -y
# ansible --version
ansible [core 2.14.17]
    config file = /etc/ansible/ansible.cfg
    configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
    ansible python module location = /usr/lib/python3.9/site-packages/ansible
    ansible collection location = /root/.ansible/collections:/usr/share/ansible/collections
    executable location = /usr/bin/ansible
    python version = 3.9.19 (main, Aug 23 2024, 00:00:00) [GCC 11.5.0 20240719 (Red Hat 11.5.0-2)] (/usr/bin/python3)
    jinja version = 3.1.2
    libyaml = True 

第 2 步:实现 master 对 agent 的免密登录,只在 master 上做。 (如果这一步不做,则在后面操作 agent 时都要加 -k 参数传密码;或者在主机清单里传密码)

master# ssh-keygen  
master# ssh-copy-id 192.168.88.102  
master# ssh-copy-id 192.168.88.103 

注意: 如果是 CentOS 7 采用 ssh-copy-id -i IP 地址,如果 CentOS Stream 9 系列,要去掉 -i 参数,否则报错!!!

第 3 步:在 master 上定义主机组,并测试连接性

master# vim /etc/ansible/hosts   
[group1]   
192.168.88.102   
192.168.88.103   
  • -m 代表模块 => module

master# ansible -m ping group1   
192.168.88.103 | SUCCESS => { "changed": false, "ping": "pong" }   
192.168.88.102 | SUCCESS => { "changed": false, "ping": "pong" }
master# ansible -m ping all  
192.168.88.103 | SUCCESS => {  
    "changed": false,  
    "ping": "pong"  
}  
192.168.88.102 | SUCCESS => {  
    "changed": false,  
    "ping": "pong"  
} 

10. 服务器分组(重点理解)

参考文档:https://docs.ansible.com/ansible/latest/inventory_guide/intro_inventory.html Ansible 通过一个"主机清单"功能来实现服务器分组。 Ansible 的默认主机清单配置文件为 /etc/ansible/hosts

案例 1: [起始值:结束值],包含起始值也包含结束值

[nginx] 
# 组名 apache[1:10].aaa.com 表示 apache1.aaa.com 到 apache10.aaa.com 这 10 台机器  
apache[1:10].aaa.com
# nginx[a:z].aaa.com 表示 nginxaaa.com 到 nginxz.aaa.com 共 26 台机器  
nginx[a:z].aaa.com
# 192.168.88.[161:165] 表示 192.168.88.161 到 192.168.88.165 这 5 台机器
192.168.88.[161:165]

案例 2:

[nginx] 
# 10.1.1.13:2222 表示 10.1.1.13 这台,但 SSH 端口为 2222
10.1.1.13:2222

案例 3:定义 10.1.1.12:2222 这台服务器的别名为 nginx1

nginx1 ansible_ssh_host=10.1.1.13 ansible_ssh_port=2222 

案例 4:没有做免密登录的服务器可以指定用户名与密码 => Paramiko 模块

nginx1 ansible_ssh_host=10.1.1.13 ansible_ssh_port=2222 ansible_ssh_user=root ansible_ssh_pass="123456" 

注意:

  • nginx1 别名

  • ansible_ssh_host: 要连接的主机的 IP 地址

  • ansible_ssh_port: SSH 对应的端口号

  • ansible_ssh_user: 用户名

  • ansible_ssh_pass: 密码

补充:

  • 临时:ANSIBLE_HOST_KEY_CHECKING=False ansible -m ping nginx1

  • 永久:ssh-keyscan -H 192.168.88.104 >> ~/.ssh/known_hosts

案例 5:

nginx1 ansible_ssh_host=10.1.1.13 ansible_ssh_port=2222 ansible_ssh_user=root ansible_ssh_pass="123456"  
nginx2 ansible_ssh_host=10.1.1.12 ansible_ssh_port=3333 ansible_ssh_user=root ansible_ssh_pass="123456"  
[nginx]  
nginx1  
nginx2 

小结:

  • /etc/ansible/hosts => 主机清单 => Ansible 是通过主机清单实现服务器分组

  • 主机清单的作用:服务器分组。

  • 主机清单的常见功能:

    1. 可以通过 IP 范围来分,主机名名字的范围来分。

    2. 如果 SSH 端口不是 22 的,可以传入新的端口。

    3. 没有做免密登录,可以传密码。

三、Ansible 模块(重点理解)

1. 求帮助

# ansible-doc -l
fortiosRouter/community_list
azure_rm_devtestlab_info
ecs_taskdefinition
lists...
avi_alertscriptconfig
lists...
tower_receive
communityLists...
netapp_e_iscsi_target
lists...
azure_rmacs
lists... 

如果要查看 ping 模块的用法,使用下面命令(其它模块以此类推):

ansible-doc ping 

2. hostname 模块

hostname 模块用于修改主机名(注意:它不能修改 /etc/hosts 文件)。 参考:https://docs.ansible.com/ansible/latest/modules/hostname_module.html#hostname-module

将其中一远程机器主机名修改为 agent1.cluster.com

master# ansible -m hostname -a 'name=agent1.cluster.com' 192.168.88.102 
  • -m:模块名称

  • -a:具体参数和参数值

  • 基本格式为:ansible 操作的机器名或组名 -m 模块名 -a "参数 1=值 1 参数 2=值 2"

扩展: 在实际工作中,主机名称必须采用 FQDN 格式。

  • 功能名称。公司域名

  • web01.itcast.cn

  • web02.itcast.cn

  • mysql.itcast.cn

小结:

  • hostname 在 Linux 操作系统中主要用于(获取或修改主机名称)。

  • 在 Ansible 里面,hostname 专门用于主机名称,注意:Linux 主机名必须要满足(FQDN)格式。

3. file 模块(重点)

作用: file 模块用于对文件相关的操作(创建,删除,软链接等)。 参考:https://docs.ansible.com/ansible/latest/modules/file_module.html#file-module

  • path = 文件或文件夹路径

  • state = 状态(touch 文件、directory 文件夹、absent 删除、recurse 递归)

案例 1:创建一个目录

master# ansible group1 -m file -a 'path=/test state=directory' 

案例 2:创建一个文件

master# ansible group1 -m file -a 'path=/test/111 state=touch' 

案例 3:递归修改 owner, group, mode

master# ansible group1 -m file -a 'path=/test recurse=yes owner=bin group=daemon mode=0777' 

案例 4:删除目录(连同目录里的所有文件)

master# ansible group1 -m file -a 'path=/test state=absent' 

案例 5:创建文件并指定 owner, group, mode 等

master# ansible group1 -m file -a 'path=/tmp/111 state=touch owner=bin group=daemon mode=0777' 

案例 6:删除文件

master# ansible group1 -m file -a 'path=/tmp/111 state=absent' 

案例 7:创建软链接文件

master# ansible group1 -m file -a 'src=/etc/fstab path=/tmp/fstab state=link'
  • 参数位置:

    • src:源文件

    • path:快捷方式路径

    • state=link:代表创建软链接

小结:

  • Ansible 中与文件管理相关的模块为(-m file)。

  • 基于 file 模块,我们可以创建文件、目录、软连接,也可以删除文件。

4. copy 模块(重点)

作用: copy 模块用于对文件的远程拷贝操作(如把本地的文件拷贝到远程的机器上)。

在 master 上准备一个文件,拷贝此文件到 group1 的所有机器上:

master# echo master > /tmp/file1  
master# ansible group1 -m copy -a 'src=/tmp/file1 dest=/tmp/file2' 

使用 content 参数直接往远程文件里写内容(会覆盖原内容)

master# ansible group1 -m copy -a 'content="ansible\n" dest=/tmp/file3' 
  • 注意: Ansible 中 -a 后面的参数里也有引号时,记得要单引双引交叉使用,如果都为双引会出现问题。

使用 force 参数控制是否强制覆盖

  • 如果目标文件已经存在,则不覆盖:

    master# ansible group1 -m copy -a 'src=/tmp/file2 dest=/tmp/file3 force=no' 
  • 如果目标文件已经存在,则会强制覆盖:

    master# ansible group1 -m copy -a 'src=/tmp/file2 dest=/tmp/file3 force=yes' 

使用 backup 参数控制是否备份文件

  • backup=yes 表示如果拷贝的文件内容与原内容不一样,则会备份一份。

  • group1 的机器上会将 /tmp/file3 备份一份(备份文件命名加上时间),再远程拷贝新的文件为 /tmp/file3

    master# ansible group1 -m copy -a 'src=/etc/fstab dest=/tmp/file3 backup=yes owner=daemon group=daemon mode=0777' 
  • 简单理解:就是对目标文件进行备份操作。

copy 模块拷贝时要注意拷贝目录后面是否带"/"符号

  • /etc/yum.repos.d 后面不带 / 符号,则表示把 /etc/yum.repos.d 整个目录拷贝到 /tmp/ 目录下。

    master# ansible group1 -m copy -a 'src=/etc/yum.repos.d dest=/tmp/' 
  • /etc/yum.repos.d/ 后面带 / 符号,则表示把 /etc/yum.repos.d/ 目录里所有文件拷贝到 /tmp/ 目录下。

    master# ansible group1 -m copy -a 'src=/etc/yum.repos.d/ dest=/tmp/' 

小结:

  • Ansible 有一个特殊模块,用于把本地文件拷贝到远程服务器,这个模块(copy)。

  • copy 模块有两个常用的参数:(src)、(dest)。

5. fetch 模块

作用: fetch 模块与 copy 模块类似,但作用相反。用于把远程机器的文件拷贝到本地,类似于收作业!!! 参考:https://docs.ansible.com/ansible/latest/modules/fetch_module.html#fetch-module

  • 注意: fetch 模块不能从远程拷贝目录到本地。

第 1 步:在两台被管理机上分别创建一个同名文件(但内容不同)

agent1# echo agent1 > /tmp/1.txt  
agent2# echo agent2 > /tmp/1.txt 

第 2 步:从 master 上 fetch 文件 (因为 group1 里有 2 台机器,为了避免同名文件文件冲突,它使用了不同的目录)

master# ansible group1 -m fetch -a 'src=/tmp/1.txt dest=/tmp/'  
192.168.88.102 | CHANGED => {  
    "changed": true,  
    "checksum": "d2911a028d3fcdf775a4e26c0b9c9d981551ae41",  
    "dest": "/tmp/192.168.88.102/tmp/1.txt",  
    "md5sum": "0d59da0b2723eb03ecfbbb0d779e6eca5",  
    "remote checksum": "d2911a028d3fcdf775a4e26c0b9c9d981551ae41",  
    "remote_md5sum": null  
}  
192.168.88.103 | CHANGED => {  
    "changed": true,  
    "checksum": "b27fb3c4285612643593d53045035bd8d972c995",  
    "dest": "/tmp/192.168.88.103/tmp/1.txt",  
    "md5sum": "cd0bd22f33d6324908dbadf6bc128f52",  
    "remote checksum": "b27fb3c4285612643593d53045035bd8d972c995",  
    "remote_md5sum": null  
} 

安装 tree:

# yum install tree -y
# 以大树结构显示文件信息(包含多层)
# tree /tmp

小结:

  • fetch 功能与 copy 功能正好相反,负责从远程服务器收集文件到本地。

  • fetch 里面一共有两个参数,srcdest

6. user 模块

作用: user 模块用于管理用户账号和用户属性。 参考:https://docs.ansible.com/ansible/latest/modules/user_module.html#user-module state 参数有两个值:① present 创建 ② absent 删除

案例 1:创建 aaa 用户,默认为普通用户,创建家目录

master# ansible group1 -m user -a 'name=aaa state=present' 

案例 2:创建 bbb 系统用户,并且登录 shell 环境为 /sbin/nologin

master# ansible group1 -m user -a 'name=bbb state=present system=yes shell="/sbin/nologin"' 

案例 3:创建 ccc 用户,使用 uid 参数指定 uid, 使用 password 参数传密码

master# echo 123456 | openssl passwd -1 -stdin
$1$Dpcyhw2G$Kb/y1f.7yLI4MpRlHU9oq0
  • passwd 可以使用 -1 选项,代表 lock,锁定账号,不允许登录系统。

  • openssl 用于生成散列密码,openssl passwd 生成密码并交给 standard 标准输入,openssl passwd -1 相当于 md5 算法。

下一句命令注意一下格式,密码要用双引号引起来,单引号的话验证时会密码不正确

master# ansible group1 -m user -a 'name=ccc uid=2000 state=present password="$1$Dpcyhw2G$Kb/y1f.1yLI4MpRlHU9oq0"' 

案例 4:创建一个普通用户叫 hadoop,并产生空密码密钥对

master# ansible group1 -m user -a 'name=hadoop generate_ssh_key=yes' 

案例 5:删除 aaa 用户,但家目录默认没有删除

master# ansible group1 -m user -a 'name=aaa state=absent' 

案例 6:删除 bbb 用户,使用 remove=yes 参数让其删除用户的同时也删除家目录

master# ansible group1 -m user -a 'name=bbb state=absent remove=yes' 

小结:

  • user 模块作用:创建用户 和 删除用户。

  • 创建用户(state=present)和删除用户(state=absent)。

7. group 模块

作用: group 模块用于管理用户组和用户组属性。 参考:https://docs.ansible.com/ansible/latest/modules/group_module.html#group-module

创建组

master# ansible group1 -m group -a 'name=yunwei gid=3000 state=present' 

删除组(如果有用户的 gid 为此组,则删除不了)

master# ansible group1 -m group -a 'name=yunwei state=absent' 

小结:

  • group 模块比较简单,主要用于(创建用户组)和(删除用户组)。

8. cron 模块

聊聊计划任务(定时器) => (重点)

# crontab -l 查看定时器
# crontab -e 编辑定时器

格式: 分 时 日 月 周 要执行命令的绝对路径

*/1 * * * * /usr/sbin/ntpdate -u ntp4.aliyun.com 
  • */1:每分钟

  • 如果不知道某个命令的绝对路径 => which 命令

  • 每天凌晨 4 点执行某个命令 => 0 4 * * * 命令

  • 每 10 分钟执行某个命令 => */10 * * * * 命令

  • 每周 3 的凌晨 2 点执行某个命令 => 0 2 * * 3 命令

图解:

命令
分钟取值范围 (0-59) 小时取值范围 (0-23) 天取值范围 (1-31) 月取值范围 (1-12) 星期取值范围 (0-7) 需要执行的命令

常见格式:

  • 星号(*): 代表所有可能的值,例如 month 字段如果是星号,则表示在满足其它字段的制约条件后每月都执行该命令操作。

  • 逗号(,): 可以用逗号隔开的值指定一个列表范围,例如,"1,2,5,7,8,9"。

  • 中杠(-): 可以用整数之间的中杠表示一个整数范围,例如 "2-6" 表示 "2,3,4,5,6"。

  • 正斜线(/): 可以用正斜线指定时间的间隔频率,例如 "0-23/2" 表示每两小时执行一次。同时正斜线可以和星号一起使用,例如 */10,如果用在 minute 字段,表示每十分钟执行一次。

时间案例:

  • 0 3 * * * 每小时 03 分执行

  • 0 1 0 2 * * 每天两点 01 分执行 (注:原文格式有误,标准 crontab 为 5 列,此处应为 0 2 * * * 或类似,保留原文意图修正为 0 2 * * *)

  • 01 02 3 * * 每个月 3 号 2 点 01 分执行

  • 01 02 3 1 * 每年 1 月 3 号 2 点 01 分执行

  • 0 1 0 2 * 0 每周日的 2 点 01 分执行 (注:标准 crontab 为 5 列,此处修正为 0 2 * * 0)

  • 01,02,03 * * * * 每小时 01 02 03 分执行

  • 01,02,03 1,2,3 * * * 每天 1 2 3 点的 01 02 03 分执行

  • */10 * * * * 每隔 10 分执行

  • 0 0 */2 * * 每隔两小时执行

  • 0 0 * */2 * 每隔两天执行

  • 2 8-20/3 * * * 从 8 点开始到 20 点结束,每隔 3 个小时的 02 分执行一次

案例:每分钟创建一个文件 /root/file_202404171733.txt

# crontab -e
* * * * * touch /root/file_$(date +\%Y\%m\%d\%H\%M).txt 

案例:针对 /tmp 目录定时压缩 => file_202404171733.tar.gz

# crontab -e
* * * * /usr/bin/tar -zcf file_$(date +\%Y\%m\%d\%H\%M).tar.gz /tmp 
  • 问题: 在 crontab 中如果出现了 % 百分号,默认无法执行。

  • 解决: 添加反斜杠转义 \%

  • 注意: 如果 crontab 没有执行成功,可以通过 cat /var/log/cron 日志文件。

cron 模块用于管理周期性时间任务 参考:https://docs.ansible.com/ansible/latest/modules/cron_module.html#cron-module 创建一个 cron 任务,不指定 user 的话,默认就是 root(因为我这里是用 root 操作的)。 如果 minute, hour, day, month, week 不指定的话,默认都为 *

master# ansible group1 -m cron -a 'name="test cron1" user=root job="touch /tmp/111" minute=*/2' 

删除 cron 任务

master# ansible group1 -m cron -a 'name="test cron1" state=absent' 

补充面试题:crond 如何备份以及如何查看 crond 执行日志?

  • 答:备份目录,/var/spool/cron,每个用户在这个目录下有一个对应文件。

  • 日志文件,/var/log/cron 代表日志文件。

  • 所谓的计划任务备份就是把 /var/spool/cron 进行整体备份即可!!!

小结:

  • cron 本身比较重要,叫做计划任务(定时器) => ① 创建计划任务 ② 如何查看 ③ 如何查日志 ④ 如何备份。

  • Ansible 可以通过 cron 模块创建或者移除计划任务!

9. yum/dnf 模块(重点)

yum 模块用于使用 yum 命令来实现软件包的安装与卸载。 参考:https://docs.ansible.com/ansible/latest/modules/yum_module.html#yum-module

使用 yum 安装一个软件(前提:group1 的机器上的 yum 配置都已经 OK)

master# ansible group1 -m yum -a 'name=vsftpd state=present' 
# 或 
master# ansible group1 -m dnf -a 'name=vsftpd state=present'

使用 yum 安装 httpd, httpd-devel 软件,state=latest 表示安装最新版本

master# ansible group1 -m yum -a 'name=httpd,httpd-devel state=latest' 
# 或  
master# ansible group1 -m dnf -a 'name=httpd,httpd-devel state=latest'

使用 yum 卸载 httpd, httpd-devel 软件

master# ansible group1 -m yum -a 'name=httpd,httpd-devel state=absent' 
# 或 
master# ansible group1 -m dnf -a 'name=httpd,httpd-devel state=absent'

10. service 模块(重点)

作用: service 模块用于控制服务的启动,关闭,开机自启动等。 参考:https://docs.ansible.com/ansible/latest/modules/service_module.html#service-module

启动 vsftpd 服务,并设为开机自动启动

master# ansible group1 -m service -a 'name=httpd state=started enabled=true' 
  • state 服务管理可以选参数:"reloaded"、"restarted"、"started"、"stopped"。

关闭 vsftpd 服务,并设为开机不自动启动

master# ansible group1 -m service -a 'name=httpd state=stopped enabled=false' 

小结:

  • yum/dnf 模块:主要负责(安装或卸载软件)。

  • service 模块:主要负责(服务的管理、启动、停止、重启、开机自启、开机不自启)。

11. script 模块

作用: script 模块用于在远程机器上执行 master 本地脚本。 参考:https://docs.ansible.com/ansible/latest/modules/script_module.html#script-module

在 master 上准备一个脚本

master# vim /tmp/1.sh 
#!/bin/bash 
mkdir /export/data -p 
touch /export/data/file{1..9} 

在 group1 的远程机器里都执行 master 上的 /tmp/1.sh 脚本(此脚本不用给执行权限)

master# ansible group1 -m script -a '/tmp/1.sh' 

适用场景: 对 Shell 脚本比较熟悉,然后希望通过 Shell 脚本完成所有机器的环境部署!

12. command 与 shell 模块

两个模块都是用于执行 Linux 命令的,这对于命令熟悉的工程师来说,用起来非常 high。 shell 模块与 command 模块差不多(command 模块不能执行一些类似 $HOME, >, <, | 等符号,但 shell 可以)。

shell 模块(重点) 参考:

master# ansible -m command group1 -a "useradd user2"
master# ansible -m command group1 -a "id user2"
master# ansible -m command group1 -a "cat /etc/passwd |wc -l"
master# ansible -m shell group1 -a "cat /etc/passwd |wc -l"
master# ansible -m command group1 -a "cd $HOME;pwd" # 失败,echo 可以输出,无法切换
master# ansible -m shell group1 -a "cd $HOME;pwd" # 成功,echo 可以输出,可以切换

回顾:wc 命令

  • wc => wordcount 统计

  • wc -c 字节数 (统计大小)

  • wc -m 字符数 (统计字符数量)

  • wc -l 统计总行数 => l == line(行)

  • wc -w 统计单词数

小结:shell 与 command

  • 都能完成 shell 命令,shell 模块会更强大一些,支持管道以及特殊符号!

  • 注意: shell 模块并不是百分之百任何命令都可以,比如 vimll 别名就不可以。不建议大家去记忆哪些命令不可以,大家只要养成任何在生产环境里的命令都要先在测试环境里测试一下的习惯就好。

四、Ansible Playbook(重点)

作用: Playbook(剧本),主要用于实现一些较为复杂的自动化部署操作。软件安装配置,如 Keepalived、MySQL 集群、Redis 集群、大数据集群、K8S 集群。

1. Playbook 概述

2. YAML 格式

  • .yaml.yml 结尾。

  • 文件的第一行以 --- 开始,表明 YAML 文件的开始 (可选的)。

  • # 号开头为注释。

  • 列表中的所有成员都开始于相同的缩进级别,并使用一个 - 作为开头 (一个横杠和一个空格)。

  • 一个字典是由一个简单的 键(key): 值(value)的形式组成 (这个冒号后面必须是一个空格)。

  • 注意: 写这种文件不要使用 Tab 键,都使用空格。

  • 参考:https://docs.ansible.com/ansible/latest/reference_appendices/YAMLSyntax.html#yaml-syntax

看一个官方案例:

# 一位职工记录
name: Example Developer  
job: Developer  
skill: Elite  
employed: True  
foods:  
  - Apple  
  - Orange  
  - Strawberry  
  - Mango  
languages: 
  ruby: Elite  
  python: Elite  
  dotnet: Lame 

3. Playbook 入门案例

参考:https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_intro.html#playbook-syntax 先直接来看一个实例,在 Linux 系统中安装 Apache 软件:① yum 安装软件 ② 编辑 httpd.conf 配置文件。

第 1 步:创建一个存放 playbook 的目录 (路径自定义)

master# mkdir /etc/ansible/playbook -p 

第 2 步:准备 httpd 配置文件,并修改成你想要的配置

master# yum install httpd -y 

按需要修改你想要的配置 (为了测试可以随意改动标记一下)

master# vim /etc/httpd/conf/httpd.conf 

第 3 步:写一个 playbook 文件 (后缀为.yml 或.yaml)

# vim /etc/ansible/playbook/example.yaml   
---   
- hosts: group1 
  remote_user: root 
  tasks: 
    - name: ensure apache is at the latest version 
      yum: 
        name: httpd,httpd-devel 
        state: latest   
    - name: write the apache config file 
      copy: 
        src: /etc/httpd/conf/httpd.conf 
        dest: /etc/httpd/conf/httpd.conf 
      notify: 
        - restart apache   
    - name: ensure apache is running (and enable it at boot) 
      service: 
        name: httpd 
        state: started 
        enabled: yes   
  handlers: 
    - name: restart apache 
      service: 
        name: httpd 
        state: restarted 

注意:task 任务格式

  • - name:任务执行时,打印的文本信息,可以是英文也可以是中文(类似注释,但是会打印输出)。

  • - ansible 模块,如 filecopyyum

  • 参数

  • notify: 与 ansible 模块是同级关系。

    • - restart apache,这个地方的名称与 handlers:name 标签同名,代表调用 handlers 中定义好的任务操作。

第 4 步:执行写好的 playbook 会显示出执行的过程,并且执行的每一步都有 ok, changed, failed 等标识。

  • 执行如果有错误 (failed) 会停止执行该主机后续任务(修正: Ansible 默认不支持自动回滚,需解决问题后,直接再执行这条命令即可,并会把 failed 改为 changed,体现幂等性)。

  • 解决问题后,直接再执行这条命令即可。

ansible-playbook /etc/ansible/playbook/example.yaml 

小结:

  • Playbook 剧本就是按照配置的 Task 任务流程,按顺序执行。

  • 在工作中,主要编写 Task 任务,一定要完全按照 Task 任务格式,具体每个模块如何使用,可以参考官网文档。

4. Playbook 常见语法

  • hosts: 用于指定要执行任务的主机,其可以是一个或多个由冒号分隔主机组,一般可以是具体组名也可以是 all

  • remote_user: 用于指定远程主机上的执行任务的用户。

- hosts: group1
  remote_user: root 
  • tasks: 任务列表,按顺序执行任务。

  • 如果一个 host 执行 task 失败,该主机后续 tasks 都会停止执行,修正 playbook 中的错误,然后重新执行即可。

tasks:  
  - name: ensure apache is at the latest version  
    yum: 
      name: httpd,httpd-devel 
      state: latest  
  - name: write the apache config file  
    copy: 
      src: /etc/httpd/conf/httpd.conf 
      dest: /etc/httpd/conf/httpd.conf 
  • handlers: 类似 task,但需要使用 notify 通知调用。

  • 注意: 不管有多少个通知者进行了 notify,等到 play 中的所有 task 执行完成之后,handlers 也只会被执行一次。

  • handlers 最佳的应用场景是用来重启服务,或者触发系统重启操作。除此以外很少用到了。

notify: 
  - restart apache 
- name: ensure apache is running (and enable it at boot) 
  service: 
    name: httpd 
    state: started 
    enabled: yes 
handlers: 
  - name: restart apache 
    service: 
      name: httpd 
      state: restarted 
  • 英文单词 => variables: 变量 => playbook 中简化了。

    1. 定义变量:

      vars: 
        user: test1
    2. 调用变量: {{ 变量名称 }}

    3. 定义变量可以被多次方便调用。

master# vim /etc/ansible/playbook/example2.yaml  
---  
- hosts: group1  
  remote_user: root  
  vars:  
    user: test1  
  tasks:  
    - name: create user  
      user:
        name: "{{ user }}" 
        state: present 

执行 Ansible Playbook

  • 语法检查,没有真正执行:

    master# ansible-playbook /etc/ansible/playbook/example2.yaml --syntax-check  
  • 执行 Playbook 脚本:

    master# ansible-playbook /etc/ansible/playbook/example2.yaml

5. Playbook 案例

写一个 playbook 实现: a. 配置 yum b. 安装 vsftpd 包 => FTP 服务 => 21 号端口 => 专门用于上传和下载的 c. 修改配置文件 (要求拒绝匿名用户登录) d. 启动服务并实现 vsftpd 服务开机自动启动

- hosts: group1
  remote_user: root
  tasks:
    - name: ensure vsftpd is at the latest version
      yum: 
        name: vsftpd 
        state: latest
    - name: write the apache config file
      copy: 
        src: /etc/vsftpd/vsftpd.conf 
        dest: /etc/vsftpd/vsftpd.conf
      notify:
        - restart vsftpd
    - name: ensure vsftpd is running (and enable it at boot)
      service: 
        name: vsftpd 
        state: started 
        enabled: yes
  handlers:
    - name: restart vsftpd
      service: 
        name: vsftpd 
        state: restarted 

扩展:循环结构

- name: write the apache config file
  copy:
    src: "{{ item.src }}" 
    dest: "{{ item.dest }}" 
  loop:
    - { src: "/etc/vsftpd/vsftpd.conf", dest: "/etc/vsftpd/vsftpd.conf" }
    - { src: "/etc/vsftpd/ftpusers", dest: "/etc/vsftpd/ftpusers" }
    - { src: "/etc/vsftpd/user_list", dest: "/etc/vsftpd/user_list" } 

扩展:FTP 软件的使用 => FTP 服务

  • FTP 服务:文件传输协议,在实际工作中主要上传下载服务,默认端口 21。

  • 怎么样允许用户连接呢?

第一步: 在连接的机器上创建一个普通账号,如 itheima,密码:123456

# useradd itheima 
# passwd itheima 

设置密码为 123456。

第二步: 找到如下两个文件 去掉 root 账号(否则 root 无法登录)。

第三步: 使用课件中提供的 FlashFXP 连接 FTP 服务,如下图所示:

image

案例:编排 NFS 搭建与客户端挂载 回顾: NFS(Network File System)网络文件系统,在实际工作中,主要用于实现海量数据存储。 在 NiuShop 电子商城项目中,我们可以基于 NFS/OSS 实现静态文件存储 => 商品图片(海报、广告图)、文件、音频、视频。 NiuShop 商城源码(服务器 1) => 挂载 NFS => (服务器 2)文件服务器。

工作职责:

  1. 负责整个项目环境搭建。

  2. 基于 NFS 实现海量数据存储。

  3. 基于 Redis 实现数据缓存。

  4. 基于 GTID 全局事务实现主从架构设计。

  5. 基于 Prometheus + Grafana 实现监控报警。

1. 在 master 上准备 nfs 配置文件

# vim /etc/exports
/share *(rw)

说明:

  • /etc/exports: NFS 默认配置文件。

  • 共享目录 * (rw)

  • * 代表任意主机均可挂载访问 /share 目录。

  • (ro)/(rw): read only 只读 / read write 可读可写。

2. 编写 yaml 编排文件

- hosts: 192.168.88.102  
  remote_user: root  
  tasks:  
    - name: 安装 nfs 服务相关软件包  
      yum: 
        name: nfs-utils,rpcbind,setup 
        state: latest  
    - name: 创建共享目录  
      file: 
        path: /share 
        state: directory  
    - name: 同步 nfs 配置文件  
      copy: 
        src: /etc/exports 
        dest: /etc/exports  
      notify: restart nfs  
    - name: 启动 rpcbind 服务,并设置为开机自启动  
      service: 
        name: rpcbind 
        state: started 
        enabled: on  
    - name: 启动 nfs 服务,并设置为开机自启动  
      service: 
        name: nfs-server 
        state: started 
        enabled: on  
  handlers:  
    - name: restart nfs  
      service: 
        name: nfs-server 
        state: restarted  
- hosts: 192.168.88.103
  remote_user: root  
  tasks:  
    - name: 安装 nfs 客户端软件包  
      yum: 
        name: nfs-utils 
        state: latest  
    - name: 挂载 nfs 服务器的共享  
      shell: mount 192.168.88.102:/share /mnt
  • rpc 一种网络协议,数据通信都需要依靠 rpc;nfs 本质就是一个 rpc 服务,所以安装使用 nfs 之前都需要安装 rpcbind。

3. 执行 playbook

ansible-playbook /etc/ansible/playbook/nfs.yaml 

小结:

  • NFS 全称:Network File System,网络文件系统,文件共享。

  • 服务器端必装软件:(nfs-utils)和(rpcbind)。

  • 注意:NFS 服务全名(nfs-server)。

五、Ansible Roles(仅供了解)

作用: 把变量、任务、handlers 分别作为角色,按文件夹来实现分离编写过程!

1. roles 介绍

roles(角色): 就是通过分别将 variables, tasks 及 handlers 等放置于单独的目录中,并可以便捷地调用它们的一种机制。 假设我们要写一个 playbook 来安装管理 lamp 环境,那么这个 playbook 就会写很长。所以我们希望把这个很大的文件分成多个功能拆分,分成 apache 管理,php 管理,mysql 管理,然后在需要使用的时候直接调用就可以了,以免重复写。就类似编程里的模块化的概念,以达到代码复用的效果。

2. 创建 roles 的目录结构

  • files:用来存放由 copy 模块或 script 模块调用的文件。

  • tasks:至少有一个 main.yml 文件,定义各 tasks。

  • handlers: 有一个 main.yml 文件,定义各 handlers。

  • templates:用来存放 Jinja2 模板。

  • vars:有一个 main.yml 文件,定义变量。

  • meta:有一个 main.yml 文件,定义此角色的特殊设定及其依赖关系,类似说明书。

  • 注意: 在每个角色的目录中分别创建 files, tasks, handlers, templates, varsmeta 目录,用不到的目录可以创建为空目录。

3. 通过 roles 实现 lamp

需定制三个角色:httpd, mysql, php

第 1 步:创建 roles 目录及文件,并确认目录结构

master# cd /etc/ansible/roles/  
master# mkdir -p {httpd,mysql,php}/{files,tasks,handlers,templates,vars,meta}  
master# touch {httpd,mysql,php}/{tasks,handlers,vars,meta}/main.yml  
master# yum install tree -y  
master# tree /etc/ansible/roles/  
/etc/ansible/roles/  
├── httpd  
│   ├── files  
│   ├── handlers  
│   │   └── main.yml  
│   ├── meta  
│   │   └── main.yml  
│   ├── tasks  
│   │   └── main.yml  
│   ├── templates  
│   └── vars  
│       └── main.yml  
├── mysql  
│   ├── files  
│   ├── handlers  
│   │   └── main.yml  
│   ├── meta  
│   │   └── main.yml  
│   ├── tasks  
│   │   └── main.yml  
│   ├── templates  
│   └── vars  
│       └── main.yml  
└── php  
    ├── files  
    ├── handlers  
    │   └── main.yml  
    ├── meta  
    │   └── main.yml  
    ├── tasks  
    │   └── main.yml  
    ├── templates  
    └── vars  
        └── main.yml 

第 2 步:准备 httpd 服务器的主页文件,php 测试页和配置文件等

master# echo "test main page" > /etc/ansible/roles/httpd/files/index.html  
master# echo -e "<?php\nphpinfo();\n?>" > /etc/ansible/roles/httpd/files/test.php  
master# yum install httpd -y  
# 按需求修改配置文件后,拷贝到 httpd 角色目录里的 files 子目录  
master# vim /etc/httpd/conf/httpd.conf  
master# cp /etc/httpd/conf/httpd.conf /etc/ansible/roles/httpd/files/

第 3 步:编写 httpd 角色的 main.yml 文件

master# vim /etc/ansible/roles/httpd/tasks/main.yml   
---   
- name: 安装 httpd 
  yum:
    name: httpd,httpd-devel 
    state: present   
- name:同步 httpd 配置文件 
  copy:
    src: /etc/ansible/roles/httpd/files/httpd.conf 
    dest: /etc/httpd/conf/httpd.conf 
  notify: restart httpd   
- name:同步主页文件 
  copy:
    src: /etc/ansible/roles/httpd/files/index.html 
    dest: /var/www/html/index.html   
- name:同步 php 测试页 
  copy:
    src: /etc/ansible/roles/httpd/files/test.php 
    dest: /var/www/html/test.php   
- name:启动 httpd 并开机自启动 
  service:
    name: httpd 
    state: started 
    enabled: yes

第 4 步:编写 httpd 角色里的 handler

master# vim /etc/ansible/roles/httpd/handlers/main.yml   
---   
- name: restart httpd 
  service:
    name: httpd 
    state: restarted 

第 5 步:编写 mysql 角色的 main.yml 文件

master# vim /etc/ansible/roles/mysql/tasks/main.yml   
---   
- name: 安装 mysql 
  yum:
    name: mariadb,mariadb-server 
    state: present   
- name: 启动 mysql 并开机自启动 
  service:
    name: mariadb 
    state: started 
    enabled: yes

第 6 步:编写 php 角色的 main.yml 文件

master# vim /etc/ansible/roles/php/tasks/main.yml   
---   
- name: 安装 php 及依赖包 
  yum:
    name: php,php-gd,php-ldap,php-odbc,php-pear,php-xml,php-mbstring,php-snmp,php-soap,curl,curl-devel,php-bcmath,php-mysqlnd 
    state: present 
  notify: restart httpd

第 7 步:编写 lamp 的 playbook 文件调用前面定义好的三个角色

master# vim /etc/ansible/playbook/lamp.yaml  
---  
- hosts: group1  
  remote_user: root  
  roles:  
    - httpd  
    - mysql  
    - php 

第 8 步:执行 lamp 的 playbook 文件

master# ansible-playbook /etc/ansible/playbook/lamp.yaml 

小结:

  • Ansible Roles:把大的 yaml 文件拆解为若干个小的 main.yml 文件。

  • 好处:方便调试,每个文件都比较小。

  • 缺点:文件夹太多,过于冗余。

常见问题:

  • 问题 1: YAML 语法有错误,千万不要按 Tab 键,如果出错了,可以借助于 YAML 校检工具 https://www.bejson.com/validators/yaml_editor/index.html

  • 问题 2: Roles 脚本是一个整体,不要单独执行某个 yaml 文件应该统一执行最终的 yaml,如 lamp.yaml。单独执行某个 main.yml,报错信息如下:

    [root@node1 roles]# ansible-playbook /etc/ansible/roles/mysql/tasks/main.yml  
    ERROR! 'yum' is not a valid attribute for a Play  
    The error appears to be in '/etc/ansible/roles/mysql/tasks/main.yml': line 3, column 3, but may be elsewhere in the file depending on the exact syntax problem.  
    The offending line appears to be:  
    # 编写 Ansible Playbook 文件,用于安装 mariadb-server 与 mariadb,安装完成后,启动 mariadb-server 服务 
    - name: 1. 安装 MariaDB 
      ^ here

    以上其实不是错误,而是因为 main.yml 文件不完整导致的!

拓展案例:通过 roles 实现 lamp 并安装 discuz

作用: 基于 Ansible Roles 搭建 Web 项目(BBS 论坛为例) => 国内做的最好的 BBS 论坛 => Discuz。

第 1 步:创建 roles 目录及文件,并确认目录结构

master# cd /etc/ansible/roles/  
master# mkdir -p {httpd,mysql,php}/{files,tasks,handlers,templates,vars,meta}  
master# touch {httpd,mysql,php}/{tasks,handlers,vars,meta}/main.yml 

第 2 步:准备 httpd 相关文件

master# cp /etc/httpd/conf/httpd.conf /etc/ansible/roles/httpd/files/  
master# ls /etc/ansible/roles/httpd/files/  
Discuz_X3.5_SC_UTF8.zip Discuz 相关软件包  
httpd.conf 配置好的 httpd.conf 配置文件

第 3 步:编写 httpd 角色的 main.yml 文件

master# vim /etc/ansible/roles/httpd/tasks/main.yml   
- name: 安装 httpd 相关软件包 
  yum:
    name: httpd,httpd-devel 
    state: latest   
- name:同步配置文件 
  copy:
    src: /etc/ansible/roles/httpd/files/httpd.conf 
    dest: /etc/httpd/conf/httpd.conf 
  notify: restart httpd   
- name:拷贝 discuz 压缩包 
  copy:
    src: /etc/ansible/roles/httpd/files/Discuz_X3.5_SC_UTF8.zip 
    dest: /tmp/   
- name:解压并 mv 网站文件到 httpd 家目录 
  shell: rm -rf /var/www/html/* && rm -rf /test/ && mkdir -p /test/ && unzip /tmp/Discuz_X3.5_SC_UTF8.zip -d /test/ && mv /test/upload/* /var/www/html/ && chown -R apache.apache /var/www/html/   
  # 上面的命令有点多,可以写成脚本,然后使用 script 模块来调用执行   
- name:启动 httpd 并开机自启动 
  service:
    name: httpd 
    state: started 
    enabled: on

第 4 步:编写 httpd 角色里的 handler

master# vim /etc/ansible/roles/httpd/handlers/main.yml   
---   
- name: restart httpd 
  service:
    name: httpd 
    state: restarted 

第 5 步:编写 mysql 角色的 main.yml 文件

master# vim /etc/ansible/roles/mysql/tasks/main.yml 
- name: 安装 mariadb 相关软件包
  yum: 
    name: mariadb-server,mariadb 
    state: latest
​
- name: 启动 mariadb 服务并设置开机自启动  
  service: 
    name: mariadb 
    state: started 
    enabled: on
​
- name: 执行建库脚本  
  script: /etc/ansible/roles/mysql/files/create.sh

第 6 步:编写 mysql 的建库脚本

master# vim /etc/ansible/roles/mysql/files/create.sh   
#!/bin/bash   
mysql << EOF   
create database if not exists discuz default charset=utf8; 
grant all on discuz.* to 'discuz'@'localhost' identified by '123'; 
flush privileges; 
EOF 

第 7 步:编写 php 角色的 main.yml 文件

master# vim /etc/ansible/roles/php/tasks/main.yml   
---   
- name: 安装 php 及依赖包 
  yum:
    name: php,php-gd,php-ldap,php-odbc,php-pear,php-xml,php-mbstring,php-snmp,php-soap,curl,curl-devel,php-bcmath,php-mysqlnd 
    state: present 
  notify: restart httpd

第 8 步:编写 lamp 的 playbook 文件调用前面定义好的三个角色

master# vim /etc/ansible/playbook/discuz.yaml  
---  
- hosts: group1  
  remote_user: root  
  roles:  
    - httpd  
    - mysql  
    - php 

第 9 步:执行 lamp 的 playbook 文件

master# ansible-playbook /etc/ansible/playbook/discuz.yaml 
Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐