自动化运维工具-Ansible

1. Ansible详解

ansible是新出现的自动化运维工具,基于Python开发,集合了众多运维工具(puppet、chef、func、fabric)的优点,实现了批量系统配置、批量程序部署、批量运行命令等功能。
  ansible是基于 paramiko 开发的,并且基于模块化工作,本身没有批量部署的能力。真正具有批量部署的是ansible所运行的模块,ansible只是提供一种框架。ansible不需要在远程主机上安装client/agents,因为它们是基于ssh来和远
程主机通讯的。ansible目前已经已经被红帽官方收购,是自动化运维工具中大家认可度最高的,并且上手容易,学习简单。是每位运维工程师必须掌握的技能之一。

ansible特点

  1. 部署简单,只需在主控端部署Ansible环境,被控端无需做任何操作;
  2. 默认使用SSH协议对设备进行管理;
  3. 有大量常规运维操作模块,可实现日常绝大部分操作;
  4. 配置简单、功能强大、扩展性强;
  5. 支持API及自定义模块,可通过Python轻松扩展;
  6. 通过Playbooks来定制强大的配置、状态管理;
  7. 轻量级,无需在客户端安装agent,更新时,只需在操作机上进行一次更新即可;
  8. 提供一个功能强大、操作性强的Web管理界面和REST API接口——AWX平台。

1.1. ansible的主要模块

Ansible:Ansible核心程序。
HostInventory:记录由Ansible管理的主机信息,包括端口、密码、ip等。
Playbooks:“剧本”YAML格式文件,多个任务定义在一个文件中,定义主机需要调用哪些模块来完成的功能。
CoreModules核心模块,主要操作是通过调用核心模块来完成管理任务。
CustomModules:自定义模块,完成核心模块无法完成的功能,支持多种语言。
ConnectionPlugins:连接插件,Ansible和Host通信使用

1.2. ansible安装

ansible安装常用几种方式,yum安装aptpip程序安装

1.2.1. yum安装

yum 安装是我们很熟悉的安装方式了。我们需要先安装一个epel-release包,然后再安装我们的 ansible 即可。

1
2
yum install epel-release -y
yum install ansible –y

1.2.2. pip安装

首先,我们需要安装一个python-pip包,安装完成以后,则直接使用pip命令来安装我们的包,具体操作过程如下:

1
2
yum install python-pip
pip install ansible

1.2.3. apt(Ubuntu)安装

配置PPA及安装ansible,执行如下命令:

1
2
3
4
$ sudo apt-get install software-properties-common
$ sudo apt-add-repository ppa:ansible/ansible
$ sudo apt-get update
$ sudo apt-get install ansible

注意:在早期Ubuntu发行版中, “software-properties-common” 名为 “python-software-properties”.

1.3. ansible 配置公私钥

上面我们已经提到过 ansible 是基于 ssh 协议实现的,所以其配置公私钥的方式与 ssh 协议的方式相同,具体操作步骤如下:

1
2
3
4
5
6
7
#1.生成私钥
[root@server ~]# ssh-keygen

#2.向主机分发私钥
[root@mgmt2 ~]# ssh-copy-id root@192.168.101.201
[root@mgmt2 ~]# ssh-copy-id root@192.168.101.202
[root@mgmt2 ~]# ssh-copy-id root@192.168.101.203

这样就实现了无密码登陆

通过for循环批量配置ssh密钥

1
2
3
4
5
6
7
8
#1.生成私钥
[root@mgmt2 ~]# ssh-keygen

#2.向主机批量分发私钥
[root@mgmt2 ~]# for i in node1 node2 node3 node4 node5
do
ssh-copy-id $i
done

注意:如果出现如下报错

1
-bash: ssh-copy-id: command not found

我们需要安装一个包

1
yum -y install openssh-clientsansible

再次执行即可

1.4. ansible配置文件

  • 安装目录如下(yum安装):
1
2
3
4
5
配置文件目录:/etc/ansible/
执行文件目录:/usr/bin/
Lib库依赖目录:/usr/lib/pythonX.X/site-packages/ansible/
Help文档目录:/usr/share/doc/ansible-X.X.X/
Man文档目录:/usr/share/man/man1/
  • ansible配置文件查找顺序

ansible与我们其他的服务在这一点上有很大不同,这里的配置文件查找是从多个地方找的,顺序如下:

1
2
3
4
1. 首先检测ANSIBLE_CONFIG变量定义的配置文件
2. 其次检查当前目录下的./ansible.cfg文件
3. 再次检查当前用户家目录下 ~/ansible.cfg 文件
4. 最后检查 /etc/ansible/ansible.cfg 文件
  • ansible配置文件

ansible 的配置文件为/etc/ansible/ansible.cfg,ansible 有许多参数,下面我们列出一些常见的参数:

1
2
3
4
5
6
7
8
inventory = /etc/ansible/hosts		#这个参数表示资源清单inventory文件的位置
library = /usr/share/ansible #指向存放Ansible模块的目录,支持多个目录方式,只要用冒号(:)隔开就可以
forks = 5 #并发连接数,默认为5
sudo_user = root #设置默认执行命令的用户
remote_port = 22 #指定连接被管节点的管理端口,默认为22端口,建议修改,能够更加安全
host_key_checking = False #设置是否检查SSH主机的密钥,值为True/False。关闭后第一次连接不会提示配置实例
timeout = 60 #设置SSH连接的超时时间,单位为秒
log_path = /var/log/ansible.log #指定一个存储ansible日志的文件(默认不记录日志)
  • ansible主机清单

在配置文件中,我们提到了资源清单,这个清单就是我们的主机清单,里面保存的是一些 ansible 需要连接管理的主机列表。我们可以来看看他的定义方式:

vim /etc/ansible/hosts

1
2
3
4
5
6
7
8
9
10
1、 直接指明主机地址或主机名:
## green.example.com#
# blue.example.com#
# 192.168.1.1
# 192.168.1.10
2、 定义一个主机组[组名]把地址或主机名加进去
[k8s]
192.168.101.201
192.168.101.202
192.168.101.203

1.5. ansible常用命令

  • ansible命令集

/usr/bin/ansible  Ansibe AD-Hoc 临时命令执行工具,常用于临时命令的执行
/usr/bin/ansible-doc  Ansible 模块功能查看工具
/usr/bin/ansible-galaxy  下载/上传优秀代码或Roles模块 的官网平台,基于网络的
/usr/bin/ansible-playbook  Ansible 定制自动化的任务集编排工具
/usr/bin/ansible-pull  Ansible远程执行命令的工具,拉取配置而非推送配置(使用较少,海量机器时使用,对运维的架构能力要求较高)
/usr/bin/ansible-vault  Ansible 文件加密工具
/usr/bin/ansible-console  Ansible基于Linux Consoble界面可与用户交互的命令执行工具

其中,我们比较常用的是/usr/bin/ansible/usr/bin/ansible-playbook

  • ansible-doc命令

ansible-doc 命令常用于获取模块信息及其使用帮助,一般用法如下:

1
2
ansible-doc -l				#获取全部模块的信息
ansible-doc -s MOD_NAME #获取指定模块的使用帮助

我们也可以查看一下ansible-doc的全部用法:

1
2
3
4
5
6
7
8
9
10
11
12
[root@master ~]# ansible-doc
Usage: ansible-doc [options] [module...]

Options:
-h, --help show this help message and exit  # 显示命令参数API文档
-l, --list List available modules  #列出可用的模块
-M MODULE_PATH, --module-path=MODULE_PATH  #指定模块的路径
specify path(s) to module library (default=None)
-s, --snippet Show playbook snippet for specified module(s)  #显示playbook制定模块的用法
-v, --verbose verbose mode (-vvv for more, -vvvv to enable  # 显示ansible-doc的版本号查看模块列表:
connection debugging)
--version show program's version number and exit

我们可以来看一下,以mysql相关的为例:

1
2
3
4
5
6
[root@master ~]# ansible-doc -l |grep mysql
mysql_db Add or remove MySQL databases from a remote...
mysql_replication Manage MySQL replication
mysql_user Adds or removes a user from a MySQL databas...
mysql_variables Manage MySQL global variables
[root@master ~]# ansible-doc -s mysql_user
  • ansible详解

命令的具体格式如下:

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

也可以通过ansible -h来查看帮助,下面我们列出一些比较常用的选项,并解释其含义:

-a MODULE_ARGS   #模块的参数,如果执行默认COMMAND的模块,即是命令参数,如: “date”,“pwd”等等
-k--ask-pass #ask for SSH password。登录密码,提示输入SSH密码而不是假设基于密钥的验证
--ask-su-pass #ask for su password。su切换密码
-K--ask-sudo-pass #ask for sudo password。提示密码使用sudo,sudo表示提权操作
--ask-vault-pass #ask for vault password。假设我们设定了加密的密码,则用该选项进行访问
-B SECONDS #后台运行超时时间
-C #模拟运行环境并进行预运行,可以进行查错测试
-c CONNECTION #连接类型使用
-f FORKS #并行任务数,默认为5
-i INVENTORY #指定主机清单的路径,默认为/etc/ansible/hosts
--list-hosts #查看有哪些主机组
-m MODULE_NAME #执行模块的名字,默认使用 command 模块,所以如果是只执行单一命令可以不用 -m参数
-o #压缩输出,尝试将所有结果在一行输出,一般针对收集工具使用
-S #用 su 命令
-R SU_USER #指定 su 的用户,默认为 root 用户
-s #用 sudo 命令
-U SUDO_USER #指定 sudo 到哪个用户,默认为 root 用户
-T TIMEOUT #指定 ssh 默认超时时间,默认为10s,也可在配置文件中修改
-u REMOTE_USER #远程用户,默认为 root 用户
-v #查看详细信息,同时支持-vvv-vvvv可查看更详细信息

