文章目录

  • Ansible
    • Ansible的安装
    • Ineventory主机清单
      • 基于密钥连接
    • Ad-Hoc
      • 常用模块
      • 帮助手册doc
      • bash模块
      • yum模块
    • 文件管理模块
      • copy文件拷贝模块
      • file文件创建模块
      • get_url文件下载模块
      • 启停服务模块
      • group组模块
      • user模块
      • crond定时任务模块
      • mount挂载模块7 nfs 8客户端挂载
      • 防火墙管理模块
  • Ansible-Playbook
    • PlayBook部署httpd小练习
    • PlayBook实战
      • 实战一
      • 实战二:2
    • Ansible跑起wordpress
  • ansible变量
    • 变量的优先级
    • 定义变量
    • 变量注册register
    • facts变量
      • setup模块
    • 关闭facts
    • 常用变量信息参数
  • playbook控制
    • Playbook条件语句
      • 示例
      • 添加yum仓库模块
    • playbook循环语句
    • handlers
    • Playbook任务标签
    • playbook文件复用
    • Playbook忽略错误
    • Playbook错误处理
    • Ansible加密模块
  • Jinja2模板
    • Jinja2基本使用
    • 配置keepalived配置文件
    • mysql剧本模块
  • Roles
    • 解压模块
    • 数据库模块
    • 配置phpmyadmin

Ansible

ansible所需要掌握的内容

Ansible基础架构(被控端、控制端、inwentory、ad-hoc、playbook)
Inventory主机清单
Ad-Hoc
Playbook
变量
判断语句
异常处理
include包含
tag标记
handlers触发器
jinja模块
role角色
galaxy
ansible加密模块

Ansible自动化管理大纲

什么是Ansible
Ansible是一个IT 自动化的配置管理工具,自动化主要体现在Ansible集成了丰富的模块及丰富的功能的组,可以通过一个命令行
完成一系列的操作。进而能减少我们重复性的工作和维护成本,以提高工作的效率。

同类型软件对比

1.puppet 学习难,安装ruby环境难,没有远程执行功能
2.ansible 轻量级,大规模环境下只通过ssh会很慢,串行的
3.saltstack 一般选择salt会使用C/S结构的模式,salt-master和salt-minion,并行的,大规模批量操作的情况下,
会比Ansible速度快一些,底层使用的是zero-MQ消协队列

自动化运维时代

自动化运维工具的优势

Ansible的功能及优点

1.远程执行
批量执行远程命令,可以对多台主机进行远程操作2.配置管理
批量配置软件服务,可以进行自动化方式配置,服务的统一配置管理,和启停3.事件驱动
通过Ansible的模块,对服务进行不同的事件驱动
比如:
1)修改配置后重启
2)只修改配置文件,不重启
3)修改配置文件后,重新加载
4)远程启停服务管理4.管理公有云
通过API接口的方式管理公有云,不过这方面做的不如saltstack.
saltstack本身可以通过saltcloud管理各大云厂商的云平台。5.二次开发
因为语法是Python,所以便于运维进行二次开发。6.任务编排
可以通过playbook的方式来统一管理服务,并且可以使用一条命令,实现一套架构的部署7.跨平台,跨系统
几乎不受到平台和系统的限制,比如安装apache和启动服务在Ubuntu上安装apache服务名字叫apache2
在CentOS上安装apache服务名字叫httpd在CentOS6上启动服务器使用命令:/etc/init.d/nginx start
在CentOS7上启动服务器使用命令:systemctl start nginx

Ansible的架构

1、连接插件connection plugins用于连接主机 用来连接被管理端
2、核心模块core modules连接主机实现操作, 它依赖于具体的模块来做具体的事情
3、自定义模块custom modules根据自己的需求编写具体的模块
4、插件plugins完成模块功能的补充
5、剧本playbookansible的配置文件,将多个任务定义在剧本中,由ansible自动执行
6、主机清单inventor定义ansible需要操作主机的范围
最重要的一点是 ansible是模块化的 它所有的操作都依赖于模块

Ansible的执行流程

1.Ansible读取playbook剧本,剧本中会记录对哪些主机执行哪些任务。
2.首先Ansible通过主机清单找到要执行的主机,然后调用具体的模块。
3.其次Ansible会通过连接插件连接对应的主机并推送对应的任务列表。
4.最后被管理的主机会将Ansible发送过来的任务解析为本地Shell命令执行。

ANsible使用场景

Ansible基础架构(被控端、控制端、inventory、ad-hoc、playbook)

Ansible的安装

搭建环境

10.0.0.61        172.16.1.61         Ansible控制端     oldboy
10.0.0.7        172.16.1.7          Ansible被控端
10.0.0.8        172.16.1.8          Ansible被控端

安装epel

1.先安装epel源(提供最新的ansible)
wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo

安装ansible

2.安装Ansible
yum install ansible -y

查看ansible的版本

3.查看ansible的版本
[root@m01 ~]# ansible --version
ansible 2.7.7config file = /etc/ansible/ansible.cfgconfigured module search path = [u'/root/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']ansible python module location = /usr/lib/python2.7/site-packages/ansibleexecutable location = /usr/bin/ansiblepython version = 2.7.5 (default, Apr 11 2018, 07:36:10) [GCC 4.8.5 20150623 (Red Hat 4.8.5-28)]

Ansible配置文件读取顺序

4.Ansible的配置文件,配置文件可以随意放,但有查找顺序
$ANSIBLE_CONFIG             #从变量下查找
ansible.cfg                 #当前目录下面查找
.ansible.cfg                #当前用户的家目录下查找
/etc/ansible/ansible.cfg[root@m01 ~]# cat /etc/ansible/ansible.cfg
#inventory      = /etc/ansible/hosts      #主机列表配置文件
#library        = /usr/share/my_modules/  #库文件存放目录
#remote_tmp     = ~/.ansible/tmp          #临时py文件存放在远程主机目录
#local_tmp      = ~/.ansible/tmp          #本机的临时执行目录
#forks          = 5                       #默认并发数
#sudo_user      = root                    #默认sudo用户
#ask_sudo_pass  = True                     #每次执行是否询问sudo的ssh密码
#ask_pass       = True                    #每次执行是否询问ssh密码
#remote_port    = 22                      #远程主机端口
host_key_checking = False                 #跳过检查主机指纹
log_path = /var/log/ansible.log           #ansible日志[privilege_escalation]                   #如果是普通用户则需要配置提权
#become=True
#become_method=sudo
#become_user=root
#become_ask_pass=False

Ineventory主机清单

1.场景一、基于密码连接

[root@oldboy.com ~]# cat /etc/ansible/hosts#方式一、主机+端口+密码
[webservers]
172.16.1.7 ansible_ssh_port=22 ansible_ssh_user=root ansible_ssh_pass='123456'
172.16.1.8 ansible_ssh_port=22 ansible_ssh_user=root ansible_ssh_pass='123456'#方式二、主机+端口+密码
[webservers]
web[1:2].oldboy.com ansible_ssh_pass='123456'#方式三、主机+端口+密码
[webservers]
web[1:2].oldboy.com
[webservers:vars]
ansible_ssh_pass='123456'

注意

如果控制端和被控制端第一次通讯,需要先添加指纹信息,那如果机器特别多少的情况下怎么办?[root@m01 ~]# vim /etc/ansible/ansible.cfg
#host_key_checking = False  打开注释,默认false他就不用认证了.#但是我们一般使用的是推送公钥的办法。

基于密钥连接

我们真正需要使用的方式

需要先创建公钥和私钥,并下发公钥至被控端
[root@m01 ~]# ssh-keygen
[root@m01 ~]# ssh-copy-id -i ~/.ssh/id_rsa.pub root@172.16.1.7
[root@m01 ~]# ssh-copy-id -i ~/.ssh/id_rsa.pub root@172.16.1.8-----------------------------------------------------------
[root@m01 ~]# cat hosts
#方式一、主机+端口+密钥
[webservers]
172.16.1.7
172.16.1.8[root@m01 ~]# ansible webservers -m ping -i ./hosts
172.16.1.8 | SUCCESS => {"changed": false, "ping": "pong"
}
172.16.1.7 | SUCCESS => {"changed": false, "ping": "pong"
}
-----------------------------------------------------------
[root@m01 ~]# cat hosts
#方式二、别名+主机+端口+密钥
[webservers]
web01 ansible_ssh_host=172.16.1.7 ansible_ssh_port=22
web02 ansible_ssh_host=172.16.1.8[root@m01 ~]# ansible webservers -m ping -i ./hosts
web02 | SUCCESS => {"changed": false, "ping": "pong"
}
web01 | SUCCESS => {"changed": false, "ping": "pong"
}
--------------------------------------------------
场景三、主机组使用方式
#1.在inventory表里面定义两个组
root@m01 ~]# cat /etc/ansible/hosts
[lbservers]
172.16.1.5
172.16.1.6[webservers]
172.16.1.7
172.16.1.8#2.servers组包括两个子组[lbservers,webserver]
[servers:children]
lbservers       #这里为组的名字
webserver        #这里为组的名字

列出当前某个组有多少台主机

[root@m01 ~]# ansible lbservers -m ping -i ./hosts --list-hostshosts (1):web01
[root@m01 ~]# ansible webservers -m ping -i ./hosts --list-hostshosts (1):web02
[root@m01 ~]# ansible servers -m ping -i ./hosts --list-hostshosts (2):web01web02
[root@m01 ~]# ansible all -m ping -i ./hosts --list-hostshosts (3):web03web02web01

