Linux常用命令--Ansible

摘要

  • 本文介绍Ansible的安装和使用。

  • 本文基于CentOS8。

Ansible简介

  • Ansible 是一个开源的自动化运维工具,它基于 Python 语言开发,支持跨平台,可以运行在 Linux、Unix、Mac OS X、Windows 等多种系统环境。

  • Ansible简单易用,只需要在控制主机上安装 Ansible 并在被管理主机上安装 Python 2.6 或更高版本即可(现有的开源Linux系统基本都自带了Python ,所以相当于远程主机什么都不需要安装),就可以管理远程主机对其进行自动化配置、编排高级工作流程以支持应用程序部署、系统更新等。

  • Ansible可以用来管理Linux、Unix、Windows、OpenStack、AWS、GCP、OpenShift、Kubernetes等系统环境。

  • Ansible官网文档:https://docs.ansible.com/ansible/latest/

  • Ansible中文权威指南:https://ansible-tran.readthedocs.io/en/latest/index.html

Ansible安装

  • Ansible安装方式有多种,这里介绍通过pip安装,当前系统中的python版本为python3.9。

1
python3 -m pip install ansible
  • 如果尚未安装pip,可以通过下面的方法进行安装

1
2
wget https://bootstrap.pypa.io/get-pip.py
python3 get-pip.py
  • 安装完成后,可以查看一下版本

1
2
3
4
5
6
7
8
9
10
ansible --version
ansible [core 2.15.9]
config file = None
configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /usr/local/lib/python3.9/site-packages/ansible
ansible collection location = /root/.ansible/collections:/usr/share/ansible/collections
executable location = /usr/local/bin/ansible
python version = 3.9.16 (main, Sep 8 2023, 00:00:00) [GCC 11.4.1 20230605 (Red Hat 11.4.1-2)] (/usr/bin/python3)
jinja version = 3.1.3
libyaml = True
  • Ansible常用参数:

参数 说明
-u 指定连接到主机的用户名
-i 指定连接hosts文件的路径
-k 指定连接到主机的密码
-m 指定模块名称,可以指定模块的参数,比如指定shell模块的参数:-m shell -a ‘ls -l’
-a 指定模块的参数,比如指定shell模块的参数:-m shell -a ‘ls -l’
-e 指定附加参数,比如指定shell端口: -e ‘ansible_port=22’
-b 切换到root权限
-f 指定并发连接数,默认为5,即可以同时管理5台主机

Ansible的配置文件

  • ansible的相关配置都是通过一个名为ansible.cfg的配置文件进行配置的,但我们通过pip安装是不会默认创建它的,实际上不创建这个配置文件也不影响ansible的执行,因为其会使用一些默认的配置,并且在大多数场景下默认的配置就能满足需求。

  • ansible查找配置文件的顺序,优先级由上到下逐渐降低

1
2
3
4
* ANSIBLE_CONFIG (一个环境变量)
* ansible.cfg (位于当前目录中)
* .ansible.cfg (位于家目录中)
* /etc/ansible/ansible.cfg
  • 如果需要修改默认配置,我们可以通过如下命令创建一份配置文件

1
2
3
4
5
6
7
8
9
10
11
# 此时会创建一份所有配置项都以 ; 开头的配置文件,我们要修改那个配置,就删除其前面的 ; ,然后修改其值即可
ansible-config init --disabled > ansible.cfg

# 查看当前的生效配置,注意绿色为默认配置,黄色为被修改的配置
ansible-config dump

# 查看帮助文件
ansible-config list

# 查看默认配置项的值和注释说明
ansible-config init list
  • 常用配置详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 通用默认配置段;
[defaults]
# 被控端IP或者DNS列表;
inventory=/etc/ansible/hosts
# Ansible默认搜寻模块的位置;
library={{ ANSIBLE_HOME ~ "/plugins/modules:/usr/share/ansible/plugins/modules" }}
# 并行进程数;
forks=5
# 是否允许切换为其它用户
become=False
# become=True时,通过sudo进行切换
become_method=sudo
# 切换为root
become_user=root
# 搜索roles的路径
roles_path={{ ANSIBLE_HOME ~ "/roles:/usr/share/ansible/roles:/etc/ansible/roles" }}
# 日志路径,默认不输出
log_path=
# Ansible命令执行默认的模块;
module_name=command
# 是否检查远程主机密钥,默认为True,如果希望不检查可以将其设为False,可以加快连接速度
host_key_checking=True

Ansible的hosts配置

  • 需要创建hosts文件,使用ansible时,ansible会到hosts文件中查找对应的远程主机配置,默认的查找路径为/etc/ansible/hosts,也可以通过ansible.cfg修改默认配置

hosts文件格式1

  • hosts文件格式如下:

1
2
3
4
5
6
7
# 可以配置ip或主机名
[webservers] # 分组名称
10.10.2.45
10.10.2.46
[dbservers]
10.10.2.47
10.10.2.48
  • 命令行里加上连接参数

1
2
3
4
# 执行命令,多个ip逗号分隔
ansible -u username -m ping --private-key=~/.ssh/id_rsa -e 'ansible_port=22' 10.10.2.45,10.10.2.46
# 指定hosts文件路径
ansible -i hosts -u username -m ping --private-key=~/.ssh/id_rsa -e 'ansible_port=22' 10.10.2.45,10.10.2.46

hosts文件格式2

  • hosts文件里也可以配置连接参数

1
2
3
[webservers]
10.10.2.45 ansible_ssh_user=username ansible_ssh_private_key=~/.ssh/id_rsa ansible_ssh_port=22
10.10.2.46 ansible_ssh_user=username ansible_ssh_private_key=~/.ssh/id_rsa ansible_ssh_port=22
  • 此时命令行连接时就不需要指定连接参数了

1
ansible -i hosts -u username -m ping 10.10.2.45,10.10.2.46

hosts文件格式3

  • ansible是基于ssh建立连接的,所以只要ssh能连上远程主机,ansible就可以管理远程主机,如果我们为ssh配置config文件,那么ansible就可以直接将config里的Host名称配置到hosts文件里,这样就不需要配置连接参数了,同时此时也可以支持跳板机。关于ssh的config详细说明可以参考Linux常用命令--ssh、scp与免密登录

    • 配置~/.ssh/config
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    # 跳板机
    Host forward
    User ec2-user
    HostName 53.22.227.200
    Port 22
    IdentityFile ~/.ssh/id_rsa

    # 远程主机
    Host remote-host
    User ec2-user
    HostName 10.9.5.123
    Port 22
    IdentityFile ~/.ssh/key.pem
    ProxyCommand ssh forward -W %h:%p

    # 远程主机 ,10.9网段的所有主机
    Host 10.9.*
    User ec2-user
    Port 22
    IdentityFile ~/.ssh/key.pem
    ProxyCommand ssh forward -W %h:%p
    • hosts文件
    1
    2
    3
    4
    5
    [webservers]
    forward # 与config中配置的Host名称一致
    remote-host # 与config中配置的Host名称一致
    10.9.5.123 # 匹配config中的网段
    10.9.5.124 # 匹配config中的网段
    • 命令行
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    # 通过指定主机名或ip
    ansible -m ping remote-host
    ansible -m ping 10.9.5.123

    # 通配符匹配,此时在匹配hosts中123和124
    ansible -m ping "10.9.5.*"

    # 通过分组名
    ansible -m ping webservers

    # 通过all指定hosts里的全部主机
    ansible -m ping all

查看hosts配置相关命令

  • 列出所有组的配置ip信息

1
ansible all --list-hosts
  • 查看当前hosts中配置所有组名称

1
ansible localhost -m debug -a 'var=groups.keys()'
  • 列出指定组的配置ip信息

1
ansible dbservers --list-hosts

Ansible模块

  • 列出所有模块