1.5.1. ansible主机清单文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@mgmt1 ~]#  vim /etc/ansible/hosts
[test]
node1

[proxy]
node2

[web]
node[3:4]

[database]
node5

[cluster:children] # 嵌套组,children为关键字
web
database

测试Ansible环境与配置是否正常

1
2
3
4
5
6
[root@mgmt1 ~]# ansible all --list-hosts   # 查看所有主机列表

[root@mgmt1 ~]# ansible node1 -m ping # 调用ping模块
[root@mgmt1 ~]# ansible node1,node2,node3 -m ping
[root@mgmt1 ~]# ansible web -m ping
[root@mgmt1 ~]# ansible web,test -m ping

1.5.2. hosts写入密码

1
2
3
4
5
[root@mgmt1 ~]#  vim /etc/ansible/hosts
[vmhost]
192.168.101.249 ansible_ssh_user=root ansible_ssh_pass=123456
192.168.101.248 ansible_ssh_user=root ansible_ssh_pass=123456
192.168.101.247 ansible_ssh_user=root ansible_ssh_pass=123456

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[root@mgmt1 ~]# ansible vmhost -m ping
192.168.101.248 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"ping": "pong"
}
192.168.101.247 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": false,
"ping": "pong"
}
192.168.101.249 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": false,
"ping": "pong"
}

注意:这里ansible配置文件里需要配置关闭:host_key_checking = False

1.6. ansible常用模块

1.6.1. 主机连通性测试

我们使用ansible k8s -m ping命令来进行主机连通性测试,效果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[root@mgmt2 ansible]# ansible k8s -m ping
192.168.101.201 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": false,
"ping": "pong"
}
192.168.101.203 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": false,
"ping": "pong"
}
192.168.101.202 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": false,
"ping": "pong"
}

1.6.2. command 模块

这个模块可以直接在远程主机上执行命令,并将结果返回本主机。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@mgmt2 ansible]# ansible k8s -m command -a 'free -h'
192.168.101.202 | CHANGED | rc=0 >>
total used free shared buff/cache available
Mem: 7.6Gi 2.1Gi 2.2Gi 33Mi 3.4Gi 5.3Gi
Swap: 0B 0B 0B

192.168.101.203 | CHANGED | rc=0 >>
total used free shared buff/cache available
Mem: 7.6Gi 2.4Gi 1.8Gi 25Mi 3.4Gi 5.3Gi
Swap: 0B 0B 0B

192.168.101.201 | CHANGED | rc=0 >>
total used free shared buff/cache available
Mem: 3.7Gi 2.3Gi 121Mi 25Mi 1.3Gi 1.3Gi
Swap: 0B 0B 0B

命令模块接受命令名称,后面是空格分隔的列表参数。给定的命令将在所有选定的节点上执行。它不会通过shell进行处理,比如$HOME和操作如”<”,”>”,”|”,”;”,”&” 工作(需要使用(shell)模块实现这些功能)。注意,该命令不支持| 管道命令
下面来看一看该模块下常用的几个命令:

chdir       # 在执行命令之前,先切换到该目录
executable # 切换shell来执行命令,需要使用命令的绝对路径
free_form   # 要执行的Linux指令,一般使用Ansible的-a参数代替。
creates  # 一个文件名,当这个文件存在,则该命令不执行,可以
用来做判断
removes # 一个文件名,这个文件不存在,则该命令不执行

下面我们来看看这些命令的执行效果:

  • chdir
1
2
3
4
5
6
7
8
9
10
11
12
[root@mgmt2 ansible]# ansible k8s -m command -a 'chdir=/opt/ ls'  #先切换到/data/ 目录,再执行“ls”命令
192.168.101.201 | CHANGED | rc=0 >>
cni
containerd

192.168.101.202 | CHANGED | rc=0 >>
cni
containerd

192.168.101.203 | CHANGED | rc=0 >>
cni
containerd
  • creates
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@mgmt2 ansible]# ansible k8s -m command -a 'creates=/data/Dockerfile ls'  #如果/data/Dockerfile存在,则不执行“ls”命令
192.168.101.202 | CHANGED | rc=0 >>
anaconda-ks.cfg
docker
docker-19.03.9.tgz
yq

192.168.101.201 | SUCCESS | rc=0 >>
skipped, since /data/Dockerfile exists

192.168.101.203 | CHANGED | rc=0 >>
anaconda-ks.cfg
docker
docker-19.03.9.tgz
yq
  • removes
1
2
3
4
5
6
7
8
9
10
11
[root@mgmt2 ansible]# ansible k8s -m command -a 'removes=/data/Dockerfile free -h'  #如果/data/Dockerfile存在,则执行“free -h”命令
192.168.101.202 | SUCCESS | rc=0 >>
skipped, since /data/Dockerfile does not exist