Ad-Hoc

1.什么是ad-hoc
ad-hoc简而言之就是“临时名列“,执行完即结束,并不会保存,2.ad-hoc模式使用场景:
比如在多台机器上查看某个进程启动,或拷贝指定文件到本地,等等。3.ad-hoc模式的命令使用,ansible 'zlx' -m command -a 'df -h',含义如下图

常用模块

command             # 执行shell命令(不支持管道等特殊字符)
shell               # 执行shell命令
scripts             # 执行shell脚本
yum_repository      # 配置yum仓库
联网下载        get_url
安装          yum
配置          copy
启动          service、systemd
创建用户与组  user、group
授权          file
定时任务        crond
挂载          mount
firewalld       firewall
selinux         selinux

批量查看磁盘信息

[root@m01 ~]# ansible web_group -m command -a 'df -h' -i ./hosts
web02 | CHANGED | rc=0 >>
文件系统        容量  已用  可用 已用% 挂载点
/dev/sda3        18G  1.1G   17G    6% /
devtmpfs        981M     0  981M    0% /dev
tmpfs           992M     0  992M    0% /dev/shm
tmpfs           992M  9.5M  982M    1% /run
tmpfs           992M     0  992M    0% /sys/fs/cgroup
/dev/sda1      1014M  124M  891M   13% /boot
tmpfs           199M     0  199M    0% /run/user/0web01 | CHANGED | rc=0 >>
文件系统        容量  已用  可用 已用% 挂载点
/dev/sda3        18G  1.1G   17G    6% /
devtmpfs        981M     0  981M    0% /dev
tmpfs           992M     0  992M    0% /dev/shm
tmpfs           992M  9.5M  982M    1% /run
tmpfs           992M     0  992M    0% /sys/fs/cgroup
/dev/sda1      1014M  124M  891M   13% /boot
tmpfs           199M     0  199M    0% /run/user/0

#批量查看内存信息

[root@m01 ~]# ansible web_group -m command -a 'free -m' -i ./hosts
web01 | CHANGED | rc=0 >>total        used        free      shared  buff/cache   available
Mem:           1982         143        1688           9         150        1668
Swap:          1023           0        1023web02 | CHANGED | rc=0 >>total        used        free      shared  buff/cache   available
Mem:           1982         142        1684           9         155        1666
Swap:          1023           0        1023

帮助手册doc

6.使用过程中需要先了解ansible-doc帮助手册

[root@m01 ~]# ansible-doc -l        # 查看所有模块说明
[root@m01 ~]# ansible-doc copy      # 表示指定模块方法
[root@m01 ~]# ansible-doc -s copy   # 表示指定模块参数

bash模块

7.command默认执行bash命令模块,模块不支持重定向或管道

[root@m01 ~]# ansible web  -a "hostname"

8.shell模块,如果需要一些管道操作,则使用shell

[root@m01 ~]# ansible web -m shell -a "ifconfig|grep eth0" -f 50

9.script脚本模块

[root@m01 ~]# cat yum.sh
#!/usr/bin/bash
yum install -y iftop#在本地运行模块,等同于在远程执行,不需要将脚本文件进行推送目标主机执行
[root@m01 ~]# ansible web -m script -a "/server/scripts/yum.sh"

yum模块

10.yum安装软件模块

[root@m01 ~]# ansible web -m yum -a "name=httpd state=installed"name                                httpd                           #指定要安装的软件包名称file://                         #指定从本地哪个目录安装rpmhttp://                          #指定从哪个网站安装rpm包state                         #指定使用yum的方法present                          #安装软件包absent                        #移除软件包latest                            #安装最新软件包
list=ansible                       #列出当前仓库可用的软件包
disablerepo="epel,ol7_latest"        #安装软件时,不从哪些仓库获取download_only=true                   #仅下载软件包,不安装
download_dir=/root                  #存放路径

文件管理模块

copy文件拷贝模块

#1.拷贝文件文件至被控节点
[root@m01 ~]# ansible oldboy -m copy -a "src=/etc/hosts dest=/tmp/test.txt"#2.对远端已有文件进行备份,按照时间信息备份
[root@m01 ~]# ansible oldboy -m copy -a "src=/etc/hosts dest=/tmp/test.txt backup=yes"#3.向被控端主机写入数据,并且会覆盖远端文件内原有数据信息
[root@m01 ~]# ansible oldboy -m copy -a "content='bgx' dest=/tmp/oldboy"src             #推送数据的源文件信息
dest            #推送数据的目标路径backup          #对推送传输过去的文件,进行备份  只有文件发生改变的时候才会产生新备份
content         #直接批量在被管理端文件中添加内容   他是覆盖式的,而不是追加,所以使用时一定要小心。group           #将本地文件推送到远端,指定文件属组信息
owner           #将本地文件推送到远端,指定文件属主信息
mode            #将本地文件推送到远端,指定文件权限信息

file文件创建模块

1.直接修改被控端的权限
[root@m01 ~]# ansible web01 -m file -a "path=/opt mode=0400" -i ./hosts2.在被控端创建目录
[root@m01 ~]# ansible oldboy -m file -a "path=/tmp/oldboy state=directory"3.在被控端创建文件
[root@m01 ~]# ansible oldboy -m file -a "path=/tmp/tt state=touch mode=555 owner=root group=root"4.递归授权目录权限
[root@m01 ~]# ansible oldboy -m file -a "path=/data owner=bgx group=bgx recurse=yes"
path            #指定远程主机目录或文件
recurse         #递归授权
state           #状态
directory       #在远端创建目录
touch           #在远端创建文件
link            #创建链接文件
absent          #表示删除文件或目录
mode            #设置文件或目录权限
owner           #设置文件或目录属主
group           #设置文件或目录属组

get_url文件下载模块


1.通过get_url下载文件或者软件
[root@m01 ~]# ansible webservers -m get_url -a "url=http,https  dest=/opt mode=0777" -i ./hosts2.下载一个文件前先进行md5校验,通过则下载,不通过则失败
ansible webservers -m get_url -a "url=http,https  dest=/opt mode=0777 checksum=md5:76eb3af80ffd" -i ./hosts
url         #文件在网络上的具体位置
dest        #下载到被控端的哪个目录下
checksum    #校验(md5  sha256)

启停服务模块

ansible管理服务的启动与停止,使用service、systemd


#1.启动crond服务,并加入开机自启
[root@m01 ~]# ansible webservers -m service -a "name=crond state=started enabled=yes"
#2.停止crond服务,并删除开机自启
[root@m01 ~]# ansible webservers -m service -a "name=crond state=stopped enabled=no"
#3.重启crond服务
[root@m01 ~]# ansible webservers -m service -a "name=crond state=restarted"
#4.重载crond服务
[root@m01 ~]# ansible webservers -m service -a "name=crond state=reloaded"name        # 定义要启动服务的名称
state       # 指定服务状态
started     #启动服务
stopped     #停止服务
restarted   #重启服务
reloaded    #重载服务
enabled         #开机自启

group组模块


[root@m01 ~]# ansible webservers -m group -a "name=oldgirl gid=888"
name            #指定创建的组名
gid             #指定组的gid
state:
absent          #移除远端主机的组
present         #创建远端主机的组(默认)

user模块


1.创建用户指定uid和gid,不创建家目录也不允许登陆
[root@m01 ~]# ansible oldboy -m user -a "name=oldgirl uid=888 group=888 shell=/sbin/nologin create_home=no"2.删除用户
[root@m01 ~]# ansible webservers -m user -a "name=tmd state=absent" -i ./hosts 3.给新创建的用户生成ssh密钥对
[root@m01 ~]# ansible webservers -m user -a "name=oo uid=6677 group=adm generate_ssh_key=yes ssh_key_bits=2048
ssh_key_file=.ssh/id_rsa" -i ./hosts 4.将明文密码进行hash加密,然后进行用户创建
[root@m01 ~]# ansible localhost -m debug -a "msg={{ '123456' | password_hash('sha512', 'salt') }}"
localhost | SUCCESS => {"msg": "$6$salt$MktMKPZJ6t59GfxcJU20DwcwQzfMvOlHFVZiOVD71w.igcOo1R7vBYR65JquIQ/7siC7VRpmteKvZmfSkNc69."
}
[root@m01 ~]# ansible webservers -m user -a 'name=xlw password=$6$salt$MktMKPZJ6t59GfxcJU20DwcwQzfMvOlHFVZ
iOVD71w.igcOo1R7vBYR65JquIQ/7siC7VRpmteKvZmfSkNc69. create_home=yes shell=/bin/bash' -i ./hosts uid             #指定用户的uid
group           #指定用户组名称
groups          #指定附加组名称
password        #给用户添加密码(记得单引号)
shell           #指定用户登录shell
create_home     #是否创建家目录

crond定时任务模块


# 正常使用crond服务(默认没写的时间都算*表示)
[root@m01 ~]# crontab -l
* * * * *  /bin/sh /server/scripts/yum.sh# 使用ansible添加一条定时任务
[root@m01 ~]# ansible webservers -m cron -a "minute=* hour=* day=* month=* weekday=* job='/bin/sh test.sh'"
[root@m01 ~]# ansible webservers -m cron -a "job='/bin/sh /server/scripts/test.sh'"# 设置定时任务注释信息,防止重复,name设定
[root@m01 ~]# ansible webservers -m cron -a "name='cron01' job='/bin/sh /server/scripts/test.sh'"# 删除相应定时任务
[root@m01 ~]# ansible webservers -m cron -a "name='ansible cron02' minute=0 hour=0 job='/bin/sh test.sh' state=absent"# 注释相应定时任务,使定时任务失效
[root@m01 scripts]# ansible oldboy -m cron -a "name='ansible cron01' minute=0 hour=0 job='/bin/sh test.sh' disabled=yes"