1
2
3
ansible-doc -l
# 也可以指定模块名称
ansible-doc -l | grep -i shell
  • 查看指定模块的参数信息,如下查看shell模块的参数说明

1
ansible-doc -s shell
  • 查看模块的帮助信息,如下查看shell模块的帮助信息,这与官方文档一致:shell模块

1
ansible-doc shell
  • Ansible管理工具常用的模块:command、shell、file、user、copy、service、yum、synchronze、cron、setup、ping等。可以在命令行里指定各个模块的参数(ansible命令),也可以将参数配置到yml文件里,然后在命令行里指定yml文件(ansible-playbook命令),下面会结合两种方式一块介绍。网上有大把的关于ansible命令的使用说明,这里只简单介绍几个模块的使用方法,其他模块的使用方法可以参考ansible模块

  • 下面介绍的都是ansible核心模块:ansible-core

debug模块

  • debug模块此模块在执行期间打印语句,可用于调试变量或表达式

  • 通过命令行执行命令

1
ansible webservers -m debug -a 'msg="System HostName: {{ inventory_hostname }}"'
  • 通过yml文件执行命令

1
2
3
4
5
6
7
8
# debug.yml
--- # yml文件的开头,规定playbook的yml文件格式,必须以---开头(必须),...结尾(非必须)
- name: Debug
hosts: webservers
tasks:
- name: Debug
debug:
msg: "System HostName: {{ inventory_hostname }}"
  • 常用参数说明:ansible-doc -s debug

参数 描述
msg 打印消息,支持变量
var 打印消息,支持变量,注册变量 , 与msg互斥

ping模块

  • ping模块用于测试主机的连通性,它会尝试连接到主机,验证可用的python,并在成功时返回“pong”,可以指定主机名或ip,也可以指定分组名,也可以指定all

  • 通过命令行执行命令

1
ansible webservers -m ping
  • 通过yml文件执行命令

1
2
3
4
5
6
7
8
9
10
11
12
# ping.yml
--- # yml文件的开头,规定playbook的yml文件格式,必须以---开头(必须),...结尾(非必须)
- name: Ping web servers
hosts: webservers # ip或组名,也可以配置all:匹配全部主机
tasks:
- name: Ping the servers
ping:
register: command_output

- name: 显示命令输出
debug:
var: command_output.stdout_lines # 注册变量输出要使用 var

ansible-playbook

  • 通过ansible-playbook命令,可以指定yml文件
  • ansible-playbook相关命令
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 检查yml文件语法格式
ansible-playbook --syntax-check ping.yml
# 列出hosts
ansible-playbook --list-hosts ping.yml
# 列出tags
ansible-playbook --list-tags ping.yml
# 列出task
ansible-playbook --list-tasks ping.yml
# 指定执行主机:--limit 或 -l,其必须是yml中hosts指定的子集
ansible-playbook ping.yml --limit 192.168.20.23
# 执行命令
ansible-playbook ping.yml
# 模拟执行,Ansible 会模拟执行任务,并输出执行结果,但不会对目标主机做出任何更改
# 注意这个命令运行不报错并不能说明一定可以执行成功,因为这个命令是模拟执行的
ansible-playbook ping.yml --check

setup模块

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

1
2
3
4
5
6
7
8
9
10
11
# 查看全部信息
ansible all -m setup

# 只查看内存信息
ansible all -m setup -a "filter=*mem*"

# 只查看网络信息
ansible all -m setup -a "gather_subset=network"

# 保存信息至指定目录,这样会每个ip对应一个文件
ansible all -m setup --tree ./
  • 常用参数说明:ansible-doc -s setup

参数 描述
gather_subset 指定要收集的系统信息的子集。可以是 all(所有信息)、network(网络信息)、hardware(硬件信息)等。默认为 all。
gather_timeout 设置信息收集的超时时间,单位为秒。默认为 10 秒。
filter 指定要收集的系统信息的过滤条件。可以是一个或多个标签,只收集匹配的信息。
fact_path 指定自定义 facts 文件的路径。

这里重点介绍一下gather_subset

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
all: 收集所有可用的系统信息。
all_ipv4_addresses: 收集所有 IPv4 地址信息。
all_ipv6_addresses: 收集所有 IPv6 地址信息。
apparmor: 收集 AppArmor 相关信息。
architecture: 收集系统架构信息。
caps: 收集进程的 capabilities 信息。
chroot: 收集 chroot 环境的信息。
cmdline: 收集内核命令行参数信息。
date_time: 收集日期和时间信息。
default_ipv4: 收集默认的 IPv4 地址信息。
default_ipv6: 收集默认的 IPv6 地址信息。
devices: 收集系统设备信息。
distribution: 收集发行版信息。
distribution_major_version: 收集发行版主版本号。
distribution_release: 收集发行版发行号。
distribution_version: 收集发行版版本信息。
dns: 收集 DNS 配置信息。
effective_group_ids: 收集有效组 ID 信息。
effective_user_id: 收集有效用户 ID 信息。
env: 收集环境变量信息。
facter: 收集 Facter 信息。
fips: 收集 FIPS 相关信息。
hardware: 收集硬件信息。
interfaces: 收集网络接口信息。
is_chroot: 收集是否处于 chroot 环境的信息。
iscsi: 收集 iSCSI 配置信息。
kernel: 收集内核信息。
local: 收集本地配置信息。
lsb: 收集 LSB 发行版信息。
machine: 收集机器信息。
machine_id: 收集机器 ID 信息。
mounts: 收集挂载点信息。
network: 收集网络相关信息。
ohai: 收集 Ohai 信息。
os_family: 收集操作系统家族信息。
pkg_mgr: 收集包管理器信息。
platform: 收集平台信息。
processor: 收集处理器信息。
processor_cores: 收集处理器核心数信息。
processor_count: 收集处理器数量信息。
python: 收集 Python 相关信息。
python_version: 收集 Python 版本信息。
real_user_id: 收集真实用户 ID 信息。
selinux: 收集 SELinux 配置信息。
service_mgr: 收集服务管理器信息。
ssh_host_key_dsa_public: 收集 SSH DSA 公钥信息。
ssh_host_key_ecdsa_public: 收集 SSH ECDSA 公钥信息。
ssh_host_key_ed25519_public: 收集 SSH ED25519 公钥信息。
ssh_host_key_rsa_public: 收集 SSH RSA 公钥信息。
ssh_host_pub_keys: 收集 SSH 主机公钥信息。
ssh_pub_keys: 收集 SSH 用户公钥信息。
system: 收集系统相关信息。
system_capabilities: 收集系统能力信息。
system_capabilities_enforced: 收集系统能力强制信息。
user: 收集用户账户信息。
user_dir: 收集用户目录信息。
user_gecos: 收集用户 GECOS 信息。
user_gid: 收集用户组 ID 信息。
user_id: 收集用户 ID 信息。
user_shell: 收集用户 Shell 信息。
user_uid: 收集用户 UID 信息。
virtual: 收集虚拟化相关信息。
virtualization_role: 收集虚拟化角色信息。
virtualization_type: 收集虚拟化类型信息。

command模块

  • command模块为ansible默认模块,主要用于执行Linux基础命令,可以执行远程服务器命令执行、任务执行等操作。

  • command模块不支持管道符号、变量,只能运行简单命令,复杂命令需要使用shell模块

  • 示例1

1
ansible webservers -m command -a "df -hT"
1
2
3
4
5
6
7
8
9
10
11
12
13
# command.yml
---
- name: web 服务器上运行 df -hT 命令
hosts: webservers
tasks:
- name: 执行 df -hT 命令
command:
cmd: "df -hT"
register: command_output # 保存命令执行结果

- name: 显示命令输出
debug:
var: command_output.stdout_lines # 输出命令执行结果
  • 示例2:参数chdir:切换到指定目录后再运行命令

可以通过ansible-doc -s command查看其支持的参数

