Bootstrap

Ansible批量自动化管理工具入门

一,Ansible概述

  • 由于互联网的快速发展导致产品更新换代速度逐步增长,运维人员每天都要进行大量的维护操作,按照传统方式进行维护使得工作效率低下。这时部署自动化运维工具就可以尽可能安全,高效的完成这些工作。
  • Ansible是基于Python开发,集合了众多优秀运维工具的优点,实现了批量运行命令,部署程序,配置系统等功能的自动化运维管理工具。默认通过SSH协议进行远程命令执行或下发配置,无需部署任何客户端代理软件,从而使得自动化环境部署变得更加简单。可同时支持多台主机并行管理,使得管理主机更加敏捷。
  • Ansible可以看作是一种基于模块进行工作的框架结构,批量部署能力就是由Ansible所运行的模块实现的。简单说Ansible是基于“模块”完成各种任务的。

二,ansible服务的安装和部署

2.1 实验环境

Ansible192.168.200.136管理服务器
web01192.168.200.143被管理主机
web02192.168.200.144被管理主机
[root@localhost ~]# cat /etc/redhat-release
CentOS Linux release 7.5.1804 (Core)
[root@localhost ~]# uname -r
3.10.0-862.3.3.el7.x86_64
[root@localhost ~]# systemctl stop firewalld
[root@localhost ~]# systemctl disable firewalld
[root@localhost ~]# systemctl stop NetworkManager
[root@localhost ~]# systemctl disable NetworkManager
#通过yum源方式安装ansible
[root@ansible ~]# yum -y install epel-release
[root@ansible ~]# yum -y install ansible
#通过Python的pip方式安装ansible
[root@ansible ~]# yum -y install epel-release
[root@ansible ~]# yum -y install python2-pip
[root@ansible ~]# pip install ansible

2.2 生产环境Ansible管理服务器ssh登陆安全策略

(1)生产环境ssh登陆策略
备份:cp /etc/ssh/sshd_config{,.bak}

[root@www ~]# cat -n /etc/ssh/sshd_config.bak | sed -n '17p;38p;43p;47p;65p;79p;115p'
17 #Port 22 #修改ssh连接端口
38 #PermitRootLogin yes #是否允许root账号远程登陆
43 #PubkeyAuthentication yes #是否开启公钥连接认证
47 AuthorizedKeysFile .ssh/authorized_keys #公钥文件的放置位置
65 PasswordAuthentication yes #是否开启密码验证登陆
79 GSSAPIAuthentication yes #是否关闭GSSAPI认证
115 #UseDNS yes #是否关闭DNS反向解析
[root@www ~]# cat -n /etc/ssh/sshd_config | sed -n '17p;38p;43p;47p;65p;79p;115p'
17 Port 22221 #工作中需要设定到1万以上的端口,避免被扫描出来。
38 PermitRootLogin yes #如果不是超大规模的服务器,为了方便我们可以暂时开启root远程登录
43 PubkeyAuthentication yes #开启公钥认证模式
47 AuthorizedKeysFile .ssh/authorized_keys #公钥放置位置
65 PasswordAuthentication no #为了安全我们关闭服务器的密码认证方式
79 GSSAPIAuthentication no #关闭GSSAPI认证,极大提高ssh连接速度
115 UseDNS no #关闭DNS反向解析,极大提高ssh连接速度
[root@localhost ssh]# useradd yunjisuan
[root@localhost ssh]# passwd yunjisuan
更改用户 yunjisuan 的密码 。
新的 密码:
重新输入新的 密码:
passwd:所有的身份验证令牌已经成功更新。
[root@localhost ssh]# visudo
在93行输入:yunjisuan       ALL=(ALL)       NOPASSWD: ALL
[root@localhost ssh]# vim sshd_config
在38行 把远程登陆关闭: PermitRootLogin no
[root@localhost ssh]# systemctl restart sshd        #重启sshd发现远程登陆不上去
然后连接普通用户 端口22221 yunjisuan 123123
sudo su -                 #可以直接通过普通用户登陆root用户

(2)设置xshell私钥登陆Linux
在yunjisuan普通用户下

