认识自动化运维
问题:
假设我要去1000台服务上做一个操作, 下面两种方法缺点明显:
1、按传统的方法, 一台连着一台服务器的ssh上去手动操作。
缺点: 效率太低。
2、写个shell脚本来做。
缺点:
- 管理的机器平台不一致,脚本可能不具备通用性。
- 传密码麻烦(在非免密登录的环境下, 需要expect来传密码)
- 效率较低,循环1000次也需要一个一个的完成,如果用&符放到后台执行,则会产生1000个进程。
自动化运维: 将日常IT运维中大量的重复性工作,小到简单的日常检查、配置变更和软件安装,大到整个变更流程的组织调度,由过去的手工执行转为自动化操作,从而减少乃至消除运维中的延迟,实现“零延时”的IT运维。
自动化运维主要关注的方面
假如管理很多台服务器,主要关注以下几个方面:
1、管理机与被管理机的连接(管理机如何将管理指令发送给被管理机)
2、服务器信息收集 (如果被管理的服务器有centos7.5外还有其它linux发行版,如suse,ubuntu等。当你要做的事情在不同OS上有所不同,你需要收集信息,并将其分开处理)
3、服务器分组(因为有些时候我要做的事情不是针对所有服务器,可能只针对某一个分组)
4、管理内容的主要分类
- 文件目录管理(包括文件的创建,删除,修改,查看状态,远程拷贝等)
- 用户和组管理
- cron时间任务管理
- yum源配置与通过yum管理软件包
- 服务管理
- 远程执行脚本
- 远程执行命令
常见的开源自动化运维工具比较
1、puppet(拓展)
基于ruby语言,成熟稳定。适合于大型架构,相对于ansible和saltstack会复杂些。
2、saltstack(拓展)
基于python语言。走的是zeromq协议,相对简单,大并发能力比ansible要好, 需要维护被管理端的服务。如果服务断开,连接就会出问题。
3、ansible
基于python语言。简单快捷,被管理端不需要开启服务。直接走ssh协议,需要验证所以机器多的话速度会较慢。
ansible
ansible是一种由Python开发的自动化运维工具,集合了众多运维工具的优点,实现了批量系统配置、批量程序部署、批量运行命令等功能。
特点:
- 部署简单
- 默认使用ssh进行管理,基于python里的paramiko模块开发
- 管理端和被管理端不需要启动服务
- 配置简单,功能强大,扩展性强
- 能过playbook(剧本)进行多个任务的编排
ansible环境搭建
实验准备
三台机器,一台管理,两台被管理
1、静态ip
2、主机名及主机名互相绑定
3、关闭防火墙, selinux
4、时间同步
5、确认和配置yum源(需要epel源)
配置阿里云源
mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup
wget -O /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo
yum clean all
yum makecache
[root@master ~]# cd /etc/yum.repos.d/
[root@master yum.repos.d]# ls
bsk CentOS-Base.repo dvd.repo
配置epel源(官方源的扩展)
yum install epel-release -y
[root@master ~]# cd /etc/yum.repos.d/
[root@master yum.repos.d]# ls
bsk CentOS-Base.repo dvd.repo epel.repo epel-testing.repo
实验过程
第1步: 管理机上安装ansible(需要epel源),被管理机必须打开ssh服务
[root@master ~]# yum install epel-release -y
[root@master ~]# yum install ansible -y
[root@master ~]# ansible --version
ansible 2.9.27
config file = /etc/ansible/ansible.cfg
configured module search path = [u'/root/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
ansible python module location = /usr/lib/python2.7/site-packages/ansible
executable location = /usr/bin/ansible
python version = 2.7.5 (default, Oct 14 2020, 14:45:30) [GCC 4.8.5 20150623 (Red Hat 4.8.5-44)]
第2步: 实现master对agent的免密登录,只在master上做。(如果这一步不做,则在后面操作agent时都要加-k参数传密码;或者在主机清单里传密码)
[root@master ~]# ssh-keygen
[root@master ~]# ssh-copy-id 192.168.192.145
[root@master ~]# ssh-copy-id 192.168.192.146
第3步: 在master上定义主机组,并测试连接性
[root@master ~]# vim /etc/ansible/hosts
[root@master ~]# tail -3 /etc/ansible/hosts
[group1]
192.168.192.145
192.168.192.146
ping文件/etc/ansible/hosts中定义的组group1
[root@master ~]# ansible -m ping group1
192.168.192.145 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"ping": "pong"
}
192.168.192.146 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"ping": "pong"
}
ping文件/etc/ansible/hosts中定义的所有的组
[root@master ~]# ansible -m ping all
192.168.192.145 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"ping": "pong"
}
192.168.192.146 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"ping": "pong"
}
服务器分组
ansible通过一个主机清单功能来实现服务器分组
ansible的默认主机清单配置文件为/etc/ansible/hosts
示例1:利用机器名字和IP地址分组
[nginx] 组名
apache[1:10].aaa.com 表示apache1.aaa.com到apache10.aaa.com这10台机器
nginx[a:z].aaa.com 表示nginxa.aaa.com到nginxz.aaa.com共26台机器
10.1.1.[11:15] 表示10.1.1.11到10.1.1.15这5台机器
示例2:利用端口号分组(适用于机器的ssh端口号做了更改)
[nginx]
10.1.1.13:2222 表示10.1.1.13这台,但ssh端口为2222
示例3: 定义10.1.1.12:2222这台服务器的别名为nginx1
nginx1 ansible_ssh_host=10.1.1.13 ansible_ssh_port=2222
示例4: 没有做免密登录的服务器可以指定用户名与密码
nginx1 ansible_ssh_host=10.1.1.13 ansible_ssh_port=2222 ansible_ssh_user=root ansible_ssh_pass="123456"
示例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
[nginx]
nginx1
nginx2
小结:
主机清单的作用: 服务器分组。
主机清单的常见功能:
1、可以通过IP范围来分, 主机名名字的范围来分
2、如果ssh端口不是22的,可以传入新的端口。
3、没有做免密登录,可以传密码。
ansible模块
ansible是基于模块工作的,本身没有批量部署的能力。真正具有批量部署的是ansible所运行的模块,ansible只是提供一种框架。
ansible支持的模块非常的多,我们并不需要把每个模块都记住,而只需要熟悉一些常见的模块,其它的模块在需要用到时再查询即可。
查看所有支持的模块
[root@master ~]# ansible-doc -l
[root@master ~]# ansible-doc -l|wc -l
3387
如果要查看ping模块的用法,使用下面命令(其它模块以此类推)
[root@master ~]# ansible-doc ping
ansible基本格式为:
ansible 操作的机器名或组名 -m 模块名 -a "参数1=值1 参数2=值2"
hostname模块
hostname模块用于修改主机名(注意: 它不能修改/etc/hosts文件)
https://docs.ansible.com/ansible/latest/modules/hostname_module.html#hostname-module
将其中一远程机器主机名修改为agent1
[root@master ~]# ansible 192.168.192.145 -m hostname -a "name=agent1"
192.168.192.145 | CHANGED => {
"ansible_facts": {
"ansible_domain": "",
"ansible_fqdn": "agent1",
"ansible_hostname": "agent1",
"ansible_nodename": "agent1",
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"name": "agent1"
}
192.168.192.145主机:
[root@agent1 ~]# hostname
agent1
在生产环境中,修改主机名通过 hostname 修改后还要修改 /etc/hosts 文件,这个文件是用来存放辨别区分主机的主机名/ip信息的。主机名通常在局域网内使用,通过 /etc/hosts 文件,主机名可以被解析到对应IP。域名通常在internet上使用。
比如说在agent1上写下:
[root@agent1 ~]# tail -4 /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
10.1.1.12 agent1
10.1.1.13 agent2
在agent2上写一样的,测试:
[root@agent2 ~]# ping agent1
PING agent1 (10.1.1.12) 56(84) bytes of data.
64 bytes from agent1 (10.1.1.12): icmp_seq=1 ttl=64 time=0.680 ms
64 bytes from agent1 (10.1.1.12): icmp_seq=2 ttl=64 time=1.15 ms
64 bytes from agent1 (10.1.1.12): icmp_seq=3 ttl=64 time=0.454 ms
关于DNS的补充:
域名作为外网的唯一名字,主机名作为内网的名字(可以重名,但不推荐)。现在自行搭建DNS已经几乎不做了,因为外网的DNS,向运营商买域名时候运营商已经给你做了,内网DNS因为ansible的出现和大规模使用,改hosts文件已经很方便了,有了hosts文件来解析主机名和IP的对应关系,就不需要DNS了。
file模块(重点)
file模块用于对文件相关的操作(创建, 删除, 软硬链接等)
https://docs.ansible.com/ansible/latest/modules/file_module.html#file-module
初始状态
[root@agent1 ~]# ll /tmp/
total 0
[root@agent2 ~]# ll /tmp/
total 0
创建一个文件
[root@master ~]# ansible group1 -m file -a "path=/tmp/1.txt state=touch"
192.168.192.145 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"dest": "/tmp/1.txt",
"gid": 0,
"group": "root",
"mode": "0644",
"owner": "root",
"size": 0,
"state": "file",
"uid": 0
}
192.168.192.146 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"dest": "/tmp/1.txt",
"gid": 0,
"group": "root",
"mode": "0644",
"owner": "root",
"size": 0,
"state": "file",
"uid": 0
}
[root@agent1 ~]# ll /tmp/
total 0
-rw-r--r-- 1 root root 0 May 14 11:37 1.txt
[root@agent2 ~]# ll /tmp/
total 0
-rw-r--r-- 1 root root 0 May 14 11:37 1.txt
递归修改owner,group,mode
[root@master ~]# ansible group1 -m file -a "path=/tmp/1.txt owner=ftp group=ftp mode=777"
[root@agent1 ~]# ll /tmp/
total 0
-rwxrwxrwx 1 ftp ftp 0 May 14 11:44 1.txt
[root@agent2 ~]# ll /tmp/
total 0
-rwxrwxrwx 1 ftp ftp 0 May 14 11:44 1.txt
注意:
owner和group必须存在
更改硬链接文件的权限时,需要加state=file,并且互为硬链接文件的权限都会被修改
创建一个目录(递归创建)
[root@master ~]# ansible group1 -m file -a "path=/tmp/aaa/bbb state=directory mode=777"
[root@agent1 ~]# ll /tmp/
total 0
-rwxrwxrwx 1 ftp ftp 0 May 14 11:44 1.txt
drwxrwxrwx 3 root root 17 May 14 11:47 aaa
[root@agent1 ~]# ll /tmp/aaa/
total 0
drwxrwxrwx 2 root root 6 May 14 11:47 bbb
[root@agent2 ~]# ll /tmp/
total 0
-rwxrwxrwx 1 ftp ftp 0 May 14 11:44 1.txt
drwxrwxrwx 3 root root 17 May 14 11:47 aaa
[root@agent2 ~]# ll /tmp/aaa
total 0
drwxrwxrwx 2 root root 6 May 14 11:47 bbb
[root@agent2 ~]# ll -d /tmp/
drwxrwxrwt. 8 root root 117 May 14 11:49 /tmp/
注意:
递归创建目录的同时修改权限,会同时修改父目录和子目录的权限
类似 chmod xxx -R
若是不想递归,加参数 recurse=no
创建软链接文件
[root@master ~]# ansible group1 -m file -a "src=/tmp/1.txt path=/tmp/2.txt state=link"
[root@agent1 ~]# ll /tmp/
total 0
-rwxrwxrwx 1 ftp ftp 0 May 14 11:44 1.txt
lrwxrwxrwx 1 root root 10 May 14 11:56 2.txt -> /tmp/1.txt
drwxrwxrwx 3 root root 17 May 14 11:47 aaa
[root@agent2 ~]# ll /tmp/
total 0
-rwxrwxrwx 1 ftp ftp 0 May 14 11:44 1.txt
lrwxrwxrwx 1 root root 10 May 14 11:56 2.txt -> /tmp/1.txt
drwxrwxrwx 3 root root 17 May 14 11:47 aaa
创建硬链接文件
[root@master ~]# ansible group1 -m file -a "src=/tmp/1.txt path=/tmp/3.txt state=hard"
[root@agent1 ~]# ll -i /tmp/
total 0
17080148 -rwxrwxrwx 2 ftp ftp 0 May 14 11:44 1.txt
16809540 lrwxrwxrwx 1 root root 10 May 14 11:56 2.txt -> /tmp/1.txt
17080148 -rwxrwxrwx 2 ftp ftp 0 May 14 11:44 3.txt
1235279 drwxrwxrwx 3 root root 17 May 14 11:47 aaa
[root@agent2 ~]# ll -i /tmp/
total 0
16777294 -rwxrwxrwx 2 ftp ftp 0 May 14 11:44 1.txt
17473010 lrwxrwxrwx 1 root root 10 May 14 11:56 2.txt -> /tmp/1.txt
16777294 -rwxrwxrwx 2 ftp ftp 0 May 14 11:44 3.txt
34346186 drwxrwxrwx 3 root root 17 May 14 11:47 aaa
注意:
为什么目录 /tmp/aaa 的硬链接数为3?
因为 /tmp/aaa 下有一个隐藏目录 . 指向 /tmp/aaa 当前目录,而其子目录 /tmp/aaa/bbb 下有一个隐藏文件 .. 也指向 /tmp/aaa 上一级目录
所以这三个文件的INODE 相同,互为硬链接文件
[root@agent2 ~]# ll -ia /tmp/aaa
total 0
34346186 drwxrwxrwx 3 root root 17 May 14 11:47 .
16777288 drwxrwxrwt. 8 root root 143 May 14 11:57 ..
50610023 drwxrwxrwx 2 root root 6 May 14 11:47 bbb
[root@agent2 ~]# ll -ia /tmp/aaa/bbb
total 0
50610023 drwxrwxrwx 2 root root 6 May 14 11:47 .
34346186 drwxrwxrwx 3 root root 17 May 14 11:47 ..
更改硬链接文件的权限时,需要加state=file,并且互为硬链接文件的权限都会被修改
[root@master ~]# ansible group1 -m file -a "path=/tmp/3.txt owner=ftp group=ftp mode=644 state=file"
[root@agent1 ~]# ll /tmp/
total 0
-rw-r--r-- 2 ftp ftp 0 May 14 11:44 1.txt
-rw-r--r-- 2 ftp ftp 0 May 14 11:44 3.txt
删除目录
[root@master ~]# ansible group1 -m file -a "path=/tmp/aaa/ state=absent"
[root@agent1 ~]# ll /tmp/
total 0
-rwxrwxrwx 2 ftp ftp 0 May 14 11:44 1.txt
lrwxrwxrwx 1 root root 10 May 14 11:56 2.txt -> /tmp/1.txt
-rwxrwxrwx 2 ftp ftp 0 May 14 11:44 3.txt
[root@agent2 ~]# ll /tmp/
total 0
-rwxrwxrwx 2 ftp ftp 0 May 14 11:44 1.txt
lrwxrwxrwx 1 root root 10 May 14 11:56 2.txt -> /tmp/1.txt
-rwxrwxrwx 2 ftp ftp 0 May 14 11:44 3.txt
注意:
会连同目录里的所有文件一起删除
删除文件
[root@master ~]# ansible group1 -m file -a "path=/tmp/2.txt state=absent"
[root@agent1 ~]# ll /tmp/
total 0
-rwxrwxrwx 2 ftp ftp 0 May 14 11:44 1.txt
-rwxrwxrwx 2 ftp ftp 0 May 14 11:44 3.txt
[root@agent2 ~]# ll /tmp/
total 0
-rwxrwxrwx 2 ftp ftp 0 May 14 11:44 1.txt
-rwxrwxrwx 2 ftp ftp 0 May 14 11:44 3.txt
stat模块(了解)
stat模块类似linux的stat命令,用于获取文件的状态信息。
https://docs.ansible.com/ansible/latest/modules/stat_module.html#stat-module
获取/etc/fstab文件的状态信息
[root@master ~]# ansible group1 -m stat -a 'path=/etc/fstab'
copy模块(重点)
copy模块用于对文件的远程拷贝操作(如把本地的文件拷贝到远程的机器上)
https://docs.ansible.com/ansible/latest/modules/copy_module.html#copy-module
在master上准备一个文件,拷贝此文件到group1的所有机器上
[root@master ~]# cat /tmp/1.txt
hello world
I am linux
[root@master ~]# ansible group1 -m copy -a "src=/tmp/1.txt dest=/tmp/1.txt"
[root@agent1 ~]# ll /tmp/
total 0
[root@agent1 ~]#
[root@agent1 ~]#
[root@agent1 ~]# ll /tmp/
total 4
-rw-r--r-- 1 root root 23 May 14 13:08 1.txt
[root@agent2 ~]# ll /tmp/
total 0
[root@agent2 ~]#
[root@agent2 ~]#
[root@agent2 ~]# ll /tmp/
total 4
-rw-r--r-- 1 root root 23 May 14 13:08 1.txt
[root@agent2 ~]# cat /tmp/1.txt
hello world
I am linux
注意:如果被管理机上已经存在 /tmp/1.txt 文件,再copy时默认会覆盖
[root@master ~]# vim /tmp/1.txt
[root@master ~]# cat /tmp/1.txt
hello world
I am linux
It is a happy day
[root@master ~]# ansible group1 -m copy -a "src=/tmp/1.txt dest=/tmp/1.txt"
[root@agent2 ~]# cat /tmp/1.txt
hello world
I am linux
It is a happy day
使用content参数可以直接往远程文件里写内容(会覆盖原内容)
[root@master ~]# ansible group1 -m copy -a 'cnotallow="ha ha\n" dest=/tmp/1.txt'
[root@agent1 ~]# cat /tmp/1.txt
ha ha
[root@agent2 ~]# cat /tmp/1.txt
ha ha
注意:
ansible中-a后面的参数里也有引号时,记得要单引双引交叉使用,如果都为双引会出现问题
使用force参数控制是否强制覆盖
force=no
如果目标文件已经存在,则不覆盖
[root@master ~]# ansible group1 -m copy -a 'src=/tmp/1.txt dest=/tmp/1.txt force=no'
[root@agent1 ~]# cat /tmp/1.txt
ha ha
如果目标文件已经存在,则会强制覆盖(默认)
[root@master ~]# ansible group1 -m copy -a 'src=/tmp/1.txt dest=/tmp/1.txt force=yes'
[root@agent1 ~]# cat /tmp/1.txt
hello world
I am linux
It is a happy day
使用backup参数控制是否备份文件
backup=yes
如果拷贝的文件内容与原内容不一样,则会备份一份(备份文件命名加上时间)
[root@master ~]# ansible group1 -m copy -a 'src=/tmp/1.txt dest=/tmp/1.txt backup=yes'
[root@agent1 ~]# ll /tmp/
total 8
-rw-r--r-- 1 root root 7 May 14 13:44 1.txt
-rw-r--r-- 1 root root 41 May 14 13:39 1.txt.4962.2022-05-14@13:44:26~
[root@agent1 ~]# cat /tmp/1.txt.4962.2022-05-14@13\:44\:26~
hello world
I am linux
It is a happy day
[root@agent1 ~]# cat /tmp/1.txt
ha ha
copy模块拷贝时要注意拷贝目录后面是否带"/"符号
/etc/yum.repos.d后面不带/符号,则表示把/etc/yum.repos.d整个目录拷贝到/tmp/目录下
[root@master ~]# ansible group1 -m copy -a 'src=/etc/yum.repos.d dest=/tmp/'
[root@agent1 ~]# rm -fr /tmp/*
[root@agent1 ~]# ls /tmp/
yum.repos.d
/etc/yum.repos.d/后面带/符号,则表示把/etc/yum.repos.d/目录里的所有文件拷贝到/tmp/目录下
[root@master ~]# ansible group1 -m copy -a 'src=/etc/yum.repos.d/ dest=/tmp/'
[root@agent1 ~]# rm -fr /tmp/*
[root@agent1 ~]# ls /tmp/
bsk CentOS-Base.repo dvd.repo epel.repo epel-testing.repo
练习: 在master上配置好所有的yum源,然后拷贝到group1的远程机器上(要求目录内的内容完全一致)
[root@master ~]# ansible group1 -m file -a "path=/etc/yum.repos.d/ state=absent"
[root@master ~]# ansible group1 -m copy -a "src=/etc/yum.repos.d dest=/etc/"
练习: 使用hostname模块修改过主机名后,在master上修改/etc/hosts文件,并拷贝到group1的远程机器上
[root@master ~]# ansible 192.168.192.145 -m hostname -a "name=agent1"
[root@master ~]# ansible 192.168.192.146 -m hostname -a "name=agent2"
[root@master ~]# vim /etc/hosts
[root@master ~]# cat /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.192.145 agent1
192.168.192.146 agent2
[root@master ~]# ansible group1 -m copy -a 'src=/etc/hosts dest=/etc/hosts'
[root@agent1 ~]# cat /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.192.145 agent1
192.168.192.146 agent2
[root@agent1 ~]#
[root@agent1 ~]#
[root@agent1 ~]# ping agent2
PING agent2 (192.168.192.146) 56(84) bytes of data.
64 bytes from agent2 (192.168.192.146): icmp_seq=1 ttl=64 time=0.517 ms
64 bytes from agent2 (192.168.192.146): icmp_seq=2 ttl=64 time=1.23 ms
^C
--- agent2 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1005ms
rtt min/avg/max/mdev = 0.517/0.877/1.238/0.361 ms
template模块(拓展)
与copy模块功能几乎一样.
template模块首先使用变量渲染jinja2模板文件成普通文件,然后再复制过去.而copy模块不支持.(jinja2是一个基于python的模板引擎)
https://docs.ansible.com/ansible/latest/modules/template_module.html#template-module
[root@master ~]# ansible -m template group1 -a "src=/etc/hosts dest=/tmp/hosts"
[root@agent1 ~]# rm -fr /tmp/*
[root@agent1 ~]# ls /tmp/
hosts
template模块不能拷贝目录
[root@master ~]# ansible -m template group1 -a "src=/etc/yum.repos.d dest=/etc/yum.repos.d/"
192.168.192.145 | FAILED! => {
"changed": false,
"msg": "could not find src=/etc/yum.repos.d, Could not find or access '/etc/yum.repos.d' on the Ansible Controller.\nIf you are using a module and expect the file to exist on the remote, see the remote_src option"
}
192.168.192.146 | FAILED! => {
"changed": false,
"msg": "could not find src=/etc/yum.repos.d, Could not find or access '/etc/yum.repos.d' on the Ansible Controller.\nIf you are using a module and expect the file to exist on the remote, see the remote_src option"
}
fetch模块
fetch模块与copy模块类似,但作用相反。用于把远程机器的文件拷贝到本地。
https://docs.ansible.com/ansible/latest/modules/fetch_module.html#fetch-module
第1步: 在两台被管理机上分别创建一个同名文件(但内容不同)
[root@agent1 ~]# echo agent1 > /tmp/1.txt
[root@agent2 ~]# echo agent2 > /tmp/1.txt
第2步: 从master上fetch文件
(因为group1里有2台机器,为了避免同名文件文件冲突,它使用了不同的目录)
[root@master ~]# ansible group1 -m fetch -a 'src=/tmp/1.txt dest=/tmp/'
192.168.192.146 | CHANGED => {
"changed": true,
"checksum": "b27fb3c4285612643593d53045035bd8d972c995",
"dest": "/tmp/192.168.192.146/tmp/1.txt", 192.168.192.146的在这里
"md5sum": "cd0bd22f33d6324908dbadf6bc128f52",
"remote_checksum": "b27fb3c4285612643593d53045035bd8d972c995",
"remote_md5sum": null
}
192.168.192.145 | CHANGED => {
"changed": true,
"checksum": "d2911a028d3fcdf775a4e26c0b9c9d981551ae41",
"dest": "/tmp/192.168.192.145/tmp/1.txt", 192.168.192.146的在这里
"md5sum": "0d59da0b2723eb03ecfbb0d779e6eca5",
"remote_checksum": "d2911a028d3fcdf775a4e26c0b9c9d981551ae41",
"remote_md5sum": null
}
[root@master ~]# ll /tmp/
total 4
drwxr-xr-x 3 root root 17 May 14 22:30 192.168.192.145
drwxr-xr-x 3 root root 17 May 14 22:30 192.168.192.146
-rw-r--r-- 1 root root 7 May 14 21:42 1.txt
drwxr-xr-x 2 root root 6 May 14 21:59 aaa
drwxr-xr-x 2 root root 6 May 14 21:59 bbb
[root@master ~]# ll /tmp/192.168.192.145
total 0
drwxr-xr-x 2 root root 19 May 14 22:30 tmp
第3步: 先删除上面fetch过来的, 然后尝试只fetch其中一台机器的,也会使用名称来做子目录区分
[root@master ~]# rm -fr /tmp/*
[root@master ~]#
[root@master ~]# ansible 192.168.192.145 -m fetch -a 'src=/tmp/1.txt dest=/tmp/'
192.168.192.145 | CHANGED => {
"changed": true,
"checksum": "d2911a028d3fcdf775a4e26c0b9c9d981551ae41",
"dest": "/tmp/192.168.192.145/tmp/1.txt", 只fetch一个,也会这样命名
"md5sum": "0d59da0b2723eb03ecfbb0d779e6eca5",
"remote_checksum": "d2911a028d3fcdf775a4e26c0b9c9d981551ae41",
"remote_md5sum": null
}
[root@master ~]# ll /tmp/
total 0
drwxr-xr-x 3 root root 17 May 14 22:34 192.168.192.145
注意:
fetch模块不能从远程拷贝目录到本地
user模块
user模块用于管理用户账号和用户属性。
在Windows平台中对应模块为 win_user
https://docs.ansible.com/ansible/latest/modules/user_module.html#user-module
创建aaa用户,默认为普通用户
[root@master ~]# ansible group1 -m user -a "name=aaa"
[root@agent1 ~]# grep aaa /etc/passwd
aaa:x:1001:1001::/home/aaa:/bin/bash
创建bbb系统用户,家目录为/home/haha,并且登录shell环境为/sbin/nologin
[root@master ~]# ansible group1 -m user -a "name=bbb system=yes home=/home/haha shell=/sbin/nologin"
[root@agent1 ~]# grep bbb /etc/passwd
bbb:x:519:519::/home/haha:/sbin/nologin
创建ccc用户, 使用uid参数指定uid, 使用password参数传密码
[root@master ~]# ansible group1 -m user -a "name=ccc uid=2000 password=123456"
[WARNING]: The input password appears not to have been hashed. The 'password' argument must be encrypted for
this module to work properly.
[root@agent1 ~]# grep ccc /etc/passwd
ccc:x:2000:2000::/home/ccc:/bin/bash
[root@agent1 ~]#
[root@agent1 ~]# grep ccc /etc/shadow
ccc:123456:19126:0:99999:7:::
密码没有复杂度,可配合哈希
[root@master ~]# echo 123456|openssl passwd -1 -stdin
$1$69HJaCjF$7Ux/iGlRKp.Bv0Z6Kw5tb/
[root@master ~]# ansible group1 -m user -a "name=ccc uid=2000 password=$1$69HJaCjF$7Ux/iGlRKp.Bv0Z6Kw5tb/"
[root@agent1 ~]# grep ccc /etc/passwd
ccc:x:2000:2000::/home/ccc:/bin/bash
[root@agent1 ~]#
[root@agent1 ~]# grep ccc /etc/shadow
ccc:9HJaCjFUx/iGlRKp.Bv0Z6Kw5tb/:19126:0:99999:7:::
删除ccc用户,但家目录默认没有删除
[root@master ~]# ansible group1 -m user -a "name=ccc state=absent"
[root@agent1 ~]# ls /home/
aaa bj bobi ccc haha
删除bbb用户,使用 remove=yes 参数让其删除用户的同时也删除家目录
[root@master ~]# ansible group1 -m user -a "name=bbb remove=yes state=absent"
[root@agent1 ~]# ls /home/
aaa bj bobi ccc
创建一个普通用户叫hadoop,并产生空密码密钥对
[root@agent1 ~]# ansible group1 -m user -a 'name=hadoop generate_ssh_key=yes'
group模块
group模块用于管理用户组和用户组属性。
https://docs.ansible.com/ansible/latest/modules/group_module.html#group-module
创建组
[root@master ~]# ansible group1 -m group -a "name=ccc gid=5000"
[root@agent1 ~]# grep ccc /etc/group
ccc:x:5000:
新建一个用户bbb,指定组为ccc
[root@master ~]# ansible group1 -m user -a "name=bbb group=5000"
[root@agent1 ~]# id bbb
uid=1002(bbb) gid=5000(ccc) groups=5000(ccc)
删除组(如果组中有用户,则删除不了)
[root@master ~]# ansible group1 -m user -a "name=bbb remove=yes state=absent"
[root@master ~]# ansible group1 -m group -a "name=ccc state=absent"
[root@agent1 ~]# grep ccc /etc/group
[root@agent1 ~]#
cron模块
回顾:
用户的定时任务存放在 /var/spool/cron/用户名 文件中
系统的定时任务存放在 /etc/crontab 文件中
所以这个模块也可以通过 copy或fetch 同步定时任务的文件去实现(会覆盖)
cron模块用于管理周期性时间任务
https://docs.ansible.com/ansible/latest/modules/cron_module.html#cron-module
创建一个cron任务,不指定user的话,默认就是root(因为我这里是用root操作的)。
如果minute,hour,day,month,week不指定的话,默认都为*
[root@master ~]# ansible group1 -m cron -a "name=cron1 user=root job='touch /tmp/test' minute=*/2"
[root@agent1 ~]# ll /tmp/
total 8
-rw-r--r-- 1 root root 7 May 14 14:29 1.txt
-rw-r--r-- 1 root root 204 May 14 14:22 hosts
-rw-r--r-- 1 root root 0 May 14 15:30 test
2分钟后
[root@agent1 ~]# ll /tmp/
total 8
-rw-r--r-- 1 root root 7 May 14 14:29 1.txt
-rw-r--r-- 1 root root 204 May 14 14:22 hosts
-rw-r--r-- 1 root root 0 May 14 15:32 test
[root@agent1 ~]# crontab -l
#Ansible: cron1
*/2 * * * * touch /tmp/test
删除cron任务
[root@master ~]# ansible group1 -m cron -a "name=cron1 state=absent"
[root@agent1 ~]# crontab -l
[root@agent1 ~]#
yum_repository模块(了解)
yum_repository模块用于配置yum仓库。
https://docs.ansible.com/ansible/latest/modules/yum_repository_module.html
这个模块可以通过copy把管理机上的/etc/yum.repos.d目录直接拷贝过去实现
增加一个/etc/yum.repos.d/local.repo配置文件
ansible group1 -m yum_repository -a "name=local descriptinotallow=localyum baseurl=file:///mnt/ enabled=yes gpgcheck=no"
注意:
此模块只帮助配置yum仓库,但如果仓库里没有软件包,安装一样会失败。
所以可以手动去挂载光驱到/mnt目录
mount /dev/cdrom /mnt
删除/etc/yum.repos.d/local.repo配置文件
ansible group1 -m yum_repository -a "name=local state=absent"
yum模块(重点)
yum模块用于使用yum命令来实现软件包的安装与卸载。
https://docs.ansible.com/ansible/latest/modules/yum_module.html#yum-module
使用yum安装一个软件(前提:group1的机器上的yum配置都已经OK)
[root@master ~]# ansible group1 -m yum -a "name=httpd,vsftpd state=present"
[root@agent1 ~]# rpm -qa|grep httpd
httpd-tools-2.4.6-97.el7.centos.5.x86_64
httpd-2.4.6-97.el7.centos.5.x86_64
[root@agent1 ~]# rpm -qa|grep vsftpd
vsftpd-3.0.2-29.el7_9.x86_64
使用yum安装httpd,httpd-devel软件,state=latest表示安装最新版本
[root@master ~]# ansible group1 -m yum -a "name=httpd,httpd-devel state=latest"
使用yum卸载httpd,httpd-devel软件
[root@master ~]# ansible group1 -m yum -a 'name=httpd,httpd-devel state=absent'
service模块(重点)
service模块用于控制服务的启动,关闭,开机自启动等。
https://docs.ansible.com/ansible/latest/modules/service_module.html#service-module
启动vsftpd服务,并设为开机自动启动
[root@master ~]# ansible group1 -m service -a "name=vsftpd state=started enabled=on"
[root@agent1 ~]# systemctl status vsftpd|grep -w active
Active: active (running) since Sat 2022-05-14 16:53:12 CST; 59s ago
关闭vsftpd服务,并设为开机不自动启动
[root@master ~]# ansible group1 -m service -a "name=vsftpd state=stopped enabled=false"
练习: 在group1的被管理机里的mariadb里创建一个abc库
远程操作mysql需要MySQL-python包的支持
master# ansible group1 -m yum -a "name=mariadb-server,MySQL-python state=present"
master# ansible group1 -m service -a "name=mariadb state=started enabled=yes"
master# ansible group1 -m mysql_db -a "login_port=3306 login_user=root name=abc state=present"
练习: 假设我主机清单里定义的group1里有多台机器,它们现在要做一个集群。此集群要求实现一个名为hadoop的普通用户之间的两两免密登录,如何实现(要求只在master上进行操作)?
[root@master ~]# ansible group1 -m user -a 'name=hadoop generate_ssh_key=yes'
[root@master ~]# ansible group1 -m fetch -a 'src=/home/hadoop/.ssh/id_rsa.pub dest=/tmp'
[root@master ~]# cat /tmp/10.1.1.12/home/hadoop/.ssh/id_rsa.pub /tmp/10.1.1.13/home/hadoop/.ssh/id_rsa.pub >> authorized_keys
[root@master ~]# ansible group1 -m copy -a "src=/tmp/authorized_keys dest=/home/hadoop/.ssh/"
[root@master ~]# useradd hadoop
[root@master ~]# su - hadoop
在master上把所有的group1组里的机器都ssh连接一遍,主要目的就是输入一次yes,把相应信息保存到/home/hadoop/.ssh/known_hosts里
[root@master ~]# su - hadoop
for i in {1..10}
do
ssh 10.1.1.$i -o StrictHostKeyChecking=no date &> /dev/null
done
[root@hadoop ~]# exit
[root@master ~]# ansible group1 -m copy -a "src=/home/hadoop/.ssh/known_hosts dest=/home/hadoop/.ssh/known_hosts"
script模块
script模块用于在远程机器上执行本地脚本。
https://docs.ansible.com/ansible/latest/modules/script_module.html#script-module
在master上准备一个脚本
[root@master ~]# vim /tmp/1.sh
[root@master ~]# cat /tmp/1.sh
#!/bin/bash
mkdir /tmp/haha
touch /tmp/haha/{1..10}
在master上直接执行/tmp/1.sh脚本(此脚本不用给执行权限)
[root@master ~]# ansible group1 -m script -a "/tmp/1.sh"
[root@agent1 ~]# ll /tmp/
total 0
drwxr-xr-x 2 root root 97 May 14 19:47 haha
[root@agent1 ~]# ll /tmp/haha/
total 0
-rw-r--r-- 1 root root 0 May 14 19:47 1
-rw-r--r-- 1 root root 0 May 14 19:47 10
-rw-r--r-- 1 root root 0 May 14 19:47 2
-rw-r--r-- 1 root root 0 May 14 19:47 3
-rw-r--r-- 1 root root 0 May 14 19:47 4
-rw-r--r-- 1 root root 0 May 14 19:47 5
-rw-r--r-- 1 root root 0 May 14 19:47 6
-rw-r--r-- 1 root root 0 May 14 19:47 7
-rw-r--r-- 1 root root 0 May 14 19:47 8
-rw-r--r-- 1 root root 0 May 14 19:47 9
扩展: 使用shell脚本实现在group1的被管理机里的mariadb里创建一个abc库
[root@master ~]# vim /tmp/1.sh
[root@master ~]# cat /tmp/1.sh
#!/bin/bash
yum install mariadb-server -y &> /dev/null
systemctl start mariadb
systemctl enable mariadb
mysql << EOF
create database abc;
quit
EOF
[root@master ~]# ansible group1 -m script -a "/tmp/1.sh"
[root@agent1 ~]# systemctl status mariadb|grep -w active
Active: active (running) since Sat 2022-05-14 19:55:39 CST; 2min 26s ago
[root@agent1 ~]# mysql
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 3
Server version: 5.5.68-MariaDB MariaDB Server
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MariaDB [(none)]> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| abc |
| mysql |
| performance_schema |
| test |
+--------------------+
5 rows in set (0.00 sec)
MariaDB [(none)]> quit
Bye
command与shell模块
两个模块都是用于执行linux命令的,这对于命令熟悉的工程师来说,用起来非常high。
shell模块与command模块差不多(command模块不能执行一些类似$HOME,>,<,|等符号,但shell可以)
https://docs.ansible.com/ansible/latest/modules/command_module.html
https://docs.ansible.com/ansible/latest/modules/shell_module.html
master# ansible -m command group1 -a "useradd user2"
master# ansible -m command group1 -a "userdel -r 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" --报错
master# ansible -m shell group1 -a "cd $HOME;pwd" --成功
注意:
shell模块并不是百分之百任何命令都可以,比如vim或ll别名就不可以。不建议去记忆哪些命令不可以,只要养成任何在生产环境里的命令都要先在测试环境里测试一下的习惯就好。
playbook
playbook(剧本): 是ansible用于配置,部署,和管理被控节点的剧本。用于ansible操作的编排。
参考:
https://docs.ansible.com/ansible/latest/user_guide/playbooks_intro.html
使用的格式为yaml格式(saltstack,elk,docker,docker-compose,kubernetes等也都会用到yaml格式)
YAML格式
1、以.yaml或.yml结尾
2、文件的第一行以 "---"开始,表明YMAL文件的开始(可选的)
3、以#号开头为注释
4、列表中的所有成员都开始于相同的缩进级别, 并且使用一个 "- " 作为开头(一个横杠和一个空格)
5、一个字典是由一个简单的 key: value 的形式组成(这个冒号后面必须是一个空格)
注意: 写这种文件不要使用tab键,都使用空格
参考: https://docs.ansible.com/ansible/latest/reference_appendices/YAMLSyntax.html#yaml-syntax
下面看一个官方的示例感受一下
---
# A list of tasty fruits
- Apple
- Orange
- Strawberry
- Mango
...
# An employee record
martin:
name: Martin D'vloper
job: Developer
skill: Elite
---
# Employee records
- martin:
name: Martin D'vloper
job: Developer
skills:
- python
- perl
- pascal
- tabitha:
name: Tabitha Bitumen
job: Developer
skills:
- lisp
- fortran
- erlang
playbook实例
先直接来看一个实例
第1步: 创建一个存放playbook的目录(路径自定义)
[root@master ~]# mkdir /etc/ansible/playbook
第2步: 准备httpd配置文件,并修改成你想要的配置
[root@master ~]# yum install -y httpd
[root@master ~]# vim /etc/httpd/conf/httpd.conf
[root@master ~]# head -2 /etc/httpd/conf/httpd.conf
# 11111111
# 2222222
第3步: 写一个playbook文件(后缀为.yml或.yaml)
[root@master ~]# vim /etc/ansible/playbook/example.yml
[root@master ~]# cat /etc/ansible/playbook/example.yml
---
- 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
第4步: 执行写好的palybook
1、会显示出执行的过程,并且执行的每一步都有ok,changed,failed等标识
2、执行如果有错误(failed)会回滚,解决问题后,直接再执行这条命令即可,并会把failed改为changed(幂等性)
[root@master ~]# ansible-playbook /etc/ansible/playbook/example.yml
PLAY [group1] *************************************************************************************************
# 收集信息
TASK [Gathering Facts] ****************************************************************************************
ok: [192.168.192.146]
ok: [192.168.192.145]
TASK [ensure apache is at the latest version] *****************************************************************
changed: [192.168.192.146]
changed: [192.168.192.145]
TASK [write the apache config file] ***************************************************************************
changed: [192.168.192.145]
changed: [192.168.192.146]
TASK [ensure apache is running (and enable it at boot)] *******************************************************
changed: [192.168.192.146]
changed: [192.168.192.145]
RUNNING HANDLER [restart apache] ******************************************************************************
changed: [192.168.192.145]
changed: [192.168.192.146]
PLAY RECAP ****************************************************************************************************
192.168.192.145 : ok=5 changed=4 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
192.168.192.146 : ok=5 changed=4 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[root@agent1 ~]# head -2 /etc/httpd/conf/httpd.conf
# 11111111
# 2222222
[root@agent1 ~]# systemctl status httpd|grep -w active
Active: active (running) since Sun 2022-05-15 10:56:38 CST; 2min 19s ago
[root@agent1 ~]# systemctl status httpd|grep -w enabled
Loaded: loaded (/usr/lib/systemd/system/httpd.service; enabled; vendor preset: disabled)
Playbook常见语法
hosts: 用于指定要执行任务的主机,其可以是一个或多个由冒号分隔主机组
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上面一个任务changed,notify才会被通知,handlers才会被执行
不管有多少个通知者进行了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
练习: 修改httpd的端口为8080,再执行playbook测试
[root@master ~]# vim /etc/httpd/conf/httpd.conf
[root@master ~]# sed -n '43p' /etc/httpd/conf/httpd.conf
Listen 8080
[root@master ~]# ansible-playbook /etc/ansible/playbook/example.yml
PLAY [group1] *************************************************************************************************
TASK [Gathering Facts] ****************************************************************************************
ok: [192.168.192.145]
ok: [192.168.192.146]
TASK [ensure apache is at the latest version] *****************************************************************
ok: [192.168.192.146]
ok: [192.168.192.145]
TASK [write the apache config file] ***************************************************************************
changed: [192.168.192.146]
changed: [192.168.192.145]
TASK [ensure apache is running (and enable it at boot)] *******************************************************
ok: [192.168.192.146]
ok: [192.168.192.145]
RUNNING HANDLER [restart apache] ******************************************************************************
changed: [192.168.192.146]
changed: [192.168.192.145]
PLAY RECAP ****************************************************************************************************
192.168.192.145 : ok=5 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
192.168.192.146 : ok=5 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[root@agent1 ~]# netstat -antp|grep httpd
tcp6 0 0 :::8080 :::* LISTEN 2761/httpd
配置文件不变的情况下,再次执行
只有4个任务,handlers并没有执行
[root@master ~]# ansible-playbook /etc/ansible/playbook/example.yml
PLAY [group1] *************************************************************************************************
TASK [Gathering Facts] ****************************************************************************************
ok: [192.168.192.145]
ok: [192.168.192.146]
TASK [ensure apache is at the latest version] *****************************************************************
ok: [192.168.192.146]
ok: [192.168.192.145]
TASK [write the apache config file] ***************************************************************************
ok: [192.168.192.145]
ok: [192.168.192.146]
TASK [ensure apache is running (and enable it at boot)] *******************************************************
ok: [192.168.192.146]
ok: [192.168.192.145]
PLAY RECAP ****************************************************************************************************
192.168.192.145 : ok=4 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
192.168.192.146 : ok=4 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
variables: 变量
定义变量可以被多次方便调用
master# vim /etc/ansible/playbook/example2.yaml
---
- hosts: group1
remote_user: root
vars:
- user: test1
tasks:
- name: create user
user: name={{user}} state=present
master# ansible-playbook /etc/ansible/playbook/example2.yaml
案例: playbook编排vsftpd
写一个playbook实现
1、配置yum(被管理机的yum没问题,这步就不需要)
2、安装vsftpd包
3、修改配置文件(要求拒绝匿名用户登录)
4、启动服务并实现vsftpd服务开机自动启动
---
- hosts: group1
remote_user: root
tasks:
- name: rm yum repository
file: path=/etc/yum.repos.d/ state=absent
- name: 同步master上的yum源到group1
copy: src=/etc/yum.repos.d dest=/etc/
- name: ensure vsftpd is at the latest version
yum: name=vsftpd state=latest
- name: write the vsftpd 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=on
handlers:
- name: restart vsftpd
service: name=vsftpd state=restarted
验证:
yum install -y ftp
[root@master ~]# ftp 192.168.192.145
Connected to 192.168.192.145 (192.168.192.145).
220 (vsFTPd 3.0.2)
Name (192.168.192.145:root):
530 Permission denied.
Login failed.
ftp>
ftp> quit
[root@master ~]# ftp 192.168.192.146
Connected to 192.168.192.146 (192.168.192.146).
220 (vsFTPd 3.0.2)
Name (192.168.192.146:root):
530 Permission denied.
Login failed.
ftp> quit
案例:playbook编排多个hosts任务
[root@master ~]# vim /etc/ansible/playbook/example.yml
[root@master ~]# cat /etc/ansible/playbook/example.yml
---
- hosts: 192.168.192.145
remote_user: root
tasks:
- name: 创建 /tmp/test1 目录
file: path=/tmp/test1 state=directory
# 这里不能用---分隔,会报语法错误
- hosts: 192.168.192.146
remote_user: root
tasks:
- name: 创建 /tmp/test2 目录
file: path=/tmp/test2 state=directory
...
[root@master ~]# ansible-playbook /etc/ansible/playbook/example.yml
PLAY [192.168.192.145] ****************************************************************************************
TASK [Gathering Facts] ****************************************************************************************
ok: [192.168.192.145]
TASK [创建 /tmp/test1 目录] ***************************************************************************************
changed: [192.168.192.145]
PLAY [192.168.192.146] ****************************************************************************************
TASK [Gathering Facts] ****************************************************************************************
ok: [192.168.192.146]
TASK [创建 /tmp/test2 目录] ***************************************************************************************
changed: [192.168.192.146]
PLAY RECAP ****************************************************************************************************
192.168.192.145 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
192.168.192.146 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[root@agent1 ~]# ll /tmp/
total 0
drwxr-xr-x 2 root root 6 May 15 12:32 test1
[root@agent2 ~]# ll /tmp/
total 0
drwxr-xr-x 2 root root 6 May 15 12:32 test2
案例: 编排nfs搭建与客户端挂载
1、在master上准备nfs配置文件
[root@master ~]# vim /etc/exports
[root@master ~]# cat /etc/exports
/share *(ro)
2、 编写yaml编排文件
[root@master ~]# vim /etc/ansible/playbook/nfs.yml
[root@master ~]# cat /etc/ansible/playbook/nfs.yml
---
- hosts: 192.168.192.145
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 state=started enabled=on
handlers:
- name: restart nfs
service: name=nfs state=restarted
- hosts: 192.168.192.146
remote_user: root
tasks:
- name: 安装nfs客户端软件包
yum: name=nfs-utils state=latest
- name: 挂载nfs服务器的共享
shell: mount 192.168.192.145:/share /mnt
...
3、执行playbook
[root@master ~]# ansible-playbook /etc/ansible/playbook/nfs.yml
PLAY [192.168.192.145] ****************************************************************************************
TASK [Gathering Facts] ****************************************************************************************
ok: [192.168.192.145]
TASK [安装nfs服务相关软件包] *******************************************************************************************
changed: [192.168.192.145]
TASK [创建共享目录] *************************************************************************************************
changed: [192.168.192.145]
TASK [同步nfs配置文件] **********************************************************************************************
changed: [192.168.192.145]
TASK [启动rpcbind服务,并设置为开机自启动] **********************************************************************************
changed: [192.168.192.145]
TASK [启动nfs服务,并设置为开机自启动] **************************************************************************************
changed: [192.168.192.145]
RUNNING HANDLER [restart nfs] *********************************************************************************
changed: [192.168.192.145]
PLAY [192.168.192.146] ****************************************************************************************
TASK [Gathering Facts] ****************************************************************************************
ok: [192.168.192.146]
TASK [安装nfs客户端软件包] ********************************************************************************************
changed: [192.168.192.146]
TASK [挂载nfs服务器的共享] ********************************************************************************************
[WARNING]: Consider using the mount module rather than running 'mount'. If you need to use command because
mount is insufficient you can add 'warn: false' to this command task or set 'command_warnings=False' in
ansible.cfg to get rid of this message.
changed: [192.168.192.146]
PLAY RECAP ****************************************************************************************************
192.168.192.145 : ok=7 changed=6 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
192.168.192.146 : ok=3 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
4、验证
执行前
[root@agent2 ~]# df -h
Filesystem Size Used Avail Use% Mounted on
devtmpfs 900M 0 900M 0% /dev
tmpfs 910M 0 910M 0% /dev/shm
tmpfs 910M 9.8M 900M 2% /run
tmpfs 910M 0 910M 0% /sys/fs/cgroup
/dev/sda3 19G 3.0G 17G 16% /
/dev/sda1 253M 158M 96M 63% /boot
tmpfs 182M 0 182M 0% /run/user/0
执行后
[root@agent2 ~]# df -h
Filesystem Size Used Avail Use% Mounted on
devtmpfs 900M 0 900M 0% /dev
tmpfs 910M 0 910M 0% /dev/shm
tmpfs 910M 9.9M 900M 2% /run
tmpfs 910M 0 910M 0% /sys/fs/cgroup
/dev/sda3 19G 3.0G 16G 16% /
/dev/sda1 253M 158M 96M 63% /boot
tmpfs 182M 0 182M 0% /run/user/0
192.168.192.145:/share 19G 3.0G 16G 16% /mnt
roles(难点)
roles介绍
roles(角色): 就是通过分别将variables, tasks及handlers等放置于单独的目录中,并可以便捷地调用它们的一种机制。
假设我们要写一个playbook来安装管理lamp环境,那么这个playbook就会写很长。所以我们希望把这个很大的文件分成多个功能拆分, 分成apache管理,php管理,mysql管理,然后在需要使用的时候直接调用就可以了,以免重复写。就类似编程里的模块化的概念,以达到代码复用的效果。
创建roles的目录结构
files:用来存放由copy模块或script模块调用的文件。
tasks:至少有一个main.yml文件,定义各tasks。
handlers:有一个main.yml文件,定义各handlers。
templates:用来存放jinjia2模板。
vars:有一个main.yml文件,定义变量。
meta:有一个main.yml文件,定义此角色的特殊设定及其依赖关系。
注意: 在每个角色的目录中分别创建files, tasks,handlers,templates,vars和meta目录,用不到的目录可以创建为空目录
通过roles实现lamp
需定制三个角色: httpd,mysql,php
第1步: 创建roles目录(可以是系统自带的)及文件,并确认目录结构
[root@master ~]# cd /etc/ansible/roles/
[root@master roles]# mkdir -p {httpd,mysql,php}/{files,tasks,handlers,templates,vars,meta}
[root@master roles]# touch {httpd,mysql,php}/{tasks,handlers,vars,meta}/main.yml
[root@master roles]# yum install tree -y
[root@master roles]# 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测试页和配置文件等
[root@master ~]# cp /etc/httpd/conf/httpd.conf /etc/ansible/roles/httpd/files/
[root@master ~]# cd /etc/ansible/roles/httpd/files/
[root@master files]# ls
httpd.conf
按需求修改配置文件
[root@master files]# vim httpd.conf
[root@master files]# echo "test main page" > /etc/ansible/roles/httpd/files/index.html
[root@master files]# echo -e "<?php\n\tphpinfo();\n?>" > /etc/ansible/roles/httpd/files/test.php
[root@master files]# ls
httpd.conf index.html test.php
[root@master files]# mkdir /etc/ansible/roles/httpd/files/httpd_doc
[root@master files]# ls
httpd.conf httpd_doc index.html test.php
[root@master files]# mv index.html httpd_doc/
[root@master files]# mv test.php httpd_doc/
[root@master files]# ls
httpd.conf httpd_doc
[root@master files]# cd httpd_doc/
[root@master httpd_doc]# ls
index.html test.php
第3步: 编写httpd角色的main.yml文件
[root@master files]# cd ..
[root@master httpd]# ls
files handlers meta tasks templates vars
[root@master httpd]# vim tasks/main.yml
[root@master httpd]# cat tasks/main.yml
---
- name: 安装httpd相关软件包
yum: name=httpd,httpd-devel state=latest
- name: 同步httpd配置文件
copy: src=/etc/ansible/roles/httpd/files/httpd.conf dest=/etc/httpd/conf/httpd.conf
notify: restart httpd
- name: 同步httpd家目录数据
copy: src=/etc/ansible/roles/httpd/files/httpd_doc/ dest=/var/www/html/
- name: 启动httpd并开机自启动
service: name=httpd state=started enabled=on
第4步: 编写httpd角色里的handler
[root@master httpd]# vim /etc/ansible/roles/httpd/handlers/main.yml
[root@master httpd]# cat /etc/ansible/roles/httpd/handlers/main.yml
- name: restart httpd
service: name=httpd state=restarted
第5步: 编写mysql角色的main.yml文件
[root@master httpd]# cd /etc/ansible/roles/mysql/
[root@master mysql]# ls
files handlers meta tasks templates vars
[root@master mysql]# vim tasks/main.yml
[root@master mysql]# cat tasks/main.yml
---
- name: 安装mysql相关软件包
yum: name=mariadb,mariadb-server,mariadb-devel state=latest
- name: 启动mysql并开机自启动
service: name=mariadb state=started enabled=on
第6步: 编写php角色的main.yml文件
[root@master mysql]# cd /etc/ansible/roles/php/
[root@master php]# ls
files handlers meta tasks templates vars
[root@master php]# vim tasks/main.yml
[root@master php]# cat tasks/main.yml
---
- name: 安装php及依赖包
yum: name=php,php-gd,php-ldap,php-odbc,php-pear,php-xml,php-xmlrpc,php-mbstring,php-snmp,php-soap,curl,curl-devel,php-bcmath,php-mysql state=present
notify: restart httpd
第7步:编写lamp的playbook文件调用前面定义好的三个角色
[root@master ~]# vim /etc/ansible/playbook/lamp.yml
[root@master ~]# cat /etc/ansible/playbook/lamp.yml
---
- hosts: group1
remote_user: root
roles:
- httpd
- mysql
- php
第8步: 执行lamp的playbook文件
[root@master ~]# ansible-playbook /etc/ansible/playbook/lamp.yml
PLAY [group1] ***************************************************************************
TASK [Gathering Facts] ******************************************************************
ok: [192.168.192.146]
ok: [192.168.192.145]
TASK [安装httpd相关软件包] *********************************************************************
ok: [192.168.192.146]
ok: [192.168.192.145]
TASK [同步httpd配置文件] **********************************************************************
changed: [192.168.192.145]
changed: [192.168.192.146]
TASK [同步httpd家目录数据] *********************************************************************
changed: [192.168.192.145]
changed: [192.168.192.146]
TASK [启动httpd并开机自启动] ********************************************************************
ok: [192.168.192.146]
ok: [192.168.192.145]
TASK [安装mysql相关软件包] *********************************************************************
changed: [192.168.192.145]
changed: [192.168.192.146]
TASK [启动mysql并开机自启动] ********************************************************************
changed: [192.168.192.145]
changed: [192.168.192.146]
TASK [安装php及依赖包] ************************************************************************
changed: [192.168.192.146]
changed: [192.168.192.145]
RUNNING HANDLER [restart httpd] *********************************************************
changed: [192.168.192.145]
changed: [192.168.192.146]
PLAY RECAP ******************************************************************************
192.168.192.145 : ok=9 changed=6 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
192.168.192.146 : ok=9 changed=6 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
第9步:验证
[root@agent1 ~]# cd /var/www/html/
[root@agent1 html]# ls
index.html test.php
通过roles实现lnmp
第1步: 先确认都要有epel源(安装nginx需要)
[root@master ~]# ls /etc/yum.repos.d/|grep epel
epel.repo
epel-testing.repo
[root@agent1 ~]# ls /etc/yum.repos.d/|grep epel
epel.repo
[root@agent2 ~]# ls /etc/yum.repos.d/|grep epel
epel.repo
第2步: 定义lnmp_mysql,lnmp_php,nginx,memcached这4个角色的目录结构
[root@master ~]# cd /etc/ansible/roles/
[root@master roles]# mkdir -p {lnmp_mysql,lnmp_php,nginx,memcached}/{files,tasks,handlers,templates,vars,meta}
[root@master roles]# touch {lnmp_mysql,lnmp_php,nginx,memcached}/{tasks,handlers,vars,meta}/main.yml
第3步: 编写nginx角色里的task任务列表
[root@master roles]# cd nginx/
[root@master nginx]# ls
files handlers meta tasks templates vars
[root@master nginx]# vim tasks/main.yml
[root@master nginx]# cat tasks/main.yml
---
- name: install nginx
yum: name=nginx state=latest
- name: copy index file
copy: src=/etc/ansible/roles/nginx/files/index.html dest=/usr/share/nginx/html/index.html
- name: copy test_php file
copy: src=/etc/ansible/roles/nginx/files/test.php dest=/usr/share/nginx/html/test.php
- name: copy conf file
copy: src=/etc/ansible/roles/nginx/files/nginx.conf dest=/etc/nginx/nginx.conf
notify:
- restart nginx
- name: start nginx
service: name=nginx enabled=on state=started
第4步: 编写nginx角色里的handler
[root@master nginx]# vim handlers/main.yml
[root@master nginx]# cat handlers/main.yml
---
- name: restart nginx
service: name=nginx state=restarted
第5步: 编写lnmp_mysql角色的task任务列表
[root@master nginx]# cd /etc/ansible/roles/lnmp_mysql/
[root@master lnmp_mysql]# ls
files handlers meta tasks templates vars
[root@master lnmp_mysql]# vim tasks/main.yml
[root@master lnmp_mysql]# cat tasks/main.yml
---
- name: install mariadb
yum: name={{item}} state=latest
with_items:
- mariadb
- mariadb-server
- mariadb-libs
- mariadb-devel
- name: start service
service: name=mariadb enabled=on state=started
第6步: 编写lnmp_php角色的task任务列表
[root@master lnmp_mysql]# cd /etc/ansible/roles/lnmp_php/
[root@master lnmp_php]# ls
files handlers meta tasks templates vars
[root@master lnmp_php]# vim tasks/main.yml
[root@master lnmp_php]# cat tasks/main.yml
---
- name: install php
yum: name={{item}} state=latest
with_items:
- php
- php-mysql
- php-gd
- php-ldap
- php-odbc
- php-pear
- php-xml
- php-xmlrpc
- php-mbstring
- php-bcmath
- php-common
- php-fpm
- php-pecl-zendopcache
- php-pecl-memcache
- libjpeg-turbo-devel
- name: copy conf file
copy: src=/etc/ansible/roles/lnmp_php/files/www.conf dest=/etc/php-fpm.d/www.conf
notify:
- restart php-fpm
- name: start php-fpm
service: name=php-fpm enabled=on state=started
第7步: 编写lnmp_php角色的handler
[root@master lnmp_php]# vim handlers/main.yml
[root@master lnmp_php]# cat handlers/main.yml
---
- name: restart php-fpm
service: name=php-fpm state=restarted
第8步: 编写memcached角色的task任务列表
[root@master lnmp_php]# cd /etc/ansible/roles/memcached/
[root@master memcached]# ls
files handlers meta tasks templates vars
[root@master memcached]# vim tasks/main.yml
[root@master memcached]# cat tasks/main.yml
---
- name: install memcached
yum: name=memcached state=latest
- name: start memcached
service: name=memcached enabled=on state=started
第9步:在master上准备配置文件,主页文件与php测试页等
[root@master ~]# yum install nginx php-fpm
php配置文件可以按需求配置,我这里就保持默认不变
[root@master ~]# cp /etc/php-fpm.d/www.conf /etc/ansible/roles/lnmp_php/files/www.conf
[root@master ~]# echo "nginx test page" > /etc/ansible/roles/nginx/files/index.html
[root@master ~]# echo -e "<?php\n\tphpinfo();\n?>" > /etc/ansible/roles/nginx/files/test.php
修改nginx配置文件,主要加上连接php的配置段
[root@master ~]# vim /etc/nginx/nginx.conf
[root@master ~]# cat /etc/nginx/nginx.conf
# For more information on configuration, see:
# * Official English Documentation: http://nginx.org/en/docs/
# * Official Russian Documentation: http://nginx.org/ru/docs/
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;
# Load dynamic modules. See /usr/share/doc/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;
events {
worker_connections 1024;
}
http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 4096;
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Load modular configuration files from the /etc/nginx/conf.d directory.
# See http://nginx.org/en/docs/ngx_core_module.html#include
# for more information.
include /etc/nginx/conf.d/*.conf;
server {
listen 80;
listen [::]:80;
server_name _;
root /usr/share/nginx/html;
index index.php index.html; # 加上主页
# Load configuration files for the default server block.
include /etc/nginx/default.d/*.conf;
error_page 404 /404.html;
location = /404.html {
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
location ~ \.php$ { # 加上支持php的配置段
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
# Settings for a TLS enabled server.
#
# server {
# listen 443 ssl http2;
# listen [::]:443 ssl http2;
# server_name _;
# root /usr/share/nginx/html;
#
# ssl_certificate "/etc/pki/nginx/server.crt";
# ssl_certificate_key "/etc/pki/nginx/private/server.key";
# ssl_session_cache shared:SSL:1m;
# ssl_session_timeout 10m;
# ssl_ciphers HIGH:!aNULL:!MD5;
# ssl_prefer_server_ciphers on;
#
# # Load configuration files for the default server block.
# include /etc/nginx/default.d/*.conf;
#
# error_page 404 /404.html;
# location = /40x.html {
# }
#
# error_page 500 502 503 504 /50x.html;
# location = /50x.html {
# }
# }
}
[root@master ~]# cp /etc/nginx/nginx.conf /etc/ansible/roles/nginx/files/nginx.conf
第10步:编写lnmp的playbook文件调用上面定义的4个roles
[root@master ~]# vim /etc/ansible/playbook/lnmp.yml
[root@master ~]# cat /etc/ansible/playbook/lnmp.yml
---
- hosts: group1
remote_user: root
roles:
- nginx
- lnmp_mysql
- lnmp_php
- memcached
第11步: 执行playbook
[root@master files]# ansible-playbook /etc/ansible/playbook/lnmp.yml
PLAY [group1] ***************************************************************************
TASK [Gathering Facts] ******************************************************************
ok: [192.168.192.146]
ok: [192.168.192.145]
TASK [install nginx] ********************************************************************
ok: [192.168.192.146]
ok: [192.168.192.145]
TASK [nginx : copy index file] **********************************************************
ok: [192.168.192.145]
ok: [192.168.192.146]
TASK [nginx : copy test_php file] *******************************************************
ok: [192.168.192.145]
ok: [192.168.192.146]
TASK [nginx : copy conf file] ***********************************************************
ok: [192.168.192.145]
ok: [192.168.192.146]
TASK [start nginx] **********************************************************************
changed: [192.168.192.146]
changed: [192.168.192.145]
TASK [lnmp_mysql : install mariadb] *****************************************************
[DEPRECATION WARNING]: Invoking "yum" only once while using a loop via squash_actions is
deprecated. Instead of using a loop to supply multiple items and specifying `name:
"{{item}}"`, please use `name: ['mariadb', 'mariadb-server', 'mariadb-libs', 'mariadb-
devel']` and remove the loop. This feature will be removed in version 2.11. Deprecation
warnings can be disabled by setting deprecation_warnings=False in ansible.cfg.
[DEPRECATION WARNING]: Invoking "yum" only once while using a loop via squash_actions is
deprecated. Instead of using a loop to supply multiple items and specifying `name:
"{{item}}"`, please use `name: ['mariadb', 'mariadb-server', 'mariadb-libs', 'mariadb-
devel']` and remove the loop. This feature will be removed in version 2.11. Deprecation
warnings can be disabled by setting deprecation_warnings=False in ansible.cfg.
ok: [192.168.192.145] => (item=[u'mariadb', u'mariadb-server', u'mariadb-libs', u'mariadb-devel'])
ok: [192.168.192.146] => (item=[u'mariadb', u'mariadb-server', u'mariadb-libs', u'mariadb-devel'])
TASK [lnmp_mysql : start service] *******************************************************
ok: [192.168.192.145]
ok: [192.168.192.146]
TASK [lnmp_php : install php] ***********************************************************
[DEPRECATION WARNING]: Invoking "yum" only once while using a loop via squash_actions is
deprecated. Instead of using a loop to supply multiple items and specifying `name:
"{{item}}"`, please use `name: ['php', 'php-mysql', 'php-gd', 'php-ldap', 'php-odbc',
'php-pear', 'php-xml', 'php-xmlrpc', 'php-mbstring', 'php-bcmath', 'php-common', 'php-
fpm', 'php-pecl-zendopcache', 'php-pecl-memcache', 'libjpeg-turbo-devel']` and remove
the loop. This feature will be removed in version 2.11. Deprecation warnings can be
disabled by setting deprecation_warnings=False in ansible.cfg.
[DEPRECATION WARNING]: Invoking "yum" only once while using a loop via squash_actions is
deprecated. Instead of using a loop to supply multiple items and specifying `name:
"{{item}}"`, please use `name: ['php', 'php-mysql', 'php-gd', 'php-ldap', 'php-odbc',
'php-pear', 'php-xml', 'php-xmlrpc', 'php-mbstring', 'php-bcmath', 'php-common', 'php-
fpm', 'php-pecl-zendopcache', 'php-pecl-memcache', 'libjpeg-turbo-devel']` and remove
the loop. This feature will be removed in version 2.11. Deprecation warnings can be
disabled by setting deprecation_warnings=False in ansible.cfg.
changed: [192.168.192.146] => (item=[u'php', u'php-mysql', u'php-gd', u'php-ldap', u'php-odbc', u'php-pear', u'php-xml', u'php-xmlrpc', u'php-mbstring', u'php-bcmath', u'php-common', u'php-fpm', u'php-pecl-zendopcache', u'php-pecl-memcache', u'libjpeg-turbo-devel'])
changed: [192.168.192.145] => (item=[u'php', u'php-mysql', u'php-gd', u'php-ldap', u'php-odbc', u'php-pear', u'php-xml', u'php-xmlrpc', u'php-mbstring', u'php-bcmath', u'php-common', u'php-fpm', u'php-pecl-zendopcache', u'php-pecl-memcache', u'libjpeg-turbo-devel'])
TASK [lnmp_php : copy conf file] ********************************************************
ok: [192.168.192.145]
ok: [192.168.192.146]
TASK [lnmp_php : start php-fpm] *********************************************************
changed: [192.168.192.145]
changed: [192.168.192.146]
TASK [install memcached] ****************************************************************
changed: [192.168.192.146]
changed: [192.168.192.145]
TASK [start memcached] ******************************************************************
changed: [192.168.192.145]
changed: [192.168.192.146]
PLAY RECAP ******************************************************************************
192.168.192.145 : ok=13 changed=5 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
192.168.192.146 : ok=13 changed=5 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
第12步:排错
一开始在启动 nginx 的时候报错:Active: failed (Result: exit-code)
出现这种问题是因为 80/443 端口被占用,所以无法启动(前面开启了httpd,使用了80端口)
解决办法是在被管理机上使用命令:
fuser -k 80/tcp
fuser -k 443/tcp
第13步:验证
[root@agent1 ~]# cd /usr/share/nginx/html/
[root@agent1 html]# ls
404.html en-US img nginx-logo.png test.php
50x.html icons index.html poweredby.png