1
ansible webservers -m command -a "chdir=/tmp ls -l"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# command2.yml
---
- name: web 服务器上列出 /tmp 目录的内容
hosts: webservers
tasks:
- name: 切换到 /tmp 目录并列出内容
command:
chdir: /tmp # 切换到 /tmp 目录
cmd: ls -l # 执行命令
register: command_output # 保存命令执行结果

- name: 显示命令输出
debug:
var: command_output.stdout_lines # 输出命令执行结果
  • 常用参数说明:ansible-doc -s command

参数 描述
argv 要执行的命令,可以是字符串形式或列表形式。
chdir 在执行命令之前切换到的目录。
cmd 要执行的命令。
creates 指定一个文件名或模式,如果匹配的文件已经存在,则不执行命令。
expand_argument_vars 是否展开作为变量的参数。默认为 true,表示展开变量。
free_form 以字符串形式指定要执行的命令。这个参数并不存在,但是 command 模块接受自由形式的字符串作为命令。
removes 指定一个文件名或模式,如果匹配的文件存在,则执行命令。
stdin 将命令的标准输入设置为指定的值。
stdin_add_newline 是否在标准输入数据后添加换行符。
strip_empty_ends 是否从标准输出的末尾剥离空行。

shell模块

  • shell模块与command模块类似,可以执行远程服务器命令执行、任务执行等操作,但是shell模块支持管道符号、变量,可以执行复杂命令

  • 示例:

1
2
3
4
5
# 查看进程
ansible webservers -m shell -a "ps -ef | grep java | grep -v 'grep'"

# 修改密码
ansible webservers -m shell -a " echo 123456 | passwd --stdin ansible"
1
2
3
4
5
6
7
8
9
10
11
12
13
# shell.yml
---
- name: web 服务器上查找 Java 进程
hosts: webservers
tasks:
- name: 执行 ps -ef | grep java | grep -v 'grep' 命令
shell:
cmd: ps -ef | grep java | grep -v 'grep'
register: command_output # 保存命令执行结果

- name: 显示命令输出
debug:
var: command_output.stdout_lines # 输出命令执行结果
  • 常用参数说明:ansible-doc -s shell

参数 描述
argv 要执行的命令,可以是字符串形式或列表形式。
chdir 在执行命令之前切换到的目录。
cmd 要执行的命令。
creates 指定一个文件名或模式,如果匹配的文件已经存在,则不执行命令。
executable 用于执行命令的可执行程序,默认情况下为 /bin/sh。
free_form 以字符串形式指定要执行的命令。这个参数并不存在,但是 shell 模块接受自由形式的字符串作为命令。
removes 指定一个文件名或模式,如果匹配的文件存在,则执行命令。
stdin 将命令的标准输入设置为指定的值。
stdin_add_newline 是否在标准输入数据后添加换行符。
strip_empty_ends 是否从标准输出的末尾剥离空行。

file模块

  • file模块主要用于文件和目录的管理,可以创建、删除、修改文件和目录,可以指定文件或目录的属性,可以指定文件或目录的权限,可以指定文件或目录的owner、group、mode等信息,等等

  • 示例:创建目录

1
ansible webservers -m file -a "path=/tmp/`date +%F`  state=directory  mode=755"
1
2
3
4
5
6
7
8
9
10
# file.yml
---
- name: web 服务器上创建日期格式的目录
hosts: webservers
tasks:
- name: 创建目录
file:
path: "/tmp/{{ ansible_date_time.date }}" # ansible_date_time.date表示当前时间获取的日期,格式为YYYY-MM-DD
state: directory # 创建目录,如果目录已经存在则不创建
mode: "755" # 设置目录权限
  • 常用参数说明:ansible-doc -s file

参数 描述
path 文件或目录的路径。
state 文件或目录的状态。可选值包括 file(文件)、directory(目录)、link(符号链接)。默认为 file。
owner 文件或目录的所有者。
group 文件或目录的所属组。
mode 文件或目录的权限。
src 源文件路径,用于复制文件或创建链接。
dest 目标文件路径,用于复制文件或创建链接。
follow 是否遵循符号链接。如果为 yes,则会遵循符号链接进行操作。默认为 yes。
selevel 文件或目录的 SELinux 安全上下文。
serole 文件或目录的 SELinux 角色。
setype 文件或目录的 SELinux 类型。
seuser 文件或目录的 SELinux 用户。
unsafe_writes 是否启用不安全的写入模式。如果为 yes,则在写入文件之前不会创建备份。默认为 no。

这里重点说一下state

1
2
3
4
5
6
file:表示要求目标主机上存在指定的文件。如果文件已经存在,则不执行任何操作;如果文件不存在,则会创建它。
directory:表示要求目标主机上存在指定的目录。如果目录已经存在,则不执行任何操作;如果目录不存在,则会创建它。
link:表示要求目标主机上存在指定的符号链接。如果符号链接已经存在,则不执行任何操作;如果符号链接不存在,则会创建它。
hard:创建硬链接
touch:如果文件不存在,则会创建一个新的文件,如果文件或目录已存在,则更新其最后修改时间
absent:删除目录、文件或者取消链接文件

copy模块

  • copy模块主要用于将文件复制到远程服务器,可以指定文件的源路径、目标路径、owner、group、mode等信息,等等

  • 示例:复制文件到指定目录

1
2
# -b 切换到root用户下执行
ansible webservers -m copy -a "src=/tmp/a.txt dest=/tmp/ owner=root group=root mode=755"
1
2
3
4
5
6
7
8
9
10
11
12
13
# copy.yml
---
- name: web 服务器上复制文件
hosts: webservers
become: yes # 切换到root用户下执行
tasks:
- name: 复制文件
copy:
src: "/tmp/a.txt" # 源文件路径
dest: "/tmp/" # 目标文件路径
owner: root # 指定文件owner
group: root # 指定文件group
mode: "755" # 指定文件权限
  • 常用参数说明:ansible-doc -s copy

参数 描述
src 源文件的路径。
dest 目标文件的路径。
backup 是否备份目标文件。如果为 yes,则在复制目标文件之前会创建一个备份文件。默认为 yes。
content 要写入目标文件的内容。
directory_mode 目标目录的权限。只有当目标是一个目录时才会生效。
follow 是否跟随符号链接。如果为 yes,则会跟随符号链接进行操作。默认为 yes。
force 是否强制覆盖目标文件。如果为 yes,则强制复制源文件,即使目标文件已经存在。默认为 no。
group 目标文件的所属组。
mode 目标文件的权限。
owner 目标文件的所有者。
remote_src 指定源文件是否在远程主机上。如果为 yes,表示源文件在远程主机上。默认为 no。

fetch模块

  • fetch模块主要用于将文件从远程服务器复制到本地,可以指定文件的源路径、目标路径、owner、group、mode等信息,等等

  • 示例:将文件从远程服务器复制到本地

1
ansible webservers -m fetch -a "src=/tmp/a.txt dest=/tmp/ flat=yes"
1
2
3
4
5
6
7
8
9
10
# fetch.yml
---
- name: web 服务器上将文件从远程服务器复制到本地
hosts: webservers
tasks:
- name: 复制文件
fetch:
src: "/tmp/a.txt"
dest: "/tmp/"
flat: yes # flat: yes # 将文件复制到dest目录下,不创建目录结构
  • 常用参数说明:ansible-doc -s fetch

参数 描述
src 远程主机上要拉取的文件的路径。
dest 本地主机上文件的目标路径。
flat 是否将文件放置在顶层目录中。如果为 yes,则所有文件都将放置在一个目录中。默认为 no。
fail_on_missing 如果为 yes,则在源文件不存在时失败。默认为 yes。
validate_checksum 是否验证远程文件的校验和。默认为 no。

cron模块

  • cron模块主要用于在远程服务器上创建、修改、删除定时任务

  • 示例:创建定时任务