[root@ansible ~]# ssh-keygen #一直回车
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:royhAEKx9bhe4jLZ3SzfZ/yvhkzPgToDIx+1gSxoOLM root@www
The keys randomart image is:
+---[RSA 2048]----+
| . . |
| + o |
| o..... . |
|.+ o.. o o |
|o =o .. S o . |
|oE= +.o= . o . |
|.+ +.ooo= = + . |
| .o. +oo.+ * + |
| . . o. .= ooo. |
+----[SHA256]-----+
[root@ansible ~]# cd .ssh/
[root@ansible .ssh]# ls
id_rsa id_rsa.pub
sudo yum -y install lrzsz
sz id_rsa      #拷贝到桌面
[root@ansible .ssh]# cat id_rsa.pub > authorized_keys
[root@ansible .ssh]# chmod 600 authorized_keys #权限必须600否则不生效

然后通过密钥连接yunjisuan普通用户
之后就可修改、etc/ssh/sshd.config配置文件
38 PermitRootLogin yes #如果不是超大规模的服务器,为了方便我们可以暂时开启root远程登录
43 PubkeyAuthentication yes #开启公钥认证模式
47 AuthorizedKeysFile .ssh/authorized_keys #公钥放置位置
65 PasswordAuthentication no #为了安全我们关闭服务器的密码认证方式
79 GSSAPIAuthentication no #关闭GSSAPI认证,极大提高ssh连接速度
115 UseDNS no #关闭DNS反向解析,极大提高ssh连接速度
这样安全设置做好了

配置/etc/ansible/hosts

vim /etc/ansible/hosts
[Web]
Web01 ansible_ssh_host=192.168.200.143
Web02 ansible_ssh_host=192.168.200.144

然后免密钥连接

[root@localhost ~]# ssh-keygen
ssh-copy-id -i ~/.ssh/id_rsa.pub 192.168.200.143
ssh-copy-id -i ~/.ssh/id_rsa.pub 192.168.200.144

3.1 ping模块

Ansible中使用ping模块来检测指定主机的连通性

[root@ansible ~]# ansible Web01 -m ping
Web01 | SUCCESS => {
"changed": false,
"ping": "pong"
}
[root@ansible ~]# ansible Web02 -m ping
Web02 | SUCCESS => {
"changed": false,
"ping": "pong"
}
[root@ansible ~]# ansible all -m ping
Web02 | SUCCESS => {
"changed": false,
"ping": "pong"
}
Web01 | SUCCESS => {
"changed": false,
"ping": "pong"
}

3.2 command模块

在远程主机执行命令,不支持管道符和重定向等复杂命令,可完全被shell模块替代

[root@ansible ~]# ansible Web01 -m command -a 'uptime'
Web01 | SUCCESS | rc=0 >>
22:14:43 up 9:43, 3 users, load average: 0.00, 0.01, 0.05
[root@ansible ~]# ansible Web01 -m command -a 'ls'
Web01 | SUCCESS | rc=0 >>
anaconda-ks.cfg

3.3 shell模块

Ansible中的shell模块可以在被管理主机上运行命令,并支持像管道符重定向这样的复杂命令。

#在Web01上创建用户yunjisuan,并非交互方式设定密码
[root@ansible ~]# ansible Web01 -m shell -a 'useradd yunjisuan'
Web01 | SUCCESS | rc=0 >>
[root@ansible ~]# ansible Web01 -m shell -a 'echo 123123 | passwd --stdin yunjisuan'
Web01 | SUCCESS | rc=0 >>
更改用户 yunjisuan 的密码 。
passwd:所有的身份验证令牌已经成功更新。
[root@ansible ~]# ansible Web01 -m shell -a 'id yunjisuan'
Web01 | SUCCESS | rc=0 >>
uid=1000(yunjisuan) gid=1000(yunjisuan) 组=1000(yunjisuan)
[root@ansible ~]# ansible Web01 -m shell -a 'tail -1 /etc/shadow'
Web01 | SUCCESS | rc=0 >>
yunjisuan:$6$4y7c1tkV$oPZW0psDdAzJp5RomBrOpSlTuvsdQ/5JaBYHU.LOPsYQ0o7EpPFRMuh/X9ruwcmBcZbN.l/glBTfDKm//jJP60:17782:0:99999:7:::
#在所有被管理的主机的/etc/hosts文件里添加Ansible管理服务器的IP地址映射
[root@ansible ~]# ansible all -m shell -a 'echo "ansible 192.168.200.183" >> /etc/hosts'
Web02 | SUCCESS | rc=0 >>
Web01 | SUCCESS | rc=0 >>
[root@ansible ~]# ansible all -m shell -a 'tail -1 /etc/hosts'
Web01 | SUCCESS | rc=0 >>
ansible 192.168.200.183
Web02 | SUCCESS | rc=0 >>
ansible 192.168.200.183

