玩转企业常见应用与服务系列(十四):自动化运维工具 Ansible 基础入门

自动化运维简介

目前,随着IT行业的高速发展,市场上出现了一大批自动化管理工具,这些工具可以使得我们通过一台设备管理控制成千上万台不同的设备,使得我们更方便、更快捷的进行运维管理。

目前主流的自动化运维工具有PSSH、Puppet、Chef、SaltStack、Ansible等等,互联网企业使用最多的是Ansible、Saltstack和Puppet。

Ansible简介

ansible是一种基于python开发的自动化运维工具,它只需要在服务端安装ansible,无需在每个客户端安装客户端程序,通过ssh的方式来进行客户端服务器的管理,基于模块来实现批量数据配置、批量设备部署以及批量命令执行。

Ansible安装使用简单,并且基于上千和模块和插件实现各种软件、平台和版本的管理,支持虚拟容器多层级的部署。但是,Ansbile有一个显著的特点,就是管理过程非常缓慢,这也是由于其基于SSH来进行远程管理的特点决定的。

  • Ansible官网网站为https://www.ansible.com/

Ansible具有以下特点

  • 1、部署简单,只需要在控制设备上部署Ansible环境,而不需要在被控制设备上进行任何操作。
  • 2、使用SSH协议对设备进行管理。
  • 3、主从集中化管理。
  • 4、配置简单、功能强大、可扩展性强。
  • 5、支持API及自定义模块,可通过Python轻松扩展。
  • 6、通过playbooks来定制强大的配置、状态管理。
  • 7、对云计算平台、大数据都有很好的支持。

Ansible架构

可以看到,Ansible主要由5部分组成:

  • 1、Ansible
    • 为Ansible的核心,主要用于处理请求。
  • 2、Modules
    • 为Ansible的模块,包括Ansible自带的核心模块和自定义模块。
  • 3、Plugins
    • 为Ansible完成模块功能的补充,包括链接插件、邮件插件等等。
  • 4、Playbooks
    • 剧本,定义Ansible多任务配置文件,由Ansible自动执行。
  • 5、Inventory
    • 定义Ansible管理主机的清单。

Ansible 工作原理

大致工作原理就是ansible程序调用读取/etc/ansible/ansible.cfg配置文件获取主机列表清单/etc/ansible/hosts文件,获取所要处理的主机列表,然后查看剧本任务,在根据剧本中一系列任务生成一个临时的脚本文件,然后将该脚本文件发送给所管理的主机,脚本文件在远程主机上执行完成后返回结果,然后删除本地临时文件。

ansible 执行流程流程图
ansible工作模式

ansible分为两种工作模式:

  • 一是adhoc(点对点模式):此模式相当于对管理主机执行单个的shell命令
  • 二是playbook(剧本模式):该模式应用较多,该模式是指将一系列任务整合形成一个剧本,以此来达成某种功能(譬如部署某个服务,数据库备份等)的目的。

上述两种模式可类比于一个是执行单个shell命令,一个是shell脚本。

Ansible 安装与命令

安装比较简单,只要安装第三方源后,使用包管理工具dnf一键安装即可,步骤如下:

代码语言:javascript
复制
yum install -y epel-release
yum install -y ansible

安装完成后,为了方便管理可以配置ssh免密,命令如下:

代码语言:javascript
复制
ssh-keygen -t rsa //在/root/.ssh/目录下生产公钥(id_rsa.pub)和私钥(id_rsa)两个文件
ssh-copy-id root@<ip> //分发秘钥至需要免密登录的目标设备
ansible 配置文件

ansible 的配置文件为/etc/ansible/ansible.cfg,下面是常见参数。

代码语言:javascript
复制
inventory #管理的主机清单文件路径
library  #ansible的模块存放的目录
remote_tmp  #上述工作原理中提到的将脚本发送至对端的临时目录
local_tmp  #上述工作原理中提到本地生成脚本存放的临时目录
forks  #并发连接数,默认为5
sudo_user #命令执行用户
remote_port #访问管理主机的端口        
host_key_checking  #设置是否检查SSH主机的密钥,默认为false
timeout  #ssh连接被管理主机的超时时间
log_path  #ansilbe日志文件路径
Ansible命令参数详解

