ansible基础

环境配置

主机列表

主机名 IP地址 角色
control 192.168.4.253 控制节点(manager)
Node1 192.168.4.1 被控制节点(test)
Node2 192.168.4.2 被控制节点(proxy)
Node3 192.168.4.3 被控制节点(web1)
Node4 192.168.4.4 被控制节点(web2)
Node5 192.168.4.5 被控制节点(database)

1 修改控制主机的/etc/hosts文件

1
2
3
4
5
6
7
8
9
10
11
[root@MiWiFi-R1CL-srv ~]# cat /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.31.162 control
192.168.31.26 node1
192.168.31.208 node2
192.168.31.248 node3
192.168.31.217 node4
192.168.31.165 node5

# 这样就可以直接ping node1

2 修改控制主机的/etc/hosts、配置SSH密钥

1
2
3
4
5
6
7
8
[root@MiWiFi-R1CL-srv ~]# ssh-keygen -f /root/.ssh/id_rsa -N ''
[root@MiWiFi-R1CL-srv ~]# for i in node1 node3 # 只开启了两台节点
> do
> ssh-copy-id $i
> done


# 此后,就可以直接ssh node1连接其他节点,无密码连接

3 控制端安装ansible

1
yum -y install ansible

4 被控制端节点操作

  • ansible默认通过ssh协议管理机器
  • 被管理主机要开启ssh服务,并允许控制主机登录
  • 被管理主机需要安装有python