1
ansible webservers -m cron -a 'name="restart httpd" hour=*/5 job="systemctl restart httpd"'
1
2
3
4
5
6
7
8
9
10
# cron.yml
---
- name: web 服务器上创建定时任务
hosts: webservers
tasks:
- name: 创建定时任务
cron:
name: "restart httpd"
hour: "*/5"
job: "systemctl restart httpd"
  • 常用参数说明:ansible-doc -s cron

参数 描述
name cron 任务的名称。
minute cron 任务执行的分钟。
hour cron 任务执行的小时。
day cron 任务执行的日期。
month cron 任务执行的月份。
weekday cron 任务执行的星期几。
job 要执行的命令或脚本。
cron_file 要操作的 cron 文件的路径。默认为 /etc/crontab。
state cron 任务的状态。可选值包括 present(默认)和 absent。表示要求任务存在或不存在。
user cron 任务的执行用户。默认为 root。
backup 是否备份 cron 文件。可选值包括 true 和 false。

state

1
2
present:表示要求指定的 cron 任务存在。如果指定的 cron 任务不存在,则 Ansible 将会创建它。如果已经存在,则不执行任何操作。
absent:表示要求指定的 cron 任务不存在。如果指定的 cron 任务存在,则 Ansible 将会删除它。如果不存在,则不执行任何操作。

yum模块

  • yum模块主要用于在远程服务器上安装、卸载、更新软件包,可以指定软件包的名称、版本、repo等信息,等等

  • 示例:安装软件包

1
2
# 安装软件包,相当于 yum install httpd
ansible webservers -m yum -a "name=httpd state=present"
1
2
3
4
5
6
7
8
9
# yum.yml
---
- name: web 服务器上安装 httpd 软件包
hosts: webservers
tasks:
- name: 安装 httpd 软件包
yum:
name: httpd # 软件包名称,相当于 yum install httpd
state: present # 安装软件包,如果软件包已经安装则不安装
  • 常用参数说明:ansible-doc -s yum

参数 解释
name 指定要操作的包的名称。
update_cache 指定是否在执行操作之前更新 yum 缓存。可选值为 yesno。默认为 yes
disable_gpg_check 指定是否禁用 GPG 检查。如果为 yes,则禁用 GPG 检查。默认为 no
disable_plugin 指定是否禁用指定的 yum 插件。可以是一个插件名称的列表。
enablerepo 指定要启用的仓库。可以是一个仓库名称的列表。
disablerepo 指定要禁用的仓库。可以是一个仓库名称的列表。
installroot 指定要安装软件包的根目录。
security 指定是否只安装安全更新。如果为 yes,则只安装安全更新。默认为 no
list 指定是否列出所有已安装的包。如果为 yes,则列出已安装的包。默认为 no
state 指定软件包的状态。可选值为 presentlatestabsentinstalledremoved。默认为 present

这里重点说一下state

1
2
3
4
5
present: 表示要求目标主机上存在指定的软件包。如果软件包已经安装,则不执行任何操作;如果软件包未安装,则会安装它。
latest: 表示要求目标主机上存在指定的软件包,并且希望保持为最新版本。如果软件包已经安装,但不是最新版本,则会更新到最新版本;如果软件包未安装,则会安装最新版本。
absent: 表示要求目标主机上不存在指定的软件包。如果软件包已安装,则会将其卸载;如果软件包未安装,则不执行任何操作。
installed: 与 present 意义相同,用于明确表达希望软件包已经安装在目标主机上。
removed: 与 absent 意义相同,用于明确表达希望软件包已从目标主机上卸载。

service模块:调用的是service命令

  • service模块主要用于在远程服务器上启动、停止、重启、重新加载、启用、禁用、检查服务,可以指定服务的名称、状态、启动方式等信息,等等

  • 示例:启动服务

1
ansible webservers -m service -a "name=httpd state=started"
1
2
3
4
5
6
7
8
9
# service.yml
---
- name: web 服务器上启动 httpd 服务
hosts: webservers
tasks:
- name: 启动 httpd 服务
service:
name: httpd
state: started
  • 常用参数说明:ansible-doc -s service

参数 描述
name 服务的名称。
state 服务的状态。可选值包括 started(已启动)、stopped(已停止)、restarted(已重启)。
enabled 是否在启动时自动启用服务。如果为 yes,则在系统启动时自动启动服务。默认为 yes。
pattern 匹配服务的模式。默认情况下为服务名称。
sleep 在重新启动服务之前等待的秒数。
arguments 启动或停止服务时要传递的参数。

state

1
2
3
started:表示要求服务处于已启动状态。如果指定的服务未启动,则 Ansible 将尝试启动该服务。如果服务已经处于运行状态,则不执行任何操作。
stopped:表示要求服务处于已停止状态。如果指定的服务正在运行,则 Ansible 将尝试停止该服务。如果服务已经停止,则不执行任何操作。
restarted:表示要求重启服务。无论服务当前处于运行状态还是停止状态,Ansible 都会尝试重新启动该服务。

systemd模块:支持centos7+,调用的是systemctl

  • systemd模块用于控制 systemd 后台服务,允许你启动、重新启动、停止或者重新加载 systemd 服务。此外,你也可以使用它来使服务在系统启动时自动启动或禁止自动启动。除此之外,systemd 模块还允许你检查服务的状态。

  • 示例

1
ansible webservers -m systemd -a "name=httpd state=restarted"
1
2
3
4
5
6
7
8
9
# systemd.yml
---
- name: web 服务器上重新启动 httpd 服务
hosts: webservers
tasks:
- name: 重新启动 httpd 服务
systemd:
name: httpd
state: restarted
  • 主要参数说明:ansible-doc -s systemd

参数 描述
enabled 指定服务是否应该在启动时自动启用。可选值为 yes 或 no。默认为 yes。
masked 指定服务是否应该被置为 masked 状态,禁止手动启动。可选值为 yes 或 no。默认为 no。
name 服务的名称。
state 指定服务的状态。可选值为 started(启动)、stopped(停止)、restarted(重新启动)、reloaded(重新加载)

script模块

  • script模块主要用于在远程服务器上执行本地的脚本

  • 示例:执行脚本

1
ansible webservers -m script -a "chdir=/tmp /tmp/a.sh"
1
2
3
4
5
6
7
8
9
# script.yml
---
- name: web 服务器上运行脚本
hosts: webservers
tasks:
- name: /tmp 目录中运行脚本
script:
chdir: /tmp # 指定脚本的执行目录
cmd: /tmp/a.sh # 本地脚本的路径
  • 常用参数说明:ansible-doc -s script

参数 描述
chdir 在远程主机上执行脚本之前切换到的目录。
free_form 要在远程主机上执行的脚本内容。
creates 如果指定的文件已经存在,则不执行脚本。
executable 指定要使用的脚本解释器。
removes 在执行脚本之后,如果指定的文件存在,则删除该文件。
cmd 指定要执行的命令。
decrypt 指定要解密的源文件。

user模块

  • user模块主要用于在远程服务器上创建、修改、删除用户,可以指定用户的名称、密码、uid、gid、home、shell等信息,等等

  • 示例:创建用户

1
ansible webservers -m user -a "name=nginx group=nginx shell=/sbin/nologin create_home=no"
1
2
3
4
5
6
7
8
9
10
# user.yml
---
- name: 创建用户 nginx
hosts: webservers
tasks:
- name: 添加用户 nginx
user:
name: nginx # 用户名称
shell: /sbin/nologin # 指定用户shell,非登录用户
createhome: no # 不创建用户主目录
  • 常用参数说明:ansible-doc -s user