3.4 cron模块

Ansible中的cron模块用于定义任务计划。主要包括两种状态(state)

  • crontab时间周期:

    • minute:分钟
    • hour:小时
    • day:日期
    • month:月份
    • weekday:周期
  • crontab任务:

    • job:指明运行的命令是什么
  • crontab任务描述:

    • name:定时任务描述(定时任务清除的依据)
  • state状态:

    • present:表示添加(省略状态时默认使用);
    • absent:表示移除;
  • crontab任务的用户身份:

    • user:指定定时任务以哪个用户身份执行
#添加定时任务计划,在所有被管理的主机里每十分钟输出hello字符串,定时任务描述为test cron job
[root@ansible ~]# ansible all -m cron -a 'minute="*/10" job="/bin/echo hello" name="test cron job"'
Web02 | SUCCESS => {
"changed": true,
"envs": [],
"jobs": [
"test cron job"
]
}
Web01 | SUCCESS => {
"changed": true,
"envs": [],
"jobs": [
"test cron job"
]
}
[root@ansible ~]# ansible all -m shell -a 'crontab -l'
Web01 | SUCCESS | rc=0 >>
#Ansible: test cron job
*/10 * * * * /bin/echo hello
Web02 | SUCCESS | rc=0 >>
#Ansible: test cron job
*/10 * * * * /bin/echo hello
#删除描述为test cron job的定时任务
[root@ansible ~]# ansible all -m cron -a 'minute="*/10" job="/bin/echo hello" name="test cron job" state=absent'
Web02 | SUCCESS => {
"changed": true,
"envs": [],
"jobs": []
}
Web01 | SUCCESS => {
"changed": true,
"envs": [],
"jobs": []
}
[root@ansible ~]# ansible all -m shell -a 'crontab -l'
Web02 | SUCCESS | rc=0 >>
Web01 | SUCCESS | rc=0 >>
#给Web01服务器上的普通用户yunjisuan添加一个定时任务
[root@ansible ~]# ansible Web01 -m shell -a 'id yunjisuan'
Web01 | SUCCESS | rc=0 >>
uid=1000(yunjisuan) gid=1000(yunjisuan) 组=1000(yunjisuan)
[root@ansible ~]# ansible Web01 -m cron -a 'minute="*/10" job="/bin/echo hello" name="yunjisuan cron job" user="yunjisuan"'
Web01 | SUCCESS => {
"changed": true,
"envs": [],
"jobs": [
"yunjisuan cron job"
]
}
[root@ansible ~]# ansible Web01 -m shell -a 'crontab -u yunjisuan -l'
Web01 | SUCCESS | rc=0 >>
#Ansible: yunjisuan cron job
*/10 * * * * /bin/echo hello
[root@ansible ~]# ansible Web01 -m cron -a 'minute="*/10" job="/bin/echo hello" name="yunjisuan cron job" user="yunjisuan" state="absent"'
Web01 | SUCCESS => {
"changed": true,
"envs": [],
"jobs": []
}
[root@ansible ~]# ansible Web01 -m shell -a 'crontab -u yunjisuan -l'
Web01 | SUCCESS | rc=0 >>

3.5 copy模块

Ansible中的copy模块用于实现文件复制和批量下发文件。其中使用src来定义本地源文件路径;使用dest定义被管理主机文件路径;使用content则是使用指定信息内容来生成目标文件。