Ansible命令语法如下:

代码语言:javascript
复制
ansible [-i 主机文件] [-f 批次] [组名] [-m 模块名称] [-a 模块参数]

Ansible详细参数如下:

代码语言:javascript
复制
-v -vv -vvv -vvvv #表示Ansible输出的详细程度,“v”越多表示输出越详细 
-i #指定了主机文件,如果不指定,则默认为/etc/ansible/hosts文件,在主机文件中,定义了Ansible要控制的主机IP或域名,及其用户、密码。  
-f #表示指定开启同步进程的个数  
-m #表示Ansible要调用的模块  
-a #表示模块的参数  
-k #表示Ansible使用的SSH密码  
-sudo #表示使用Ansible获得sudo权限  
-u #表示指定Ansible执行的用户  
-C #表示命令测试

Ansible常用模块

以下模块举例主机组为client

ping模块连通性测试
代码语言:javascript
复制
[root@server ~]# ansible client -m ping                      //检测主机组主机连通性

192.168.91.128 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": false,
"ping": "pong"
}

command模块
代码语言:javascript
复制
[root@server ~]# ansible client -m command -a 'df -h'              //执行某个命令,不会通过shell进行处理,不支持管道

192.168.91.128 | CHANGED | rc=0 >>
Filesystem Size Used Avail Use% Mounted on
devtmpfs 362M 0 362M 0% /dev
tmpfs 392M 0 392M 0% /dev/shm
tmpfs 392M 26M 366M 7% /run
tmpfs 392M 0 392M 0% /sys/fs/cgroup
/dev/mapper/cl-root 17G 8.8G 8.3G 52% /
/dev/sda1 1014M 255M 760M 26% /boot
tmpfs 79M 24K 79M 1% /run/user/1000
/dev/sr0 723M 723M 0 100% /run/media/wujw/CentOS-8-4-2105-x86_64-dvd
tmpfs 79M 0 79M 0% /run/user/0

shell模块
代码语言:javascript
复制
ansible client -m shell -a 'df -h|grep dev'           //万能模块,支持所有shell命令

192.168.91.128 | CHANGED | rc=0 >>
devtmpfs 362M 0 362M 0% /dev
tmpfs 392M 0 392M 0% /dev/shm
/dev/mapper/cl-root 17G 8.8G 8.3G 52% /
/dev/sda1 1014M 255M 760M 26% /boot
/dev/sr0 723M 723M 0 100% /run/media/wujw/CentOS-8-4-2105-x86_64-dvd

copy模块

支持的参数:

代码语言:javascript
复制
src  #被复制到远程主机的本地文件。可以是绝对路径,也可以是相对路径。
content #用于替换"src",可以直接指定文件的内容。
dest #将源文件复制到的远程主机的绝对路径。
backup #当文件内容发生改变后,在覆盖之前把源文件备份。
force #当目标主机包含该文件,但内容不同时,设为"yes",表示强制覆盖;设为"no",表示目标主机的目标位置不存在该文件才复制。默认为"yes"。
代码语言:javascript
复制
[root@server ~]# ansible client -m copy -a 'src=mem.txt dest=/root'      //从当前设备拷贝文件至对端

192.168.91.128 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": true,
"checksum": "321b4039b2d0805781db9c38df6ab75cc1a4fe32",
"dest": "/root/mem.txt/192.168.91.128",
"gid": 0,
"group": "root",
"md5sum": "fb3a8f8cafdeccd3312e69eb18b5c205",
"mode": "0644",
"owner": "root",
"secontext": "system_u:object_r:admin_home_t:s0",
"size": 333,
"src": "/root/.ansible/tmp/ansible-tmp-1653312782.0254133-7348-72341069223651/source",
"state": "file",
"uid": 0
}