mount挂载模块7 nfs 8客户端挂载

[root@m01 ~]# ansible web01 -m yum -a 'name=nfs-utils state=present' -i ./hosts
[root@m01 ~]# ansible web01 -m file -a 'path=/data state=directory' -i ./hosts
[root@m01 ~]# ansible web01 -m copy -a 'content="/data 172.16.1.0/24(rw,sync,no_all_squash)" dest=/etc/exports' -i ./hosts
[root@m01 ~]# ansible web01 -m systemd -a "name=nfs state=started enabled=yes" -i ./hosts[root@m01 ~]# ansible web02 -m mount -a "src=172.16.1.7:/data path=/data fstype=nfs opts=defaults state=present"
[root@m01 ~]# ansible web02 -m mount -a "src=172.16.1.7:/data path=/data fstype=nfs opts=defaults state=mounted"
[root@m01 ~]# ansible web02 -m mount -a "src=172.16.1.7:/data path=/data fstype=nfs opts=defaults state=unmounted"
[root@m01 ~]# ansible web02 -m mount -a "src=172.16.1.7:/data path=/data fstype=nfs opts=defaults state=absent"present     # 开机挂载,仅将挂载配置写入/etc/fstab
mounted     # 挂载设备,并将配置写入/etc/fstab
unmounted   # 卸载设备,不会清除/etc/fstab写入的配置
absent      # 卸载设备,会清理/etc/fstab写入的配置

防火墙管理模块


Selinux模块
[root@m01 ~]# ansible webservers -m selinux -a "state=disabled" -i ./hostsfirewalld模块
[root@m01 ~]# ansible webservers -m systemd -a "name=firewalld state=started" -i ./hosts
[root@m01 ~]# ansible webservers -m firewalld -a "service=http immediate=yes permanent=yes state=enabled" -i ./hosts
[root@m01 ~]# ansible webservers -m firewalld -a "port=8080-8090/tcp immediate=yes permanent=yes state=enabled" -i ./hosts service               #指定开放或关闭的服务名称
port                #指定开放或关闭的端口
masquerade          #开启地址伪装
immediate           #临时生效
permanent           #是否添加永久生效
state               #开启或是关闭zone             #指定配置某个区域
rich_rule           #配置富规则
source              #指定来源IP

Ansible-Playbook

什么是PlayBook

PlayBook即”剧本”,”兵书”之意,PlayBook是由以下部分组成的- host(play:) 定义的是主机的角色。(主角还是配角,找哪个明星)
task: 定义的是具体执行的任务。(角色的台词和动作)playbook: 由一个或多个play(角色)组成,一个play(角色)可以包含多个task(台词,动作,大腕每集拍什么)。简单理解为: 使用不同的模块完成一件事情

在Ansible中”剧本文件”是以yml结尾的文件。
在SaltStack中”剧本文件”是以sls结尾的文件。
但是语法,使用的都是yaml语法

PlayBook与ad-hoc

1.PlayBook功能比ad-hoc更全,是对ad-hoc的一种编排.
2.PlayBook能很好的控制先后执行顺序, 以及依赖关系.
3.PlayBook语法展现更加的直观.
4.playbook可以持久使用,ad-hoc无法持久使用.

YAML语法

语法 描述
缩进 YAML使用固定的缩进风格表示层级结构,每个缩进由两个空格组成, 不能使用TAB
冒号 以冒号结尾的除外,其他所有冒号后面所有必须有空格
短横线 表示列表项,使用一个短横杠加一个空格,多个项使用同样的缩进级别作为同一列表
yum:name: vsftpdstate: presentyum:name:- httpd- nginx- php-fpmstate: present

PlayBook部署httpd小练习

编写httpd剧本


#创建目录剧本存放目录
[root@m01 ~]# mkdir httpd#编辑Inventory
[root@m01 ~]# vim /etc/ansible/hosts
[web]
web01 ansible_ssh_host=10.0.0.7
web02 ansible_ssh_host=10.0.0.8

需求一:编写安装httpd剧本

[root@m01 ~]# vim /root/httpd/httpd.yml
---
- hosts: web_grouptasks:- name: Install httpd Serveryum:name: httpdstate: present#检查语法
[root@m01 ~]# ansible-playbook --syntax-check httpd/httpd.ymlplaybook: httpd/httpd.yml#测试安装
[root@m01 ~]# ansible-playbook -C httpd/httpd.ymlPLAY [web_group] *******************************************************************************TASK [Gathering Facts] ************************************************************************************************
ok: [web02]
ok: [web01]TASK [Install httpd Server] ************************************************************************************************
changed: [web02]
changed: [web01]PLAY RECAP *************************************************************************************
web01                      : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
web02                      : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

需求二:安装完httpd服务并启动加入开机自启

[root@m01 ~]# vim /root/httpd/httpd.yml
---
- hosts: web_group#安装httpdtasks:- name: Install httpd Serveryum:name: httpdstate: present
#启动httpd- name: Start Httpd Serversystemd:name: httpdstate: startedenabled: yes#测试安装和启动
[root@m01 ~]# ansible-playbook -C httpd/httpd.ymlPLAY [web_group]
************************************************************************TASK [Gathering Facts] ************************************************************************
ok: [web02]
ok: [web01]TASK [Install httpd Server] ************************************************************************
changed: [web02]
changed: [web01]TASK [Start Httpd Server] ************************************************************************
changed: [web02]
changed: [web01]PLAY RECAP *************************************************************
web01                      : ok=3    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
web02                      : ok=3    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

需求三:编写网站页面并启动

- hosts: web_group#安装httpdtasks:- name: Install httpd Serveryum:name: httpdstate: present#配置网站- name: Config Httpd Servercopy:content: oldboy_web_pagedest: /var/www/html/index.html
#启动httpd- name: Start Httpd Serversystemd:name: httpdstate: startedenabled: yes#执行
[root@m01 httpd]# ansible-playbook /root/httpd/httpd.yml

需求四:开启防火墙端口

- hosts: web_group#安装httpdtasks:- name: Install httpd Serveryum:name: httpdstate: present#配置网站- name: Config Httpd Servercopy:content: oldboy_web_pagedest: /var/www/html/index.html
#启动httpd- name: Start Httpd Serversystemd:name: httpdstate: startedenabled: yes#启动防火墙- name: Start Firewalld Serversystemd:name: firewalldstate: startedenabled: yes#开启防火墙的80端口- name: Config Firewalld Serverfirewalld:service: httpimmediate: yespermanent: yesstate: enabled

打开浏览器访问网站:

http://10.0.0.7
http://10.0.0.8

需求五:不同的主机配置不同的网站

---
- hosts: web_group#安装httpdtasks:- name: Install httpd Serveryum:name: httpdstate: present#启动httpd- name: Start Httpd Serversystemd:name: httpdstate: startedenabled: yes#启动防火墙- name: Start Firewalld Serversystemd:name: firewalldstate: startedenabled: yes#开启防火墙的80端口- name: Config Firewalld Serverfirewalld:service: httpimmediate: yespermanent: yesstate: enabled#单独配置web01页面
- hosts: web01tasks:- name: Config Httpd Servercopy:content: oldboy_web01_pagedest: /var/www/html/index.html#单独配置web02页面
- hosts: web02tasks:- name: Config Httpd Servercopy:content: oldboy_web02_pagedest: /var/www/html/index.html[root@m01 httpd]# ansible-playbook  /root/httpd/httpd.yml

打开浏览器访问网站:

http://10.0.0.7
http://10.0.0.8

PlayBook实战

实战一

配置环境

主机名 公网ip 私网ip 服务 角色
m01 10.0.0.61 172.16.1.61 Ansible 控制端
backup 10.0.0.41 172.16.1.41 rsync服务端 被控端
web01 10.0.0.7 172.16.1.7 rsync服务端 被控端
web02 10.0.0.8 72.16.1.8 rsync服务端 被控端

准备配置文件

#创建rsync剧本存放目录
[root@m01 ~]# mkdir rsyncd#编辑Inventory
[root@m01 ~]# vim /etc/ansible/hosts
[web_group]
web01 ansible_ssh_host=10.0.0.7
web02 ansible_ssh_host=10.0.0.8[backup_group]
backup ansible_ssh_host=10.0.0.41#准备rsync配置文件
[root@m01 rsyncd]# vim /root/rsyncd/rsyncd.j2
uid = www
gid = www
port = 873
fake super = yes
use chroot = no
max connections = 200
timeout = 600
ignore errors
read only = false
list = false
auth users = rsync_backup
secrets file = /etc/rsync.passwd
log file = /var/log/rsyncd.log
#####################################
[backup]
comment = welcome to oldboyedu backup!
path = /backup

playbook