参数 描述
append 是否将用户添加到现有组,而不是替换组。
comment 对用户的注释信息。
createhome 是否创建用户的家目录。
expires 用户帐户过期日期。
force 是否强制创建或更改用户帐户。
generate_ssh_key 是否生成用户的 SSH 密钥对。
group 用户所属组的名称或 ID。
groups 用户所属的其他组。
home 用户的家目录路径。
login_class 用户登录类。
move_home 是否在更改用户家目录路径时移动其内容。
name 用户的名称。
non_unique 允许用户具有非唯一的数字 ID。
password 用户的密码哈希值或加密后的密码。
remove 是否删除用户。
shell 用户的 shell。
state 用户帐户的状态。
system 是否为系统用户。
uid 用户的数字 ID。

state

1
2
present:表示要求指定的用户账户存在。如果指定的用户账户不存在,则 Ansible 将会创建该账户。如果用户账户已经存在,则不执行任何操作。
absent:表示要求指定的用户账户不存在。如果指定的用户账户存在,则 Ansible 将会删除该账户。如果用户账户不存在,则不执行任何操作。

get_url模块

1
ansible webservers -m get_url -a "url=https://www.example.com dest=/tmp"
1
2
3
4
5
6
7
8
9
# get_url.yml
---
- name: 下载文件到目标主机的指定目录
hosts: webservers
tasks:
- name: Download files from remote host
get_url:
url: https://www.example.com/file.conf
dest: /tmp
  • 主要参数说明:ansible-doc -s get_url

参数 描述
url 要下载的文件的 URL 地址。
dest 下载文件保存的目标路径。
force 是否强制覆盖目标路径中的文件。可选值为 yes 或 no。默认为 yes。
timeout 下载超时时间,单位为秒。默认为 10 秒。
validate_certs 是否验证 SSL 证书。可选值为 yes 或 no。默认为 yes。
owner 下载后文件的所有者。
group 下载后文件的所属组。
mode 下载后文件的权限模式。
backup 是否创建备份文件。可选值为 yes 或 no。默认为 no。
headers 附加的 HTTP 请求头。
force_basic_auth 是否强制使用 HTTP 基本身份验证。可选值为 yes 或 no。默认为 yes。
http_agent 用于 HTTP 请求的代理。

lineinfile模块

  • lineinfile模块主要用于在远程主机上查找和替换文件中的行,可以指定要查找的行、要替换的行、要添加的行等信息,等等

  • 示例:设置环境变量,需要注意的是此时环境变量在剧本的上下文中是不生效的,如果需要在剧本上下文生效的环境变量,可以使用 shell 模块

1
ansible webservers -m lineinfile -a "path=/etc/profile line='PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin' state=present backup=yes"
1
2
3
4
5
6
7
8
9
10
11
# lineinfile.yml
---
- name: 设置环境变量
hosts: webservers
tasks:
- name: 设置环境变量
lineinfile:
path: /etc/profile
line: 'PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin'
state: present
backup: yes
  • 主要参数说明:ansible-doc -s lineinfile

参数 描述
path 要修改的文件的路径。
line 要添加、修改或删除的行内容。
state 指定要执行的操作。
regexp 用于匹配行的正则表达式。
search_string 用于匹配行的字符串。
backup 是否创建备份文件。可选值为 yes 或 no。默认为 no。
backrefs 是否允许在 regexp 中使用反向引用。
insertbefore 指定一个行,将新行插入到它之前。
insertafter 指定一个行,将新行插入到它之后。
firstmatch 是否只匹配第一个匹配项。

state说明

1
2
present:确保指定的行存在于文件中。如果文件中不存在指定的行,则会添加该行。如果文件中已经存在该行,则不做任何改变。
absent:确保指定的行不存在于文件中。如果文件中存在指定的行,则会将该行删除。如果文件中不存在该行,则不做任何改变。

ansible-playbook:剧本

  • ansible-playbook是Ansible的核心命令,用于执行playbook文件,playbook文件是Ansible执行任务的最小单元,一个playbook文件可以包含多个play,每个play可以包含多个task,每个task可以包含多个module,每个module可以执行一个操作,比如创建目录、安装软件包、启动服务、执行脚本等

  • Ansible中文全文指南–Playbooks

  • 上面在介绍ansible模块时,我们已经编写了一些playbook文件,就是那些yml文件,运行时也是通过ansible-playbook命令执行的,但是基本上都是单个任务单个模块,下面我们来看一个复杂一些的示例

  • 示例:安装nginx并配置

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
# playbook.yml
---
- name: 安装nginx并配置
hosts: webservers
become: yes # 切换到root用户下执行
tasks:
- name: install nginx
yum:
name: gcc,zlib-devel,pcre-devel,openssl-devel
state: installed
- name: wget nginx tar.gz
shell:
cmd: wget https://nginx.org/download/nginx-1.22.1.tar.gz
chdir: /usr/local
- name: tar -zxvf nginx-1.22.1.tar.gz
shell:
cmd: tar -zxvf nginx-1.22.1.tar.gz
chdir: /usr/local
- name: add user nginx
user:
name: nginx
state: present
shell: /sbin/nologin
createhome: no
- name: configure ,make and make install
shell:
cmd: cd nginx-1.22.1;./configure --prefix=/usr/local/nginx --with-http_stub_status_module --with-http_ssl_module --user=nginx;make -j4;make -j4 install;
chdir: /usr/local
- name: start nginx
shell:
cmd: /usr/local/nginx/sbin/nginx
  • 优化1

问题: 上面的剧本有个问题,就是如果系统已经安装了nginx,则运行这个剧本还是会重新安装
解决方法: 先判断nginx是否已经安装,如果已经安装,直接跳过安装步骤,直接执行启动命令,否则执行安装步骤,然后再执行启动命令

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
---
- name: 安装nginx并配置
hosts: webservers
become: yes # 切换到root用户下执行
tasks:
- name: 检查系统是否已安装Nginx
shell: /usr/local/nginx/sbin/nginx -v
register: nginx_installed
ignore_errors: yes

- name: 安装Nginx依赖软件包
yum:
name: "{{ item }}"
state: present
loop:
- gcc
- zlib
- zlib-devel
- pcre-devel
- openssl
- openssl-devel
when: nginx_installed.rc != 0 # 只有当Nginx未安装时执行

- name: 检查系统是否已启动Nginx
shell: ps aux | grep nginx | grep -v grep
register: nginx_status
ignore_errors: yes

- name: 下载Nginx源码包
get_url:
url: https://nginx.org/download/nginx-1.22.1.tar.gz
dest: /usr/local
when: nginx_installed.rc != 0 # 只有当Nginx未安装时执行

- name: 解压Nginx源码包
shell:
cmd: tar -zxvf nginx-1.22.1.tar.gz
chdir: /usr/local
when: nginx_installed.rc != 0 # 只有当Nginx未安装时执行

- name: 添加nginx用户
user:
name: nginx
state: present
shell: /sbin/nologin
createhome: no

- name: 编译Nginx并安装
shell:
cmd: |
cd nginx-1.22.1
./configure --prefix=/usr/local/nginx --with-http_stub_status_module --with-http_ssl_module --user=nginx
make -j4
make -j4 install
chdir: /usr/local
when: nginx_installed.rc != 0 # 只有当Nginx未安装时执行

- name: 启动Nginx服务
shell:
cmd: /usr/local/nginx/sbin/nginx
when: nginx_status.rc != 0 # 只有当Nginx未启动时执行

register :用于将命令执行的结果保存到变量中,我们可以调用变量的属性,比如rc表示命令返回的状态码($?),stdout表示命令的输出,stderr表示命令的错误输出
ignore_errors :用于忽略错误
loop :用于循环列表
when :用于判断条件,只有条件满足时才执行

  • 优化2

问题: 上面的步骤有点多,比如下载、解压、编译nginx,这些都是在没有安装nginx的情况下要运行的任务
解决方法: 使用shell模块可以将这些步骤封装成一个任务

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
---
- name: 安装nginx并配置
hosts: webservers
become: yes # 切换到root用户下执行
tasks:
- name: 检查系统是否已安装Nginx
shell: /usr/local/nginx/sbin/nginx -v
register: nginx_installed
ignore_errors: yes