代码语言:javascript
复制
[root@server ~]# ansible client -m shell -a 'ls -l /root'    //查看文件已发送过来

192.168.91.128 | CHANGED | rc=0 >>
total 24
-rw-r--r--. 1 root root 142 May 18 10:27 1.sh
drwxr-xr-x. 2 root root 6 May 8 04:15 123
-rw-------. 1 root root 1429 Aug 20 2021 anaconda-ks.cfg
-rw-r--r--. 1 root root 1721 Aug 21 2021 initial-setup-ks.cfg
drwxr-xr-x. 2 root root 28 May 23 09:33 mem.txt
-rw-r--r--. 1 root root 53 May 18 10:54 temp.txt
drwxr-xr-x. 2 root root 79 May 8 04:04 test
-rw-r--r--. 1 root root 142 May 18 10:54 test.sh
-rw-r--r--. 1 tcpdump tcpdump 39 May 18 10:34 test.txt

file模块

支持的参数:

代码语言:javascript
复制
group #定义文件/目录的所属组
owner #定义文件/目录的所属用户。后面必须跟上path:定义文件/目录的路径
dest #被链接到的路径,只应用于state=link的情况
state #状态,有以下选项:
#directory:如果目录不存在,就创建目录
#file:即使文件不存在,也不会被创建
#link:创建软链接
#hard:创建硬链接
#touch:如果文件不存在,则会创建一个新的文件,如果文件或目录已存在,则更新其 最后修改时间
#absent:删除目录、文件或者取消链接文件
代码语言:javascript
复制
[root@server ~]# ansible client -m file -a 'path='/root/mem.txt' state=absent'
//删除文件/root/mem.txt
192.168.91.128 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": true,
"path": "/root/mem.txt",
"state": "absent"
}
代码语言:javascript
复制
[root@server ~]# ansible client -m shell -a 'ls -l /root'
//查看文件/root/mem.txt已被删除
192.168.91.128 | CHANGED | rc=0 >>
total 24
-rw-r--r--. 1 root root 142 May 18 10:27 1.sh
drwxr-xr-x. 2 root root 6 May 8 04:15 123
-rw-------. 1 root root 1429 Aug 20 2021 anaconda-ks.cfg
-rw-r--r--. 1 root root 1721 Aug 21 2021 initial-setup-ks.cfg
-rw-r--r--. 1 root root 53 May 18 10:54 temp.txt
drwxr-xr-x. 2 root root 79 May 8 04:04 test
-rw-r--r--. 1 root root 142 May 18 10:54 test.sh
-rw-r--r--. 1 tcpdump tcpdump 39 May 18 10:34 test.txt
fetch模块
代码语言:javascript
复制
dest  #用来存放文件的目录
src #在远程拉取的文件,并且必须是一个file,不能是目录
代码语言:javascript
复制
[root@server ~]# ansible client -m fetch -a 'src=/root/test.sh dest=/root'

192.168.91.128 | SUCCESS => {
"changed": false,
"checksum": "061375bc913062287ccd658347e8575d38dfa857",
"dest": "/root/192.168.91.128/root/test.sh",
"file": "/root/test.sh",
"md5sum": "35dcd724c02d10850a560df9129a1cf2"
}

cron模块
代码语言:javascript
复制
day #每天应该运行的工作( 1-31)
hour #每小时 ( 0-23 )
minute #每分钟( 0-59 )
month #每月( 1-12 )
weekday #每周 ( 0-6 for Sunda y-Saturday,, )
job# 指明运行的命令
name #定时任务描述
reboot #任务在重启时运行,不建议使用,建议使用special_time
special_time #特殊的时间范围,参数:reboot(重启时),annually(每年),monthly(每月),weekly(每周),daily(每天),hourly(每小时)
state #指定状态,present表示添加定时任务,也是默认设置,absent表示删除定时任务
user #以哪个用户的身份执行
代码语言:javascript
复制
[root@server ~]# ansible client -m cron -a 'name="restart httpd" hour=*/5 job="systemctl restart httpd"'
//每5小时重启httpd模块
192.168.91.128 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": true,
"envs": [],
"jobs": [
"restart httpd"
]
}
[root@server ~]# ansible client -m shell -a 'crontab -l'
192.168.91.128 | CHANGED | rc=0 >>
#Ansible: restart httpd

  • */5 * * * systemctl restart httpd