#编写剧本
[root@m01 ~]# vim /root/rsyncd/rsyncd.yml
- hosts: alltasks:#安装rsync- name: Install Rsyncd Serveryum:name: rsyncstate: present#创建www组- name: Create www Groupgroup:name: wwwgid: 666#创建www用户- name: Create www Useruser:name: wwwgroup: wwwuid: 666create_home: falseshell: /sbin/nologin- hosts: backup_grouptasks:#推送rsync配置文件- name: Scp Rsync Configcopy:src: ./rsyncd.j2dest: /etc/rsyncd.confowner: rootgroup: rootmode: 0644#创建密码文件并授权- name: Create Passwd Filecopy:content: 'rsync_backup:123'dest: /etc/rsync.passwdowner: rootgroup: rootmode: 0600#创建/backup目录- name: Create backup Directoryfile:path: /backupstate: directorymode: 0755owner: wwwgroup: wwwrecurse: yes#启动rsync服务- name: Start Rsyncd Serversystemd:name: rsyncdstate: started#检测语法
[root@m01 ~]# ansible-playbook --syntax-check /root/rsyncd/rsyncd.ymlplaybook: /root/rsyncd/rsyncd.yml#测试
[root@m01 ~]# ansible-playbook -C /root/rsyncd/rsyncd.ymlPLAY [all] ************************************************************************TASK [Gathering Facts] ************************************************************
ok: [backup]
ok: [web02]
ok: [web01]TASK [Install Rsyncd Server]*******************************************************
changed: [backup]
changed: [web02]
changed: [web01]TASK [Scp Rsync Config]************************************************************
changed: [web02]
changed: [web01]
changed: [backup]TASK [Create www Group] ***********************************************************************************
changed: [backup]
changed: [web01]
changed: [web02]TASK [Create www User] ***********************************************************************************
changed: [web02]
changed: [web01]
changed: [backup]TASK [Create backup Directory] ***********************************************************************************
changed: [web01]
changed: [backup]
changed: [web02]TASK [Start Rsyncd Server] ***********************************************************************************
changed: [web01]
changed: [web02]
changed: [backup]PLAY RECAP ************************************************************************
backup                     : ok=7    changed=6    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
web01                      : ok=7    changed=6    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
web02                      : ok=7    changed=6    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

扩展需求

1.给客户端推送脚本
2.加入crontab做备份
完成刚开始学架构阶段的rsync的实战案例

实战二:2

部署NFS服务,NFS服务端,敞开大门提供挂载点给web01和web02

主机名 公网ip 私网ip 服务 角色
m01 10.0.0.61 172.16.1.61 Ansible 控制端
backup 10.0.0.41 172.16.1.41 rsync服务端 被控端
web01 10.0.0.7 172.16.1.7 rsync服务端 被控端
web02 10.0.0.8 72.16.1.8 rsync服务端 被控端

环境部署

#编辑Ansible Inventory
[root@m01 ~]# vim /etc/ansible/hosts
[web]
web01 ansible_ssh_host=10.0.0.7
web02 ansible_ssh_host=10.0.0.8[backup]
backup ansible_ssh_host=10.0.0.41[nfs]
nfs ansible_ssh_host=10.0.0.31[nfs_all:children]
web_group
nfs_group#创建项目存放目录
[root@m01 ~]# mkdir nfs#准备nfs配置文件
[root@m01 ~]# cat /root/nfs/nfs.j2
/data 10.0.0.0/24(rw,sync,all_squash,anonuid=666,anongid=666)

playbook

[root@m01 ~]# vim /root/nfs/nfs.yml
- hosts: nfs_alltasks:#安装nfs- name: Install nfs-utilsyum:name: nfs-utilsstate: present#创建www组- name: Create www Groupgroup:name: wwwgid: 666#创建www用户- name: Create www Useruser:name: wwwgroup: wwwuid: 666create_home: falseshell: /sbin/nologin- hosts: nfstasks:#推送配置文件- name: Scp NFS Servercopy:src: ./nfs.j2dest: /etc/exportsowner: rootgroup: rootmode: 0644#创建挂载目录并授权- name: Create data Directoryfile:path: /datastate: directoryowner: wwwgroup: wwwmode: 0755recurse: yes#启动nfs-server- name: Start NFS Serversystemd:name: nfs-serverstate: startedenabled: yes#web01和web02挂载目录
- hosts: webtasks:- name: Mount NFS Servermount:path: /optsrc: 10.0.0.31:/datafstype: nfsopts: defaultsstate: mounted#检查语法
[root@m01 ~]# ansible-playbook --syntax-check /root/nfs/nfs.yml
playbook: /root/nfs/nfs.yml#执行
[root@m01 ~]# ansible-playbook /root/nfs/nfs.yml

Ansible跑起wordpress

部署需求:

1.使用yum安装httpd、php、php-mysql、php-pdo、mariadb
2.启动httpd、mariadb服务
3.下载wordpress代码
4.部署到httpd站点目录

1.环境准备

主机名 公网ip 私网ip 服务 角色
m01 10.0.0.61 172.16.1.61 Ansible 控制端
web01 10.0.0.7 172.16.1.7 rsync服务端 被控端
web02 10.0.0.8 72.16.1.8 rsync服务端 被控端
因为这只是一个练习,所以我们将apache mariadb php全部都写在一个yml文件中,并且放在一个目录下,
注意:在生产中,我们是不会这么做的,我们需要每一个服务单独拎出来,解耦。
#创建项目目录
[root@m01 ~]# cd lamp/#编辑Inventory
[root@m01 lamp]# cat /etc/ansible/hosts
[web]
web01 ansible_ssh_host=10.0.0.7
web02 ansible_ssh_host=10.0.0.8[backup]
backup ansible_ssh_host=10.0.0.41[backup_all:children]
web
backup[nfs]
nfs ansible_ssh_host=10.0.0.31[nfs_all:children]
web_group
nfs_group

playbook

- hosts: web01vars:                        #变量package:                    #变量名- httpd- mariadb-server           #安装需要全名- php                      #安装需要卸载原有文件- php-mysql- php-pdo- MySQL-python   tasks:- name: Istall Httpd Mariadb Php Serveryum:name: "{{ package }}" state: present- name: Stop Nginxsystemd:name: nginxstate: stopped- name: Started Httpdsystemd:name: httpdstate: startedenabled: yes- name: Started Mariadbsystemd:name: mariadbstate: startedenabled: yes- name: Install Wordpressunarchive:             #解压模块src: /root/wordpress/wordpress-5.0.3.tar.gzdest: /var/www/html/copy: yes            #解压并复制,这里必须开启- name: Change Mode Httpdfile:path: /var/www/html/owner: apachegroup: apacherecurse: yes- name: Create Database usermysql_user:name: autumnpassword: autumn123.compriv: '*.*:ALL'state: presenthost: localhost- name: Create Database wordpressmysql_db:login_user: autumnlogin_password: autumn123.comlogin_host: localhostlogin_port: 3306name: wordpressstate: present

打开浏览器访问:

http://10.0.0.7/wordpress/wp-admin/setup-config.php
http://10.0.0.8/wordpress/wp-admin/setup-config.php

ansible变量

概述

变量提供了便捷的方式来管理ansible项目中的动态值。比如zabbix-3.4.15,可能后期会反复的使用这个版本的值,那么如果将
此值设置为变量,后续使用和修改将变得非常方便,这样可以简化项目的创建和维护

定义变量分为如下三种方式

1.通过命令行进行变量定义     #使用-e参数,优先级是最高的
2.在play文件中进行定义变量   #使用vars_file或者vars定义变量
3.通过inventory在主机组或单个主机中设置变量   #通过host_vars或者group_vars定义变量

变量的优先级

如果定义的变量出现重复,且造成冲突,优先级如下:

命令行变量--->play中的vars_files--->play中的vars变量-->host_vars中定义的变量--->group_vars/组--->group_vars/all
[root@m01 wget]# ansible-playbook wget.yml -e "webserver=zarafa-devel"  #命令行变量.优先级第一名[root@m01 project1]# cat p5.yml
- hosts: webservers
#  vars:                        #play中vars变量  优先级第三
#    filename: play_vars
#  vars_files:                  #play中var_files变量,优先级第二
#    - ./vars.ymltasks:[root@m01 project1]# cat host_vars/webserver    #优先级第四[root@m01 project1]# cat group_vars/webserver   #优先级第五[root@m01 project1]# cat group_vars/all         #优先级第六

定义变量

playbook变量

playbook变量可以通过多种方式进行定义,最简单的方式就是在playbook的开头通过vars进行定义#方法一:
- hosts: web_groupvars:packages:- httpd- mariadb-server- php- php-mysql- php-pdotasks:       - name: Install httpd  mariadb php Serveryum:name: "{{ packages }}"
#方法二:
- hosts: web_groupvars:- web_server: httpd- db_server: mariadb-server- php_server: php,php-mysql,php-pdotasks:- name: Install httpd  mariadb php Serveryum:name:- "{{ web_server }}"- "{{ db_server }}"- "{{ php_server }}"

这种在playbook中使用vars定义变量,有一个缺陷,就是其他的playbook无法使用该变量。

使用vars_file定义变量
#定义阶段

[root@m01 project1]# cat vars.yml
web_package: httpd
ftp_package: vsftpd[root@m01 project1]# cat p2.yml
- hosts: webserversvars_files: ./vars.ymltasks:- name: Installed Packagesyum: name: - "{{ web_package }}"- "{{ ftp_package }}"state: present

在inventory中定义变量,主机变量优先级高于主机组变量(不推荐使用)

[root@m01 project1]# vim /etc/ansible/hosts
[webservers]
web01 ansible_ssh_host=172.16.1.7
web02 ansible_ssh_host=172.16.1.8
[webservers:vars]
filename=group_vars[root@m01 project1]# cat p3.yml
- hosts: webserverstasks:- name: Create Filefile: path=/tmp/{{ filename }} state=touch

