认识自动化运维

问题:

假设我要去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环境搭建

实验准备

三台机器,一台管理,两台被管理

自动化工具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/hostsgroup1
[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.comapache10.aaa.com10
nginx[a:z].aaa.com nginxa.aaa.comnginxz.aaa.com26
10.1.1.[11:15] 10.1.1.1110.1.1.155

示例2:利用端口号分组(适用于机器的ssh端口号做了更改)

[nginx]
10.1.1.13:2222 10.1.1.13ssh2222

示例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


ownergroup
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:


bbbccc
[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库

mysqlMySQL-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
mastergroup1sshyes/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



4handlers
[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: masteryumgroup1
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的目录结构

filescopyscript
tasksmain.ymltasks
handlers:main.ymlhandlers
templatesjinjia2
varsmain.yml
metamain.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

自动化工具ansible_ansible_02

自动化工具ansible_ansible_03

通过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

自动化工具ansible_ansible_04

自动化工具ansible_ansible_05