yum模块

支持的参数:

代码语言:javascript
复制
name:安装包名称
state:
present:安装
latest:安装最新的
absent: 卸载软件
代码语言:javascript
复制
ansible client -m yum -a 'name=httpd state=present'
service模块

支持的参数:

代码语言:javascript
复制
enabled :设置开机启动。
name : 服务名称
state :四种状态
#started:启动服务
#stopped:停止服务
#restarted:重启服务
#reloaded:重载配置
代码语言:javascript
复制
[root@server ~]# ansible client -m service -a 'name=httpd state=restarted'    //重启httpd服务

192.168.91.128 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": true,
"name": "httpd",
"state": "started",
"status": {
"ActiveEnterTimestamp": "Mon 2022-05-23 10:16:02 EDT",
"ActiveEnterTimestampMonotonic": "134284476109",
"ActiveExitTimestamp": "Mon 2022-05-23 10:16:01 EDT",
"ActiveExitTimestampMonotonic": "134283332327",
"ActiveState": "active",
........
}
}

user模块

支持的参数:

代码语言:javascript
复制
name:指定用户名
group:指定基本组
state:设置帐号状态,不指定为创建,指定值为absent表示删除
system:当创建一个用户,设置这个用户是系统用户。这个设置不能更改现有用户
uid: 指定用户的uid
password: 指定用户密码
代码语言:javascript
复制
[root@server ~]# ansible client -m user -a 'name=test uid=23'    //创建用户test,uid为23

192.168.91.128 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": true,
"comment": "",
"create_home": true,
"group": 1001,
"home": "/home/test",
"name": "test",
"shell": "/bin/bash",
"state": "present",
"system": false,
"uid": 23
}

删除用户state=absent

group模块

支持的参数:

代码语言:javascript
复制
gid:设置组ID
name:组的名称
state:组的状态,默认为创建,设置值为absent为删除
system:值为yes,表示创建为系统组
代码语言:javascript
复制
[root@server ~]# ansible client -m group -a 'name=test1 gid=233'     //创建组test1,gid为233

192.168.91.128 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": true,
"gid": 233,
"name": "test1",
"state": "present",
"system": false
}

script模块

该模块用于将本地的一个脚本文件在管理设备上执行,脚本文件需有可执行权限

代码语言:javascript
复制
ansible client -m script -a '/root/192.168.91.128/test.sh'
setup模块

该模块用于采集被管理设备信息并返回给服务端,后面跟--tree <目录>,可以将采集信息以ip为文件名保存至指定目录下。

代码语言:javascript
复制
ansible client -m setup    //输出所有设备信息

输出内容较多,可通过过滤采集我们需要的设备信息,譬如只要内存信息