创建host_vars和group_vars目录,使用变量

[root@m01 project1]# cat /etc/ansible/hosts
[webservers]
web01 ansible_ssh_host=172.16.1.7
web02 ansible_ssh_host=172.16.1.8# host_vars目录下必须存放和inventory清单文件中定义的组名一致[root@m01 project1]# cat host_vars/web01
web_package: zlib-static
ftp_package: zmap# group_vars目录下必须存放和inventory清单文件中定义的组名一致,如下[root@m01 project1]# cat group_vars/webservers
web_package: httpd
ftp_package: vsftpd[root@m01 project1]# cat group_vars/all注意:系统提供了特殊的组,all,也就说在group_vars目录下创建一个all文件,定义变量对所有的主机都生效
[root@m01 project1]# cat host_vars/web01
web_package: zlib-static
ftp_package: zmap[root@m01 project1]# cat group_vars/webservers
web_package: httpd
ftp_package: vsftpd[root@m01 project1]#  cat p4.yml
- hosts: webservers
#- hosts: otherserverstasks:- name: Installed Packagesyum: name: - "{{ web_package }}"- "{{ ftp_package }}"state: present[root@m01 project1]# ansible-playbook p4.yml PLAY [webservers] *****************************************************TASK [Gathering Facts] ***********************************************************************
ok: [web02]
ok: [web01]TASK [Installed Packages] ***********************************************************************
ok: [web02]
changed: [web01]PLAY RECAP ************************************************************
web01                      : ok=2    changed=1    unreachable=0    failed=0
web02                      : ok=2    changed=0    unreachable=0    failed=0

变量注册register

当ansible的模块运行之后,其实会有一些result结果,就像是执行脚本,我们有的时候需要脚本给我们return返回值,
我们才知道是否可以执行成功,但是,默认情况下,ansible的result并不会显示出来,所以,我们可以把这些返回值"存储"到变量中,
这样我们就能通过'调用'对应的变量名,从而获取到这些result,这种将模块的返回值,写入到变量中的方法被称为变量注册
- hosts: webserverstasks:- name: Get Network Port Statusshell: netstat -lntpregister: net_port      #定义变量(netstat -lntp)- name: OutPut Network Port Statusdebug:                     #debug模块msg: "{{ net_port.stdout_lines }}"    #这里.stdout_lines是指仅仅只输出shell命令中netstat -lntup中的.stdout_lines包含的值#debug :调试模块,用于在调试中输出信息 常用参数:
msg:调试输出的消息var:将某个任务执行的输出作为变量传递给debug模块,
debug会直接将其打印输出 verbosity:debug的级别(默认是0级,全部显示)

变量也支持层级定义,使用".“可能会有问题,建议使用”[]"代替。

[root@m01 project1]# cat vars1.yml
rainbow:web:web_package: httpddb_package: mariadbcode:web:filename: code_web_filename[root@m01 project1]# cat p8.yml
- hosts: webserversvars_files: ./vars1.ymltasks:- name: Install Packageyum: name= "{{ rainbow['web']['web_package'] }}"- name: create filenamefile: path: /tmp/{{ code.web.filename }}state: touch

facts变量

Ansible facts是在被管理主机上通过ansible自动采集发现的变量。facts包含每台特定主机信息。
比如:被控制的主机、IP地址、系统版本、CPU数据、内存状态、磁盘状态等等。

fact使用场景

1.通过facts检查CPU,来生成对应的Nginx配置文件,
2.通过facts检查主机名信息,来生成不同的Zabbix配置文件。
3.通过facts检查的内存情况来自定义mysql的配置文件

setup模块

命令行取变量用setup模块

使用setup模块可以取到主机所有的硬件信息及变量名(静态信息)

[root@m01 vars]# ansible web01 -m setup -a 'filter=ansible_fqdn'
[root@m01 vars]# ansible web01 -m setup -a "filter="ansible_fqdn"

取变量示例

[root@m01 debug]# vim debug.yml- name: OutPut Variables Ansible Factsdebug:msg: >this default IPv4 address"{{ansible_fqdn}}"is"{{ansible_eth1.ipv4.address }}"

使用ansible facts安装zabbix-agent

copy模块不支持解析变量template模块支持解析变量,其他功能根本copy模块一样
[root@m01 ~]# vim /etc/zabbix/zabbix_agentd.conf Server= {{ zabbix_server }}    # 定义变量playbook里的变量Hostname={{ ansible_hostname }}   # 读取facts的变量#facts(setup模块)
他们其实是同样的原理和功能,setup模块的原理就是facts[root@m01 zabbix]# vim zabbix.yml
- hosts: web01 #gather_facts: no #关闭信息采集vars:- zabbix_server: 172.16.1.71      #tasks:- name: Copy zabbix configuretemplate:                           #如果使用copy模块不能解析变量src: /etc/zabbix/zabbix_agentd.confdest: /tmp/zabbix_agentd.conf

facts自定义安装memcached

我们在这里要学会用使用变量百分之比的值给被控端配置文件

playbook安装一个memcached[root@m01 ~]# yum install memcached -y[root@m01 project1]# cat memcached.j2       #配置文件内容
PORT="11211"
USER="memcached"
MAXCONN="1024"
CACHESIZE="{{ ansible_memtotal_mb //2 }}"   #设置变量并且除以2
OPTIONS=""[root@m01 project1]# cat p11.yml            #编写剧本
- hosts: webserverstasks:- name: Installed Memcachedyum: name=memcached state=present- name: Configure Memcachedtemplate: src=./memcached.j2 dest=/etc/sysconfig/memcached- name: Start Memcachedservice: name=memcached state=started enabled=yes

关闭facts

[root@m01 ~]# vim facts.yml
- hosts: web_groupgather_facts: no #关闭信息采集tasks:# 在这里我们要知道,如果关闭了facts变量,我们就无法在当前的剧本使用facts里的变量的值了

常用变量信息参数

ansible_all_ipv4_addresses:仅显示ipv4的信息。
ansible_devices:仅显示磁盘设备信息。
ansible_distribution:显示是什么系统,例:centos,suse等。
ansible_distribution_major_version:显示是系统主版本。
ansible_distribution_version:仅显示系统版本。
ansible_machine:显示系统类型,例:32位,还是64位。
ansible_eth0:仅显示eth0的信息。
ansible_hostname:仅显示主机名。
ansible_kernel:仅显示内核版本。
ansible_lvm:显示lvm相关信息。
ansible_memtotal_mb:显示系统总内存。
ansible_memfree_mb:显示可用系统内存。
ansible_memory_mb:详细显示内存情况。
ansible_swaptotal_mb:显示总的swap内存。
ansible_swapfree_mb:显示swap内存的可用内存。
ansible_mounts:显示系统磁盘挂载情况。
ansible_processor:显示cpu个数(具体显示每个cpu的型号)。
ansible_processor_vcpus:显示cpu个数(只显示总的个数)。

playbook控制

-------task任务处理相关流程与控制参数when             判断item          循环handlers      触发器(需要task使用notify通知)tags           标签(调试使用)include_tasks 包含task任务ignore_errors   忽略错误错误处理force_handlers  #扩展changed_when false   抑制改变的状态为ok(获取系统的状态信息时)
-------重要(检查服务的配置是否正常,正常则不处理,不正常则中断)register: httpd_checkchanged_when: - httpd_check.stdout.find('OK')- false

Playbook条件语句

判断在Ansible任务中的使用频率非常高。比如yum模块可以检测软件包是否已经安装,而这个过程中我们不用做太多的人工干预。
但是部分任务需要进行判断,比如:web服务器角色都需要使用nginx仓库,但是其他的服务器角色并不需要,此时就会用到when判断。
比如:CentOS和Ubuntu系统都需要安装httpd服务,那么久需要使用when判断主机系统,然后调用不同的模块执行。

示例

根据不同操作系统,安装相同的软件包

[root@m01 when]# cat when.yml
- hosts: webserverstasks:- name: Install httpd Serveryum: name=httpd state=presentwhen: ansible_distribution == "CentOS"- name: Install httpd Serverapt: name=httpd2 state=presentwhen: ansible_distribution == "Ubuntu"

按照不同的主机名称进行判断

[root@m01 when]# cat when.yml
- hosts: webvars:remote_ip: "{{ ansible_default_ipv4.address }}"host_name: "{{ ansible_fqdn }}"tasks:- name: Print ipdebug:msg:  - "{{ remote_ip }}" - "{{ host_name }}"- name: Reboot Servercommand: rebootwhen: ansible_fqdn  == "web02"

按照不同的IP地址进行判断

[root@m01 when]# cat when.yml
- hosts: webvars:remote_ip: "{{ ansible_default_ipv4.address }}"host_name: "{{ ansible_fqdn }}"tasks:- name: Print ipdebug:msg:  - "{{ remote_ip }}" - "{{ host_name }}"- name: Reboot Servercommand: rebootwhen: ansible_default_ipv4.address  == "10.0.0.7"

按照获取客户端的某参数值得大小判断

[root@m01 when]# cat when.yml
- hosts: webvars:remote_ip: "{{ ansible_default_ipv4.address }}"host_name: "{{ ansible_fqdn }}"host_mem: "{{ ansible_memtotal_mb }}"tasks:- name: Print ipdebug:msg:  - "{{ remote_ip }}" - "{{ host_name }}"- "{{ host_mem }}"- name: Reboot Servercommand: rebootwhen: ansible_memtotal_mb|int < "2000"