#将本地的/etc/hosts文件拷贝到所有被管理的主机的/etc/hosts路径下覆盖同名文件,并指定属主和权限,若拷贝的文件与目标文件内容不同,则备份目标文件再覆盖。
[root@ansible ~]# ansible all -m shell -a 'tail -1 /etc/hosts'
Web01 | SUCCESS | rc=0 >>
ansible 192.168.200.183
Web02 | SUCCESS | rc=0 >>
ansible 192.168.200.183
[root@ansible ~]# echo "web01 192.168.200.184" >> /etc/hosts
[root@ansible ~]# tail -1 /etc/hosts
web01 192.168.200.184
[root@ansible ~]# ansible all -m copy -a 'src=/etc/hosts dest=/etc/hosts owner=root mode=640 backup=yes'
Web01 | SUCCESS => {
"backup_file": "/etc/hosts.13083.2018-09-09@00:38:35~",
"changed": true,
"checksum": "80244bc6f9638a3505aae1a2bcf2228e69a00420",
"dest": "/etc/hosts",
"gid": 0,
"group": "root",
"md5sum": "de48f3cf45d11215fa7cfd0d558be954",
"mode": "0640",
"owner": "root",
"size": 180,
"src": "/root/.ansible/tmp/ansible-tmp-1536424715.44-39054260112988/source",
"state": "file",
"uid": 0
}
Web02 | SUCCESS => {
"backup_file": "/etc/hosts.12643.2018-09-09@00:38:36~",
"changed": true,
"checksum": "80244bc6f9638a3505aae1a2bcf2228e69a00420",
"dest": "/etc/hosts",
"gid": 0,
"group": "root",
"md5sum": "de48f3cf45d11215fa7cfd0d558be954",
"mode": "0640",
"owner": "root",
"size": 180,
"src": "/root/.ansible/tmp/ansible-tmp-1536424715.45-15307684711466/source",
"state": "file",
"uid": 0
}
[root@ansible ~]# ansible all -m shell -a 'tail -1 /etc/hosts'
Web02 | SUCCESS | rc=0 >>
web01 192.168.200.184
Web01 | SUCCESS | rc=0 >>
web01 192.168.200.184
[root@ansible ~]# ansible all -m shell -a 'ls /etc/hosts*'
Web01 | SUCCESS | rc=0 >>
/etc/hosts
/etc/hosts.13083.2018-09-09@00:38:35~ #这就是备份的文件
/etc/hosts.allow
/etc/hosts.deny
Web02 | SUCCESS | rc=0 >>
/etc/hosts
/etc/hosts.12643.2018-09-09@00:38:36~ #这就是备份的文件
/etc/hosts.allow
/etc/hosts.deny
#将本地/tmp/test.sh的脚本复制到远程主机上并远程激活
[root@ansible ~]# cat /tmp/test.sh
#!/bin/bash
echo "welcome to yunjisuan"
[root@ansible ~]# ansible all -m copy -a 'src=/tmp/test.sh dest=/tmp owner=root mode=500'
Web01 | SUCCESS => {
"changed": true,
"checksum": "70ae837e7367f5d4de9a3197709639ae14743000",
"dest": "/tmp/test.sh",
"gid": 0,
"group": "root",
"md5sum": "5ff4338de7d9ff0ded9fa3e0ecd15bab",
"mode": "0500",
"owner": "root",
"size": 41,
"src": "/root/.ansible/tmp/ansible-tmp-1536425049.22-149074464676784/source",
"state": "file",
"uid": 0
}
Web02 | SUCCESS => {
"changed": true,
"checksum": "70ae837e7367f5d4de9a3197709639ae14743000",
"dest": "/tmp/test.sh",
"gid": 0,
"group": "root",
"md5sum": "5ff4338de7d9ff0ded9fa3e0ecd15bab",
"mode": "0500",
"owner": "root",
"size": 41,
"src": "/root/.ansible/tmp/ansible-tmp-1536425049.23-32532320097185/source",
"state": "file",
"uid": 0
}
[root@ansible ~]# ansible all -m shell -a '/tmp/test.sh'
Web01 | SUCCESS | rc=0 >>
welcome to yunjisuan
Web02 | SUCCESS | rc=0 >>
welcome to yunjisuan