192.168.101.203 | SUCCESS | rc=0 >>
skipped, since /data/Dockerfile does not exist

192.168.101.201 | CHANGED | rc=0 >>
total used free shared buff/cache available
Mem: 3.7Gi 2.3Gi 116Mi 25Mi 1.3Gi 1.3Gi
Swap: 0B 0B 0B

1.6.3. shell 模块

shell模块可以在远程主机上调用shell解释器运行命令,支持shell的各种功能,例如管道等。

1
2
3
4
5
6
7
8
9
10
11
12
[root@mgmt2 ansible]# ansible k8s -m shell -a 'cat /etc/passwd |grep "root"'
192.168.101.201 | CHANGED | rc=0 >>
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin

192.168.101.202 | CHANGED | rc=0 >>
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin

192.168.101.203 | CHANGED | rc=0 >>
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin

只要是我们的shell命令,都可以通过这个模块在远程主机上运行,但不可以使用shell模块执行交互命令,如vim,top等。

使用chdir参数切换工作目录

1
ansible k8s -m shell -a 'chdir=/tmp touch my.txt pwd'

shell模块支持判断(creates、removes)

  • creates 文件名: 文件存在,不执行shell命令
  • removes 文件名:文件不存在,不执行shell命令
1
2
3
4
5
ansible test -m shell -a "ssh-keygen -f ~/.ssh/id_rsa -N creates=~/.ssh/id_rsa"
# 如果已经有密钥文件id_rsa,则不创建密钥(skip跳过)

ansible test -m shell -a "unzip xx.zip removes=/bin/unzip"
# 如果没有安装unzip软件包,则不执行解压命令(skip跳过)

1.6.4. copy模块

这个模块用于将文件复制到远程主机,同时支持给定内容生成文件和修改权限等。
其相关选项如下:

src    #被复制到远程主机的本地文件。可以是绝对路径,也可以是相对路径。如果路径是一个目录,则会递归复制,用法类似于”rsync”
content   #用于替换”src”,可以直接指定文件的值
dest    #必选项,将源文件复制到的远程主机的绝对路径
backup   #当文件内容发生改变后,在覆盖之前把源文件备份,备份文件包含时间信息
directory_mode    #递归设定目录的权限,默认为系统默认权限
force    #当目标主机包含该文件,但内容不同时,设为yes,表示强制覆盖;设为no,表示目标主机的目标位置不存在该文件才复制。默认为yes
others    #所有的 file 模块中的选项可以在这里使用

用法举例如下:

  • 复制文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
[root@mgmt2 ansible]# ansible web -m copy -a 'src=./ansible.cfg dest=/data/'
192.168.101.202 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": false,
"checksum": "a2a782df5d7bd49afb0971fc6be21cd825daaaa3",
"dest": "/data/ansible.cfg",
"gid": 0,
"group": "root",
"mode": "0644",
"owner": "root",
"path": "/data/ansible.cfg",
"size": 20012,
"state": "file",
"uid": 0
}
192.168.101.201 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": false,
"checksum": "a2a782df5d7bd49afb0971fc6be21cd825daaaa3",
"dest": "/data/ansible.cfg",
"gid": 0,
"group": "root",
"mode": "0644",
"owner": "root",
"path": "/data/ansible.cfg",
"size": 20012,
"state": "file",
"uid": 0
}
  • 给定内容生成文件,并制定权限
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
[root@mgmt2 ansible]# ansible web -m copy -a 'content="I love you" dest=/data/love mode=666'
192.168.101.202 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": true,
"checksum": "ce48c9870c7ae19796438aed65458c8bdc335157",
"dest": "/data/love",
"gid": 0,
"group": "root",
"md5sum": "e4f58a805a6e1fd0f6bef58c86f9ceb3",
"mode": "0666",
"owner": "root",
"size": 10,
"src": "/root/.ansible/tmp/ansible-tmp-1631174440.548185-142417634763846/source",
"state": "file",
"uid": 0
}
192.168.101.201 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": true,
"checksum": "ce48c9870c7ae19796438aed65458c8bdc335157",
"dest": "/data/love",
"gid": 0,
"group": "root",
"md5sum": "e4f58a805a6e1fd0f6bef58c86f9ceb3",
"mode": "0666",
"owner": "root",
"size": 10,
"src": "/root/.ansible/tmp/ansible-tmp-1631174440.5443904-137441252807840/source",
"state": "file",
"uid": 0
}

我们现在去查看一下我们生成的文件及其权限:

1
2
3
4
5
6
7
8
9
10
[root@mgmt2 ansible]# ansible web -m shell -a 'ls -la /data/'
192.168.101.202 | CHANGED | rc=0 >>
total 24
-rw-r--r-- 1 root root 20012 Sep 9 15:54 ansible.cfg
-rw-rw-rw- 1 root root 10 Sep 9 16:00 love