添加yum仓库模块

所有为web主机名的添加nginx仓库,其余的都跳过添加

 1.如何添加yum仓库2.如何判断,判断什么内容
---
[root@m01 when]# cat when.yml
- hosts: webtasks:- name: Add Nginx Reposyum_repository:          #模块name: nginx_tet        #它代表/etc/yum.repos.d/nginx_tet.repodescription: Nginx YUM repo      #描述  (不加会报错)baseurl: http://nginx.org/packages/centos/7/$basearch/gpgcheck: no           #把效验本地给关掉when: (ansible_hostname is match ("web*")) or (ansible_hostname is match ("lb*"))     #根据官方的写法

列表方式判断 and关系 并且关系

[root@m01 when]# cat when.yml
- hosts: webvars:remote_ip: "{{ ansible_default_ipv4.address }}"host_name: "{{ ansible_fqdn }}"host_mem: "{{ ansible_memtotal_mb }}"host_ver: "{{ ansible_distribution }}"tasks:- name: Print ipdebug:msg:  - "{{ remote_ip }}" - "{{ host_name }}"- "{{ host_mem }}"- "{{ host_ver }}"- name: Reboot Serverfile: path: /root/web01.txtstate: touchwhen: - ansible_distribution == "CentOS"- ansible_fqdn == "web01"

修改Nginx配置文件 把Nginx从ansible服务器拷贝到web服务器

拷贝过去需要启动或者重启  如果Nginx配置文件不对 不让加载 如何判断Nginx文件配置是否正确?1. 安装Nginx 2. 拷贝文件到Nginx.conf 判断文件是否正确 ngixn -t 变量注册接收结果3. 启动 4. 重新加载  在重新加载的地方进行when判断
[root@m01 when]# cat nginx.yml
- hosts: web02tasks:- name: Install Nginxyum:name: nginxstate: present- name: scp configure Nginxcopy: src: ./nginx.j2dest: /etc/nginx/nginx.conf- name: Check Nginx Configurecommand: /usr/sbin/nginx -tregister: resultignore_errors: yes             # 忽略错误继续执行- name: print resultdebug:msg: "{{ result.rc }}"- name: Reload Nginxsystemd:name: nginxstate: reloadedwhen: result.rc == 0                # 判断nginx -t执行结果 是否为0 通过变量注册获取的------------------------------ name: Reload Nginxsystemd:name: nginxstate: reloadedwhen: result.rc is match "0"

判断http是否存活

- hosts: web02tasks:- name: Install Nginxyum:name: nginxstate: present- name: scp configure Nginxcopy: src: ./nginx.j2dest: /etc/nginx/nginx.conf- name: Check Nginx Configurecommand: /usr/sbin/nginx -tregister: resultignore_errors: yes- name: Check HTTP Servercommand: systemctl is-active httpdregister: result_httpignore_errors: yes    - name: print resultdebug: var=result_http- name: Reload Nginxsystemd:name: nginxstate: reloadedwhen: result.rc == 0

playbook循环语句

有时候我们写playbook的时候发现了很多tasks都要反复引用某个模块,比如一次启动10个服务,或者一次拷贝10个文件,
如果按照传统的写法最少要写10次,这样会显得很臃肿,如果使用循环的方式来编写playbook,这样可以减少重复使用某个模块.

==实践案例一 ==

弃用的写法,因为有可能会提示warn

[root@m01 when]# vim when.yml
- hosts: webtasks:- name: Started Serversystemd:name: "{{ item }}"    #官方定义的state: startedwith_items:    #定义的循环的服务内容loop:          #或者这么使用- httpd- mariadb#在这里httpd和mariadb对被控端重复循环

这种循环写法是官方推荐的写法

[root@m01 when]# vim when.yml    #但是他这里只属于yum模块的命令
- hosts: webtasks:- name: Started Serveryum:name: "{{ packages }}"state: presentvars:packages:- httpd- mariadb

实践案例三:使用字典的方式创建用户和批量拷贝文件

[root@m01 when]# vim when.yml
- hosts: webtasks:- name: add useruser:name: "{{ item.name }}"groups: "{{ item.groups}}"state: presentwith_items:    或者使用loop- { name: 'testuser1', groups: 'adm' }- { name: 'testuser2', groups: 'root' }

[root@m01 when]# vim when.yml
- hosts: webserverstasks:- name: Copy Rsync configure and Rsync passwdcopy: src={{ item.src }} dest={{ item.dest }} mode={{ item.mode }}loop:- { src: "./rsyncd.conf", dest: "/etc/rsyncd.conf", mode: "0644" }- { src: "./rsync.passwd", dest: "/tmp/rsync.passwd", mode: "0600" }

handlers

handlers注意事项

1.无论多少个task通知了相同的handlers,handlers仅会在所有tasks结束后运行一次。
2.只有task发生改变了才会通知handlers,没有改变则不会触发handlers
3.不能使用handlers替代tasks
[root@m01 project2]# cat han.yml
- hosts: web#force-handlers: yes         #强制执行handlers,在这里使用的次数比较少vars:- http_port: 8083tasks:- name: Install Http Serveryum: name=httpd state=present- name: configure httpd servertemplate: src=./httpd.conf.j2 dest=/etc/httpd/conf/httpd.confnotify:                     #在这里我们要注意notify是模块层- Restart Httpd Server    #与handlers的- name对应,必须一致- Restart PHP Server      #与handlers的- name对应,必须一致- name: start httpd server        #这里必须启动服务器来,handlers无法替代tasks起来启动服务。service: name=httpd state=started enabled=yeshandlers:                       #handlers是模块层- name: Restart Httpd Serversystemd: name=httpd state=restarted - name: Restart PHP Serversystemd: name=php-fpm state=restarted#handlers是同过notify监听服务是否发生改变来出发的,如果服务没发生改变,handlers是无法触发的。

Playbook任务标签

默认情况下,Ansible在执行的一个playbook时,会执行playbook中定义的所有任务。Ansible的标签(Tags)功能可以给
单独任务甚至整个playbook打上标签,然后利用这些标签来指定要运行的playbook中的个别任务,或不执行指定的任务。
1.打标签的方式有几种,比如:
对一个task打一个标签、对一个task打多个标签、对多个task打一个标签2.对task打完标签应该如何使用
-t: 执行指定的tag标签任务
--skip-tags:执行--skip-tags之外的标签任务

可以通过–list-tags查看yml剧本中的标签数

[root@m01 project2]# ansible-playbook tag.yml --list-tags

可以用-t参数对指定tags执行命令

[root@m01 project2]# ansible-playbook tag.yml -t tags(定义的标签)
这里-t也执行单独运行多个tags标签[root@m01 project2]# ansible-playbook tag.yml -t tags1,tags2

可以用–skip-tags对指定tags排除

[root@m01 project2]# ansible-playbook tag.yml --skip-tags  tags(定义的标签)同时--skip-tags也可以排除多个tags标签[root@m01 project2]# ansible-playbook tag.yml --skip-tags  tags1,tags2

示例

[root@m01 project2]# cat tag.yml
- hosts: webvars:- http_port: 8083tasks:- name: Install Http Serveryum: name=httpd state=presenttags: - install_httpd- httpd_server- name: configure httpd servertemplate: src=./httpd.conf.j2 dest=/etc/httpd/conf/httpd.confnotify: Restart Httpd Servertags: - confiure_httpd- httpd_server- name: start httpd serverservice: name=httpd state=started enabled=yestags: service_httpdhandlers:- name: Restart Httpd Serversystemd: name=httpd state=restarted

由于生产场景里的playbook过于臃肿,其中如果某个task出了错误,
我们测试可能需要全部再执行一次,需要浪费很多的时间,非常不方便,如果我们对单独的tasks指定tags,对单个的tags进行调试,我们会方便很多

playbook文件复用


include(import_playbook)
#这种方式已经弃用,现在使用的是import_playbook,它包括的是任务文件.include_tasks
#这种方式,include包含的是命令文件.[root@m01 project2]# cat task.yml
- hosts: webvars:                       #定义端口变量- http_port: 801      tasks:- include_tasks: task_install.yml            #包含安装文件- include_tasks: task_configure.yml          #包含配置文件- include_tasks: task_start.yml              #包含启动文件handlers:                                      #触发器,一旦改变,重启服务使他生效- name: Restart Httpd Serversystemd: name=httpd state=restarted[root@m01 project2]# cat task_install.yml        #安装文件的内容
- name: Install Http Serveryum: name=httpd state=present[root@m01 project2]# cat task_configure.yml      #配置文件的内容
- name: configure httpd servertemplate: src=./httpd.conf.j2 dest=/etc/httpd/conf/httpd.confnotify: Restart Httpd Server[root@m01 project2]# cat task_start.yml          #启动服务的文件
- name: start httpd serverservice: name=httpd state=started enabled=yes

Playbook忽略错误

默认Playbook会检查tasks执行的返回状态,如果遇到错误则会立即终止playbook的后续执行,然而有时候playbook即使执行错误了也要让其继续执行
加入参数:ignore_errors:yes  忽略错误.

示例

[root@manager ~]# cat f9.yml
- hosts: webtasks:- name: Ignore Falsecommand: /bin/falseignore_errors: yes- name: touch new filefile: path=/tmp/bgx_ignore state=touch