- name: 安装Nginx依赖软件包
yum:
name: "{{ item }}"
state: present
loop:
- gcc
- zlib
- zlib-devel
- pcre-devel
- openssl
- openssl-devel
when: nginx_installed.rc != 0 # 只有当Nginx未安装时执行

- name: 检查系统是否已启动Nginx
shell: ps aux | grep nginx | grep -v grep
register: nginx_status
ignore_errors: yes

- name: 添加nginx用户
user:
name: nginx
state: present
shell: /sbin/nologin
createhome: no

- name: Nginx并安装
shell:
cmd: |
wget https://nginx.org/download/nginx-1.22.1.tar.gz
tar -zxvf nginx-1.22.1.tar.gz
cd nginx-1.22.1
./configure --prefix=/usr/local/nginx --with-http_stub_status_module --with-http_ssl_module --user=nginx
make -j4
make -j4 install
chdir: /usr/local
when: nginx_installed.rc != 0 # 只有当Nginx未安装时执行

- name: 启动Nginx服务
shell:
cmd: /usr/local/nginx/sbin/nginx
when: nginx_status.rc != 0 # 只有当Nginx未启动时执行
  • 优化3

问题: 上面的剧本虽然已经满足了我的需求,但是不够简练,因为很多任务并不是主要任务,我需要在每个任务中进行条件判断来决定是否执行该任务,能否只保留主要任务,其它任务只有当这些主要任务成功运行了才会被运行呢?比如这里的主要任务就是两个:1-判断是否安装了nginx,没安装就去安装,2-判断是否启动了nginx,没启动就去启动
解决方法: 使用基于handlers的notify语句,这样可以减少重复执行任务的次数

何为handlers
Handler 本身是一种非同步的 callback function ,在这里则是指关连于特定 tasks 的事件 (event) 触发机制。当关联 handler 的 tasks 状态为被改变 (changed) 且都已被执行时,才会触发一次

何为 tasks 状态为被改变 (changed)
在Ansible中,task的状态会根据任务执行的结果而改变。当一个任务成功完成并且使得系统的状态与预期不同(即执行了一些更改),那么这个任务的状态就会被标记为"changed"。
具体来说,task状态为"changed"的条件包括但不限于以下情况:

1
2
3
4
5
6
文件变更:例如复制、创建、删除文件等操作导致了文件系统的变更。
服务状态变更:例如启动、停止、重启服务等操作导致了服务状态的改变。
配置更改:例如修改配置文件、添加配置项等操作导致了系统配置的改变。
软件包安装/卸载:例如安装新的软件包或者删除已安装的软件包导致了系统软件状态的改变。
权限变更:例如修改文件权限、更改用户组、更改用户等操作导致了系统权限的改变。
当一个或多个任务的状态被标记为"changed"时,与这些任务关联的handlers会被触发执行。这样可以确保在所有相关的任务都完成并且导致了系统状态的改变时,才执行后续的处理操作,从而保证了任务的一致性和可靠性。
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
---
- name: 安装nginx并配置
hosts: webservers
become: yes # 切换到root用户下执行
tasks:
- name: 判断是否存在nginx安装目录,不存在就安装nginx
file:
path: /usr/local/nginx
state: directory
notify: # 执行通知任务,顺序为从下到上
- Nginx安装 # 匹配任务name
- 安装Nginx依赖软件包
- 添加nginx用户

- name: 检查系统是否已启动Nginx
shell: ps aux | grep nginx | grep -v grep
register: nginx_status
ignore_errors: yes

- name: 检查是否需要启动nginx
shell: echo "start nginx"
when: nginx_status.rc != 0 # 只有当条件满足时才会运行该任务
notify: # 运行任务后只有状态为changed才会发送notify给handler
- 启动Nginx服务

handlers: # handlers是一类特殊任务,当有notify时,会自动执行该任务
- name: 添加nginx用户
user:
name: nginx
state: present
shell: /sbin/nologin
createhome: no

- name: 安装Nginx依赖软件包
yum:
name: "{{ item }}"
state: present
loop:
- gcc
- zlib
- zlib-devel
- pcre-devel
- openssl
- openssl-devel

- name: Nginx安装
shell:
cmd: |
wget https://nginx.org/download/nginx-1.22.1.tar.gz
tar -zxvf nginx-1.22.1.tar.gz
cd nginx-1.22.1
./configure --prefix=/usr/local/nginx --with-http_stub_status_module --with-http_ssl_module --user=nginx
make -j4
make -j4 install
chdir: /usr/local

- name: 启动Nginx服务
shell:
cmd: /usr/local/nginx/sbin/nginx
  • 优化4

问题: 上面的剧本中nginx的安装版本和安装路径都是写死的,能否动态配置呢?
解决方法: 提取变量,使用vars

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
---
- name: 安装nginx并配置
hosts: webservers
become: yes # 切换到root用户下执行

vars: # 定义变量
nginx_version: "1.22.1"
work_dir: "/usr/local"
nginx_install_path: "/usr/local/nginx"

tasks:
- name: 判断是否存在nginx安装目录,不存在就安装nginx
file:
path: "{{ nginx_install_path }}" # 变量一定要双括号
state: directory
notify: # 执行通知任务,顺序为从下到上
- Nginx安装 # 匹配任务name
- 安装Nginx依赖软件包
- 添加nginx用户

- name: 检查系统是否已启动Nginx
shell: ps aux | grep nginx | grep -v grep
register: nginx_status
ignore_errors: yes

- name: 检查是否需要启动nginx
shell: echo "start nginx"
when: nginx_status.rc != 0
notify:
- 启动Nginx服务

handlers: # handlers是一类特殊任务,当有notify时,会自动执行该任务
- name: 添加nginx用户
user:
name: nginx
state: present
shell: /sbin/nologin
createhome: no

- name: 安装Nginx依赖软件包
yum:
name: "{{ item }}"
state: present
loop:
- gcc
- zlib
- zlib-devel
- pcre-devel
- openssl
- openssl-devel

- name: Nginx安装
shell:
cmd: |
wget https://nginx.org/download/nginx-{{ nginx_version }}.tar.gz
tar -zxvf nginx-{{ nginx_version }}.tar.gz
cd nginx-{{ nginx_version }}
./configure --prefix={{ nginx_install_path }} --with-http_stub_status_module --with-http_ssl_module --user=nginx
make -j4
make -j4 install
chdir: "{{ work_dir }}"

- name: 启动Nginx服务
shell:
cmd: "{{ nginx_install_path }}/sbin/nginx"
1
2
3
4
# 默认
ansible-playbook nginx.yml
# 修改变量
ansible-playbook nginx.yml -e "nginx_version=1.24.0" -e "work_dir=/root"

小贴士

  • 除了我们自己定义的变量,我们也可以使用Ansible内置的变量,Ansible内置的变量可以参考官方文档
  • 我们用的最多的就是ansible_facts变量获取主机信息,如IP地址、MAC地址、操作系统版本等。也就是通过setup模块获取的信息。
  • 在使用ansible_facts变量时需要开启gather_facts选项,默认是开启的。
  • 常用的ansible_facts变量如下,注意这里属性名称前是不加ansible_前缀的:
1
2
3
ansible_facts['distribution']: 远程主机的操作系统分发名称。
ansible_facts['distribution_version']: 远程主机的操作系统版本号。
ansible_facts['user_id']: 远程主机上当前用户的用户 ID。

ansible-playbook:Template

  • Ansible Playbook Template是一种Ansible的特性,它允许您在Playbook中使用Jinja2模板语言来动态生成配置文件或其他文本文件。通过使用模板,您可以根据变量、条件、循环等动态信息来生成目标文件,从而使配置文件更具可扩展性和灵活性。

  • 下面是使用Ansible Playbook Template的一些常见用法和特性:

1
2
3
4
5
Jinja2模板语言: Ansible Playbook Template使用Jinja2模板语言,这是一种功能强大的模板引擎,支持变量替换、条件语句、循环语句等功能。
变量替换: 您可以在模板中使用Ansible的变量来替换文本中的占位符。这使得您可以根据不同的环境或条件生成不同的配置文件。
条件语句: 您可以在模板中使用条件语句来根据不同的条件生成不同的文本。这允许您根据需要灵活地调整生成的配置文件。
循环语句: 您可以在模板中使用循环语句来对列表或字典中的元素进行迭代,从而生成重复的文本块。
包含其他模板文件: 您可以在一个模板中包含其他模板文件,从而使模板更加模块化和易于管理。
  • Jinja2模板文件后缀为.j2,常用语法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 打印变量
{{ index_title }}

# for循环
{% for key in index_for_array %}
<p>show {{ key }}</p>
{% endfor %}

# if判断
{% if blog_href_show %}
<p>
<a href="{{ blog_href }}">{{ index_title }}</a>.<br/>
</p>
<p><em>Thank you for using {{ index_title }}.</em></p>
{% endif %}
  • 结合上面部署nginx的示例,我这里增加一个任务,就是替换nginx发布目录下的index.html,然后重启nginx,这里给出index.html.j2

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
<!DOCTYPE html>
<html>
<head>
<title>{{ index_title }}</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>

<h1>Hello World!</h1>
<img src="{{ blog_img }}">

{% for key in index_for_array %}
<p>show {{ key }}</p>
{% endfor %}


{% if blog_href_show %}
<p>
<a href="{{ blog_href }}">{{ index_title }}</a>.<br/>
</p>
<p><em>Thank you for using {{ index_title }}.</em></p>
{% endif %}
</body>
</html>
  • 接着我们修改剧本yml

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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
---
- name: 安装nginx并配置
hosts: webservers
become: yes # 切换到root用户下执行

vars: # 定义变量
nginx_version: "1.22.1"
work_dir: "/usr/local"
nginx_install_path: "/usr/local/nginx"
index_title: "hanqf's blog"
index_for_array:
- hello
- world
blog_href_show: true
blog_href: "https://blog.hanqunfeng.com"
blog_img: "me.png"


tasks:
- name: 判断是否存在nginx安装目录,不存在就安装nginx
file:
path: "{{ nginx_install_path }}" # 变量一定要双括号
state: directory
notify: # 执行通知任务,顺序为从下到上
- Nginx安装 # 匹配任务name
- 安装Nginx依赖软件包
- 添加nginx用户

- name: 检查系统是否已启动Nginx
shell: ps aux | grep nginx | grep -v grep
register: nginx_status
ignore_errors: yes

- name: 检查是否需要启动nginx
shell: echo "start nginx"
when: nginx_status.rc != 0
notify:
- 启动Nginx服务

- name: 上传图片
copy:
src: me.png # 与yml在同一目录
dest: "{{ nginx_install_path }}/html/me.png"

- name: 发布index.html
template:
src: index.html.j2 # 与yml在同一目录
dest: "{{ nginx_install_path }}/html/index.html"
force: true # 强制覆盖
backup: true # 覆盖前备份
notify:
- 重启Nginx服务

handlers: # handlers是一类特殊任务,当有notify时,会自动执行该任务
- name: 添加nginx用户
user:
name: nginx
state: present
shell: /sbin/nologin
createhome: no

- name: 安装Nginx依赖软件包
yum:
name: "{{ item }}"
state: present
loop:
- gcc
- zlib
- zlib-devel
- pcre-devel
- openssl
- openssl-devel

- name: Nginx安装
shell:
cmd: |
wget https://nginx.org/download/nginx-{{ nginx_version }}.tar.gz
tar -zxvf nginx-{{ nginx_version }}.tar.gz
cd nginx-{{ nginx_version }}
./configure --prefix={{ nginx_install_path }} --with-http_stub_status_module --with-http_ssl_module --user=nginx
make -j4
make -j4 install
chdir: "{{ work_dir }}"

- name: 启动Nginx服务
shell:
cmd: "{{ nginx_install_path }}/sbin/nginx"

- name: 重启Nginx服务
shell:
cmd: |
pkill nginx
"{{ nginx_install_path }}/sbin/nginx"
  • 替换之后的效果

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
<!DOCTYPE html>
<html>
<head>
<title>hanqf's blog</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>

<h1>Hello World!</h1>
<img src="me.png">

<p>show hello</p>
<p>show world</p>


<p>
<a href="https://blog.hanqunfeng.com">hanqf's blog</a>.<br/>
</p>
<p><em>Thank you for using hanqf's blog.</em></p>
</body>
</html>
  • template模块的常用参数说明 ansible-doc -s template

参数 必需 默认值 描述
src 模板文件的路径。
dest 目标文件的路径。
force false 如果目标文件已经存在,是否强制覆盖。
backup false 如果设置为true,则在覆盖目标文件之前创建备份。
unsafe_writes false 如果设置为true,则会跳过文件的暂时性写入保护(如确保在写入文件之前不会更改其内容)。
newline_sequence \n 用于生成文件时的换行符序列。
validate 要应用于生成文件的验证器脚本的路径。
mode 目标文件的权限模式。
owner 目标文件的所有者。
group 目标文件的所属组。
  • 这里要注意template模块copy模块的区别,前者在上传时会进行变量替换。

ansible-playbook:Role

  • Ansible中的Role是一种组织和管理剧本的方法,它允许您将相关的任务、变量、文件和处理程序组合到一个可重用的单元中。Role使得您可以更轻松地管理和组织大型的Ansible项目,并促进了可维护性和复用性。

  • Role的特性

1
2
3
4
5
6
7
8
9
10
11
组织性:Role允许您将相关的任务和文件组织在一起,使得代码更易于理解和维护。每个Role通常都有一个特定的目的,例如安装特定的软件、配置服务或执行特定的系统管理任务。

可重用性:Role可以被设计成可重用的组件,可以在不同的项目中多次使用。这样一来,您可以将常见的功能和配置封装到Role中,并在需要时轻松地调用它们,从而提高了代码的复用性和可移植性。

结构化布局:Ansible推荐一种特定的目录结构来组织Role,包括tasks、handlers、templates、files、vars和defaults等目录。这种结构化布局有助于更清晰地分离任务、变量、文件和处理程序,并使Role更易于管理和维护。

参数化:Role可以使用变量来接受外部输入,从而使其更加灵活和通用。通过参数化,您可以定制Role的行为,使其适用于不同的环境和需求。

依赖关系:Role可以定义依赖关系,即一个Role可能依赖于另一个Role。这种依赖关系使得您可以构建复杂的系统配置,而不必重复编写相同的代码。

可测试性:由于Role是可重用的组件,因此它们也是可测试的。您可以编写测试用例来验证Role的行为是否符合预期,并确保在修改代码时不会破坏现有功能。
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
# 在当前目录下创建一个名称为nginx-install的role,实际上就是创建一个名称为nginx-install的目录结构
ansible-galaxy init nginx-install

# 在指定目录下创建role
ansible-galaxy init --init-path /usr/local nginx-install

# nginx-install目录结构
$ tree nginx-install/
nginx-install/
├── README.md
├── defaults
│   └── main.yml
├── files
├── handlers
│   └── main.yml
├── meta
│   └── main.yml
├── tasks
│   └── main.yml
├── templates
├── tests
│   ├── inventory
│   └── test.yml
└── vars
└── main.yml