192.168.101.201 | CHANGED | rc=0 >>
total 230280
-rw-r--r-- 1 root root 20012 Sep 9 15:54 ansible.cfg
-rw-rw-rw- 1 root root 10 Sep 9 16:00 love

可以看出我们的love文件已经生成,并且权限为666

  • 关于覆盖

我们把文件的内容修改一下,然后选择覆盖备份:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
[root@mgmt2 ansible]# ansible web -m copy -a 'content="I love you today" backup=yes dest=/data/love mode=666'
192.168.101.201 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"backup_file": "/data/love.1710731.2021-09-09@16:08:11~",
"changed": true,
"checksum": "aae6d5443c11825389059f97953a5596f52472d0",
"dest": "/data/love",
"gid": 0,
"group": "root",
"md5sum": "ca125268f92d23fbb2ba11b5033d97a6",
"mode": "0666",
"owner": "root",
"size": 16,
"src": "/root/.ansible/tmp/ansible-tmp-1631174889.8834162-86186609378458/source",
"state": "file",
"uid": 0
}
192.168.101.202 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"backup_file": "/data/love.2458311.2021-09-09@16:08:11~",
"changed": true,
"checksum": "aae6d5443c11825389059f97953a5596f52472d0",
"dest": "/data/love",
"gid": 0,
"group": "root",
"md5sum": "ca125268f92d23fbb2ba11b5033d97a6",
"mode": "0666",
"owner": "root",
"size": 16,
"src": "/root/.ansible/tmp/ansible-tmp-1631174889.8911483-24337496953154/source",
"state": "file",
"uid": 0
}

现在我们可以去查看一下:

1
2
3
4
5
6
7
8
9
10
11
12
[root@mgmt2 ansible]# ansible web -m shell -a 'ls -la /data'
192.168.101.201 | CHANGED | rc=0 >>
total 230288
-rw-r--r-- 1 root root 20012 Sep 9 15:54 ansible.cfg
-rw-rw-rw- 1 root root 16 Sep 9 16:08 love
-rw-rw-rw- 1 root root 10 Sep 9 16:00 love.1710731.2021-09-09@16:08:11~

192.168.101.202 | CHANGED | rc=0 >>
total 32
-rw-r--r-- 1 root root 20012 Sep 9 15:54 ansible.cfg
-rw-rw-rw- 1 root root 16 Sep 9 16:08 love
-rw-rw-rw- 1 root root 10 Sep 9 16:00 love.2458311.2021-09-09@16:08:11~

可以看出,我们的源文件已经被备份,我们还可以查看一下love文件的内容:

1
2
3
4
5
6
[root@mgmt2 ansible]# ansible web -m shell -a 'cat /data/love'
192.168.101.201 | CHANGED | rc=0 >>
I love you today

192.168.101.202 | CHANGED | rc=0 >>
I love you today

证明,这正是我们新导入的文件的内容。

1.6.5. file模块

该模块主要用于设置文件的属性,比如创建文件、创建链接文件、删除文件等。
下面是一些常见的命令:

force  #需要在两种情况下强制创建软链接,一种是源文件不存在,但之后会建立的情况下;另一种是目标软链接已存在,需要先取消之前的软链,然后创建新的软链,有两个选项:yes|no
group  #定义文件/目录的属组。后面可以加上mode:定义文件/目录的权限
owner  #定义文件/目录的属主。后面必须跟上path:定义文件/目录的路径
recurse  #递归设置文件的属性,只对目录有效,后面跟上src:被链接的源文件路径,只应用于state=link的情况
dest  #被链接到的路径,只应用于state=link的情况
state  #状态,有以下选项:

directory:如果目录不存在,就创建目录
file:即使文件不存在,也不会被创建
link:创建软链接
hard:创建硬链接
touch:如果文件不存在,则会创建一个新的文件,如果文件或目录已存在,则更新其最后修改时间
absent:删除目录、文件或者取消链接文件

用法举例如下:

  • 创建目录
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
[root@mgmt2 ansible]# ansible web -m file -a 'path=/data/app state=directory'
192.168.101.202 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": true,
"gid": 0,
"group": "root",
"mode": "0755",
"owner": "root",
"path": "/data/app",
"size": 6,
"state": "directory",
"uid": 0
}
192.168.101.201 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": true,
"gid": 0,
"group": "root",
"mode": "0755",
"owner": "root",
"path": "/data/app",
"size": 6,
"state": "directory",
"uid": 0
}

我们可以查看一下

1
2
3
4
5
6
7
8
[root@mgmt2 ansible]# ansible web -m shell -a 'ls -l /data'
192.168.101.202 | CHANGED | rc=0 >>
total 32
drwxr-xr-x 2 root root 6 Sep 9 16:13 app

192.168.101.201 | CHANGED | rc=0 >>
total 230288
drwxr-xr-x 2 root root 6 Sep 9 16:13 app

可以看出,我们的目录已经创建完成

  • 创建链接文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