3.6 script模块

Ansible中的script模块可以将本地脚本复制到被管理主机的内存中并运行,不会在被管理主机中留下脚本文件。

#编写一个脚本,然后通过ansible的script模块远程向被管理主机执行此脚本
[root@ansible ~]# echo 'echo "1111" >> /tmp/test' >> /tmp/test.sh
[root@ansible ~]# cat /tmp/test.sh
echo "1111" >> /tmp/test
[root@ansible ~]# ansible all -m script -a '/tmp/test.sh'
Web01 | SUCCESS => {
"changed": true,
"rc": 0,
"stderr": "Shared connection to 192.168.200.184 closed.\r\n",
"stderr_lines": [
"Shared connection to 192.168.200.184 closed."
],
"stdout": "",
"stdout_lines": []
}
Web02 | SUCCESS => {
"changed": true,
"rc": 0,
"stderr": "Shared connection to 192.168.200.185 closed.\r\n",
"stderr_lines": [
"Shared connection to 192.168.200.185 closed."
],
"stdout": "",
"stdout_lines": []
}
[root@ansible ~]# ansible all -m shell -a 'cat /tmp/test'
Web02 | SUCCESS | rc=0 >>
1111
Web01 | SUCCESS | rc=0 >>
1111

3.7 yum模块

利用yum模块安装软件包,虽然能被shell模块替代
但是用yum模块更显专业一些

  • 软件包名:

    • name:指定软件包的名字
  • state状态:

    • present:安装软件包(默认就是这个)
    • absent:卸载软件包
#安装nmap软件包
[root@ansible ~]# ansible all -m yum -a 'name=nmap'
#卸载nmap软件包
[root@ansible ~]# ansible all -m yum -a 'name=nmap state=absent'

3.8 service模块

利用service模块管理服务程序,虽然能被shell模块替代
但是用service模块更显专业一些

  • 服务名称:

    • name:指定服务的名字
  • state状态:

    • started:启动服务
    • stopped:停止服务
    • restarted:重启服务
    • reloaded:平滑重载
  • enabled开机自启动:

    • true:设置开机自启动
    • false:设置开启不启动
#启动firewalld并设置开机自启动
[root@ansible ~]# ansible Web01 -m service -a 'name=firewalld state=started enabled=true'
#关闭firewalld并设置开机不启动
[root@ansible ~]# ansible Web01 -m service -a 'name=firewalld state=stopped enabled=false'

3.9 user模块

用户管理模块。管理用户账号

  • :指定用户名
    • name:指定操作的用户的名字
  • :用户描述
    • comment:指定用户的描述信息
  • :createhome:是否创建家目录
  • :uid:指定用户的uid号
  • :groups:指定用户的附加组(默认创建和用户名相同的组)
  • :password:指定用户的密码
  • :update_password:更新用户的密码
  • :shell指定用户的登陆方式
    • /bin/bash:能登录系统
    • /sbin/nologin:不能登录系统
  • :home:指定用户的家目录路径
  • :state状态:
    • present:创建用户(默认就是这个)
    • absent:删除用户
  • :remove:当指定state=absent时,确认是否删除用户家目录
    • true
    • false
#在Web02上创建一个普通用户yunjisuan,并设置用户的密码为123123
[root@ansible ~]# ansible Web02 -m user -a 'name=yunjisuan comment="welcom to yunjisuan" uid=1066 groups=wheel password=123123 shell=/bin/bash home=/home/yunjisuan'
Web02 | SUCCESS => {
"changed": true,
"comment": "welcom to yunjisuan",
"create_home": true,
"group": 1066,
"groups": "wheel",
"home": "/home/yunjisuan",
"name": "yunjisuan",
"password": "NOT_LOGGING_PASSWORD",
"shell": "/bin/bash",
"state": "present",
"system": false,
"uid": 1066
}
[root@ansible ~]# ansible Web02 -m shell -a 'tail -1 /etc/passwd'
Web02 | SUCCESS | rc=0 >>
yunjisuan:x:1066:1066:welcom to yunjisuan:/home/yunjisuan:/bin/bash
[root@ansible ~]# ansible Web02 -m shell -a 'tail -1 /etc/shadow'
Web02 | SUCCESS | rc=0 >>
yunjisuan:123123:17783:0:99999:7::: #密码居然是明文!!!

