13-Ansible-playbook剧本进阶
devops运维开发高度自动化。
运维,python首要语言。
Ansible-playbook 有许多高级特性,这些特性可以帮助用户编写更复杂、灵活和高效的自动化脚本。以下为你详细介绍一些常见的高级特性:
1. 变量和事实(Variables and Facts)
变量定义与使用
在 Playbook 中可以定义多种类型的变量,如全局变量、主机变量、组变量等,并且可以在task中引用这些变量。
---
- name: Use variables in playbook
hosts: webservers
vars:
app_port: 8080
tasks:
- name: Start application
command: ./app start --port {{ app_port }}
事实(Facts)
Ansible 会自动收集目标主机的系统信息,即事实(Facts),这些信息可以作为变量在 Playbook 中使用。例如,ansible_os_family
可以用于判断目标主机的操作系统家族。
---
- name: Install package based on OS family
hosts: all
tasks:
- name: Install appropriate package
package:
#jinja2 变量替换,条件判断语句
name: "{{ 'apache2' if ansible_os_family == 'Debian' else 'httpd' }}"
state: present
2. 条件判断(When Statements)
使用 when
语句可以根据条件决定是否执行某个任务。
---
- name: Conditional task execution
hosts: servers
tasks:
- name: Restart service on specific hosts
service:
name: myservice
state: restarted
when: ansible_hostname in ['server1', 'server2']
3. 循环(Loops)
简单循环
可以使用 loop
关键字对列表或字典进行循环操作。
---
- name: Install multiple packages
hosts: all
tasks:
- name: Install packages
apt:
name: "{{ item }}"
state: present
loop:
- nginx
- mysql-server
- php
嵌套循环
支持嵌套循环,处理更复杂的数据结构。
---
- name: Create multiple users with different groups
hosts: all
tasks:
- name: Create users
user:
name: "{{ item[0] }}"
groups: "{{ item[1] }}"
state: present
loop:
- ['user1', 'group1']
- ['user2', 'group2']
4. 错误处理(Error Handling)
忽略错误
使用 ignore_errors
关键字可以忽略某个任务执行过程中产生的错误,继续执行后续任务。
---
- name: Ignore errors in a task
hosts: web
tasks:
- name: Try to run a command that may fail
command: /path/to/command
#ignore_errors: true
- name: Continue with the next task
debug:
msg: "This task will run even if the previous one failed."
失败时重试
使用 retries
和 delay
关键字可以在任务失败时进行重试。
---
- name: Example playbook with retries and delay
hosts: web # 替换为实际的目标主机或主机组
gather_facts: false
tasks:
- name: Run a command with retries
command: /usr/bin/some_command # 替换为实际要执行的命令
retries: 3 # 重试次数
delay: 5 # 每次重试间隔的秒数
register: result # 注册任务结果,用于后续判断
until: result.rc == 0 # 直到命令返回状态码为0(表示成功)才停止重试
- name: Print result
debug:
var: result
代码解释
command
模块:用于在远程主机上执行指定的命令。retries
:设置任务失败后重试的次数为 3 次。delay
:设置每次重试之间的间隔时间为 5 秒。register
:将任务的执行结果存储在变量result
中,方便后续使用。until
:指定一个条件,当该条件满足时停止重试。在这个例子中,当命令的返回状态码rc
为 0 时,表示命令执行成功,停止重试。
运行剧本
5. 角色(Roles)
角色是一种组织 Playbook 代码的方式,将相关的任务、变量、模板等组织在一起,提高代码的复用性和可维护性。
角色目录结构
roles/
└── webserver/ LNMP(nginx,mysql,php,配置文件变量,启动脚本)
├── defaults/
│ └── main.yml
├── files/nginx.conf *.conf
├── handlers/
│ └── main.yml
├── meta/
│ └── main.yml
├── tasks/
│ └── main.yml
├── templates/
└── vars/
└── main.yml
在 Playbook 中使用角色
---
- name: Use roles in playbook
hosts: webservers
roles:
- webserver
6. 块(Blocks)
块可以将多个任务组合在一起,方便进行统一的错误处理、条件判断等操作。
---
- name: Use blocks in playbook
hosts: all
tasks:
- block:
- name: Task 1
command: /path/to/command1
- name: Task 2
command: /path/to/command2
when: ansible_os_family == "Debian"
rescue:
- name: Handle errors
debug:
msg: "An error occurred in the block."
7. 标签(Tags)
使用标签可以选择性地执行 Playbook 中的部分任务。
---
- name: Use tags in playbook
hosts: all
tasks:
- name: Task 1
command: /path/to/command1
tags: tag1
- name: Task 2
command: /path/to/command2
tags: tag2
# 只执行带有 tag1 标签的任务
ansible-playbook playbook.yml --tags tag1
8. 异步执行(Asynchronous Execution)
对于一些耗时较长的任务,可以使用异步执行来避免长时间等待。
---
- name: Asynchronous task execution
hosts: all
tasks:
- name: Run a long-running command asynchronously
command: /path/to/long-running-command
async: 3600 # 最大执行时间(秒)
poll: 0 # 不轮询结果
剧本高级特性篇
循环
在写 playbook 的时候发现了很多 task 都要重复引用某个相同的模块,比如一次启动10个服务,或者一次拷贝10个文件,如果按照传统的写法最少要写10次,这样会显得 playbook 很臃肿。
如果使用循环的方式来编写 playbook ,这样可以减少重复编写 task 带来的臃肿。
https://docs.ansible.com/ansible/latest/user_guide/playbooks_loops.html?highlight=loop
关于循环的标准用法
早期ansible教程中,关于循环关键字是with_item
ansible自2.5版本后,通过loop关键字提供循环功能
root@ansible-01:~/ansible_shell# ansible --version
ansible 2.10.8
config file = /etc/ansible/ansible.cfg
configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /usr/lib/python3/dist-packages/ansible
executable location = /usr/bin/ansible
python version = 3.10.12 (main, Jan 17 2025, 14:35:34) [GCC 11.4.0]
在 Ansible Playbook 里,loop
是一个强大的特性,它能够对列表、字典等数据结构进行迭代,从而让单个任务可以多次执行,下面为你详细介绍 loop
的语法和不同使用场景。
基本语法
loop
的基本使用形式是在任务中添加 loop
关键字,后面紧跟要迭代的数据结构。示例如下:
- name: 任务名称
模块名:
参数: "{{ item }}"
loop:
- 元素1
- 元素2
- 元素3
在这个结构中,item
是一个特殊的变量,它在每次迭代时会依次代表列表里的各个元素。
下面详细介绍一下 Ansible Playbook 中使用 loop
遍历列表的各种用法和技巧。
1. 基本用法
对于一个简单的列表(例如字符串列表),你可以直接用 loop
来迭代每个元素:
- name: 安装常用软件包
hosts: h_nfs
become: yes
tasks:
- name: 安装软件包
apt:
name: "{{ item }}"
state: present
update_cache: yes
loop:
- vim
- git
- curl
说明:
- 每次迭代时,
item
会依次取列表中的"vim"
、"git"
、"curl"
。
2. 循环字典列表
有时列表中的每个元素都是一个字典,这样可以为每个项定义多个属性。例如:
- name: 创建用户
hosts: nfs
become: yes
vars:
users:
- name: alice
uid: 1001
shell: /bin/bash
addr: Beijing
phone: 10086
- name: bob
uid: 1002
shell: /bin/zsh
addr: JiangSu
phone: 10010
tasks:
- name: 添加用户
user:
name: "{{ item.name }}"
uid: "{{ item.uid }}"
shell: "{{ item.shell }}"
state: present
loop: "{{ users }}"
说明:
- 列表
users
中的每个元素都是一个字典,通过item.name
、item.uid
等方式可以访问字典中的属性。
3. 使用loop_control 自定义循环变量
如果你需要在循环中访问当前项的索引或修改 item
的名称,可以使用 loop_control
。
- name: 输出列表元素及其索引
hosts: localhost
gather_facts: no
tasks:
- name: 打印元素和索引
debug:
msg: "索引 {{ index }} 的值是 {{ item }}"
loop:
- apple
- banana
- cherry
loop_control:
index_var: yu_index
说明:
loop_control.index_var
允许你定义一个变量(本例中为index
),来存储当前项的索引(从 0 开始)。- 除了
index_var
,还可以使用label
(如果你想显示其他信息)等选项。
进阶loop_control
loop_control
在 Ansible 中的使用教程
在 Ansible 中,loop_control
用于增强 loop
循环的控制能力,提供更细粒度的配置,如修改 index_var
变量名、跳过空值、调整循环的输出格式等。
1. 基本使用
loop_control
主要用于:
- 控制
index_var
变量名(循环索引) - 设置
label
(日志输出时显示更友好的信息) - 跳过空值
extended
选项(用于控制如何解析loop
)
示例:
- hosts: localhost
gather_facts: no
tasks:
- name: 使用 loop_control 进行循环控制
debug:
msg: "当前用户:{{ item }},索引:{{ my_index }}"
loop:
- user1
- user2
- user3
loop_control:
index_var: my_index
输出示例
TASK [使用 loop_control 进行循环控制] ***
ok: [localhost] => (item=user1) => {
"msg": "当前用户:user1,索引:0"
}
ok: [localhost] => (item=user2) => {
"msg": "当前用户:user2,索引:1"
}
ok: [localhost] => (item=user3) => {
"msg": "当前用户:user3,索引:2"
}
2. 使用 label让日志更易读
默认情况下,日志输出 item
可能会很长或不易读。可以使用 label
让日志更加直观。
示例:
- hosts: localhost
gather_facts: no
tasks:
- name: 任务执行
debug:
msg: "正在处理 {{ item.name }},ID:{{ item.id }}"
loop:
- { name: "Alice", id: 101 }
- { name: "Bob", id: 102 }
loop_control:
label: "{{ item.name }}"
输出示例
TASK [任务执行] ***
ok: [localhost] => (item=Alice) => {
"msg": "正在处理 Alice,ID:101"
}
ok: [localhost] => (item=Bob) => {
"msg": "正在处理 Bob,ID:102"
}
4. 使用 pause 结合 loop_control
在循环中添加 pause
任务,并在日志中显示更友好的信息:
- hosts: localhost
gather_facts: no
tasks:
- name: 每个用户执行任务前暂停 2 秒
pause:
seconds: 8
loop:
- Alice
- Bob
loop_control:
label: "暂停2s前处理 {{ item }}"
输出
TASK [每个用户执行任务前暂停 5 秒] ***
Pausing for 5 seconds (暂停前处理 Alice)
(ctrl+C 可跳过)
Pausing for 5 seconds (暂停前处理 Bob)
(ctrl+C 可跳过)
结合 with_items 和 loop
虽然较早版本的 Ansible 使用 with_items
进行循环,但推荐使用 loop
。下面是对比示例:
with_items 示例:
- hosts: localhost gather_facts: no tasks: - name: 使用 with_items 安装软件包 apt: name: "" state: present with_items: - vim - git
loop 示例:
- name: 使用 loop 安装软件包 apt: name: "" state: present loop: - vim - git
建议: 新项目中推荐使用 loop
,因为它语法统一、扩展性好。
5. 循环嵌套列表或使用 subelements
在 Ansible 的 loop
中,subelements
主要用于遍历嵌套列表(字典),即当一个列表中的每个元素本身,包含另一个列表时,subelements
可以展开子元素并进行迭代。
当你有一个包含子项的字典列表,需要对每个字典的子项进行迭代时。
常见用于用户管理、权限分配、磁盘挂载等场景。
如果列表元素中嵌套了其它列表,可以使用 subelements
来处理。例如:
- name: 遍历用户及其所属组
hosts: localhost
gather_facts: no
vars:
users:
- name: alice
groups:
- admin
- developers
- name: bob
groups:
- testers
- developers
tasks:
- name: 输出用户及其组
debug:
msg: "用户 {{ item.0.name }} 属于组 {{ item.1 }}"
loop: "{{ users | subelements('groups') }}"
7. 小结
- 基本用法: 使用
loop
遍历列表,item
表示当前迭代的元素。 - 字典列表: 对于列表中的字典,可以直接通过
item.属性
访问各属性。 - 自定义变量: 利用
loop_control
可以增加索引变量或自定义显示信息。 - 嵌套数据结构: 使用
subelements
处理嵌套列表。 - 字典遍历: 推荐使用
dict2items
过滤器,将字典转换为易于遍历的列表。
这些用法可以帮助你更灵活地处理各类数据结构,并在 Ansible Playbook 中实现复杂的循环逻辑。
创建多个系统用户
需求,在nfs机器组中创建5个用户test1~5,且均设置密码yuchao666
比较low的写法,看着都头疼,但是没办法,语法就是这样
(添加、或是删除用户,区别在于state=absent)
---
- name: create user test1~5
hosts: nfs
vars:
tasks:
- name: create test1
user:
name: test1
state: absent
- name: create test2
user:
name: test2
state: absent
- name: create test3
user:
name: test3
state: absent
- name: create tes4
user:
name: test4
state: absent
- name: create test5
user:
name: test5
state: absent
循环创建用户
在 Ansible Playbook 中使用 loop
循环可以很方便地创建多个系统用户。以下是详细的实现步骤和示例代码。
实现思路
- 定义用户列表:在 Playbook 中定义一个包含多个用户信息的列表,每个用户信息可以包含用户名、密码、家目录等。
- 使用
loop
循环:在创建用户的任务中使用loop
关键字,对用户列表进行迭代,为每个用户执行创建操作。 - 使用
user
模块:user
模块是 Ansible 中用于管理系统用户的模块,可以通过该模块指定用户名、密码、家目录等参数来创建用户。
通过vars变量定义循环变量
上面会发现已然有重复的变量,还可以再简化
- 通过vars关键字定义用户列表,变量一般定义在任务开始之前
- 通过item关键字提取loop中每次循环的数据
在 Ansible 中,可以使用 loop
来批量创建用户,并使用 password
进行加密设置密码。以下是一个适用于 Ubuntu 的 ansible-playbook
,用于创建 10 个用户并设置密码:
1. 生成密码
Ansible 需要加密密码才能正确设置用户密码。可以使用 mkpasswd
生成加密密码:
apt install -y whois # 安装 mkpasswd
mkpasswd -m sha-512 "你的密码"
复制生成的加密密码,在 playbook.yml
中使用。
2. 编写 playbook.yml
chaoge666
bob666
Tom666
- name: Create users and set passwords
hosts: h_nfs
become: yes
tasks:
- name: Create users
user:
name: "{{ item.name }}"
password: "{{ item.password }}"
shell: /bin/bash
state: present
loop:
- { name: "jack01", password: "$6$.BkTLJlokQZIvb9d$53YcUedHNAycMkN8NfqC5vxKzcOqIgE82vnsV2QzWEYqWZ7yMondl88WiPBA.jpuFg2Up/NowP/qdr1TopWd7/" }
- { name: "bob01", password: "$6$tRGM8qdcXjjiBetl$cq561R/RYlxdhG1n8j.ebWwKrIkoHpb/Su5PHEf8jCZr6oW8IlccClrtTLO/VX/Dp0clTaajdN5QtqNHGbYD6." }
- { name: "tom01", password: "$6$htz7bcNnngDsvRK/$dji.yoCQ5gT/67lJVaLlKWiqRmvZuUD5cCTrXxZ6LgAhwqDjHdc7fU7BYw6eM8.FnMP1zI1nd3VoeqNYNwB3J." }
- name: Ensure users can log in
file:
path: "/home/{{ item.name }}"
state: directory
owner: "{{ item.name }}"
group: "{{ item.name }}"
mode: "0755"
loop:
- { name: "jack01" }
- { name: "bob01" }
- { name: "tom01" }
3. 运行 Playbook
ansible-playbook -i inventory.ini playbook.yml
inventory.ini
示例:
[servers]
your_server_ip ansible_user=your_ssh_user ansible_password=your_ssh_password ansible_become_pass=your_sudo_password
这样就会创建 10 个用户,并设置他们的密码。
循环处理字典数据
创建用户以及用户id号
low办法超哥就不再写了,太墨迹
循环字典数据如下,字典就是key:value这样的数据
字典用法,主要是根据key、获取value
---
- name: create user
hosts: h_nfs
tasks:
- name: create user and uid
user:
name: "{{ item.user }}"
uid: "{{ item.uid }}"
loop:
- {user: 'test1', uid: '2000'}
- {user: 'test2', uid: '2001'}
- {user: 'test3', uid: '2002'}
- {user: 'test4', uid: '2003'}
写法2:通过vars定义字典数据
- vars定义字典数据
- loop提供循环功能,通过item变量提取循环数据
---
- name: create user
hosts: nfs
vars:
users_list:
- {user: 'test1', uid: '2000'}
- {user: 'test2', uid: '2001'}
- {user: 'test3', uid: '2002'}
- {user: 'test4', uid: '2003'}
tasks:
- name: create user and uid
user:
name: "{{ item.user}}"
uid: "{{ item.uid }}"
loop: "{{ users_list }}"
案例
在 Ansible Playbook 里,你可以借助 loop
对字典数据进行循环处理。下面为你详细介绍不同场景下循环处理字典数据的方法和示例。
场景一:遍历字典的键值对
当你需要对字典中的每一个键值对进行操作时,可以采用以下方式。
示例代码
---
- name: Loop through dictionary key - value pairs
hosts: localhost
gather_facts: no
vars:
user_info:
name: John
age: 30
occupation: Engineer
tasks:
- name: Print key - value pairs
debug:
msg: "Key: {{ item.0 }}, Value: {{ item.1 }}"
loop: "{{ user_info.items() }}"
场景二:根据字典数据创建多个文件
假设你有一个字典,其中键表示文件名,值表示文件内容,你可以利用循环来创建这些文件。
示例代码
---
- name: Create files based on dictionary data
hosts: localhost
gather_facts: no
become: true
vars:
file_content:
file1.txt: "This is the content of file1."
file2.txt: "This is the content of file2."
file3.txt: "This is the content of file3."
tasks:
- name: Create files
copy:
content: "{{ item.1 }}"
dest: "/opt/{{ item.0 }}"
loop: "{{ file_content.items() }}"
场景三:根据字典数据配置多个服务
假设你有一个字典,包含多个服务的配置信息,你可以使用循环来配置这些服务。
示例代码
---
- name: Configure services based on dictionary data
hosts: localhost
gather_facts: no
become: true
vars:
service_config:
httpd:
state: started
enabled: yes
nginx:
state: stopped
enabled: no
tasks:
- name: Configure services
service:
name: "{{ item.key }}"
state: "{{ item.value.state }}"
enabled: "{{ item.value.enabled }}"
loop: "{{ service_config.items() }}"
items方法将字典元素,转换为 列表 [key,value]
循环安装多个软件(yum基础环境安装)
在咱们期中综合架构开篇时,需要大家系统初始化,这个初始化步骤也是需要你做成ansible脚本的。
比如如下大量的基础软件,如何安装?
yum install -y tree wget bash-completion bash-completion-extras lrzsz net-tools sysstat iotop iftop htop unzip telnet ntpdate lsof
# apt yum软件包名不一样
必然不能挨个的执行yum模块去安装,那得累死,循环写法如下
- name: yuchaoit.cn
hosts: nfs
remote_user: root
tasks:
- name: install basic packages
yum:
name: "{{ item }}"
state: installed
loop:
- tree
- wget
- bash-completion
- bash-completion-extras
- lrzsz
- net-tools
- sysstat
- iotop
- iftop
- htop
- unzip
- telnet
- ntpdate
- lsof
写法2:通过vars定义变量
- name: yuchaoit.cn
hosts: nfs
remote_user: root
vars:
basic_packages:
- tree
- wget
- bash-completion
- bash-completion-extras
- lrzsz
- net-tools
- sysstat
- iotop
- iftop
- htop
- unzip
- telnet
- ntpdate
- lsof
tasks:
- name: install basic packages
yum:
name: "{{ item }}"
state: installed
loop: "{{ basic_packages }}"
案例
在 Ansible Playbook 中,你可以使用 loop
来循环安装多个软件。以下分别针对不同操作系统家族(Debian 和 Red Hat)给出具体示例。
针对 Debian 系系统(如 Ubuntu)
示例 Playbook
---
- name: Install multiple packages on Debian systems
hosts: debian_servers
become: true
tasks:
- name: Install packages
apt:
name: "{{ item }}"
state: present
update_cache: yes
loop:
- nginx
- mysql-server
- php-fpm
代码解释
hosts
:指定该 Playbook 要在哪些主机或主机组上执行,这里假设存在名为debian_servers
的主机组。become: true
:表示使用sudo
权限执行任务,因为安装软件通常需要管理员权限。tasks
:name
:任务的描述性名称,方便在执行时识别。apt
:Ansible 中用于在 Debian 系系统上管理软件包的模块。name: ""
:item
是loop
迭代时的变量,每次迭代会依次取loop
列表中的元素作为软件包名称。state: present
:确保软件包已安装。update_cache: yes
:在安装软件包之前更新软件包缓存。loop
:包含要安装的软件包名称的列表。
针对 Red Hat 系系统(如 CentOS)
示例 Playbook
---
- name: Install multiple packages on Red Hat systems
hosts: redhat_servers
become: true
tasks:
- name: Install packages
yum:
name: "{{ item }}"
state: present
loop:
- httpd
- mariadb-server
- php
代码解释
hosts
:指定目标主机或主机组,这里假设存在名为redhat_servers
的主机组。become: true
:同样是为了获取管理员权限。tasks
:name
:任务名称。yum
:Ansible 中用于在 Red Hat 系系统上管理软件包的模块。name: ""
:通过item
变量迭代获取软件包名称。state: present
:保证软件包已安装。loop
:列出要安装的软件包。
通用示例(兼容不同系统)
示例 Playbook
---
- name: Install multiple packages on different systems
hosts: all
become: true
tasks:
- name: Install packages on Debian systems
apt:
name: "{{ item }}"
state: present
update_cache: yes
loop:
- nginx
- mysql-server
- php-fpm
when: ansible_os_family == "Debian"
- name: Install packages on Red Hat systems
yum:
name: "{{ item }}"
state: present
loop:
- httpd
- mariadb-server
- php
when: ansible_os_family == "RedHat"
代码解释
hosts: all
:表示该 Playbook 会在所有主机上执行。when
条件:根据目标主机的操作系统家族(通过ansible_os_family
变量判断)来决定执行哪个安装任务。如果是 Debian 系系统,则执行apt
模块的安装任务;如果是 Red Hat 系系统,则执行yum
模块的安装任务。
你可以将上述 Playbook 保存为一个 .yml
文件,然后使用 ansible-playbook
命令来执行,例如:
ansible-playbook install_packages.yml
这样就能通过循环的方式在目标主机上安装多个软件包了。
rsync文件夹场景
比如部署nfs、rsync、nginx的综合剧本;
1.要安装多个软件
2.创建多个目录
3.复制多个目录
4.每个文件的权限都不一样
循环风格1:单行模式
比如rsync创建备份目录,有多个目录需要创建,普通的写法出现了诸多重复语句
- name: create data dir
file: path=/data state=directory owner=www group=www
- name: create backup dir
file: path=/backup state=directory owner=www group=www
循环风格2:缩进模式
上述创建备份目录的剧本语法,也可以用如下的缩进模式写,但是依然很多重复语句
- name: create data dir
file:
path: /data
state: directory
owner: www
group: www
- name: create backup dir
file:
path: /backup
state: directory
owner: www
group: www
改造为循环语句,使用yaml的缩进语法
- name: create data,backup dir
file:
path: "{{ item }}"
state: directory
owner: www
group: www
loop:
- /data
- /backup
循环风格3:混合语法
- 等号赋值语法
- 缩进语法
- name: create data backup
file: path="{{ item }}" state=directory owner=www group=www
loop:
- /data
- /backup
循环风格4:循环结合字典取值
比如rsync服务部署,需要创建多个文件夹,以及对应的权限设置
- name: yuchaoit.cn
hosts: backup
tasks:
- name: create_data
file:
path: "{{ item.file_path }}"
state: directory
owner: www
group: www
mode: "{{ item.mode }}"
loop:
- { file_path:'/data' ,mode:'755' }
- { file_path:'/backup' ,mode:'755' }
变量定义
官网文档
https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html
在ansible中使用变量,能让我们的工作变得更加灵活,在ansible中,变量的使用方式有很多种,我们慢慢聊。
先说说怎样定义变量,变量名应该由字母、数字、下划线组成,变量名需要以字母开头,ansible内置的关键字不能作为变量名。
变量我们已经在循环知识中使用过了,主要通过vars关键字定义变量名、以及对应的变量值。
在 Ansible Playbook 中,变量是非常重要的特性,它可以让 Playbook 更加灵活和可复用。以下从变量的定义、作用域、优先级以及使用方法等方面详细介绍 Ansible Playbook 变量。
变量定义方式
1. 在 Playbook 中直接定义
可以在 Playbook 的 vars
部分直接定义变量。
---
- name: Use variables in playbook
hosts: all
vars:
app_port: 8080
app_name: "my_app"
tasks:
- name: Print variables
debug:
msg: "The application {{ app_name }} is running on port {{ app_port }}."
在这个例子中,app_port
和 app_name
是在 vars
部分定义的变量,然后在 debug
模块的 msg
参数中引用。
2. 在 Inventory 文件中定义
可以在 Ansible 的 Inventory 文件(如 /etc/ansible/hosts
)中为特定的主机或主机组定义变量。
[webservers]
webserver1.example.com app_port=80
webserver2.example.com app_port=8080
[webservers:vars]
app_name="web_app"
这里为 webservers
主机组中的每个主机定义了 app_port
变量,同时为整个 webservers
主机组定义了 app_name
变量。在 Playbook 中可以直接使用这些变量。
3. 在单独的变量文件中定义
可以将变量定义在单独的 YAML 文件中,然后在 Playbook 中引用。
变量文件 vars.yml
app_port: 8080
app_name: "my_app"
Playbook
---
- name: Use variables from file
hosts: all
vars_files:
- vars.yml
tasks:
- name: Print variables
debug:
msg: "The application {{ app_name }} is running on port {{ app_port }}."
通过 vars_files
关键字引用变量文件。
4. 通过命令行传递变量
在执行 ansible-playbook
命令时,可以使用 -e
或 --extra-vars
选项传递变量。
ansible-playbook playbook.yml -e "app_port=8080 app_name=my_app"
在 Playbook 中可以直接使用这些传递的变量。
变量作用域
- 全局变量:在
group_vars/all
或host_vars/all
中定义的变量对所有主机和所有 Playbook 都有效。 - 组变量:在
group_vars
目录下为特定主机组定义的变量,只对该主机组的主机有效。 - 主机变量:在
host_vars
目录下为特定主机定义的变量,只对该主机有效。 - Play 变量:在 Playbook 的
vars
部分定义的变量,只对当前 Play 有效。 - 任务变量:在任务中使用
vars
关键字定义的变量,只对当前任务有效。
变量优先级
当同一个变量在不同的地方定义时,Ansible 会根据以下优先级来决定使用哪个值(从高到低):
- 命令行传递的变量(
-e
或--extra-vars
) - 任务变量
- Play 变量
- 主机变量
- 组变量
- 全局变量
变量使用方法
1. 在任务中引用变量
在任务的参数中可以使用双花括号 {{ }}
来引用变量。
---
- name: Use variable in task
hosts: localhost
vars:
package_name: "nginx"
tasks:
- name: Install package
apt:
name: "{{ package_name }}"
state: present
- name: 打印变量看看,其他task也能访问这个全局变量
debug:
msg: "获取变量值:{{package_name}}"
2. 在条件判断中使用变量
可以在 when
语句中使用变量进行条件判断。
---
- name: Conditional task based on variable
hosts: localhost
vars:
is_production: true
tasks:
- name: Restart service in production
debug:
msg: "我被执行啦,因为{{is_production}}"
when: is_production
3. 变量的运算和过滤
可以对变量进行运算和使用过滤器来处理变量的值。
模板语言进阶知识点了
---
- name: Variable operation and filtering
hosts: all
vars:
num1: 10
num2: 20
tasks:
- name: Print sum of variables
debug:
msg: "The sum of {{ num1 }} and {{ num2 }} is {{ num1 + num2 }},乘积{{num1*num2}}"
- name: Print variable in uppercase
debug:
msg: "The uppercase of 'hello' is {{ 'hello' | upper }}"
在这个例子中,进行了变量的加法运算和字符串的大小写转换。
应用场景
1.通过自定义变量,可以在多个tasks中,通过变量名调用,取值
2.ansible在启动时,默认收集了客户端机器大量的静态属性(变量),可以提取该客户端的机器信息。
vars自定义变量
定义多个文件夹变量,创建rsync的数据目录,配置文件
- hosts: backup
vars:
data_path: /data
dest_path: /etc
config_path: /etc/rsync.passwd
tasks:
- name: 01 mkdir data dir
file:
path: "{{ data_path }}"
state: directory
- name: 02 copy config file
copy:
src: "{{ config_path }}"
dest: "{{ dest_path }}"
获取主机静态属性(ip地址)
Facts
内置获取ip的变量
ansible_all_ipv4_addresses #适用于多ip
ansible_default_ipv4.address #适用单ip
剧本
- hosts: web
tasks:
- name: 01 get ip address
debug: msg="该web组机器,ip是 {{ ansible_all_ipv4_addresses }}"
- name: 02 get hostname
debug: msg="该web组,主机名是 {{ ansible_hostname }}"
- name: 03 单ip
debug: msg="{{ansible_default_ipv4.address }}"
- name: 04 eth0 ip地址是
debug: msg="{{ansible_facts.enp0s5.ipv4.address}}"
- name: 05 eth1 ip地址是
debug: msg="{{ansible_facts.enp0s5.ipv4.address}}"
完整的ansible内置变量手册
https://docs.ansible.com/ansible/latest/user_guide/playbooks_vars_facts.html
通过setup模块可以看到所有的内置变量,提取变量的值需要遵循python的数据类型语法,如列表还是字典取值
[root@master-61 ~]#ansible web -m ansible.builtin.setup |wc -l
主机清单文件中也用到了变量
1.主机清单文件中定义变量
[root@master-61 ~]#tail -20 /etc/ansible/hosts
[all:vars]
ansible_port=22999
#ansible_user=root
#ansible_password=123123
my_name='yuchao'
[web:vars]
nginx_version='1.19'
[web]
172.16.1.7 port=22999
172.16.1.8 port=22999
172.16.1.9 port=22999
[nfs]
172.16.1.31
[backup]
172.16.1.41
2.只要操作该主机组,即可使用该变量
- hosts: web
tasks:
- name: 获取自定义hosts中的变量,每个组都能拿到
debug: msg="{{my_name}}"
执行
[root@master-61 ~]#ansible-playbook get_nginx.yaml
loop循环中引用变量
- hosts: localhost
vars:
rsyncd_config: /script/rsyncd.conf
rsyncd_pwd: /script/rsync.passwd
tasks:
- name: 01 copy config
debug:
msg: "{{ item.t_src }}---{{ item.t_dest }}---{{ item.t_mode }} "
loop:
- {t_src: "{{ rsyncd_config }}",t_dest: "/etc/",t_mode: '0644'}
- {t_src: "{{ rsyncd_pwd }}",t_dest: "/etc/",t_mode: '0600'}
注册变量
在 Ansible Playbook 里,注册变量(Registered Variables)是一项非常实用的功能。
借助它,你能够捕获某个任务的执行结果,并将这些结果存储到一个变量中,方便在后续的任务里使用。以下为你详细介绍注册变量的用法、应用场景和示例。
基本语法
要注册一个变量,需要在任务中使用 register
关键字,后面紧跟变量的名称。示例如下:
- name: 执行命令并注册结果
command: ls -l /tmp
register: command_output
在这个例子中,command
模块执行 ls -l /tmp
命令,执行结果会被存储到 command_output
变量里。
注册变量的结构
注册变量通常是一个字典,包含多个键值对,常见的键有:
changed
:一个布尔值,表明任务是否对目标主机的状态产生了改变。stdout
:命令执行后的标准输出内容,以字符串形式呈现。stderr
:命令执行后的错误输出内容,以字符串形式呈现。rc
:命令执行后的返回码,0
通常表示命令成功执行,非0
则表示有错误发生。
示例
1. 简单示例
---
- name: 使用注册变量示例
hosts: localhost
tasks:
- name: 执行命令并注册结果
command: ls -l /opt
register: command_output
- name: 输出命令执行结果
debug:
var: command_output
- name: 输出标准输出内容
debug:
msg: "命令的标准输出是: {{ command_output.stdout }} 更简单的信息: {{command_output.stdout_lines}}"
在这个示例中,第一个任务执行 ls -l /tmp
命令,并把结果注册到 command_output
变量中。第二个任务使用 debug
模块输出整个 command_output
变量的内容,第三个任务则只输出 stdout
键的值。
2. 基于注册变量进行条件判断
---
- name: 基于注册变量进行条件判断
hosts: all
tasks:
- name: 检查文件是否存在
stat:
path: /etc/passwd
register: file_status
- name: 若文件存在则输出信息
debug:
msg: "/etc/passwd 文件存在"
when: file_status.stat.exists
在这个示例中,stat
模块用于检查 /etc/passwd
文件是否存在,结果被注册到 file_status
变量中。后续任务根据 file_status.stat.exists
的值进行条件判断,若文件存在则输出相应信息。
3. 循环结合注册变量
---
- name: 循环结合注册变量
hosts: localhost
tasks:
- name: 循环执行命令并注册结果
command: echo "{{ item }}"
loop:
- "Hello"
- "World"
register: loop_output
- name: 输出每次循环的结果
debug:
msg: "循环的输出是: {{ item.stdout_lines}}"
loop: "{{ loop_output.results }}"
loop_control:
label: "{{item.cmd}}"
在这个示例中,command
模块在循环中执行 echo
命令,每次循环的结果都会被注册到 loop_output
变量中。loop_output.results
是一个包含每次循环结果的列表,后续任务对这个列表进行循环,输出每次循环的标准输出内容。
注意事项
- 数据类型和结构:要清楚注册变量的数据类型和结构,这样才能正确地访问其中的值。
- 错误处理:在使用注册变量进行条件判断或后续操作时,要考虑可能出现的错误情况,例如命令执行失败时返回码和输出内容的处理。
使用场景
调试,回显命令的执行结果
把状态保存为变量,其他task再继续调用
用内置变量获取IP地址写入文件,并且显示文件内容
- hosts: nfs
tasks:
- name: echo ip address
shell: "echo {{ ansible_default_ipv4.address }} >> /tmp/ip.log"
- name: cat ip.log
shell: "cat /tmp/ip.log"
register: about_ip_log
- name: debug about_ip_log
debug:
msg: "{{ about_ip_log.stdout_lines }}"
注册多个变量
同时记录且显示客户端的ip信息、主机名信息
结合循环知识,打印多个命令的结果
- name: yuchaoit.cn
hosts: localhost
tasks:
- name: 01 get ip
shell: "echo {{ ansible_default_ipv4.address }} > /tmp/ip.log"
- name: 02 get hostname
shell: "echo {{ ansible_hostname }} > /tmp/hostname.log"
- name: 03 echo hostname
shell: "cat /tmp/hostname.log"
register: hostname_log
- name: 04 echo ip
shell: "cat /tmp/ip.log"
register: ip_log
- debug:
msg: "{{item}}"
loop:
- "{{ ip_log.stdout_lines}}"
- "{{ hostname_log.stdout_lines}}"
判断当配置文件变化后,就重启服务
我们重启配置服务的标准是,修改了配置文件,否则无须重启
例如,判断rsyncd.conf文件状态发生变化后,就重启服务。
- name: 重启nginx的判断
hosts: localhost
tasks:
- name: 修改配置文件nginx.conf
copy:
src: ./nginx.conf
dest: /etc/nginx/sites-enabled/default
register: conf_status
- name: 打印注册变量
debug:
msg: "{{conf_status}}"
- name: 重启nginx,当它nginx.conf被修改
systemd:
name: nginx
state: restarted
when: conf_status.changed
查看rsync进程端口是否更新,即为rsyncd服务是否重启
[root@master-61 ~]#ansible backup -m shell -a "netstat -tunlp|grep rsync"
172.16.1.41 | CHANGED | rc=0 >>
tcp 0 0 0.0.0.0:873 0.0.0.0:* LISTEN 635/rsync
tcp6 0 0 :::873 :::* LISTEN 635/rsync
修改rsyncd.conf,再次执行剧本
[root@master-61 ~]#ansible backup -m shell -a "netstat -tunlp|grep rsync"
172.16.1.41 | CHANGED | rc=0 >>
tcp 0 0 0.0.0.0:873 0.0.0.0:* LISTEN 8274/rsync
tcp6 0 0 :::873 :::* LISTEN 8274/rsync
when条件判断语句
文档
https://docs.ansible.com/ansible/latest/user_guide/playbooks_conditionals.html
在 Ansible Playbook 中,when
语句是一个非常重要的特性,它允许你根据特定条件来决定是否执行某个任务。
以下将从基本语法、常见使用场景、示例代码等方面详细介绍 when
语句。
基本语法
when
语句通常放在任务的定义中,其基本形式如下:
- name: 任务名称
模块名:
参数: 值
when: 条件表达式
其中,条件表达式
是一个布尔表达式,当该表达式的值为 true
时,任务会被执行;当值为 false
时,任务会被跳过。
常见使用场景及示例
1. 根据目标主机的操作系统类型执行任务
---
- name: Install package based on OS family
hosts: localhost
become: true
tasks:
- name: Install Apache on Debian systems
apt:
name: apache2
state: present
when: ansible_os_family == "Debian"
- name: Install Apache on Red Hat systems
yum:
name: httpd
state: present
when: ansible_os_family == "RedHat"
在这个示例中,ansible_os_family
是 Ansible 自动收集的目标主机操作系统家族信息。根据这个信息,使用 when
语句判断目标主机是 Debian 系还是 Red Hat 系,然后执行相应的软件包安装任务。
2. 根据变量的值执行任务
prod线上
stage预生产
dev 开发测试环境
test 测试
---
- name: Conditional task based on variable
hosts: localhost
vars:
is_prod: true
tasks:
- name: Restart service in production
service:
name: nginx
state: restarted
when: is_prod
这里定义了一个变量 is_production
,并在任务的 when
语句中使用该变量进行判断。如果 is_production
的值为 true
,则执行重启服务的任务;否则,跳过该任务。
3. 根据注册变量的值执行任务
---
- name: Conditional task based on registered variable
hosts: localhost
tasks:
- name: Check if file exists
stat:
path: /etc/passwdd
register: file_status
- name: Print message if file exists
debug:
#msg: "/etc/passwd file exists"
msg: "{{file_status}}"
when: file_status.stat.exists
在这个示例中,首先使用 stat
模块检查 /etc/passwd
文件是否存在,并将结果注册到 file_status
变量中。然后,在后续任务的 when
语句中,根据 file_status.stat.exists
的值判断文件是否存在,如果存在则输出相应的消息。
4. 多条件判断
when
语句支持使用逻辑运算符(如 and
、or
、not
)进行多条件判断。
---
- name: Multi - condition task
hosts: localhost
vars:
is_production: true
has_sufficient_memory: true
tasks:
- name: Perform action if conditions are met
debug:
msg: "任务执行了。。"
when: is_production or has_sufficient_memory
这里使用 and
运算符将两个条件组合起来,只有当 is_production
和 has_sufficient_memory
的值都为 true
时,才会执行任务。
注意事项
- 变量引用:在
when
语句中引用变量时,不需要使用双花括号 ,因为when
语句本身就是一个表达式上下文。 - 语法正确性:确保
when
语句中的条件表达式语法正确,否则可能会导致任务执行逻辑出错。
通过使用 when
语句,你可以根据不同的条件灵活控制 Ansible Playbook 中任务的执行,从而实现更复杂的自动化场景。
使用场景
判断nfs配置文件是否存在
1.存在,则显示其文件内容
2.不存在,则输出 /etc/exports is not exists。
答案
- name: yuchaoit.cn
hosts: localhost
vars:
nfs_file: /etc/exports
tasks:
- name: 01 check nfs config
shell: "cat {{nfs_file}}"
register: nfs_result
ignore_errors: true
- name: 打印注册变量
debug:
msg: "{{nfs_result}}"
- name: 02 debug nfs config
debug:
msg: "{{ansible_hostname}} has {{nfs_file}},file content is : {{nfs_result.stdout}}"
when: nfs_result is success
- name: 03 debug nfs not exists
debug: msg="{{nfs_file}} is not exists."
when: nfs_result is failed
高级特性handler
官网文档
https://docs.ansible.com/ansible/latest/user_guide/playbooks_handlers.html
Handlers: running operations on change
处理程序:对更改运行操作
在 Ansible Playbook 里,handler
(处理程序)是一项关键特性,它能够让你在特定任务的执行结果满足条件时;
触发相应的操作,最常见的应用场景是在配置文件发生变更后重启服务。
下面为你详细介绍 handler
的概念、使用方法、工作原理以及示例。
概念
handler
本质上是一种特殊的任务,只有在其他任务通过 notify
关键字对其进行通知时才会被执行。
而且,handler
会在当前 Play
里所有任务都执行完毕之后统一执行,就算有多个任务都通知了同一个 handler
,该 handler
也仅会执行一次。
使用方法
1.定义 handler
handler
通常在 Playbook
的 handlers
部分进行定义,其语法格式和普通任务类似。示例如下:
handlers:
- name: Restart Nginx service
service:
name: nginx
state: restarted
在这个例子中,定义了一个名为 Restart Nginx service
的 handler
,其作用是重启 nginx
服务。
2.通知 handler
在普通任务中,使用 notify
关键字来通知 handler
。示例如下:
tasks:
- name: Copy Nginx configuration file
copy:
src: nginx.conf
dest: /etc/nginx/nginx.conf
mode: '0644'
notify:
- Restart Nginx service
这里的 copy
任务负责复制 nginx
配置文件,当该任务的执行结果为 changed
(即配置文件被修改)时,会通知名为 Restart Nginx service
的 handler
。
工作原理
- 任务执行:Ansible 按照
Playbook
中定义的顺序依次执行各个任务。 - 状态检测:每个任务执行完毕后,Ansible 会检查其执行结果是否导致目标主机的状态发生改变。如果发生改变,任务状态会被标记为
changed
。 - 通知
handler
:当任务的状态为changed
且使用了notify
关键字时,会记录要通知的handler
。 handler
执行:在当前Play
里所有任务都执行完毕后,Ansible 会统一执行被通知的handler
。
示例
以下是一个完整的 Playbook
示例,展示了如何使用 handler
在 Nginx 配置文件变更后重启 Nginx 服务。
---
- name: Manage Nginx service and configuration
hosts: localhost
become: true # 使用 sudo 权限执行任务
tasks:
- name: Copy Nginx configuration file
copy:
src: nginx.conf
dest: /etc/nginx/nginx.conf
mode: '0644'
notify:
- restart_nginx_handler
- name: 测试任务2
debug:
msg: "echo 任务2执行啦"
- name: 测试任务3
debug:
msg: "echo 任务23执行啦"
handlers:
- name: restart_nginx_handler
service:
name: nginx
state: restarted
注意事项
handler
名称:notify
中指定的handler
名称必须和handlers
部分定义的名称完全一致,否则handler
不会被触发。- 执行时机:
handler
会在当前Play
中所有任务执行完毕后执行,而不是在通知它的任务执行后立即执行。 - 权限问题:由于重启服务等操作通常需要管理员权限,所以在
Playbook
中可能需要使用become: true
来获取相应权限。
通过合理运用 handler
,可以有效地管理系统服务,确保在配置文件变更等情况下及时更新服务状态。
handler这个机制一般用于服务状态管理,如
- 你在tasks中修改了nginx的配置文件,你就必须得重启nginx服务,才能生效
handler解决什么问题
1.现状、配置文件修改了,程序不可能自己重启,得手动restart
利用handler添加在剧本中,实现效果
1.配置文件没变化,就不执行restart重启
2.配置文件发生了变化,就执行restart重启
在 Ansible Playbook 里,要实现当配置文件发生变化后就重启服务,可以借助 Ansible 的 handlers
(处理程序)和 notify
(通知)机制。下面为你详细介绍实现步骤和示例代码。
实现思路
- 复制或修改配置文件:使用
copy
或者template
模块来复制或修改目标主机上的配置文件。 - 检测文件变化:当配置文件被修改时,Ansible 会将该任务标记为
changed
。 - 使用
notify
通知处理程序:在复制或修改配置文件的任务中,使用notify
关键字指定一个处理程序。 - 定义处理程序:在
handlers
部分定义一个处理程序,用于重启服务。当notify
被触发时,这个处理程序会被执行。
示例代码
以下是一个具体的示例,假设要管理 Nginx 服务,当 Nginx 配置文件发生变化时,就重启 Nginx 服务。
iNotify,rsync
---
- name: Manage Nginx service and configuration
hosts: webservers
become: true # 使用 sudo 权限执行任务
tasks:
- name: Copy Nginx configuration file
copy:
src: nginx.conf
dest: /etc/nginx/nginx.conf
mode: '0644'
notify:
- Restart Nginx service
handlers:
- name: Restart Nginx service
service:
name: nginx
state: restarted
代码解释
tasks
部分:name: Copy Nginx configuration file
:任务的名称,用于描述该任务的作用。copy
模块:用于将本地的nginx.conf
文件复制到目标主机的/etc/nginx/nginx.conf
路径下,并设置文件权限为0644
。notify: - Restart Nginx service
:当这个任务的执行结果为changed
(即配置文件被修改)时,会通知名为Restart Nginx service
的处理程序。
handlers
部分:name: Restart Nginx service
:处理程序的名称,要和notify
中指定的名称一致。service
模块:用于管理系统服务,这里将nginx
服务的状态设置为restarted
,即重启服务。
注意事项
- 处理程序的执行时机:处理程序会在当前 Play 中所有任务执行完毕后统一执行。也就是说,如果多个任务都通知了同一个处理程序,该处理程序只会执行一次。
- 权限问题:由于重启服务通常需要管理员权限,所以在 Playbook 中使用了
become: true
。确保 Ansible 控制节点在目标主机上有足够的权限来执行这些操作。 - 服务名称:要根据目标主机的实际情况确保服务名称(如
nginx
)的正确性。不同的操作系统可能对服务名称的命名有所不同。
通过上述方法,你可以在 Ansible Playbook 中实现当配置文件变化后自动重启服务的功能。
low办法实现
- name: yuchaoit.cn
hosts: localhost
remote_user: root
tasks:
- name: nfs文件变更
copy:
src=./exports
dest=/etc/exports
register: nfs_file_change
- name: xxx
debug:
msg: "{{nfs_file_change}}"
- name: 重启nfs服务
systemd:
name: nfs-server
state: restarted
when: nfs_file_change.changed
你会发现,这个用法,无论你配置文化改没改,都必然会执行重启的task,这个写法就不太合理。
改造为handler
1.handlers中的任务会被tasks调用
2.只有tasks的确执行了,发生了change状态,handler才会执行
3.在tasks任务列表中,定义notify属性,用于触发handler的执行
剧本
- name: yuchaoit.cn
hosts: localhost
tasks:
- name: 01 copy rsyncd.conf
copy:
src=exports
dest=/etc/exports
notify:
- restart_nfs
handlers:
- name: restart_nfs
systemd:
name: nfs
state: restarted
细节注意
1.handlers必须写在结尾
2.handlers定义的任务名字,必须和notify一致
给task打上tag标签
在 Ansible Playbook 中,tag
(标签)是一个非常实用的特性,它允许你选择性地执行 Playbook 中的部分任务,而不是执行整个 Playbook。以下详细介绍 tag
的使用方法、应用场景及示例。
初始化剧本
初始化apt源
主机名
dns
hosts文件
swap关闭
磁盘内存同步,清理
软件源更新
等等
基本语法
在任务定义中,可以使用 tags
关键字为任务添加一个或多个标签。示例如下:
- name: Task with tag
apt:
name: nginx
state: present
tags:
- install
- webserver
在这个任务中,为其添加了 install
和 webserver
两个标签。
使用 tag
执行部分任务
当执行 ansible-playbook
命令时,可以使用 --tags
选项指定要执行的标签,使用 --skip-tags
选项指定要跳过的标签。
执行指定标签的任务
ansible-playbook playbook.yml --tags "install"
这会执行 playbook.yml
中所有带有 install
标签的任务。
跳过指定标签的任务
ansible-playbook playbook.yml --skip-tags "webserver"
这会执行 playbook.yml
中除了带有 webserver
标签之外的所有任务。
应用场景
1. 分阶段执行任务
假设一个 Playbook 包含安装、配置和启动服务三个阶段的任务,你可以为每个阶段的任务添加不同的标签,以便根据需要分阶段执行。
---
- name: Manage web server
hosts: localhost
become: true
tasks:
- name: Install web server
apt:
name: nginx
state: present
tags:
- t_install
- name: remove nginx
apt:
name: nginx-common
state: absent
purge: yes
tags:
- t_remove
- t_absent
- name: Configure web server
copy:
src: nginx.conf
dest: /etc/nginx/sites-enabled/default
tags:
- t_configure
- name: Start web server
service:
name: nginx
state: started
tags:
- t_start
- name: stop nginx
service:
name: nginx
state: stopped
tags:
- t_stop
- name: 重启nginx
systemd:
name: nginx
state: restarted
tags:
- t_restart
如果你只想安装服务,可以执行:
ansible-playbook playbook.yml --tags "install"
如果你已经完成了安装和配置,只想启动服务,可以执行:
选择多个tags
Ansible Playbook 支持通过命令行传入 多个 Tags,并且这是其核心功能之一。以下是具体用法、示例及注意事项:
1. 基本语法
同时指定多个 Tags
- 逗号分隔:直接在一个
--tags
参数后列出多个标签,用逗号分隔:ansible-playbook playbook.yml --tags "tag1,tag2,tag3"
- 多次指定:使用多个
--tags
参数:ansible-playbook playbook.yml --tags tag1 --tags tag2
排除多个 Tags
使用 --skip-tags
排除特定标签:
ansible-playbook playbook.yml --skip-tags "skip_tag1,skip_tag2"
2. 示例场景
Playbook 示例
假设 Playbook 中包含以下任务:
- name: 安装 Nginx
apt:
name: nginx
state: present
tags:
- install
- web
- name: 配置防火墙
ufw:
rule: allow
port: "80"
tags:
- firewall
- name: 部署静态文件
copy:
src: files/
dest: /var/www/html/
tags:
- deploy
- web
执行命令
- 仅运行
install
和firewall
标签的任务:ansible-playbook playbook.yml --tags "install,firewall"
- 运行所有标记为
web
或deploy
的任务:ansible-playbook playbook.yml --tags web,deploy
- 排除
install
和firewall
任务:ansible-playbook playbook.yml --skip-tags "install,firewall"
3. 逻辑关系
- 包含标签(
--tags
):任务只需匹配任意一个指定标签即会执行(逻辑 OR)。 - 排除标签(
--skip-tags
):任务若匹配任意一个排除标签则跳过。
4. 高级用法
标签通配符
使用 *
匹配部分标签名(Ansible 2.5+ 支持):
ansible-playbook playbook.yml --tags "web*"
特殊标签
always
:无论是否指定,默认执行(除非显式跳过)。never
:除非显式指定,否则不执行。tagged
:仅运行有标签的任务。untagged
:仅运行无标签的任务。
示例:
ansible-playbook playbook.yml --tags "tagged" # 只运行有标签的任务
ansible-playbook playbook.yml --tags "untagged" # 只运行无标签的任务
5. 注意事项
- 标签命名规范:建议使用清晰的命名(如
install
,config
,test
),避免歧义。 - 性能优化:合理使用标签可以显著缩短 Playbook 执行时间(跳过无关任务)。
- 依赖关系:确保标签之间的任务没有隐式依赖,或者通过
meta: flush_handlers
强制触发依赖。 - 版本兼容性:通配符 (
*
) 和特殊标签需要 Ansible 2.5+。
总结
通过灵活使用多标签功能,可以精准控制 Playbook 的执行范围,非常适合以下场景:
- 选择性部署:仅更新部分服务(如
--tags "deploy_web,deploy_db"
)。 - 环境隔离:为开发、测试、生产环境定义不同标签。
- 快速调试:跳过耗时任务(如
--skip-tags "migration,backup"
)。
ansible-playbook playbook.yml --tags "start"
2. 调试和测试
在开发和调试 Playbook 时,你可能只想执行部分任务来验证其功能。通过为这些任务添加特定的标签,可以方便地进行测试。
---
- name: Test task
hosts: all
tasks:
- name: Task for testing
debug:
msg: "This is a test task."
tags:
- test
执行以下命令只运行测试任务:
ansible-playbook playbook.yml --tags "test"
3. 维护和更新
在对系统进行维护或更新时,可能只需要执行部分相关任务。例如,只更新配置文件而不重新安装软件。
---
- name: Update web server configuration
hosts: webservers
become: true
tasks:
- name: Install web server
apt:
name: nginx
state: present
tags:
- install
- name: Update web server configuration
copy:
src: new_nginx.conf
dest: /etc/nginx/nginx.conf
tags:
- update_config
- name: Restart web server
service:
name: nginx
state: restarted
tags:
- restart
若要只更新配置文件并重启服务,可以执行:
ansible-playbook playbook.yml --tags "update_config,restart"
注意事项
- 标签的命名:标签名应具有描述性,方便识别和管理。
- 标签的继承:如果一个
role
中的任务有标签,在 Playbook 中使用该role
时,这些标签也会被继承。
通过使用 tag
,可以提高 Playbook 的灵活性和可维护性,根据不同的需求灵活执行部分任务。
你写了一个很长的playbook,其中有很多的任务,这并没有什么问题,不过在实际使用这个剧本时,你可能只是想要执行其中的一部分任务而已
或者,你只想要执行其中一类任务而已,而并非想要执行整个剧本中的全部任务
这个时候我们该怎么办呢?我们可以借助tags实现这个需求。
见名知义,tags可以帮助我们对任务进行'打标签'
当任务存在标签以后,我们就可以在执行playbook时,借助标签,指定执行哪些任务,或者指定不执行哪些任务了。
tag作用
调试,选择性的执行某个task
1.部署nfs-server剧本参考
- name: yuchaoit.cn
hosts: nfs
tasks:
- name: 01 安装nfs-utils 服务
apt: name=nfs-utils state=installed
tags: 01_install_nfs_service
- name: 02 安装rpcbind 服务
yum: name=rpcbind state=installed
tags: 02_install_rpcbind_service
- name: 03 创建组
group: name=www gid=666
tags: 03_add_group
- name: 04 创建用户
user: name=www uid=666 group=www create_home=no shell=/sbin/nologin
tags: 04_add_user
- name: 05 创建共享目录
file: path=/data owner=www group=www state=directory
tags: 05_create_data_dir
- name: 06 拷贝配置文件
copy: src=/script/exports dest=/etc/exports
tags: 06_copy_nfs_exports
- name: 07 创建关于rsync密码文件
copy: content='yuchao666' dest=/etc/rsync.passwd mode=600
tags: 07_create_rsync_passwd
- name: 08 启动rpcbind
service: name=rpcbind state=started enabled=yes
tags: 08_start_rpcbind
- name: 09 启动nfs
systemd: name=nfs state=started enabled=yes
tags: 09_start_nfs
2.打印剧本中可用的标签
也就是你可以直接选择执行哪些任务
[root@master-61 ~]#ansible-playbook --list-tags tag_nfs.yaml
playbook: tag_nfs.yaml
play #1 (nfs): yuchaoit.cn TAGS: []
TASK TAGS: [01_install_nfs_service, 02_install_rpcbind_service, 03_add_group, 04_add_user, 05_create_data_dir, 06_copy_nfs_exports, 07_create_rsync_passwd, 08_start_rpcbind, 09_start_nfs]
3.指定运行某个标签
[root@master-61 ~]#ansible-playbook -t 01_install_nfs_service tag_nfs.yaml
[root@master-61 ~]#ansible-playbook -t 04_add_user tag_nfs.yaml
4.指定运行多个标签
[root@master-61 ~]#ansible-playbook -t "01_install_nfs_service,05_create_data_dir,04_add_user" tag_nfs.yaml
5.指定不运行一个/多个标签
[root@master-61 ~]#ansible-playbook --skip-tags 01_install_nfs_service,05_create_data_dir,04_add_user tag_nfs.yaml
选择tasks执行
使用场景
1.调试剧本时,task数量太多,不想从头执行,可以指定执行位置
查看task列表
[root@master-61 ~]#ansible-playbook --list-tasks tag_nfs.yaml
playbook: tag_nfs.yaml
play #1 (nfs): yuchaoit.cn TAGS: []
tasks:
01 安装nfs-utils 服务 TAGS: [01_install_nfs_service]
02 安装rpcbind 服务 TAGS: [02_install_rpcbind_service]
03 创建组 TAGS: [03_add_group]
04 创建用户 TAGS: [04_add_user]
05 创建共享目录 TAGS: [05_create_data_dir]
06 拷贝配置文件 TAGS: [06_copy_nfs_exports]
07 创建关于rsync密码文件 TAGS: [07_create_rsync_passwd]
08 启动rpcbind TAGS: [08_start_rpcbind]
09 启动nfs TAGS: [09_start_nfs]
选择执行的task位置
从第五步骤开始
[root@master-61 ~]#ansible-playbook --start-at-task '05 创建共享目录' tag_nfs.yaml
总结(playbook规范流程)
通过这个流程,去编写、检验、阅读所有的playbook都是一个靠谱的办法
无论是你自己写好剧本,进行校验
还是工作后,先看公司里现成的剧本,进行维护,都可以基于这个思路
1.检查剧本语法
如果语法不对,ansible会具体告诉你错误的位置
[root@master-61 ~]#ansible-playbook --syntax-check tag_nfs.yaml
playbook: tag_nfs.yaml
2.检查该剧本操作的主机有哪些
搞清楚这个剧本会影响到哪些主机,关乎于这些机器的作用
[root@master-61 ~]#ansible-playbook --list-hosts get_ip.yaml
playbook: get_ip.yaml
play #1 (web): web TAGS: []
pattern: [u'web']
hosts (3):
172.16.1.7
172.16.1.8
172.16.1.9
3.查看剧本有哪些任务
轻松的搞清楚这个剧本有什么作用,整体的工作流程
[root@master-61 ~]#ansible-playbook --list-tasks tag_nfs.yaml
playbook: tag_nfs.yaml
play #1 (nfs): yuchaoit.cn TAGS: []
tasks:
01 安装nfs-utils 服务 TAGS: [01_install_nfs_service]
02 安装rpcbind 服务 TAGS: [02_install_rpcbind_service]
03 创建组 TAGS: [03_add_group]
04 创建用户 TAGS: [04_add_user]
05 创建共享目录 TAGS: [05_create_data_dir]
06 拷贝配置文件 TAGS: [06_copy_nfs_exports]
07 创建关于rsync密码文件 TAGS: [07_create_rsync_passwd]
08 启动rpcbind TAGS: [08_start_rpcbind]
09 启动nfs TAGS: [09_start_nfs]
4.模拟剧本执行
模拟执行,查看执行流程是否存在错误,以及执行的状态
[root@master-61 ~]#ansible-playbook -C tag_nfs.yaml
# 任务2,创建文件, 任务3,报错。
5.真正执行剧本
对目标机器发生实质性的改变、修改操作
[root@master-61 ~]#ansible-playbook tag_nfs.yaml
ansible剧本大作业(吐血提醒)
等你坐在访客室、开始1v1和面试官交流面试
你就会回想起,当初于超老师让我好好学ansible,结合备份同步访问写剧本,以及让我锻炼口述能力,叙述架构流程的重要性,悔不当初啊!!
以及面试官问你
ansible熟悉吗?写下你用过的哪些模块?
用过ansible高级特性吗?说一说有哪些
你心中一万个羊驼。。。超哥当时怎么教来着,我为什么没好好做作业,害!
使用学过的模块
如
shell
copy
file
group
user
script
yum
apt
systemd
等
尽量结合剧本高级特性
使用高级特性,是为了简化脚本写法,提升可维护性,同时也会提升阅读难度,需要额外学习。
循环 loop
变量 vars
注册变量 register
条件语句 when
触发任务 handler
标签tag
综合练习
继续改造 ,完全改造为ansible-playbook 一键部署
rsync
nfs
lsyncd
nginx
Nginx综合案例
以下是一个基于 Ubuntu 系统,使用 Ansible 完成 Nginx 部署的 Playbook,综合运用了你所提到的模块(shell
、copy
、file
、group
、user
、script
、yum
这里替换为 apt
因为是 Ubuntu 系统、systemd
)以及高级特性(loop
、vars
、register
、when
、handler
)。
需求概述
在 Ubuntu 目标主机上部署并配置 Nginx,涵盖创建 Nginx 用户和组、安装 Nginx、复制配置文件、启动并管理 Nginx 服务等步骤。
root@ansible-01:/opt# ls /etc/nginx/ -l
total 64
drwxr-xr-x 2 root root 4096 Feb 15 02:40 conf.d # 子配置文件目录 *.conf
-rw-r--r-- 1 root root 1125 May 31 2023 fastcgi.conf
-rw-r--r-- 1 root root 1055 May 31 2023 fastcgi_params
-rw-r--r-- 1 root root 2837 May 31 2023 koi-utf
-rw-r--r-- 1 root root 2223 May 31 2023 koi-win
-rw-r--r-- 1 root root 3957 May 31 2023 mime.types
drwxr-xr-x 2 root root 4096 Feb 15 02:40 modules-available # nginx可用模块
drwxr-xr-x 2 root root 4096 Feb 26 19:07 modules-enabled # nginx已开启的模块
-rw-r--r-- 1 root root 1447 May 31 2023 nginx.conf # 这是个入口文件,include加载
-rw-r--r-- 1 root root 180 May 31 2023 proxy_params
-rw-r--r-- 1 root root 636 May 31 2023 scgi_params
drwxr-xr-x 2 root root 4096 Feb 26 19:07 sites-available # nginx站点,多个站点,可用
drwxr-xr-x 2 root root 4096 Feb 26 19:07 sites-enabled #已开启,导入
drwxr-xr-x 2 root root 4096 Feb 26 19:07 snippets
-rw-r--r-- 1 root root 664 May 31 2023 uwsgi_params
-rw-r--r-- 1 root root 3071 May 31 2023 win-utf
nginx.conf
user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections 768;
# multi_accept on;
}
http {
sendfile on;
tcp_nopush on;
types_hash_max_size 2048;
include /etc/nginx/mime.types;
default_type application/octet-stream;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE
ssl_prefer_server_ciphers on;
##
# Logging Settings
##
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
##
# Gzip Settings
##
gzip on;
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
# include /etc/nginx/sites-available/*;
}
defualt
server {
listen 34444 default_server;
root /var/www/html;
# Add index.php to the list if you are using PHP
index index.html index.htm index.nginx-debian.html;
server_name _;
location / {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
try_files $uri $uri/ =404;
}
}
Playbook 代码
---
- name: Deploy and Configure Nginx on Ubuntu
hosts: web
become: true
vars:
nginx_user: www-data
nginx_group: www-data
nginx_config_dir: /etc/nginx
nginx_log_dir: /var/log/nginx
nginx_conf_file: nginx.conf
nginx_sites_available: /etc/nginx/sites-available
nginx_sites_enabled: /etc/nginx/sites-enabled
nginx_site_name: default
nginx_service_name: nginx
tasks:
- name: 创建nginx运行,组信息
group:
name: "{{nginx_group}}"
state: present
- name: 创建nginx运行,用户信息
user:
name: "{{nginx_user}}"
group: "{{nginx_group}}"
state: present
- name: 更新缓存
apt:
update_cache: yes
cache_valid_time: 3600
- name: 安装nginx
apt:
name: nginx
state: present
tags:
- t_install
- name: 创建nginx日志目录
file:
path: "{{ nginx_log_dir }}"
state: directory
owner: "{{ nginx_user }}"
group: "{{ nginx_group }}"
mode: '0755'
- name: 创建sites-available 和 sites-enabled ,其他站点目录
file:
path: "{{ item }}"
state: directory
owner: "{{ nginx_user }}"
group: "{{ nginx_group }}"
mode: '0755'
loop:
- "{{ nginx_sites_available }}"
- "{{ nginx_sites_enabled }}"
- name: 拷贝nginx主配置文件
copy:
src: "{{ nginx_conf_file }}"
dest: "{{ nginx_config_dir }}/{{ nginx_conf_file }}"
owner: "{{ nginx_user }}"
group: "{{ nginx_group }}"
mode: '0644'
notify:
- Reload Nginx configuration
- name: 拷贝站点端口,域名,网页目录,代理规则,配置文件
copy:
src: "{{ nginx_site_name }}"
dest: "{{ nginx_sites_enabled }}/{{ nginx_site_name }}"
owner: "{{ nginx_user }}"
group: "{{ nginx_group }}"
mode: '0644'
notify:
- Reload Nginx configuration
- name: 判断nginx是否运行
shell: systemctl is-active {{ nginx_service_name }}
register: nginx_service_status
ignore_errors: true
- name: 打印nginx运行结果查看
debug:
msg: "{{nginx_service_status}}"
- name: Start and enable Nginx service
systemd:
name: "{{ nginx_service_name }}"
state: started
enabled: yes
when: nginx_service_status.rc != 0
handlers:
- name: Reload Nginx configuration
systemd:
name: "{{ nginx_service_name }}"
state: reloaded
listen: "Reload Nginx configuration"
详细解释
1. vars
部分
- 定义了一系列变量,用于在后续任务中引用。
nginx_user
和nginx_group
:通常 Ubuntu 上 Nginx 默认使用www-data
用户和组。nginx_config_dir
:Nginx 配置文件所在目录。nginx_log_dir
:Nginx 日志文件所在目录。nginx_conf_file
:Nginx 主配置文件名称。nginx_sites_available
和nginx_sites_enabled
:Nginx 虚拟主机配置文件目录。nginx_site_name
:虚拟主机配置文件名称。nginx_service_name
:Nginx 服务名称。
2. tasks
部分
- 用户和组管理:
- 使用
group
模块确保nginx_group
存在。 - 使用
user
模块确保nginx_user
存在,并将其归属到nginx_group
。
- 使用
- 软件安装:
- 使用
apt
模块更新软件包缓存,cache_valid_time
设定缓存有效期为 3600 秒(1 小时)。 - 继续使用
apt
模块安装 Nginx。
- 使用
- 目录创建:
- 使用
file
模块创建 Nginx 日志目录,并设置正确的用户、组和权限。 - 利用
loop
特性,使用file
模块创建sites-available
和sites-enabled
目录。
- 使用
- 配置文件复制:
- 使用
copy
模块复制 Nginx 主配置文件和虚拟主机配置文件到目标位置,同时使用notify
关键字通知handler
在配置文件有变化时重新加载 Nginx 配置。
- 使用
- 符号链接创建:使用
file
模块为虚拟主机配置文件创建符号链接,并通知handler
。 - 服务状态检查与启动:
- 使用
shell
模块检查 Nginx 服务是否正在运行,并将结果注册到nginx_service_status
变量中。 - 使用
systemd
模块,结合when
条件判断,如果服务未运行则启动并设置为开机自启。
- 使用
3. handlers
部分
- 重新加载 Nginx 配置:当配置文件发生变化时,
handler
会接收到notify
通知,使用systemd
模块重新加载 Nginx 服务配置。
使用说明
- 准备工作
- 确保
ubuntu_web_servers
主机组在 Ansible 清单文件中正确定义。 - 在本地准备好
nginx.conf
和default
配置文件。
- 确保
- 执行 Playbook
ansible-playbook playbook.yml
通过这个案例,你可以综合运用 Ansible 的模块和高级特性完成 Ubuntu 系统上 Nginx 的部署和配置。