[root@mgmt2 ansible]# ansible web -m file -a 'path=/data/hhh.cfg src=ansible.cfg state=link'
192.168.101.202 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": true,
"dest": "/data/hhh.cfg",
"gid": 0,
"group": "root",
"mode": "0777",
"owner": "root",
"size": 11,
"src": "ansible.cfg",
"state": "link",
"uid": 0
}
192.168.101.201 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": true,
"dest": "/data/hhh.cfg",
"gid": 0,
"group": "root",
"mode": "0777",
"owner": "root",
"size": 11,
"src": "ansible.cfg",
"state": "link",
"uid": 0
}

我们可以去查看一下:

1
2
3
4
5
6
7
8
9
10
[root@mgmt2 ansible]# ansible web -m shell -a 'ls -l /data'
192.168.101.202 | CHANGED | rc=0 >>
total 32
-rw-r--r-- 1 root root 20012 Sep 9 15:54 ansible.cfg
lrwxrwxrwx 1 root root 11 Sep 9 16:19 hhh.cfg -> ansible.cfg

192.168.101.201 | CHANGED | rc=0 >>
total 230288
-rw-r--r-- 1 root root 20012 Sep 9 15:54 ansible.cfg
lrwxrwxrwx 1 root root 11 Sep 9 16:19 hhh.cfg -> ansible.cfg

我们的链接文件已经创建成功

  • 删除文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[root@mgmt2 ansible]# ansible web -m file -a 'path=/data/ansible.cfg state=absent'
192.168.101.201 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": true,
"path": "/data/ansible.cfg",
"state": "absent"
}
192.168.101.202 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": true,
"path": "/data/ansible.cfg",
"state": "absent"
}

我们可以查看一下

1
2
3
4
5
6
[root@mgmt2 ansible]# ansible web -m shell -a 'ls /data/ansible.cfg'
192.168.101.201 | FAILED | rc=2 >>
ls: cannot access '/data/ansible.cfg': No such file or directorynon-zero return code

192.168.101.202 | FAILED | rc=2 >>
ls: cannot access '/data/ansible.cfg': No such file or directorynon-zero return code

已经没有这个文件

1.6.6. fetch

该模块用于从远程某主机获取(复制)文件到本地。
有两个选项:

dest:用来存放文件的目录
src:在远程拉取的文件,并且必须是一个file,不能是目录

具体举例如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
root@mgmt2 ansible]# ansible web -m fetch -a 'src=/data/love dest=/data'
192.168.101.202 | CHANGED => {
"changed": true,
"checksum": "aae6d5443c11825389059f97953a5596f52472d0",
"dest": "/data/192.168.101.202/data/love",
"md5sum": "ca125268f92d23fbb2ba11b5033d97a6",
"remote_checksum": "aae6d5443c11825389059f97953a5596f52472d0",
"remote_md5sum": null
}
192.168.101.201 | CHANGED => {
"changed": true,
"checksum": "aae6d5443c11825389059f97953a5596f52472d0",
"dest": "/data/192.168.101.201/data/love",
"md5sum": "ca125268f92d23fbb2ba11b5033d97a6",
"remote_checksum": "aae6d5443c11825389059f97953a5596f52472d0",
"remote_md5sum": null
}

我们可以在本机上查看一下文件是否复制成功。要注意,文件保存的路径是我们设置的接收目录下的被管制主机ip目录下:

1
2
3
4
5
6
7
8
9
10
11
[root@mgmt2 ansible]# cd /data/
[root@mgmt2 data]# ls
192.168.101.201 192.168.101.202
[root@mgmt2 data]# cd 192.168.101.201
[root@mgmt2 192.168.101.201]# ls
data
[root@mgmt2 192.168.101.201]# cd data/
[root@mgmt2 data]# ls
love
[root@mgmt2 data]# pwd
/data/192.168.101.201/data

1.6.7. cron模块

该模块适用于管理cron计划任务的。
其使用的语法跟我们的crontab文件中的语法一致,同时,可以指定以下选项:

day= #日应该运行的工作( 1-31, *, */2, )
hour= # 小时 ( 0-23, *, */2, )
minute= #分钟( 0-59, *, */2, )
month= # 月( 1-12, *, /2, )
weekday= # 周 ( 0-6 for Sunday-Saturday,, )
job= #指明运行的命令是什么
name= #定时任务描述
reboot # 任务在重启时运行,不建议使用,建议使用special_time
special_time #特殊的时间范围,参数:reboot(重启时),annually(每年),monthly(每月),weekly(每周),daily(每天),hourly(每小时)
state #指定状态,present表示添加定时任务,也是默认设置,absent表示删除定时任务
user # 以哪个用户的身份执行

