← 返回首页
🔧

Ansible Playbook 编写

📂 devops ⏱ 4 min 764 words

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

最佳实践

总结

掌握 Ansible Playbook 的高级特性可以编写出灵活、可维护的自动化脚本。合理使用变量、条件、循环和模板是编写高质量 Playbook 的关键。