Playbook错误处理

通常情况下,当task失败后,play将会终止,任何在前面已经被tasks notify的handlers都不会执行.
如果你在play中设置了force_handlers:yes参数,被通知的handlers就会强制执行.(有些特殊场景可能会使用到)

示例一

force_handlers: yes #强制调用handlers

[root@m01 project2]# cat tag.yml
- hosts: webvars:- http_port: 8083force_handlers: yes     #强制调用handlerstasks:- name: Install Http Serveryum: name=httpd state=presenttags: - install_httpd- httpd_server- name: configure httpd servertemplate: src=./httpd.conf.j2 dest=/etc/httpd/conf/httpd.confnotify: Restart Httpd Servertags: - confiure_httpd- httpd_server- name: start httpd serverservice: name=httpd state=started enabled=yestags: service_httpdhandlers:- name: Restart Httpd Serversystemd: name=httpd state=restarted

示例二

changed_when: false #当文件状态没有被改变,我们可以用这个参数抑制changed状态的报告。

- hosts: webvars:- http_port: 8083force_handlers: yes     #强制调用handlerstasks: - name: Commandshell: netstat -lntp|grep httpdregistet: check_httpdchanged_when: false  #当文件状态没有被改变,我们可以用这个参数抑制changed状态的报告。- name: Debugdebug: msg={{ check_httpd.stdout_lines }}

示例三

在这里我们加上了判断,如果我们加上了判断,这里如果配置文件发生了错误,
将不会重启配置文件,这样就能在保证生产线的情况下让我有足够的时间排错

[root@m01 project2]# cat tag.yml
- hosts: webvars:- http_port: 8083force_handlers: yes     #强制调用handlerstasks:- name: configure httpd servertemplate: src=./httpd.conf.j2 dest=/etc/httpd/conf/httpd.conf- name: Check Httpdshell: /usr/sbin/httpd -t    #检测apache配置文件语法格式是否正确registet: check_httpdchanged_when: - check_httpd.stdout.find('OK')- falsenotify: Restart Httpd Server- name: Srart Httpd Serversystemd: name=httpd state=started enabled=yeshandlers:- name: Restart Httpd Serversystemd: name=httpd state=restarted

判断失败示例

failed_when
命令不依赖返回状态码来判定是否执行失败,而是要查看命令返回内容来决定,比如返回内容中包括 failed 字符串,则判定为失败。示例如下:
- name: this command prints FAILED when it fails command: /usr/bin/example-command -x -y -z register: command_result failed_when: "'FAILED' in command_result.stderr"

Ansible加密模块

ansible加密模块

[root@m01 project2]# ansible-vault --help
Usage: ansible-vault [create|decrypt|edit|encrypt|encrypt_string|rekey|view] [options] [vaultfile.yml]

加密一个文件

[root@m01 project2]# ansible-vault encrypt include.yml

查看一个文件

[root@m01 project2]# ansible-vault view include.yml
Vault password:
- import_playbook: han.yml
- import_playbook: when2.yml

修改加密的文件内容


[root@m01 project2]# ansible-vault edit include.yml

rekey 修改密码


[root@m01 project2]# ansible-vault rekey include.yml
Vault password:
New Vault password:
Confirm New Vault password:
Rekey successful

执行加密的playbook


[root@m01 project2]# echo "1" >pass
[root@m01 project2]# chmod 600 pass
[root@m01 project2]# ansible-playbook include.yml  --vault-password-file=pass

移除密码

[root@m01 project2]# ansible-vault decrypt include.yml
Vault password:

Jinja2模板

什么是jinja2

Jinja2是Python的全功能模板引擎

Jinja2模板与Ansible有什么关系

Ansible通常会使用Jinja2模板来修改被管理主机的配置文件,例如给10台远程主
机装上httpd服务,但是要求每个服务器的端口不一样,如何解决?

Ansible如果使用Jinja2模板

使用ansible的Jinja2模板,也就是template模板.该模块跟copy模块一样,都是将
文件复制到远端主机上去,但是区别在于template模块可以获取要复制的文件中变
量的值,而copy则是原封不动的把文件内容复制过去,不如:针对不同的主机定义不
同的变量,template会在将配置文件分发出去前读取变量Jinja2模块,然后分发到
不同的被管理主机上.

Jinja2基本使用

语法:

{{EXPR}}输出变量的值(会输出自定义变量的值或facts)
1)playbook文件使用template参数
2)模板文件里面变量使用(名称),比如{PORT}或者使用facts。

jinja模板逻辑关系