8 directories, 8 files
1
2
3
4
5
6
7
8
files:存放由copy或script模块等调用的文件
templates:template模块查找所需要模板文件的目录
tasks:定义task、role的基本元素,至少应该包含一个名为main.yml的文件。其他文件需要在此文件中通过include进行包含
handlers:至少应该包含一个名为main.yml的文件,其他文件需要在此文件中通过include进行包含
vars:定义变量,至少应该包含一个名为main.yml的文件,其他文件需要在此文件中通过include进行包含
meta:定义当前角色的特殊设定及其依赖关系,至少应该包含一个名为main.yml的文件,其他文件需在此文件中通过include进行包含
default:设定默认变量时使用此目录中的main.yml文件,比vars的优先级低
tests:定义测试用例,至少应该包含一个名为inventory的文件,其他文件需要在此文件中通过include进行包含
  • ansible查找role的路径,推荐放到/etc/ansible/roles

1
2
3
4
5
$(pwd)/roles
/$(whoami)/.ansible/roles
/usr/share/ansible/roles
/etc/ansible/roles
$(pwd)

我们将上面安装nginx那个剧本修改为role的方式

  • nginx-install/tasks/main.yml

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
---
# tasks file for nginx-install
- name: Check if running as root
fail:
msg: "This role must be run as root user"
when: ansible_facts['user_id'] != 'root' # 因为我们的剧本是需要root用户运行,所以这里加一个判断,这里就用到了ansible_facts特殊变量


- name: 判断是否存在nginx安装目录,不存在就安装nginx
file:
path: "{{ nginx_install_path }}"
state: directory
notify: # 执行通知任务,顺序为从下到上
- Nginx安装
- 安装Nginx依赖软件包
- 添加nginx用户

- name: 检查系统是否已启动Nginx
shell: ps aux | grep nginx | grep -v grep
register: nginx_status
ignore_errors: yes

- name: 检查是否需要启动nginx
shell: echo "start nginx"
when: nginx_status.rc != 0
notify:
- 启动Nginx服务

- name: 上传图片
copy:
src: me.png # 与yml在同一目录
dest: "{{ nginx_install_path }}/html/me.png"

- name: 发布index.html
template:
src: index.html.j2 # 与yml在同一目录
dest: "{{ nginx_install_path }}/html/index.html"
force: true # 强制覆盖
backup: true # 覆盖前备份
notify:
- 重启Nginx服务
  • nginx-install/handlers/main.yml

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
---
# handlers file for nginx-install
- name: 添加nginx用户
user:
name: nginx
state: present
shell: /sbin/nologin
createhome: no

- name: 安装Nginx依赖软件包
yum:
name: "{{ item }}"
state: present
loop:
- gcc
- zlib
- zlib-devel
- pcre-devel
- openssl
- openssl-devel

- name: Nginx安装
shell:
cmd: |
wget https://nginx.org/download/nginx-{{ nginx_version }}.tar.gz
tar -zxvf nginx-{{ nginx_version }}.tar.gz
cd nginx-{{ nginx_version }}
./configure --prefix={{ nginx_install_path }} --with-http_stub_status_module --with-http_ssl_module --user=nginx
make -j4
make -j4 install
chdir: "{{ work_dir }}"

- name: 启动Nginx服务
shell:
cmd: "{{ nginx_install_path }}/sbin/nginx"

- name: 重启Nginx服务
shell:
cmd: |
pkill nginx
"{{ nginx_install_path }}/sbin/nginx"
  • nginx-install/vars/main.yml

1
2
3
4
5
6
7
8
9
10
11
12
---
# vars file for nginx-install
nginx_version: "1.22.1"
work_dir: "/usr/local"
nginx_install_path: "/usr/local/nginx"
index_title: "hanqf's blog"
index_for_array:
- hello
- world
blog_href_show: true
blog_href: "https://blog.hanqunfeng.com"
blog_img: "me.png"

小贴士

  • 上面介绍的taskshandlers,其目录中都含有一个main.yml文件,这个文件是必须存在的
  • 但是如果配置的内容比较多,都写在main.yml文件中,那么这个文件就显得有点臃肿,所以可以将这些内容拆分成多个文件,然后在main.yml文件中通过include进行包含
1
2
3
---
- include: other1.yml
- include: other2.yml
  • tasks或者handlers中还可以使用include_tasks进行包含
1
2
3
4
5
6
7
8
---
- include_tasks: other1.yml
- include_tasks: other2.yml
# 或者
- include_tasks: "{{ item }}"
loop:
- other1.yml
- other2.yml
  • vars里也可以定义多个yml文件存储变量,然后在tasks中通过include_vars进行包含,注意,必须放在task的yml文件中

1
2
3
4
5
6
7
8
# 此时不需要加上vars路径,会自动从vars目录下查找
- include_vars: "v-other1.yml"
- include_vars: "v-other2.yml"
# 或者
- include_vars: "{{ item }}"
loop:
- v-other1.yml
- v-other2.yml
  • 将模板文件和图片文本保存到对应的路径

nginx-install/templates/index.html.j2
nginx-install/files/me.png

  • 执行
    假设我们将创建的角色安装到了/etc/ansible/roles

创建启动剧本nginx-install-start.yml,我们也可以修改tests/test.yml

1
2
3
4
5
- name: 安装nginx并配置
hosts: webservers
become: yes # 切换到root用户下执行
roles: # 调用role
- nginx-install # role名称

执行role

1
ansible-playbook nginx-install-start.yml

ansible-galaxy

什么是ansible-galaxy

role

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 搜索role
ansible-galaxy search mysql
# 或者
ansible-galaxy role search mysql

# 查看已经安装的role
ansible-galaxy list
# 或者
ansible-galaxy role list

# 安装role,默认安装到 ~/.ansible/roles 下
ansible-galaxy role install shaneholloman.redis
# 安装到指定路径
ansible-galaxy role install <role-name> -p <path>

collection

  • ansible-galaxy同时支持下载 collection

1
2
3
4
5
6
7
# 查看已经安装的collection
ansible-galaxy collection list

# 安装collection,,默认安装到 ~/.ansible/collections/ansible_collections 下
ansible-galaxy collection install saiello.kafka
# 安装到指定路径
ansible-galaxy collection install <collection-name> -p <path>
  • Ansible Collection 是 Ansible 社区为了更好地管理和组织 Ansible 角色、模块、插件等内容而引入的概念。它可以被视为一种打包机制,用于将相关的 Ansible 内容打包成单独的单元,使得其更易于分享、安装和维护。

  • 具体来说,Ansible Collection 具有以下作用:

1
2
3
4
5
6
7
8
9
组织和管理角色、模块和插件:通过 Collection,可以将相关的角色、模块和插件打包在一起,形成逻辑上的单元,提高了内容的组织性和可管理性。

易于分享和分发:Ansible Collection 可以被轻松地分享和分发给其他用户。开发人员可以将自己的角色、模块打包成 Collection,并将其发布到 Ansible Galaxy 或其他集中式的仓库中,供其他用户使用。

版本控制和更新:与角色相比,Collection 更容易进行版本控制和更新。开发人员可以为 Collection 指定版本,并在更新内容时发布新的版本,使得用户可以选择使用特定版本或者升级到最新版本。

依赖管理:Collection 支持依赖管理,可以在 Collection 中指定依赖关系,确保相关的角色、模块和插件能够正常工作。

提供文档和示例:开发人员可以将文档和示例代码与 Collection 打包在一起,使得用户更容易理解和使用其中的内容。
  • 总的来说,Ansible Collection 提供了一种更高级别的组织和管理方式,使得 Ansible 内容更易于分享、使用和维护,从而提高了 Ansible 的整体生态系统的健壮性和可用性。

小贴士

  • 前面的剧本中我们多次使用到了become切换用户到root用户,其实在ansible.cfg中我们可以配置become_user,这样我们就可以不用每次都切换用户了
  • 但这里有个需要注意的点,就是因为 become 会以一个全新的环境执行任务,所以其不会包含原用户的环境变量。可以理解为其是通过sudo su切换到root,而非sudo -i

后记

关于ansible的知识点还有很多没有研究到,后面会慢慢补吧……