代码语言:javascript
复制
[root@server root]# ansible client -m setup -a 'filter=*mem*'
192.168.91.128 | SUCCESS => {
"ansible_facts": {
"ansible_memfree_mb": 55,
"ansible_memory_mb": {
"nocache": {
"free": 216,
"used": 566
},
"real": {
"free": 55,
"total": 782,
"used": 727
},
"swap": {
"cached": 56,
"free": 1175,
"total": 2047,
"used": 872
}
},
"ansible_memtotal_mb": 782,
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": false
}

再或者仅要ipv4地址:

代码语言:javascript
复制
[root@server root]# ansible client -m setup -a 'filter=*all_ipv4*'

192.168.91.128 | SUCCESS => {
"ansible_facts": {
"ansible_all_ipv4_addresses": [
"192.168.122.1",
"192.168.91.128"
],
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": false
}

unarchive 模块

将ansible主机上的压缩包在本地解压缩后传到远程主机上,或者将远程主机上的某个压缩包解压缩到指定路径下。

常用选项:

代码语言:javascript
复制
creates #一个文件名,当它已经存在时,这个步骤将不会被运行。
copy #默认为yes,拷贝的文件从ansible主机复制到远程主机,no在远程主机上寻找src源文件解压
src #tar源路径,可以是ansible主机上的路径,也可以是远程主机上的路径,如果是远程主机上的路径,则需设置copy=no
dest #远程主机上的目标绝对路径
mode #设置解压缩后的文件权限
exec #列出需要排除的目录和文件
remote_src #设置remote_src=yes为解包目标上已经存在的档案。对于Windows目标,改用win_unzip模块。
owner #解压后文件或目录的属主
group #解压后的目录或文件的属组
代码语言:javascript
复制
#解压ansible管理机上的压缩文件到远程主机并设置权限
[root@localhost ~]$ ansible all -m unarchive -a "src=/tmp/install/zabbix-3.0.4.tar.gz dest=/tmp/ mode=755 copy=yes"

#在远程主机上解压文件并设置权限
[root@localhost ~]$ ansible all -m unarchive -a 'src=/srv/tomcat8/apache-tomcat-8.0.29.tar.gz dest=/usr/local copy=no mode=755'

  • hosts: test
    remote_user: root
    gather_facts: False
    tasks:

    • name: tar tomcat
      unarchive: src=/srv/tomcat8/apache-tomcat-8.0.29.tar.gz dest=/usr/local copy=yes
      notify:
      • restart tomcat

    handlers:

    • name: restart tomcat
      shell: service tomcat restart
lineinfile 模块

用于对远程受控节点的文件编辑模块。常用选项:

代码语言:javascript
复制
path #指定要修改的配置文件, 包括:
regexp:匹配要修改的内容,可以使用政策
line:要增加或者修改的内容
state #状态, 包括:
absent:表示删除,当匹配到时进行删除
present:表示增加,当匹配到时进行修改,当没有匹配到时在最后增加一行,默认为此项
backrefs #该参数值包括:
no:表示如果没有匹配到,则增加line;如果匹配成功,则替换line;
yes:表示如果没有匹配到,则不变line;如果匹配成功,则替换line;
backup#该参数值包括:
no:表示如果没有匹配到,则增加line;如果匹配成功,则替换line;不备份原文件
yes:表示如果没有匹配到,则增加line;如果匹配成功,则替换line;备份原文件
insertafter(匹配的是此行) #在匹配到的行之后添加一行. (经测试, 发现是匹配到的行的最后一行的后面添加一行)
insertbefore(匹配的是此行) #在匹配到的行之前添加一行. (经测试, 发现是匹配到的行的最后一行的前面添加一行)
代码语言:javascript
复制
#将远程受控节点的/data/test文件中的"123"字段修改为"wangshibo"
[root@localhost ~] ansible web-nodes -m lineinfile -a 'path=/data/test regexp="123" line="wangshibo" backrefs=no'

将line开头的行替换为test test

[root@localhost ~]$ ansible test -m lineinfile -a "path=/testdir/test regexp='^line' line='test test'"

#匹配到的行后增加一行
[root@localhost ~]$ ansible test -m lineinfile -a 'dest=/data/test insertafter="wangshibo" line="huihui"'

#匹配到的行前增加一行
[root@localhost ~]$ ansible test -m lineinfile -a 'dest=/data/test insertbefore="root" line="huihui"'

#删除匹配到的行:
[root@localhost ~]$ ansible test -m lineinfile -a 'path=/data/test regexp="123" state=absent'

mount 模块

在远程受控节点上挂载文件系统。常用选项:

代码语言:javascript
复制
present  #开机挂载,仅将挂载配置写入/etc/fstab(不常用)
mounted #挂载设备,并将配置写入/etc/fstab
unmounted #卸载设备,不会清除/etc/fstab写入的配置
absent #卸载设备,会清理/etc/fstab写入的配置
代码语言:javascript
复制
#将受控节点的/dev/sd0设备挂载到/mnt/data目录上, 文件格式为ext4, 只读属性
[root@localhost ~]$ ansible web-nodes -m mount -a "path=/mnt/data src=/dev/sd0 fstype=ext4 ots=ro state=present"

#仅将挂载的配置写入/etc/fstab,并不会执行挂载操作
[root@localhost ~]$ ansible test -m mount -a "src=172.16.60.220:/data path=/data fstype=nfs opts=defaults state=present"

#临时挂载设备,并将挂载信息写入/etc/fstab
[root@localhost ~]$ ansible test -m mount -a "src=172.16.60.220:/data path=/data fstype=nfs opts=defaults state=mounted"

#临时卸载,不会清理/etc/fstab
[root@localhost ~]$ ansible test -m mount -a "src=172.16.60.220:/data path=/data fstype=nfs opts=defaults state=unmounted"

#卸载,不仅临时卸载,同时会清理/etc/fstab
[root@localhost ~]$ ansible test -m mount -a "src=172.16.60.220:/data path=/data fstype=nfs opts=defaults state=absent"

wait_for模块

当你利用service 启动tomcat,或数据库后,他们真的启来了么?这个你是否想确认下?wait_for模块就是干这个的。

######选项

代码语言:javascript
复制
connect_timeout
#连接的超时时间,默认是5秒。
delay
#开始轮询之前等待的秒数,默认是0。
exclude_hosts
#与state=drained一起使用。用于指定,在寻找活跃的TCP链接的时候,要忽略的主机或IP列表。
host
#要等待的 可解析的主机名 或 IP地址。
path
#在继续之前,文件系统上必须存在的文件的路径。
port
#要轮询的端口。
search_regex
#用于匹配文件或socket链接中的一个字符串。
state
#可以是present、started、stopped、absent、drained。
当检查端口的时候,started会确保端口打开;stopped会确保端口关闭;drained会检查活跃的链接。当检查文件或搜索字符串的时候,present和started会确保文件或字符串存在。absent会确保文件不存在或被移除。(Choices: present, started, stopped, absent, drained)[Default: started]
timeout
#等待的超时时间。默认是300秒。
例子
代码语言:javascript
复制
wait_for: port:8000 delay=10
等待8000端口打开,每10秒检查一次。超时时间是300秒。

wait_for: host=0.0.0.0 port=8000 delay=10 state=drained
等待所有本地IP上的8000端口,关闭活跃连接。每10秒检查一次,超时时间是300秒。

wait_for: host=0.0.0.0 port=8000 state=drained exclude_hosts=10.2.1.2,10.2.1.3
等待所有本地IP上的8000端口,关闭活跃的连接。忽略来自10.2.1.2和10.2.1.3上的连接。超时时间是300秒。

wait_for: path=/tmp/foo
一直等到/tmp/foo这个文件存在。

wait_for: path=/tmp/foo search_regex=completed
一直等到字符串completed出现在文件/tmp/foo中。

wait_for: path=/var/lock/file.lock state=absent
一直等到lock文件被删除。

wait_for: path=/proc/3466/status state=absent
一直等到进程结束,并且pid被销毁。

local_action: wait_for port=22 host="{{ ansible_ssh_host | default(inventory_hostname) }}" search_regex=OpenSSH delay=10
等待22端口被打开,并且包含字符串OpenSSH。并且不确保inventory_hostname是可解析的。每10秒检查一次,超时时间是300秒。

参考链接:https://blog.csdn.net/weixin_40228200
/article/details/123428190 https://blog.csdn.net/
m0_64496909/article/details/124913290
https://blog.csdn.net/w918589859/article

/details/111467805