Ansible Playbook 编写
Ansible Playbook 编写
Playbook 基础结构
---
- name: 描述 Playbook 的目的
hosts: 目标主机组
become: yes # 使用 sudo
gather_facts: yes # 收集系统信息
vars:
变量名: 值
tasks:
- name: 任务名称
模块名:
参数: 值
变量使用
变量定义位置
# 1. 在 Playbook 中定义
vars:
app_name: myapp
app_port: 8080
# 2. 在 vars_files 中引用文件
vars_files:
- vars/common.yml
- "vars/{{ ansible_distribution }}.yml"
# 3. 在命令行传递
ansible-playbook playbook.yml -e "app_version=1.0.0"
ansible-playbook playbook.yml -e "@vars/extra.yml"
变量优先级
命令行变量 > role 变量 > inventory 变量 > playbook vars > facts
使用变量
tasks:
- name: 使用变量
debug:
msg: "应用 {{ app_name }} 运行在端口 {{ app_port }}"
- name: 访问字典变量
debug:
msg: "数据库主机是 {{ db_config.host }}"
- name: 访问列表变量
debug:
msg: "第一个包是 {{ packages[0] }}"
条件判断
when 语句
tasks:
- name: 仅在 Debian 上执行
apt:
name: nginx
state: present
when: ansible_os_family == "Debian"
- name: 仅在 CentOS 上执行
yum:
name: nginx
state: present
when: ansible_os_family == "RedHat"
- name: 检查变量
debug:
msg: "变量已定义"
when: my_variable is defined
- name: 复杂条件
debug:
msg: "满足条件"
when:
- ansible_distribution == "Ubuntu"
- ansible_distribution_major_version | int >= 18
条件表达式
tasks:
- name: 等于
debug:
msg: "匹配"
when: ansible_distribution == "Ubuntu"
- name: 不等于
debug:
msg: "不匹配"
when: ansible_distribution != "CentOS"
- name: 大于
debug:
msg: "版本较新"
when: ansible_distribution_major_version | int > 16
- name: 在列表中
debug:
msg: "匹配列表"
when: ansible_distribution in ["Ubuntu", "Debian"]
循环
简单循环
tasks:
- name: 安装多个包
apt:
name: "{{ item }}"
state: present
loop:
- nginx
- git
- curl
- vim
循环字典
tasks:
- name: 创建用户
user:
name: "{{ item.name }}"
groups: "{{ item.groups }}"
state: present
loop:
- name: alice
groups: admin
- name: bob
groups: developer
使用 with_items
tasks:
- name: 创建目录
file:
path: "{{ item }}"
state: directory
with_items:
- /opt/app
- /opt/app/logs
- /opt/app/data
循环读取文件行
tasks:
- name: 读取文件内容
debug:
msg: "{{ item }}"
with_file:
- /etc/hosts
模板 (Jinja2)
基本模板
{# templates/nginx.conf.j2 #}
server {
listen {{ http_port }};
server_name {{ server_name }};
location / {
proxy_pass http://127.0.0.1:{{ app_port }};
}
}
使用模板
tasks:
- name: 复制配置文件
template:
src: templates/nginx.conf.j2
dest: /etc/nginx/nginx.conf
owner: root
group: root
mode: '0644'
backup: yes
模板条件
{# templates/config.j2 #}
{% if enable_ssl %}
server {
listen 443 ssl;
ssl_certificate /etc/ssl/certs/{{ domain }}.crt;
ssl_certificate_key /etc/ssl/private/{{ domain }}.key;
}
{% endif %}
{% for server in upstream_servers %}
server {{ server.host }}:{{ server.port }} weight={{ server.weight | default(1) }};
{% endfor %}
模板过滤器
{# 字符串操作 #}
{{ hostname | upper }}
{{ hostname | lower }}
{{ path | basename }}
{{ path | dirname }}
{# 数字操作 #}
{{ port | int }}
{{ price | float }}
{# 默认值 #}
{{ variable | default('default_value') }}
{# 列表操作 #}
{{ packages | join(', ') }}
{{ list | length }}
{{ list | first }}
{{ list | last }}
Handler
handlers:
- name: Restart Nginx
service:
name: nginx
state: restarted
listen: "restart web services"
- name: Reload Nginx
service:
name: nginx
state: reloaded
tasks:
- name: 复制 Nginx 配置
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: Restart Nginx
- name: 复制站点配置
template:
src: site.conf.j2
dest: /etc/nginx/sites-available/default
notify: Reload Nginx
错误处理
ignore_errors
tasks:
- name: 忽略错误
shell: /bin/false
ignore_errors: yes
block/rescue/always
tasks:
- block:
- name: 可能失败的任务
shell: /bin/false
- name: 后续任务
debug:
msg: "不会执行"
rescue:
- name: 错误处理
debug:
msg: "发生错误,执行恢复"
always:
- name: 始终执行
debug:
msg: "无论成功失败都执行"
failed_when
tasks:
- name: 自定义失败条件
shell: /usr/bin/check_something
register: result
failed_when: "'error' in result.stdout"
Tags
tasks:
- name: 安装任务
apt:
name: nginx
tags:
- install
- web
- name: 配置任务
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
tags:
- config
- web
# 仅执行特定标签的任务
ansible-playbook playbook.yml --tags "install"
# 跳过特定标签的任务
ansible-playbook playbook.yml --skip-tags "config"
# 查看标签
ansible-playbook playbook.yml --list-tags
实践案例
完整的 Web 应用部署
---
- name: 部署 Web 应用
hosts: webservers
become: yes
vars:
app_name: myapp
app_port: 8080
app_version: "{{ lookup('env', 'APP_VERSION') | default('1.0.0') }}"
handlers:
- name: Restart App
systemd:
name: "{{ app_name }}"
state: restarted
- name: Reload Nginx
service:
name: nginx
state: reloaded
tasks:
- name: 安装依赖
apt:
name:
- python3
- python3-pip
- nginx
- supervisor
state: present
update_cache: yes
tags: install
- name: 创建应用用户
user:
name: "{{ app_name }}"
system: yes
shell: /bin/false
tags: setup
- name: 创建应用目录
file:
path: "/opt/{{ app_name }}"
state: directory
owner: "{{ app_name }}"
group: "{{ app_name }}"
mode: '0755'
tags: setup
- name: 部署应用代码
git:
repo: "https://github.com/example/{{ app_name }}.git"
dest: "/opt/{{ app_name }}"
version: "v{{ app_version }}"
become_user: "{{ app_name }}"
notify: Restart App
tags: deploy
- name: 安装 Python 依赖
pip:
requirements: "/opt/{{ app_name }}/requirements.txt"
executable: pip3
tags: deploy
- name: 复制应用配置
template:
src: app.conf.j2
dest: "/opt/{{ app_name }}/config.ini"
owner: "{{ app_name }}"
group: "{{ app_name }}"
mode: '0640'
notify: Restart App
tags: config
- name: 配置 Supervisor
template:
src: supervisor.conf.j2
dest: "/etc/supervisor/conf.d/{{ app_name }}.conf"
notify:
- Restart App
tags: config
- name: 配置 Nginx
template:
src: nginx.conf.j2
dest: "/etc/nginx/sites-available/{{ app_name }}"
notify: Reload Nginx
tags: config
- name: 启用 Nginx 站点
file:
src: "/etc/nginx/sites-available/{{ app_name }}"
dest: "/etc/nginx/sites-enabled/{{ app_name }}"
state: link
notify: Reload Nginx
tags: config
最佳实践
- 使用有意义的任务名称
- 利用变量提高可重用性
- 使用 Handler 处理服务重启
- 使用 Tags 组织任务
- 编写清晰的 Playbook 文档
总结
掌握 Ansible Playbook 的高级特性可以编写出灵活、可维护的自动化脚本。合理使用变量、条件、循环和模板是编写高质量 Playbook 的关键。