举例如下

  • 添加计划任务
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@mgmt2 ~]# ansible web -m cron -a 'name="ntp update every 5 min" minute=*/5 job="/sbin/ntpdate 172.17.0.1 &> /dev/null"'
192.168.101.202 | SUCCESS => {
"changed": true,
"envs": [],
"jobs": [
"ntp update every 5 min"
]
}
192.168.101.201 | SUCCESS => {
"changed": true,
"envs": [],
"jobs": [
"ntp update every 5 min"
]
}

我们可以去查看一下:

1
2
3
4
5
6
7
8
[root@mgmt2 ~]# ansible web -m shell -a 'crontab -l'
192.168.37.122 | SUCCESS | rc=0 >>
#Ansible: ntp update every 5 min
*/5 * * * * /sbin/ntpdate 172.17.0.1 &> /dev/null

192.168.37.133 | SUCCESS | rc=0 >>
#Ansible: ntp update every 5 min
*/5 * * * * /sbin/ntpdate 172.17.0.1 &> /dev/null

可以看出,我们的计划任务已经设置成功了。

1.6.8. script模块

如果复杂的命令怎么办?

  • script允许在本地写脚本,拷贝到被管理端并执行脚本
  • 脚本不是shell脚本(如python、perl脚本等),可以没有-x
1
2
3
4
5
6
7
[root@mgmt2 ~]# cat test.sh
#!/bin/bash
yum -y install httpd
systemctl start httpd

[root@mgmt2 ~]# ansible test -m script -a "./test.sh"
# test是主机组的名称

1.6.9. lineinfile 和 replace模块

在修改单个文件的单行内容时可以使用lineinfile模块

1
2
3
4
[root@mgmt2 ~]# ansible test -m lineinfile -a "path=/etc/issue regexp="hello" line='ni hao'"
# 在/etc/issue文件中正则匹配包含hello的行,把整行内容替换为ni hao
# 如果无法匹配到hello,则在文件最后添加一行nihao
# 如果有多行内容包含hello,则仅替换最后一行

lineinfile会替换一整行,replace可以替换关键词

1
2
[root@mgmt2 ~]# ansible test -m replace -a "path=/etc/issue.net regexp=Kernel replace=Ocena"
# 将/etc/issue文件全文所有的Kernel替换为Ocean

1.6.10. user模块

user模块可以实现Linux系统账户管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[root@mgmt2 ~]# ansible test -m user -a "name=tuser1"
# 远程test组中的所有主机并创建系统账户tuser1

[root@mgmt2 ~]# ansible test -m user -a "name=tuser2 uid=1010 group=adm groups=daemon,root home=/home/tuser2"
# 创建账户并设置对应的账户属性

[root@mgmt2 ~]# ansible test -m user -a "name=tuser1 password={{'abc'|password_hash('sha512')}}"
# 修改账户密码

[root@mgmt2 ~]# ansible test -m user -a "name=tuser1 groups=root,daemon"
# 修改tuser1账户的附加组

[root@mgmt2 ~]# ansible test -m user -a "name=tuser1 state=absent"
# 删除账户tuser1

[root@mgmt2 ~]# ansible test -m user -a "name=tuser2 state=absent remove=true"
# 删除tuser2账户同时删除家目录、邮箱

1.6.11. yum_repository模块

使用yum_repository模块可以创建或修改yum源配置文件

1
2
3
4
5
6
7
[root@mgmt2 ~]# ansible test -m yum_repository -a "name=myyum description=hello baseurl=ftp://192.168.4.254/centos gpgcheck=no"
# 新建一个yum源配置文件/etc/yum.repos.d/myyum.repo
# yum源文件名为myyum,该文件的内容如下:
[myyum]
baseurl = ftp://192.168.4.254/centos
gpgcheck = 0
name = hello

使用yum模块可以安装、卸载、升级软件包

state:present(安装)|absent(卸载)|latest(升级)

1
2
3
4
5
6
7
8
[root@mgmt2 ~]# ansible test -m yum -a "name=unzip state=present"
# 安装unzip软件包

[root@mgmt2 ~]# ansible test -m yum -a "name=unzip state=latest"
# 升级unzip软件包,软件名称可以是*,代表升级所有软件包

[root@mgmt2 ~]# ansible test -m yum -a "name=unzip state=absent"
# 卸载unzip软件包

1.6.12. service模块

service为服务管理模块(启动、关闭、重启服务等)

state:started | stopped | restarted

enable:yes 设置开机启动

1
2
3
4
5
6
7
[root@mgmt2 ~]# ansible test -m yum -a "name=httpd"
# 安装httpd软件包

[root@mgmt2 ~]# ansible test -m service -a "name=httpd state=started"
[root@mgmt2 ~]# ansible test -m service -a "name=httpd state=stopped"
[root@mgmt2 ~]# ansible test -m service -a "name=httpd state=restarted"
[root@mgmt2 ~]# ansible test -m service -a "name=httpd enabled=yes"

1.6.13. 逻辑卷相关模块

lvg模块:创建、删除卷组(VG),修改卷组大小

state:present(创建) | absent(删除)