利用ansible的user模块状态用户时要注意在password参数的后边添加密文,否则不能登陆用户
通过Python的pip程序安装passlib即可为密码加密

yum -y groupinstall "Development tools"
yum install -y gcc gcc-c++ zlib zlib-devel libffi-devel openssl openssl-devel pcre pcre-devel yum-utils vim wget tree htop sqlite-devel
mkdir -p /data/python
cd /data/python/
#下载python源码包
wget https://www.python.org/ftp/python/3.11.3/Python-3.11.3.tgz
 tar -xzf Python-3.11.3.tgz 
ls
cd Python-3.11.3/
ls
./configure --prefix=/usr/local/python311/
make && make install     #编译安装python3
cat /etc/profile.d/python311.sh        #配置系统环境变量       
export PATH="/usr/local/python311/bin:${PATH}"
source /etc/profile          #使环境变量⽣效
pip -V                        #查看pip的版本
python3 -m pip install --upgrade pip   #升级pip版本
mkdir -p ~/.pip
vim ~/.pip/pip.conf                 #修改pip.conf文件
[global]
index-url = http://mirrors.aliyun.com/pypi/simple/
 
[install]
trusted-host = mirrors.aliyun.com
python3 -m pip list    查看已安装的第三⽅模块

#生成密文密码
[root@ansible ~]# python -c "from passlib.hash import sha512_crypt;import getpass;print sha512_crypt.encrypt(getpass.getpass())"
Password: #输入你想要加密的密码
$6$rounds=656000$Tw15COd8DLh/VS94$Mcmz/8CcjBKiEl0mYHcOQQCxEA5mz66EcGH2qXVk6o.Sm7FsRS.DsDVy6ET8iI6jDa045I94slZqWFwyYnRSW1 #加密后的密码
#删除之前创建的yunjisuan用户,并删除它的家目录
[root@ansible ~]# ansible Web02 -m user -a 'name=yunjisuan state=absent remove=true'
Web02 | SUCCESS => {
"changed": true,
"force": false,
"name": "yunjisuan",
"remove": true,
"state": "absent"
}
#继续在Web02上创建yunjisuan用户
[root@ansible ~]# ansible Web02 -m user -a 'name=yunjisuan comment="welcom to yunjisuan" uid=1066 groups=wheel password=$6$rounds=656000$Tw15COd8DLh/VS94$Mcmz/8CcjBKiEl0mYHcOQQCxEA5mz66EcGH2qXVk6o.Sm7FsRS.DsDVy6ET8iI6jDa045I94slZqWFwyYnRSW1 shell=/bin/bash' home=/home/yunjisuan'
[root@ansible ~]# ansible Web02 -m shell -a 'tail -1 /etc/shadow'
Web02 | SUCCESS | rc=0 >>
yunjisuan:$6$rounds=656000$Tw15COd8DLh/VS94$Mcmz/8CcjBKiEl0mYHcOQQCxEA5mz66EcGH2qXVk6o.Sm7FsRS.DsDVy6ET8iI6jDa045I94slZqWFwyYnRSW1:17783:0:99999:7::: #终于密文了

3.10 setup模块

Ansible中使用setup模块收集,查看被管理主机的facts(facts是Ansible采集被管理主机设备信息的一个功能)。每个被管理主机在接收并运行管理命令之前,都会将自己的相关信息(操作系统版本,IP地址等)发送给控制主机

#查看远程主机的facts信息
[root@ansible ~]# ansible Web01 -m setup | head
Web01 | SUCCESS => {
"ansible_facts": {
"ansible_all_ipv4_addresses": [
"192.168.200.184"
],
"ansible_all_ipv6_addresses": [
"fe80::20c:29ff:fe77:16ad"
],
"ansible_apparmor": {
"status": "disabled"
;