{% for i in EXPR %}...{% endfor%}作为循环表达式
{% if EXPR %}...{% elif EXPR %}...{% endif%}作为条件判断
{# COMMENT #}表示注释

jinja模板使用示例,使用facts变量的示例

1.编辑playbook

[root@m01 ~]# vim jinja2.yml
- hosts: web_grouptasks:- name: Copy Template Filetemplate:src: ./motd.j2dest: /etc/motd

2.准备motd.j2文件

[root@m01 ~]# vim motd.j2
Welcome to {{ ansible_fqdn }}
This system total mem is : {{ ansible_memtotal_mb }} MB
This system free mem is: {{ ansible_memfree_mb }} MB

3.执行playbook

[root@m01 ~]# ansible-playbook jinja2.yml#查看结果
[root@m01 ~]# ansible web_group -a 'cat /etc/motd'
web01 | CHANGED | rc=0 >>
Welcome to web01
This system total mem is : 1982 MB
This system free mem is: 1106 MBweb02 | CHANGED | rc=0 >>
Welcome to web02
This system total mem is : 1982 MB
This system free mem is: 1096 MB

jinja 模板逻辑关系

Nginx

[root@m01 project2]# cat jinja_nginx.yml
- hosts: webserversvars:- http_port: 80- server_name: www.oldboyedu.comtasks:- name: Copy Nginx COnfiguretemplate: src: ./oldboyedu.conf.j2 dest: /etc/nginx/conf.d/oldboyedu_proxy.conf
[root@m01 project2]# cat oldboyedu.conf.j2
upstream {{ server_name }} {
{% for i in range(1,20) %}server 172.16.1.{{i}}:{{http_port}};
{%endfor%}
}
server {listen {{ http_port }};server_name {{ server_name }};location / {proxy_pass http://{{ server_name }};proxy_set_header Host $http_host;}
}

配置keepalived配置文件

[root@m01 project2]# cat jinja_keepalived.yml
- hosts: webserverstasks:- name: Copy Keepalived Configuretemplate: src: ./kee.conf.j2 dest: /tmp/keepalived.conf
[root@m01 project2]# cat kee.conf.j2
global_defs {     router_id {{ ansible_hostname }}
}vrrp_instance VI_1 {
{%if ansible_hostname =="web01" %}state MASTERpriority 150
{%elif ansible_hostname == "web02" %}state BACKUPpriority 100
{%endif%}interface eth0virtual_router_id 50advert_int 1authentication {auth_type PASSauth_pass 1111
}virtual_ipaddress {10.0.0.3}
}

mysql剧本模块

数据库模块

- name: Create Database Usermysql_user:name: autumn                          #创建的用户password: autumn123.com               #创建用户的密码priv: '*.*:ALL,GRANT'                 #授权用户host: '%'                             #授权的网段state: present                        #创建- name: Create New Databases mysql_db:                               #添加库信息name: - wordpress- phpshelogin_user: root                      #登录的用户为rootlogin_port: 3306                      #登录的端口login_host                            #登录的网段login_password                        #登录的密码state: present                        #创建
[root@m01 project2]# cat jinja_mysql.yml
- hosts: webserversgather_facts: novars:PORT: 13306# PORT: false  #相当于开关tasks:- name: Copy MySQL Configuretemplate: src=./my.cnf.j2 dest=/tmp/my.cnf
[root@m01 project2]# cat my.cnf.j2
{% if PORT %}
bind-address=0.0.0.0:{{ PORT }}
{% else %}
bind-address=0.0.0.0:3306
{%endif%}

Roles

概述

roles不管是Ansible还是saltstack,我在写一键部署的时候,都不可能把所有的步骤全部写入到
一个’剧本’文件当中,我们肯定需要把不同的工作模块,拆分开来,解耦,那么说到解耦,我们
就需要用到roles官方推荐,因为roles的目录结构层次更加清晰。例如:我们之前推荐大家写一个base.yml里面写所有基础优化的项
目,其实把所有东西摞进去也是很鸡肋的,不如我们把这些功能全部拆分开,谁需要使用,就调用即可。建议:每个roles最好只使用一个tasks这样方便我们去调用,能够很好的做到解耦。(SOA)

Ansible Roles目录结构

[root@m01 ~]# cd /etc/ansible/roles
[root@m01 roles]# mkdir {nfs,rsync,web}/{vars,tasks,template,handlers,files,meta} -p
[root@m01 roles]# tree
.
|── nfs
├── defaults        #低优先级变量
├── files           #存放文件
├── handlers        #触发器文件
├── meta            #依赖关系文件
├── tasks           #工作任务文件
├── templates       #jinja2模板文件
├── tests           #测试文件
└── vars            #变量文件

Ansible Roles依赖关系

roles允许你再使用roles时自动引入其他的roles。role依赖关系存储在roles目录中meta/main.yml文件中。例如:推送wordpress并解压,前提条件,必须要安装nginx和php,把服务跑起来,才能运行wordpress的页面,此时我们就可以在wordpress的roles中定义依赖nginx和php的roles
[root@m01 roles]# cat wordpress/meta/main.yml
dependencies:- role: web_serverwhen: ansible_hostname is match "web"- role: php_serverwhen: ansible_hostname is match "web"- role: mysql_serverwhen: ansible_hostname is match "db"
[root@m01 roles]# cat nfs/tasks/install.yml
- name: Install NFS-utils Serveryum: name: nfs-utils state: present
[root@m01 roles]# cat nfs/tasks/config.yml
- name: Configure Nfs-utils Servertemplate: src: ./exports.j2 dest: /etc/exports owner: root group: root mode: 0644notify: Restart NFS Server
[root@m01 roles]# cat nfs/tasks/start.yml
- name: Start NFS Serversystemd: name: nfs state: started enabled: yes
[root@m01 roles]# cat nfs-client/tasks/main.yml
- name: Mount NFS Servermount: path: /opt src=172.16.1.7:/data fstype=nfs opts=defaults state: mounted
[root@m01 roles]# cat site.yml
- hosts: web01roles:- nfs- hosts: web02roles:- nfs-client

解压模块

- name: 解压压缩包unarchive: src: wordpress.tar.gzdest: /codeowner: wwwgroup: wwwcreates: /code/wordpress  如果存在就不解压

数据库模块

- name: 创建数据库表文件mysql_db:name:- wordpress  - phpshe login_user: rootlogin_port: 3306state: present- name: 创建数据库用户mysql_user:name: autumnpassword: autumn123.compriv: `*.*ALL,GRANT`host: `%`state: present

配置phpmyadmin

tasks目录

[root@m01 roles]# cat phpmyadmin/tasks/main.yml
- name: Configure PhpMyadmincopy:src: "{{ item.src }}"dest: "{{ item.dest }}"loop:- { src: php.ini,dest: /etc/php.ini }- { src: www.conf,dest: /etc/php-fpm.d/www.conf }notify: Restart PHP- name: Create Code Dirfile:path: /codestate: directory- name: Configure PhpMyadmin Serverunarchive:src: phpmyadmin.tar.gz dest: /codeowner: wwwgroup: wwwcreates: /code/myadmin- name: Configure PhpMyadmin Conftemplate:src: "{{ item.src }}"dest: "{{ item.dest }}"loop:- { src: myadmin.oldboy.com.conf ,dest: /etc/nginx/conf.d/myadmin.oldboy.com.conf }- { src: nginx.conf,dest: /etc/nginx/nginx.conf }notify: Restart Nginx- name: Check Nginxshell: /usr/sbin/nginx -tregister: result - name: Start Nginxsystemd:name: nginxstate: startedenabled: yes- name: Start PHP-FPMsystemd:name: php-fpmstate: startedenabled: yes- name: Create Dirfile:path: /server/scriptstate: directoryrecurse: yes- name: Configure Push Scripttemplate:src: client_push_data.shdest: /server/script/client_push_data.sh- name: Cron Push Script  cron:name: "push backup"minute: "*/5"job: "sh /server/script/client_push_data.sh > /dev/null"

j2目录

[root@m01 roles]# ll phpmyadmin/templates/
total 12
-rw-r--r-- 1 root root 793 Apr 19 16:37 client_push_data.sh
-rw-r--r-- 1 root root 275 Apr 20 17:38 myadmin.oldboy.com.conf
-rw-r--r-- 1 root root 639 Mar 27 15:31 nginx.conf

files目录

[root@m01 roles]# ll phpmyadmin/files
total 13584
-rw-r--r-- 1 root root    62688 Apr 20 19:52 php.ini
-rw-r--r-- 1 root root 13822582 Apr 20 15:27 phpmyadmin.tar.gz
-rw-r--r-- 1 root root    17964 Apr 20 12:05 www.conf

Ansible-大总结(六)相关推荐

  1. 2d的公式_西师大版六年级数学上册全册必背公式+高清版电子课文,收藏预习

    六年级数学上册必背公式 长度单位换算 1千米=1000米 1米=10分米 1分米=10厘米 1米=100厘米 1厘米=10毫米 面积单位换算 1平方千米=100公顷 1公顷=10000平方米 1平方米 ...

  2. 最新职场调查显示:男性、00后跳槽焦虑更大,六成以上企业无法提供钱少、事少、不累的工作 | 美通社头条...

    美通社消息:从静默到开放.从远程办公到回归办公室,不平凡的2022年让多数职场人开始重新思考工作的意义,越来越多的工作者开始把家庭.生活与健康置于工作之前.是不是越高薪的工作越有意义?相对低薪的工作可 ...

  3. [ 渗透入门篇 ] 渗透行业必备术语大集合(六)

    学渗透是一个漫长的过程,需要掌握的知识点很广,需要心无旁骛地去学. 有关这方面的专业术语有很多很多,我大概是总结了200来个,在这里分享一下. 在连五篇文章介绍完了前面125个: [ 渗透入门篇 ] ...

  4. 微信小程序,前端大梦想(六)

      微信小程序之联合百度API实现定位 定位功能对于我们都不陌生,在移动端的应用中更是不可或缺的功能,小程序中也提供了对应的API帮助我们完成定位的实现,但是目前小程序的定位API只能返回经纬度,所以 ...

  5. Expo大作战(三十六)--expo sdk api之 ImagePicker,ImageManipulator,Camera

    简要:本系列文章讲会对expo进行全面的介绍,本人从2017年6月份接触expo以来,对expo的研究断断续续,一路走来将近10个月,废话不多说,接下来你看到内容,讲全部来与官网 我猜去全部机翻+个人 ...

  6. 面试官问单表数据量大一定要分库分表吗?我们用六个字和十张图回答

    1 文章概述 在业务发展初期单表完全可以满足业务需求,在阿里巴巴开发手册也建议:单表行数超过500万行或者单表容量超过2GB才推荐进行分库分表,如果预计三年后数据量根本达不到这个级别,请不要在创建表时 ...

  7. 胡小明:大数据应用方向思考

    一.警惕大数据过热 1.1 过热产生盲目性 国内大数据的宣传早已过热,很多区县级政府也在考虑成立大数据局,政府对大数据热几乎没有抵抗力,企业没有紧跟就对了,在大数据高潮中反省政府的大数据行为.冷静一下 ...

  8. Expo大作战(十八)--expo如何发布成独立应用程序,打包成apk或者ipa,发布到对应应用商店...

    简要:本系列文章讲会对expo进行全面的介绍,本人从2017年6月份接触expo以来,对expo的研究断断续续,一路走来将近10个月,废话不多说,接下来你看到内容,讲全部来与官网 我猜去全部机翻+个人 ...

  9. Expo大作战(三十)--expo sdk api之Permissions(权限管理模块),Pedometer(计步器api)

    简要:本系列文章讲会对expo进行全面的介绍,本人从2017年6月份接触expo以来,对expo的研究断断续续,一路走来将近10个月,废话不多说,接下来你看到内容,讲全部来与官网 我猜去全部机翻+个人 ...

  10. Expo大作战(十九)--expo打包后,发布分用程序到商店的注意事项

    简要:本系列文章讲会对expo进行全面的介绍,本人从2017年6月份接触expo以来,对expo的研究断断续续,一路走来将近10个月,废话不多说,接下来你看到内容,讲全部来与官网 我猜去全部机翻+个人 ...

最新文章

  1. iOS使用自签名证书实现HTTPS请求
  2. 使用搜索引擎的10个搜索技巧
  3. SQL FOREIGN KEY 约束
  4. css3学习 之 css选择器(结构性伪类选择器)
  5. python dict 排序原理_第一章Python数据结构和算法(字典排序)
  6. 驱动下通过进程PID获得进程名 (动态获取ImageFileName在EPROCESS结构体中的相对偏移)...
  7. 1-10 之间的整数相加,得到累加值大于 20 的当前数
  8. 为什么要返回function_宇宙飞船返回时,为什么要冒着烧毁的风险加速穿过大气层?...
  9. python自动登录qq空间_python 利用splinter组件,自动登录QQ空间
  10. Django之数据表增删改查
  11. div和img之间的缝隙问题
  12. Android开发学习笔记---搭建Android开发环境
  13. HTTP Cookies
  14. 【软考】数据库系统工程师备考指南(一)
  15. 高等数学学习笔记——第九十五讲——函数的幂级数展开
  16. ITRF框架转换程序实现的理论
  17. 良心推荐:12个免费学习网站,赶紧收藏
  18. 量子计算机进行量子传输,量子技术新突破:按一下按钮就能实现量子传输!
  19. web网站搭建(nginx优化)二
  20. 服务器显示器无法调节分辨率,求救:分辨率调的太高,显示器无法显示怎么办?...

热门文章

  1. mixin的使用方法
  2. mysql 迪卡尔运算_「笛卡尔乘积」mysql笛卡儿积 - seo实验室
  3. 电脑开机黑屏提示:ERROR 0199:System Security-Security password retry count exceeded(by 星空武哥)
  4. 2018年 中南大学研究生复试机试题(1025~ 1028)
  5. mysql 星座_mysql 查询年龄段,星座以及最近7天生日的sql
  6. WordPress 主题模板QUX9.1.4开心版无授权限制 DUX二开增强主题
  7. 路由器以太网口静态链路聚合
  8. 机器学习(二):k近邻法(kNN)
  9. PbootCMS采集-PbootCMS自动采集-PbootCMS免登录发布插件
  10. mv命令 – 移动或改名文件