5 修改配置文件

  • 主配置文件ansible.cfg(/etc/ansible/ansible.cfg)

  • ansible配置文件查找顺序

    • 首先检测ANSIBLE_CONFIG变量定义的配置文件
    • 首次检查当前目录的./absible.cfg文件
    • 再次检查当前用户家目录下~/ansible.cfg文件
    • 最后检查/etc/ansible/ansible.cfg文件
  • 建议,在用户家目录创建配置文件

    1
    2
    3
    4
    5
    [root@MiWiFi-R1CL-srv ~]# mkdir ~/ansible
    [root@MiWiFi-R1CL-srv ~]# vim ~/ansible/ansible.cfg
    [root@MiWiFi-R1CL-srv ansible]# cat ansible.cfg
    [defaults]
    inventory = ~/ansible/hosts
  • 将被管理端主机写入一个主机列表文件(主机清单)

    参考/etc/ansible/hosts

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    [root@MiWiFi-R1CL-srv ansible]# vim ~/ansible/hosts
    [root@MiWiFi-R1CL-srv ansible]# cat ~/ansible/hosts
    [test]
    node1
    [proxy]
    node2
    [webserver]
    node3
    node4
    [database]
    node5
    [cluster:children]
    webserver
    database
  • 测试ansible环境与配置是否正常

    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
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    [root@MiWiFi-R1CL-srv ansible]# ansible all --list-hosts
    hosts (5):
    node2
    node1
    node3
    node4
    node5

    [root@MiWiFi-R1CL-srv ansible]# ansible node1 -m ping
    node1 | SUCCESS => {
    "ansible_facts": {
    "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
    }

    [root@MiWiFi-R1CL-srv ansible]# ansible node1,node2,node3 -m ping
    node3 | SUCCESS => {
    "ansible_facts": {
    "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
    }
    node1 | SUCCESS => {
    "ansible_facts": {
    "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
    }
    node2 | UNREACHABLE! => {
    "changed": false,
    "msg": "Failed to connect to the host via ssh: ssh: connect to host node2 port 22: No route to host",
    "unreachable": true
    }

    [root@MiWiFi-R1CL-srv ansible]# ansible webserver -m ping
    node4 | UNREACHABLE! => {
    "changed": false,
    "msg": "Failed to connect to the host via ssh: ssh: Could not resolve hostname node4: Name or service not known",
    "unreachable": true
    }
    node3 | SUCCESS => {
    "ansible_facts": {
    "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
    }

ansible ad-hoc

Ansible ad-hoc是一种通过命令行批量管理的方式

  • 格式: ansible 主机集合-m模块名-a “参数”
  • 其他参数:
    • -k 使用密码远程
    • -i 指定主机列表文件
  • 模块就是脚本(多数为python脚本)
    • 多数脚本都支持参数
    • 默认模块为command
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
[root@MiWiFi-R1CL-srv ansible]# ansible node1 -m command -a 'uptime'
node1 | CHANGED | rc=0 >>
15:49:13 up 25 min, 2 users, load average: 0.01, 0.03, 0.05

[root@MiWiFi-R1CL-srv ansible]# ansible node1 -m command -a 'uname -r'
node1 | CHANGED | rc=0 >>
3.10.0-1160.59.1.el7.x86_64

[root@MiWiFi-R1CL-srv ansible]# ansible node1 -a 'ip a s'
node1 | CHANGED | rc=0 >>
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:0c:29:1b:77:65 brd ff:ff:ff:ff:ff:ff
inet 192.168.31.26/24 brd 192.168.31.255 scope global noprefixroute dynamic ens33
valid_lft 41663sec preferred_lft 41663sec
inet6 fe80::5e91:f1e8:6f7a:b159/64 scope link noprefixroute
valid_lft forever preferred_lft forever
3: virbr0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default qlen 1000
link/ether 52:54:00:1f:76:c2 brd ff:ff:ff:ff:ff:ff
inet 192.168.122.1/24 brd 192.168.122.255 scope global virbr0
valid_lft forever preferred_lft forever
4: virbr0-nic: <BROADCAST,MULTICAST> mtu 1500 qdisc pfifo_fast master virbr0 state DOWN group default qlen 1000
link/ether 52:54:00:1f:76:c2 brd ff:ff:ff:ff:ff:ff

[root@MiWiFi-R1CL-srv ansible]# ansible test -a 'date'
node1 | CHANGED | rc=0 >>
2022年 03月 19日 星期六 15:50:20 CST

[root@MiWiFi-R1CL-srv ansible]# ansible node1 -m command -a 'uptime'
node1 | CHANGED | rc=0 >>
15:52:17 up 28 min, 2 users, load average: 0.00, 0.01, 0.05

入门

// 列出所有模块

ansible-doc -l

// 过滤模块

ansible-doc -l |grep yum

// 查看模块帮助

ansible-doc yum

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
# 查看安装的模块
[root@MiWiFi-R1CL-srv ansible]# ansible-doc -l
fortios_router_community_list Configure commun...
azure_rm_devtestlab_info Get Azure DevTes...
ecs_taskdefinition register a task ...
avi_alertscriptconfig Module for setup...
tower_receive Receive assets f...
netapp_e_iscsi_target NetApp E-Series ...
azure_rm_acs Manage an Azure ...
fortios_log_syslogd2_filter Filters for remo...
junos_rpc Runs an arbitrar...
na_elementsw_vlan NetApp Element S...
pn_ospf CLI command to a...
pn_snmp_vacm CLI command to c...
cp_mgmt_service_sctp Manages service-...
onyx_ospf Manage OSPF prot...
icx_command Run arbitrary co...
cs_snapshot_policy Manages volume s...
nxos_install_os Set boot options...
cnos_static_route Manage static IP...

# 查看总共的模块数
[root@MiWiFi-R1CL-srv ansible]# ansible-doc -l | wc -l
3387

shell模块

command和shell模块的区别

  • command模块的命令不启动shell,直接通过ssh执行命令
  • command不支持bash的特性,如管道和重定向等功能
  • 所有需要调用shell的功能都无法使用
1
2
3
4
5
6
7
8
9
10
11
12
13
#test主机中添加文件
[root@MiWiFi-R1CL-srv ansible]# ansible test -m shell -a "touch /tmp/123.txt"
[WARNING]: Consider using the file module with state=touch rather than running
'touch'. If you need to use command because file is insufficient you can add
'warn: false' to this command task or set 'command_warnings=False' in ansible.cfg
to get rid of this message.
node1 | CHANGED | rc=0 >>

# 查看test主机的连接情况
[root@MiWiFi-R1CL-srv ansible]# ansible test -m shell -a "who"
node1 | CHANGED | rc=0 >>
root pts/0 2022-03-19 15:25 (192.168.31.33)
root pts/1 2022-03-19 16:07 (control)
  • ansible使用ssh远程连接被管理主机

    退出ssh后所有状态失效

1
2
3
4
5
6
7
8
# 切换目录
[root@MiWiFi-R1CL-srv ansible]# ansibel test -m shell -a "cd/tmp"
# 创建文件(注意:没有指定路径,是在家目录创建的文件)
[root@MiWiFi-R1CL-srv ansible]# ansible test -m shell -a "touch my.txt"
# 查看文件失败
[root@MiWiFi-R1CL-srv ansible]# ansible test -m shell -a "ls /tmp/my.txt"
# 使用chdir参数切换工作目录
[root@MiWiFi-R1CL-srv ansible]# ansible test -m shell -a "chdir=/tmp touch my.txt"
  • shell模块支持判断(creates、removes)
    • creates文件名:文件存在,不执行shell命令
    • removes文件名:文件不存在,不执行shell命令
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 如果test主机存在密钥就不执行,不存在就生成密钥
[root@MiWiFi-R1CL-srv ansible]# ansible test -m shell -a "ssh-keygen -f ~/.ssh/id_rsa -N '' creates=~/.ssh/id_rsa"
node1 | SUCCESS | rc=0 >>
skipped, since /root/.ssh/id_rsa exists

# 如果没有按照unzip软件包,则不执行解压命令
[root@MiWiFi-R1CL-srv ansible]# ansible node1,node2 -m shell -a "unzip /root/test.zip removes=/bin/unzip"
[WARNING]: Consider using the unarchive module rather than running 'unzip'. If
you need to use command because unarchive is insufficient you can add 'warn:
false' to this command task or set 'command_warnings=False' in ansible.cfg to get
rid of this message.
node2 | FAILED | rc=9 >>
unzip: cannot find or open /root/test.zip, /root/test.zip.zip or /root/test.zip.ZIP.non-zero return code
node1 | FAILED | rc=9 >>
unzip: cannot find or open /root/test.zip, /root/test.zip.zip or /root/test.zip.ZIP.non-zero return code

script模块

1
2
# 将脚本文件test.sh传入node1主机并执行,这个脚本文件是临时文件,执行完后,node1会删除
[root@MiWiFi-R1CL-srv ansible]# ansible node1 -m script -a "./test.sh"

file模块

file模块可以创建文件,目录,链接;修改权限与属性等

幂等性:任意次执行所产生的影响均与一次执行的影响相同

1
2
3
4
5
6
#新建文件state=touch
[root@MiWiFi-R1CL-srv ansible]# ansible test -m file -a "path=/tmp/file.txt state=touch"
#创建目录state=directory
[root@MiWiFi-R1CL-srv ansible]# ansible test -m file -a "path=/tmp/mydir state=directory"
#修改文件或目录权限
[root@MiWiFi-R1CL-srv ansible]# ansible test -m file l -a "path=/tmp/file.txt owner=sshd group=adm mode=0777"

copy模块

将文件拷贝到远程主机

backup=yes如果目标主机有同名文件,则先备份

1
2
3
4
5
6
7
#新建测试文件
[root@MiWiFi-R1CL-srv ansible]# echo AAA >~la3.txt

[root@MiWiFi-R1CL-srv ansible]# ansible test -m copy -a "src=~la3.txt dest=/root/"
[root@MiWiFi-R1CL-srv ansible]# ansible test -m copy -a "src=~la3.txt dest=/root/3a.txt"
#通过content可以直接提供文件内容, n代表回车
[root@MiWiFi-R1CL-srv ansible]# ansible test -m copy l -a "content='hello the world\n' dest=/root/new.txt"

fetch模块

与copy类似,但是作用相反

可以将其他主机的文件拷贝到本地

1
2
3
4
# 将远程主机的hostname文件下载到本地家目录,dest是本地目录
# 多台主机fetch时,如果重名后天会自行重命名
[root@MiWiFi-R1CL-srv ansible]# ansible test -m fetch -a "src=/etc/hostname dest=~/"
[root@MiWiFi-R1CL-srv ansible]# ls ~l

lineinfile | replace模块

  • 在修改单个文件的单行内容时可以使用lineinfile模块
1
2
3
4
5
6
#在letc/issue文件中添加一行内容hello world,默认添加到最后
[root@MiWiFi-R1CL-srv ansible]# ansible test -m lineinfile -a "path=/etc/lissue line='hello world"
#基于幂等原则,重复执行,不会创建多行内容
[root@MiWiFi-R1CL-srv ansible]# ansible test -m lineinfile -a "path=/etc/issue line='helle world"
#将内容插入到Kernel行的后面
[root@MiWiFi-R1CL-srv ansible]# ansible test -m lineinfile -a "path=/etc/issue line='insert' insertafter='Kernel'"
  • lineinfile会替换一整行,replace可以替换关键词
1
2
#将/etc/issue文件全文所有的Kernel替换为0cean
[root@MiWiFi-R1CL-srv ansible]# ansible test -m replace l -a "path=/etc/issue.net regexp=Kernel replace=Ocean"

user模块

  • user模块可以实现Linux系统账户管理
1
2
3
4
5
6
7
8
#远程test组中的所有主机并创建系统账户tuser1
[root@MiWiFi-R1CL-srv ansible]# ansible test -m user -a "name=tuser1"

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

#修改账户密码,修改密码必须使用password_hash()算法
[root@MiWiFi-R1CL-srv ansible]# ansible test -m user -a "name=tuser1 password={{'abc'| password_hash('sha512')}}"

yum_repository模块

  • 使用yum_repository可以创建或修改yum源配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
#新建一个yum源配置文件/etclyum.repos.d/myyum.repo
[root@MiWiFi-R1CL-srv ansible]# ansible test -m yum_repository -a "name=myyum description=hello baseurl=ftp://192.168.31.126/centos gpgcheck=no"

#yum源文件名为myyum,该文件的内容如下:
[myyum]
baseurl = ftp://192.168.31.126/centosgpgcheck = 0
name = hello

#修改yum源文件内容
[root@MiWiFi-R1CL-srv ansible]# ansible test -m yum_repository -a "name=myyumdescription=test baseurl=ftp://192.168.4.254/centos gpgcheck=yes gpgkey=..."

#删除yum源文件myyum
[root@MiWiFi-R1CL-srv ansible]# ansible test -m yum_repository -a "name=myyum state=absent"

yum模块

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

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

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

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

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

逻辑卷相关模块

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

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

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

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

#修改卷组大小
[root@MiWiFi-R1CL-srv ansible]# ansible test -m lvg -a "vg=myvg pvs=/dev/sdb1,/devlsdb2"
  • lvol模块:创建、删除逻辑卷(LV),修改逻辑卷大小

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

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

#修改LV逻辑卷大小
[root@MiWiFi-R1CL-srv ansible]# ansible test -m Ivol -a "Iv=mylv vg=myvg size=4G"

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

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

sudo提权

基本概念

  • sudo(以超级管理员或其他的身份执行命令)
  • 基本流程
    • 管理员需要先授权(修改/etc/sudoers文件)
    • 普通用户已sudo的形式执行命令
    • 可以通过sudo -l查看授权情况

sudoers语法

  • 修改/etc/sudoers的方法
    • visudo(带语法检查,默认没有颜色提示)
    • vim /etc/sudoers(不带语法检查,默认有颜色提示)
  • 授权格式如下:
    • 用户或组 主机列表=(提权身份) [NOPASSWD]:命令列表
    • 命令需要些绝对路径
1
2
3
[root@MiWiFi-R1CL-srv ansible]# cat /etc/sudoers
root ALL=(ALL) ALL
%wheel ALL=(ALL) ALL
  • 测试(在node1中)
1
2
3
4
5
6
7
8
9
10
11
12
13
# 在node1创建一个新用户jerry
[root@MiWiFi-R1CL-srv ~]# useradd jerry
[root@MiWiFi-R1CL-srv ~]# passwd jerry
更改用户 jerry 的密码 。
新的 密码:
无效的密码: 密码少于 8 个字符
重新输入新的 密码:
passwd:所有的身份验证令牌已经成功更新。

# 此时很多服务命令jerry用户无法使用,需要root授权

# 修改配置文件,使用visudo,和vim /etc/sudoers都可以
# 此时就可以使用sudo systemctl方式使用这个命令

  • 在所有被管理主机配置sudo,让alice可以管理系统服务
    • 默认普通用户无权执行systemctl命令
1
2
3
4
[root@MiWiFi-R1CL-srv ansible]# ansible all -m lineinfile -a "path=/etc/sudoers line='alice ALL=(ALL)/usr/bin/systemctl'"
[root@MiWiFi-R1CL-srv ansible]# ssh alice@node1 # 密码123456
[alice@proxy ~]$ sudo -l
[alice@proxy ~]$ sudo systemctl restart chronyd #验证
  • 在所有被管理主机配置sudo,让alice可以执行任何命令
    • 使用NOPASSWD开启无密码验证
1
2
3
4
[root@MiWiFi-R1CL-srv ansible]# ansible all -m lineinfile -a "path=/etc/sudoers line='aliceALL=(ALL) NOPASSWD:ALL"
[root@MiWiFi-R1CL-srv ansible]# ssh alice@node1
[alice@proxy ~]$ sudo -l
[alice@proxy ~]$ sudo systemctl restart chronyd #不需要输入密码

高级配置

  • 修改~/ansible/ansible.cfg
    • 修改sudo相关配置(参考/etc/ansible/ansible.cfg)
1
2
3
4
5
6
7
8
9
10
11
[root@MiWiFi-R1CL-srv ansible]# vim ~/ansible/ansible.cfg
[defaults]
inventory = ~/ansible/hosts
remote_user = alice #以什么用户远程被管理主机
#host_key_checking = False #是否校验密钥

[privilege_escalation]
become = True #是否需要切换用户
become_method = sudo #如何切换用户
become_user = root #切换成什么用户
become_ask_pass = False #sudo是否需要输入密码
  • 使用alice远程被管理主机,需要提前配置SSH密钥
1
2
3
4
5
6
[root@control ~]# for i in node1 node2 node3 node4 node5
do
ssh-copy-id alice@$i
done

[root@control~]# ansible all -m command -a "who" #测试效果
  • inventory配置

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

1
2
3
4
5
6
7
8
9
10
11
12
[root@control~]# cat ~lansible/hosts
[test]
node1 ansible_ssh_port=220 #自定义远程SSH端口
[proxy]
node2 ansible_ssh_user=alice #自定义远程连接的账户名
[webserver]
node[3:4] ansible_ssh_pass=密码 #自定义远程连接的密码
[database]
node5 ansible_ssh_private_key_file=密钥文件 #自定义远程连接的密钥
[cluster:children]
webserver
database

playbook

概述

  • Ansible ad-hoc可以通过命令行形式远程管理其他主机

    • 适合执行一些临时性简单任务
  • Ansible playbook中文名称叫剧本

    • 将经常需要执行的任务写入一个文件(剧本)
    • 剧本中可以包含多个任务
    • 剧本写后,我们随时调用剧本,执行相关的任务命令
    • playbook剧本要求按照YAML格式编写
    • 适合执行周期性经常执行的复杂任务

YAML格式

  • “#”代表注释,一般第一行为三个横杠

  • 键值对使用“∵”表示,数组使用”-“表示

  • 缩进必须由两个或以上空格组成

  • 相同层级的缩进必须对齐

  • 全文不可以使用tab键

  • 区分大小写、扩展名为yml或者yaml

  • 跨行数据需要使用>或者|(|会保留换行符)

  • YAML格式的键值对数据

    • key和value之间使用“:”分割
    • “:”后面必须有空格
    • 缩进代表层级关系
1
2
3
4
5
# 例子
"诗仙": "李白"
或者
"诗仙":
"李白"
  • YAML格式的数组数据

    使用短横杠和空格表示,一行表示数据格式[值,值,值……]

综合示例

|会保留换行符

-和:后面必须有空格

playbook语法格式

  • playbook采用YAML格式编写

  • playbook文件中由一个或多个play组成

  • 每个play中可以包含:

    • hosts(主机)、tasks(任务)

    • variables(变量)、roles(角色)、handlers等元素组成

  • 使用ansible-playbook命令运行playbook剧本

测试第一个playbook

1
2
3
4
5
6
7
8
[root@control ansible]# vim ~/ansible/test.yml
---
- hosts: all
tasks:
- name: This is my first playbook
ping:

[root@control ansible]# ansible-playbook ~/ansible/test.yml
  • hosts由一个或多个组或主机组成,逗号分隔

  • tasks由一个或多个任务组成,多个任务按顺序执行

  • 可以使用-f选项自定义并发量

1
2
3
4
5
6
7
8
9
[root@control ansible]# vim ~/ansible/test.yml
---
- hosts: test,webserver
tasks:
- name: This is my first playbook
ping:
- name: Run a shell command
shell: touch ~/shell.txt
[root@control ansible]# ansible-playbook ~lansible/test.yml -f 5
  • 一个playbook中可以有多个play
1
2
3
4
5
6
7
8
9
10
[root@control ansible]# vim ~/ansible/test.yml
---
- hosts: test
tasks:
- name: This is first play
ping:
- hosts: webserver
tasks:
- name: This is second play
ping:

修改VIM配置

  • 使用2个空格自动替换tab键

    tabstop=2、expandtab

  • 开启自动缩进对其,缩进宽度为2个空格

    shiftwidth=2

1
2
[root@control ansible]# cat ~/.vimrc
autocmd FileType yaml setlocal ai ts=2 sw=2 et

playbook应用案例之用户

  • 编写playbook创建系统账户、账户属性、设置密码
1
2
3
4
5
6
7
8
9
10
[root@control ansible]# cat ~/ansible/test_john.yml
---
- hosts: webserver
tasks:
- name: Add the user 'johnd'
user:
name: johnd
uid: 1040
group: daemon
password: "{{ '123' | password_hash('sha512') }}"

花括号外面必须有双引号

1
2
3
4
5
6
7
8
9
10
[root@control ansible]# cat ~/ansible/user_james.yml
---
- hosts: webserver
tasks:
- name: Add 'james' with a bash shell, set 'bin' and 'adm' to the user's groups
user:
name: james
shell: /bin/bash
groups: bin,adm
password: "{{ '123'| password_hash('sha512') }}”
  • 编写playbook删除系统账户johnd
1
2
3
4
5
6
7
8
[root@control ansible]# cat ~/ansible/user_johnd.yml
---
- hosts: webserver
tasks:
- name: Remove the user 'johnd'
user:
name: johnd
state: absent
  • 使用vdb创建卷组合逻辑卷(手动添加虚拟磁盘)
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
[root@control ansible]# cat ~/ansible/lvm.yml
---
- hosts: test
tasks:
- name: Create a new primary partition with a size of 1GiB
parted:
device: /dev/sdb
number: 1
state: present
part_end: 1GiB
- name: Create a new primary partition with a size of 2GiB
parted:
device: /dev/sdbnumber: 2
state: present
part_start: 1GiB
part_end: 3GiB
- name: Create a volume group on top of /dev/sdb1
lvg:
vg: my_vg
pvs: /dev/sdb1
- name: Create a logical volume of 512m
lvol:
vg: my_vg
lv: my_lv
size: 512m

playbook应用案例之软件管理

  • 安装软件、升级软件、安装组包
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[root@control ansible]# cat ~/ansible/pacakge.yml
---
- hosts: webserver
tasks:
- name: Install a list of packages
yum:
name:
- httpd
- mariadb
- mariadb-server
- name: install the 'Development tools' package group
yum:
name: "@Development tools"
- name: update software
yum:
name: '*'
state: latest

setup模块

  • ansible_facts用于采集被管理设备的系统信息

  • 所有收集的信息都被保存在变量中

  • 每次执行playbook默认第一个任务就是Gathering Facts

  • 使用setup模块可以查看收集到的facts信息

1
2
3
4
5
6
[root@control ansible]# ansible test -m setup
192.168.4.10 |SUCCESS => {
"ansible_facts": {
"ansible_all_ipv4_addresses": [
"192.168.4.10"
…省略部分内容…

debug模块

  • debug模块可以显示变量的值,可以辅助排除
    • debug模块有两个参数,var和msg(引用变量需要两个大括号括起来)
1
2
3
4
5
6
7
8
9
10
11
12
[root@control ansible]# cat ~/ansible/debug.yml
---
- hosts: test
tasks:
- debug:
var: ansible all_ipv4_addresses
- debug:
msg: "主机名是:{{ iansible_hostname }}"
- debug:
var: ansible devices.vda.partitions.vda1.size
- debug:
msg: "总内存大小:{{ ansible_memtotal_mb }}"

定义变量

  • ansible支持十几种定义变量的方式
  • 这里我们仅介绍其中一部分变量,根据优先级排序
    • inventory变量
    • host facts变量
    • register变量
    • playbook变量
    • playbook提示变量
    • 变量文件
    • 命令行变量

应用案例

  • inventory变量(在主机清单配置文件中定义变量)
1
2
3
4
5
6
7
8
9
[root@control ansible]# cat ~/ansible/hosts
[test]
node1 myvar1="hello the world" myvar2="content"
[proxy]
node2
[webserver]
node[3:4]
[webserver:vars]
yourname="jacob"
1
2
3
4
5
6
7
8
9
10
11
[root@control ansible]# cat ~/ansible/linventory_var.yml
---
- hosts: test
tasks:
- name: create a file with var.
shell: echo i{ myvar1 > /tmp/{{myvar2}}
- hosts: webserver
tasks:
- name: create a user with var.
user:
name: "{{ yourname }}"
  • host facts变量(可以直接调用ansible手机的系统信息)
1
2
3
4
5
6
7
8
[root@control ansible]# cat ~/ansible/facts_var.yml
---
- hosts: test
tasks:
- name: Use facts info.
copy:
content: "{{ansible_hostname}}:{{ansible_bios_version}}"
dest: /tmp/facts.txt
  • register语句可以将某个命令的执行结果保存到变量中
1
2
3
4
5
6
7
8
9
10
11
12
13
[root@control ansible]# cat ~/ansible/register.yml
---
- hosts: test
tasks:
- name: save shell result to a variable.
shell: hostname
register: myvar
- name: print the variable's value through debug
debug:
msg: "{{ myvar }}"

# 通过"."我们还可以仅提取部分数据
msg: "{{myvar.stdout}}"
  • playbook变量(使用vars关键词可以在playbook内定义变量)
1
2
3
4
5
6
7
8
9
10
11
[root@control ansible]# cat ~/ansible/playbook_var.yml
---
- hosts: test
vars:
iname: heal
ipass: '123456' # 注意密码必须是字符串,需安引号
tasks:
- name: Use variables create user.
user:
name: "{{ iname }}"
password: "{{ ipass | password_hash('sha512') }}"
  • playbook提示变量(根据提示输入变量的值)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@control ansible]# cat ~/ansible/prompt_var.yml
---
- hosts: test
vars_prompt:
- name: iname
prompt:"请输入用户名"
private: no #回显用户名
- name: ipasswd
prompt: "请输入密码"
private: yes #不显示密码
tasks:
- name: Create a user.
user:
name: "{{ iname }}"
password: "{{ ipasswd | password_hash('sha512')}}"
  • 单独定义个变了文件,在playbook中用var_files调用该文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@control ansible]# cat ~/ansible/variables.yml
---
iname: cloud
ipass: '123456'

[root@control ansible]# cat ~lansible/file_var.yml
---
- hosts: test
vars_files: variables.yml
tasks:
- name: create user.
user:
name: "f{i iname }}”
password: "{{ ipass | password_hash('sha512')}}”
  • 执行ansible-playbook命令时使用-e参数定义变量
1
2
3
4
5
6
7
8
9
10
[root@control ansible]# cat ~lansible/command_var.yml
---
- hosts: test
tasks:
- name: create user
user:
name: "{{ iname }}"
password: "{{ ipass | password_hash('sha512')}}"

[root@control ansible]# ansible-playbook command_var.yml -e iname="beth" -e ipass="123456"

ansible模块应用

firewalld模块

  • 使用firewalld模块可以配置防火墙策略
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[root@control ~]#cat ~/ansible/firewall.yml
---
- hosts: test
tasks:
- name: install firewalld.
yum:
name: firewalld
state: present
- name: run firewalld.
service:
name: firewalld
state: started
enabled: yes
- name: set firewalld rule.
firewalld:
port:80/tcp
permanent: yes
state: enabled

template模块

  • copy模块可以将一个文件拷贝给远程主机

  • 但是如果希望每个拷贝的文件内容都不一样呢?

  • 如何给所有web主机拷贝index.html内容是各自的IP地址?

  • Ansible可以利用Jinja2模板引擎读取变量

    • 之前在playbook中调用变量,也是Jinja2的功能

    • Jinja2模块的表达式包含在分隔符{{ }}

案例:

  • 给webserver主机拷贝首页,每个主机内容不同
1
2
3
4
5
6
7
8
9
10
11
12
13
[root@control ansible]# mkdir ~/ansible/template
[root@control ansible]# cat ~/ansible/template/index.html
Welcom to {{ansible_hostname}} on {{ ansible_eth0.ipv4.address }}.

#模板文件中调用变量不需要双引号
[root@control ansible]# cat ~/ansible/template.yml
---
- hosts: webserver
tasks:
- name: use template copy index.html to webserver.
template:
src: ~/ansible/templatelindex.html
dest: /var/www/html/index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
[root@control ansible]# cat ~lansible/template/source.j2
{{ welcome }} {{ iname }} ...
[root@control ansible]# cat ~/ansible/template_2.yml
---
- hosts: webserver
vars:
welcome: 'hello'
iname: 'jack'
tasks:
- name: use template copy a file to remote host.
template:
src: ~lansible/templatelsource.j2
dest: /tmpl

ansible高级语法

error处理机制

  • 默认ansible在遇到error会立即停止playbook
1
2
3
4
5
6
7
8
9
10
11
12
[root@control ansible]# cat ~lansible/error.yml
---
- hosts: test
tasks:
- name: start a service that does not exist.
service:
name: hehe #没有这个服务
state: started
- name: touch a file.
file:
path: /tmplservice.txt
state: touch
  • 使用ignore_errors可以忽略错误,继续后续的任务
1
2
3
4
5
6
7
8
9
10
11
12
13
[root@control ansible]# cat ~lansible/error.yml
---
- hosts: test
tasks:
- name: start a service that does not exist.
service:
name: hehe
state: started
ignore_errors: true # 针对某一个任务忽略错误
- name: touch a file.
file:
path: /tmp/service.txt
state: touch

handlers

  • 当某个任务需要依赖其他任务怎么办?
    • 可以通过handlers定义一组任务
    • 仅当某个任务触发(notify)handlers时才执行相应的任务
    • 如果有多个notify触发执行handlers任务,也仅执行一次
    • 仅当任务的执行状态为changed时handlers任务才执行
    • handlers任务在所有其他任务都执行后才执行

案例:

  • 通过notify触发执行handlers任务
1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@control ansible]# cat ~/ansible/handlers.yml
---
- hosts: test
tasks:
- name: create directory. # 多次执行playbook该任务状态不再是changed
file:
path: /tmp/parents/subdir/
state: directory
notify: touch file # notify后面名称必须和handlers中的任务名称一致
handlers:
- name: touch file
file:
path: /tmp/parents/subdir/new.txt
state: touch

when条件判断

  • when可以定义判断条件,条件为真时才执行某个任务

  • 常见条件操作符如下:

    • ==、!=、>、>=、<、<=
  • 多个条件可以使用and或or分割

  • when表达式中调用变量不要使用{{ }}

案例:

  • 远程主机剩余内存不足700M则关闭NetworkManager服务
1
2
3
4
5
6
7
8
9
[root@control ansible]# cat ~/ansible/when_1.yml
---
- hosts: test
tasks:
- name: check memory size.
service:
name: NetworkManager
state: stopped
when: ansible_memfree_mb < 700
  • 判断操作系统是Centos7则创建测试文件

    • >支持多行输入,不保留换行符
1
2
3
4
5
6
7
8
9
10
11
12
[root@control ansible]# cat ~/ansible/when_2.yml
---
- hosts: test
tasks:
- name: touch a file
file:
path: /tmp/when.txt
state: touch
when: >
ansible_distribution == "Centos"
and
ansible_distribution_major_version == "7"

block任务块

  • 使用block可以将多个任务合并为一个组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@control ansible]# cat ~lansible/block_1.yml
---
- hosts: test
tasks:
- name: define a group of tasks.
block:
- name: install httpd
yum:
name: httpd
state: present
- name: start httpd
service:
name: httpd
state: started
when: ansible_distribution == "Centos"
  • rescue定义block任务执行失败时要执行的其他任务

  • always定义无论block任务是否成功,都要执行的任务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[root@control ansible]# cat ~lansible/block_2.yml
---
- hosts: test
tasks:
- block:
- name: touch a file test1.txt
file:
path: /tmp/test1.txt #如果改为/tmplxyz/test1.txt就无法创建成功
state: touch
rescue:
- name: touch a file test2.txt
file:
path: /tmp/test2.txt
state: touch
always:
- name: touch a file test3.txt
file:
path: /tmp/test3.txt
state: touich

loop循环

  • 很多任务都在用相同的模块?使用loop循环避免重复
1
2
3
4
5
6
7
8
9
10
11
12
[root@control ansible]# cat ~/ansible/simple_loop.yml
---
- hosts: test
tasks:
- name: mkdir multi directory.
file:
path: /tmpl{{item}} #注意,item是关键字
state: directory
loop:
- School
- Legend
- Life
1
2
3
4
5
6
7
8
9
10
11
[root@control ansible]# cat ~/ansible/complex_loop.yml
---
- hosts: test
tasks:
- name: create multi user.
user:
name: "{{item.iname}}"
password: "{{item.ipass | password_hash('sha512')}}"
loop:
- { iname: 'term', ipass: '123456'}
- { iname: 'amy' , ipass: '654321'}

ansible vault

加密文件

  • ansible有时需要访问一些敏感数据,如密码、key等
  • 使用ansible-vault可以加密和 解密数据
    • encrypt(加密)、decrypt(解密)、view(查看)
1
2
3
4
[root@control ansible]# echo 123456 > data.txt   #新建测试文件
[root@control ansible]# ansible-vault encrypt data.txt #加密文件
[root@control ansible]# cat data.txt
[root@control ansible]# ansible-vault view data.txt #查看加密文件
  • ansible-vault rekey可以修改加密的密码
1
2
3
4
5
6
7
[root@control ansible]# ansible-vault encrypt data.txt   #加密文件,密码111
New Vault password: 111
Confirm New Vault password:111
[root@control ansible]# ansible-vault rekey data.txt #修改密码
Vault password:<旧密码>
New Vault password:<新密码>
Confirm New Vault password:<确认新密码>
  • 加密、解密每次都输入密码很麻烦,可以将密码写入文件
1
2
3
4
5
6
[root@control ansible]# echo "'m secret data" > data.txt   #需要加密的敏感数据
[root@control ansible]# echo 123456 > pass.txt #加密的密码
[root@control ansible]# ansible-vault encrypt --vault-id=pass.txt data.txt
[root@control ansible]# cat data.txt
[root@control ansible]# ansible-vault decrypt --vault-id=pass.txt data.txt
[root@control ansible]# cat data.txt
  • 传输敏感数据到远程数据
1
2
3
4
[root@control ansible]# echo "I'm secret data" > data.txt   #需要加密的敏感数据
[root@control ansible]# echo 123456 > pass.txt #加密的密码
[root@control ansible]# ansible-vault encrypt --vault-id=pass.txt data.txt
[root@control ansible]# ansible test -m copy --vault-id=pass.txt -a "src=data.txt dest=/tmpl mode=0600"
  • playbook调用敏感数据(账户名、密码等)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@control ansible]# cat ~/ansible/variables.yml
iname: cloud
ipass: '123456'
[root@control ansible]# ansible-vault encrypt variables.yml
[root@control ansiblej# cat ~/ansible/vault.yml
---
- hosts: test
vars_files: variables.yml
tasks:
- name: include vault data, create user.
user:
name: "{{iname}}"
password: "{{ipasslpassword_hash('sha512')}}”
[root@control ansibilej# ansible-playbook --ask-vault-pass vault.yml

ansible roles

什么是roles

  • 在实际生产环境中,为了实现不同的功能,我们会编写大量的playbook文件

  • 而且,每个playbook还可能会调用其他文件(如变量文件)

  • 对于海量的、无规律的文件,管理起来非常痛苦!

  • Ansible从1.2版本开始支持Roles

  • Roles是管理ansible文件的一种规范(目录结构)

    • Roles会按照标准的规范,自动到特定的目录和文件中读取数据

roles规范的目录结构

  • 如果我们创建了一个名称为user.example的role
    • 其标准的目录结构如下所示

  • defualts/main.yml:定义变量的缺省值,优先级较低

  • files目录:存储静态文件的目录

  • handlers/main.yml:定义handlers

  • meta/main.yml:写作者、版本等描述信息

  • README.md:整个角色(role)的描述信息

  • tasks/main.yml:定义任务的地方

  • templates目录:存放动态数据文件的地方(模板文件)

  • vars/main.yml:定义变量,优先级高

小结:roles就是一组规范的目录结构

应用案例

  • ansible-galaxy命令可以创建、管理自己的roles
1
2
3
4
5
6
7
[root@control ansible]# mkdir ~/ansible/roles

[root@control ansible]# ansible-galaxy init ~/ansible/roles/issue
#创建一个Role,该Role的目的是使用模板修改远程主机的/etc/issue文件

[root@control ansible]# tree ~/ansible/roles/issuel
#查看目录结构,如果没有tree命令则需要使用yum安装该软件
  • 定义issue文件的模板文件
1
2
3
4
[root@control ansible]# cat ~lansible/roles/issue/templates/issue.j2
This is the system {{fansible_hostname}}
Today's date is:{fansible_date_time.date}}
Contact to {{ admin }}
  • 定义变量文件
1
2
3
4
[root@control ansible]# cat ~lansible/roles/issue/vars/main.yml
---
# vars file for /root/ansible/roles/issue
admin: yoyo@tedu.cn
  • 修改任务文件,任务文件中不需要tasks关键字
    • role的各个文件之间相互调用不需要写路径
1
2
3
4
5
6
7
[root@control ansible]# cat ~lansible/roleslissue/tasks/main.yml
---
# tasks file for /root/ansible/roles/issue
- name: delever issue file
template:
src: issue.j2
dest:/etc/issue

在playbook中调用role

  • 方法一:在role相同目录下创建一个playbook调用
  • 方法二:在ansible.cfg设置roles_path=路径
1
2
3
4
5
6
7
8
9
10
[root@control ansible]# cat ~/ansiblelansible.cfg
[defaults]
remote_user = root
inventory = ./inventory
roles_path = ./roles
[privilege_escalation]
become=True
become_method=sudo
become_user=root
become_ask_pass=False
  • 编写playbook文件,通过roles关键词调用role
1
2
3
4
5
6
[root@control ansible]# cat ~/ansible/issue.yml
---
- hosts: test
roles:
- issue
#-role2 #支持加载多个role

ansible-galaxy

1
2
3
4
5
6
7
8
9
10
11
[root@control ansible]# ansible-galaxy search 'httpd'
#联网搜索roles

[root@control ansible]# ansible-galaxy info acandid.httpd
#查看roles基本信息

[root@control ansible]# ansible-galaxy install acandid.httpd -p ~/ansible/roles/
#下载roles到特定的目录

[root@control ansible]# ansible-galaxy list -p roles/
#列出本地有哪些roles
  • 下载roles的方法:
    • 使用ansible-galaxy install或者编写requirements.yml文件
1
2
3
4
5
6
7
8
9
10
11
12
[root@control ansible]# cat ~lansible/roles/requirements.yml
#格式一:直接从Ansible Galaxy官网下载
- src: acandid.httpd
#格式二:从某个git服务器下载
- src: http://gitlab.com/xxx/xxx.git
scm: git
version: 56e00a54
name: nginx-acme
#格式三:下载tar包,支持http、https、file
- src: http://example.com/myrole.tar
name: myrole
[root@control ansible]# ansible-galaxy install -r roles/requirements.yml -p roles

综合案例

自动化部署web集群

  • 创建role,通过role完成项目(可能需要多个role)
  • 部署Nginx调度器
  • 部署2台LNMP服务器
  • 部署MariaDB数据库

创建role部署后端LNMP平台环境

1
2
3
4
5
6
[root@MiWiFi-R1CL-srv ansible]# ansible-galaxy init ~/ansible/roles/lnmp
- Role /root/ansible/roles/lnmp was created successfully
[root@MiWiFi-R1CL-srv ansible]# ls roles
lnmp
[root@MiWiFi-R1CL-srv ansible]# ls roles/lnmp/
defaults files handlers meta README.md tasks templates tests vars

在控制端中files文件中放入Nginx安装包

编写脚本

1
2
3
4
5
6
7
8
9
10
11
12
[root@MiWiFi-R1CL-srv lnmp]# vim ~/ansible/roles/lnmp/files/install_nginx.sh
#!/bin/bash
conf="/usr/local/nginx/conf/nginx.conf"
yum -y install gcc pcre-devel openssl-devel make
cd /tmp/
tar -xf nginx-1.20.2.tar.gz
cd nginx-1.20.2
./configure --with-http_ssl_module
make && make install
sed -i '65,71s/#//' $conf # 删除65-71行的#
sed -i '/SCRIPT_FILENAME/d' $conf # 删除此行
sed -i 's/fastcgi_params/fastcgi.conf/' $conf

编写网页

1
2
3
[root@MiWiFi-R1CL-srv lnmp]# vim ~/ansible/roles/lnmp/templates/index.html
welcome to {{ ansible_hostname }} on {{ ansible_all_ipv4_addresses }}

在task中编写任务

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
[root@MiWiFi-R1CL-srv lnmp]# vim ~/ansible/roles/lnmp/tasks/main.yml
---
# tasks file for /root/ansible/roles/lnmp
- name: copy nginx-1.20.2.tar.gz to webserver.
copy:
src: nginx-1.20.2.tar.gz
dest: /tmp/
- name: install nginx through shell script.
script: install_nginx.sh
args:
creates: /usr/local/nginx/sbin/nginx
- name: copy index.html to webserver.
template:
src: index.html
dest: /usr/local/nginx/html/index.html
- name: install php
yum:
name:
- php
- php-fpm
- php-mysqlnd
- mariadb-devel
- name: run all service
block:
- service:
name: php-fpm
state: started
- shell: /usr/local/nginx/sbin/nginx
args:
creates: /usr/local/nginx/logs/nginx.pid

编写自动化部署剧本

1
2
3
4
5
6
[root@MiWiFi-R1CL-srv lnmp]# vim ~/ansible/lnmp.yml
[root@MiWiFi-R1CL-srv lnmp]# cat ~/ansible/lnmp.yml
---
- hosts: webserver
roles:
- lnmp

运行

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
[root@MiWiFi-R1CL-srv ansible]# ansible-playbook lnmp.yml 

PLAY [webserver] ******************************************************************

TASK [Gathering Facts] ************************************************************
ok: [node4]
ok: [node3]

TASK [lnmp : copy nginx-1.20.2.tar.gz to webserver.] ******************************
changed: [node4]
changed: [node3]

TASK [lnmp : install nginx through shell script.] *********************************
changed: [node4]
changed: [node3]

TASK [lnmp : copy index.html to webserver.] ***************************************
changed: [node3]
changed: [node4]

TASK [lnmp : install php] *********************************************************
changed: [node4]
changed: [node3]

TASK [lnmp : service] *************************************************************
changed: [node4]
fatal: [node3]: FAILED! => {"changed": false, "msg": "Unable to start service php-fpm: Job for php-fpm.service failed because the control process exited with error code. See \"systemctl status php-fpm.service\" and \"journalctl -xe\" for details.\n"}

TASK [lnmp : shell] ***************************************************************
changed: [node4]

PLAY RECAP ************************************************************************
node3 : ok=5 changed=4 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
node4 : ok=7 changed=6 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0



# 出现问题,查看对应主机的日志
[root@MiWiFi-R1CL-srv ~]# tail -20 /var/log/messages


# 再次执行,成功
[root@MiWiFi-R1CL-srv ansible]# ansible-playbook lnmp.yml

PLAY [webserver] ******************************************************************

TASK [Gathering Facts] ************************************************************
ok: [node4]
ok: [node3]

TASK [lnmp : copy nginx-1.20.2.tar.gz to webserver.] ******************************
ok: [node4]
ok: [node3]

TASK [lnmp : install nginx through shell script.] *********************************
skipping: [node3]
skipping: [node4]

TASK [lnmp : copy index.html to webserver.] ***************************************
ok: [node4]
ok: [node3]

TASK [lnmp : install php] *********************************************************
ok: [node4]
ok: [node3]

TASK [lnmp : service] *************************************************************
ok: [node4]
changed: [node3]

TASK [lnmp : shell] ***************************************************************
ok: [node4]
changed: [node3]

PLAY RECAP ************************************************************************
node3 : ok=6 changed=2 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
node4 : ok=6 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0

在node3和node4主机确认一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Nginx已安装
# ls /usr/local/nginx/
client_body_temp fastcgi_temp logs sbin uwsgi_temp
conf html proxy_temp scgi_temp

# Nginx已启动
# ss -nutlp | grep :80
tcp LISTEN 0 128 *:80 *:* users:(("nginx",pid=4689,fd=6),("nginx",pid=4688,fd=6))

# PHP已安装
# rpm -q php
php-5.4.16-48.el7.x86_64

# php-fpm已安装
# rpm -q php-fpm
php-fpm-5.4.16-48.el7.x86_64

# cat /usr/local/nginx/html/index.html
welcome to MiWiFi-R1CL-srv on [u'192.168.122.1', u'192.168.31.248']

创建role部署代理服务环境

1
2
3
4
5
[root@MiWiFi-R1CL-srv ansible]# ansible-galaxy init ~/ansible/roles/proxy
- Role /root/ansible/roles/proxy was created successfully
[root@MiWiFi-R1CL-srv ansible]# ls ~/ansible/roles/proxy/
defaults files handlers meta README.md tasks templates tests vars
[root@MiWiFi-R1CL-srv ansible]# cp ~/ansible/roles/lnmp/files/* ~/ansible/roles/proxy/files/

配置代理服务器

1
2
3
4
5
6
7
8
9
10
11
12
[root@MiWiFi-R1CL-srv files]# cat ~/ansible/roles/proxy/files/install_nginx.sh 
#!/bin/bash
conf="/usr/local/nginx/conf/nginx.conf"
yum -y install gcc pcre-devel openssl-devel make
cd /tmp/
tar -xf nginx-1.20.2.tar.gz
cd nginx-1.20.2
./configure --with-http_ssl_module
make && make install
sed -i '/^http/a upstream webs { \n server 192.168.31.248;\n server 192.168.31.217;\n }\n' $conf
sed -i '49i proxy_pass http://webs;' $conf
/usr/local/nginx/sbin/nginx

编写任务

1
2
3
4
5
6
7
8
9
10
11
[root@MiWiFi-R1CL-srv proxy]# vim ~/ansible/roles/proxy/tasks/main.yml
---
# tasks file for /root/ansible/roles/proxy
- name: copy source file to node2
copy:
src: nginx-1.20.2.tar.gz
dest: /tmp/
- name: install nginx.
script: install_nginx.sh
args:
creates: /usr/local/nginx/sbin/nginx

编写剧本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[root@MiWiFi-R1CL-srv ansible]# vim ~/ansible/proxy.yml
---
- hosts: node2
roles:
- proxy

- hosts: node5
tasks:
- name: install mariadb server
yum:
name:
- mariadb
- mariadb-server
- mariadb-devel
- name: run mariadb server
service:
name: mariadb
state: started

执行

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@MiWiFi-R1CL-srv ansible]# ansible-playbook proxy.yml 

PLAY [node2] **********************************************************************

TASK [Gathering Facts] ************************************************************
Warning: the ECDSA host key for 'node2' differs from the key for the IP address '192.168.31.208'
Offending key for IP in /root/.ssh/known_hosts:2
Matching host key in /root/.ssh/known_hosts:6
Are you sure you want to continue connecting (yes/no)? yes
ok: [node2]

TASK [proxy : copy source file to node2] ******************************************
changed: [node2]

TASK [proxy : install nginx.] *****************************************************
changed: [node2]

PLAY [node1] **********************************************************************

TASK [Gathering Facts] ************************************************************
ok: [node1]

TASK [install mariadb server] *****************************************************
changed: [node1]

TASK [run mariadb server] *********************************************************
changed: [node1]

PLAY RECAP ************************************************************************
node1 : ok=3 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
node2 : ok=3 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

在node2关闭http

1
[root@MiWiFi-R1CL-srv ~]# firewall-cmd --add-service=http