1
2
3
4
5
6
7
8
[root@mgmt2 ~]# ansible test -m yum -a "name=lvm2"
# 安装lvm2软件包

[root@mgmt2 ~]# ansible test -m lvg -a "vg=myvg pvs=/dev/vdb1"
# 创建名称为myvg的卷组,该卷组由/dev/vdb1组成

[root@mgmt2 ~]# ansible test -m lvg -a "vg=myvg pvs=/dev/vdb1,/dev/vdb2"
# 修改卷组大小

lvol模块:创建、删除逻辑卷(LV),修改逻辑卷大小

state:present(创建)| absent(删除)

1
2
3
4
5
6
7
8
9
10
11
[root@mgmt2 ~]# ansible test -m lvol -a "lv=mylv vg=myvg size=2G"
# 使用myvg这个卷组创建一个名称为mylv的逻辑卷

[root@mgmt2 ~]# ansible test -m lvol -a "lv=mylv vg=myvg size=4G"
# 修改LV逻辑卷大小

[root@mgmt2 ~]# ansible test -m lvol -a "lv=mylv vg=myvg state=absent force=yes"
# 删除逻辑卷

[root@mgmt2 ~]# ansible test -m lvg -a "vg=myvg state=absent"
# 删除卷组myvg

1.7. sudo提权

1.7.1. 基本概念

sudo

  • superuser or another do
  • 以超级管理员或其他人的身份执行命令

基本流程

  • 管理员需要先授权(修改/etc/sudoers文件)
  • 普通用户以sudo的形式执行命令
  • 可以通过sudo -l查看授权情况

1.7.2. sudoers语法

修改/etc/sudoers的方法

  • visudo(带语法检查,默认没有颜色提示)
  • vim /etc/sudoers (不带语法检查,默认有颜色提示)

授权格式如下:

  • 用户或组 主机列表=(提权身份) [NOPASSWD]:命令列表
  • 命令需要写绝对路径
1
2
3
4
[root@mgmt2 ~]# cat /etc/sudoers
... ...
root ALL=(ALL) ALL
%wheel ALL=(ALL) ALL

1.7.3. 配置sudo提权

为所有被管理主机创建系统账户

用户名为alice,密码为123456

1
[root@mgmt2 ~]# ansible all -m user -a "name=alice password={{'123456'|password_hash('sha512')}}"

在所有被管理主机配置sudo,让alice可以管理系统服务

默认普通用户无权执行systemctl命令

1
2
3
4
[root@mgmt2 ~]# ansible all -m lineinfile -a "path=/etc/sudoers line='alice ALL=(ALL) /usr/bin/systemctl'"
[root@mgmt2 ~]# ssh alice@node1 # 密码:123456
[root@mgmt2 ~]# sudo -l
[root@mgmt2 ~]# sudo systemctl restart chronyd # 验证

在所有被管理主机配置sudo,让alice可以执行任何命令

使用NOPASSWD开启无密码验证

1
2
3
4
[root@mgmt2 ~]# ansible all -m lineinfile -a "path=/etc/sudoers line='alice ALL=(ALL) NOPASSWD:ALL'"
[root@mgmt2 ~]# ssh alice@node1
[root@mgmt2 ~]# sudo -l
[root@mgmt2 ~]# sudo systemctl restart chronyd # 不需要输入密码

1.8. Ansible配置进阶

1.8.1. 修改ansible配置文件

修改~/ansible/ansible.cfg

修改sudo相关配置(参考/etc/ansible/ansible.cfg)

1
2
3
4
5
6
7
8
9
10
11
[root@mgmt2 ~]# vim /etc/ansible/ansible.cfg
[defaults]
inventory = /etc/ansible/hosts
remote_user = alice #以什么用户远程被管理主机
#host_key_checking = False #是否校验密钥

[privilege_escalation]
become = true #是否需要切换用户
become_method = sudo #如何切换用户
become_user = root #切换成什么用户
become_ask_pass = no #sudo是否需要输入密码

1.8.2. 配置ssh密钥

使用alice远程被管理主机,需要提前配置SSH密钥

1
2
3
4
5
6
7
[root@mgmt2 ~]# for i in node1 node2 node3 node4 node5
do
ssh-copy-id alice@$i
done

# 测试效果
[root@mgmt2 ~]# ansible all -m command -a "who"

1.8.3. inventory配置

修改主机清单文件,添加变量(多个变量空格分隔)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@mgmt2 ~]# cat /etc/ansible/hosts
[test]
node1

[proxy]
node2

[web]
node[3:4]

[database]
node5

[cluster:children]
web
database

ansible_ssh_prot=220 #自定义远程ssh端口

ansible_ssh_user=alice #自定义远程连接的账户名

ansible_ssh_pass=密码 #自定义远程连接的密码

ansible_ssh_private_key_file=密钥文件 #自定义远程连接的密钥

这里的配置项可以单独配置到对应的主机。

-------------本文结束感谢您的阅读-------------