ansible-expert

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Ansible Expert

Ansible专家指南

Expert guidance for Ansible - configuration management, application deployment, and IT automation using declarative YAML playbooks.
提供Ansible的专家级指导——使用声明式YAML Playbook进行配置管理、应用部署和IT自动化。

Core Concepts

核心概念

Ansible Architecture

Ansible架构

  • Control node (runs Ansible)
  • Managed nodes (target systems)
  • Inventory (hosts and groups)
  • Playbooks (YAML automation scripts)
  • Modules (units of work)
  • Roles (reusable automation units)
  • Plugins (extend functionality)
  • 控制节点(运行Ansible)
  • 受管节点(目标系统)
  • 清单(主机和组)
  • Playbook(YAML自动化脚本)
  • 模块(工作单元)
  • 角色(可复用自动化单元)
  • 插件(扩展功能)

Key Features

关键特性

  • Agentless (SSH-based)
  • Idempotent operations
  • Declarative syntax
  • Human-readable YAML
  • Extensible with modules
  • Push-based configuration
  • Parallel execution
  • 无代理(基于SSH)
  • 幂等操作
  • 声明式语法
  • 人类可读的YAML格式
  • 可通过模块扩展
  • 基于推送的配置
  • 并行执行

Use Cases

应用场景

  • Configuration management
  • Application deployment
  • Provisioning
  • Continuous delivery
  • Security automation
  • Orchestration
  • 配置管理
  • 应用部署
  • 资源置备
  • 持续交付
  • 安全自动化
  • 编排调度

Installation

安装

bash
undefined
bash
undefined

Using pip

使用pip安装

pip install ansible
pip install ansible

Using apt (Ubuntu/Debian)

使用apt(Ubuntu/Debian)

sudo apt update sudo apt install ansible
sudo apt update sudo apt install ansible

Using yum (RHEL/CentOS)

使用yum(RHEL/CentOS)

sudo yum install ansible
sudo yum install ansible

Verify installation

验证安装

ansible --version
undefined
ansible --version
undefined

Inventory

清单

Basic Inventory (INI format)

基础清单(INI格式)

ini
undefined
ini
undefined

inventory/hosts

inventory/hosts

[webservers] web1.example.com web2.example.com ansible_host=192.168.1.10
[databases] db1.example.com ansible_user=dbadmin db2.example.com
[production:children] webservers databases
[production:vars] ansible_python_interpreter=/usr/bin/python3 ansible_connection=ssh
undefined
[webservers] web1.example.com web2.example.com ansible_host=192.168.1.10
[databases] db1.example.com ansible_user=dbadmin db2.example.com
[production:children] webservers databases
[production:vars] ansible_python_interpreter=/usr/bin/python3 ansible_connection=ssh
undefined

YAML Inventory

YAML格式清单

yaml
undefined
yaml
undefined

inventory/hosts.yml

inventory/hosts.yml

all: children: webservers: hosts: web1.example.com: web2.example.com: ansible_host: 192.168.1.10 databases: hosts: db1.example.com: ansible_user: dbadmin db2.example.com: production: children: webservers: databases: vars: ansible_python_interpreter: /usr/bin/python3 ansible_connection: ssh
undefined
all: children: webservers: hosts: web1.example.com: web2.example.com: ansible_host: 192.168.1.10 databases: hosts: db1.example.com: ansible_user: dbadmin db2.example.com: production: children: webservers: databases: vars: ansible_python_interpreter: /usr/bin/python3 ansible_connection: ssh
undefined

Dynamic Inventory

动态清单

python
#!/usr/bin/env python3
python
#!/usr/bin/env python3

inventory/aws_ec2.py

inventory/aws_ec2.py

import json import boto3
def get_inventory(): ec2 = boto3.client('ec2', region_name='us-east-1') response = ec2.describe_instances(Filters=[ {'Name': 'instance-state-name', 'Values': ['running']} ])
inventory = {
    '_meta': {'hostvars': {}},
    'all': {'hosts': []},
    'webservers': {'hosts': []},
    'databases': {'hosts': []},
}

for reservation in response['Reservations']:
    for instance in reservation['Instances']:
        ip = instance['PrivateIpAddress']
        tags = {tag['Key']: tag['Value'] for tag in instance.get('Tags', [])}

        inventory['all']['hosts'].append(ip)
        inventory['_meta']['hostvars'][ip] = {
            'ansible_host': ip,
            'instance_id': instance['InstanceId'],
            'instance_type': instance['InstanceType'],
        }

        # Group by role tag
        role = tags.get('Role', '')
        if role in inventory:
            inventory[role]['hosts'].append(ip)

return inventory
if name == 'main': print(json.dumps(get_inventory(), indent=2))
undefined
import json import boto3
def get_inventory(): ec2 = boto3.client('ec2', region_name='us-east-1') response = ec2.describe_instances(Filters=[ {'Name': 'instance-state-name', 'Values': ['running']} ])
inventory = {
    '_meta': {'hostvars': {}},
    'all': {'hosts': []},
    'webservers': {'hosts': []},
    'databases': {'hosts': []},
}

for reservation in response['Reservations']:
    for instance in reservation['Instances']:
        ip = instance['PrivateIpAddress']
        tags = {tag['Key']: tag['Value'] for tag in instance.get('Tags', [])}

        inventory['all']['hosts'].append(ip)
        inventory['_meta']['hostvars'][ip] = {
            'ansible_host': ip,
            'instance_id': instance['InstanceId'],
            'instance_type': instance['InstanceType'],
        }

        # 按角色标签分组
        role = tags.get('Role', '')
        if role in inventory:
            inventory[role]['hosts'].append(ip)

return inventory
if name == 'main': print(json.dumps(get_inventory(), indent=2))
undefined

Playbooks

Playbook

Basic Playbook

基础Playbook

yaml
undefined
yaml
undefined

playbooks/webserver.yml

playbooks/webserver.yml


  • name: Configure web servers hosts: webservers become: yes vars: app_port: 8080 app_user: webapp
    tasks:
    • name: Install nginx apt: name: nginx state: present update_cache: yes
    • name: Start and enable nginx systemd: name: nginx state: started enabled: yes
    • name: Copy nginx configuration template: src: templates/nginx.conf.j2 dest: /etc/nginx/sites-available/default mode: '0644' notify: Reload nginx
    • name: Create application user user: name: "{{ app_user }}" state: present shell: /bin/bash
    handlers:
    • name: Reload nginx systemd: name: nginx state: reloaded
undefined

  • name: 配置Web服务器 hosts: webservers become: yes vars: app_port: 8080 app_user: webapp
    tasks:
    • name: 安装nginx apt: name: nginx state: present update_cache: yes
    • name: 启动并启用nginx systemd: name: nginx state: started enabled: yes
    • name: 复制nginx配置文件 template: src: templates/nginx.conf.j2 dest: /etc/nginx/sites-available/default mode: '0644' notify: 重新加载nginx
    • name: 创建应用用户 user: name: "{{ app_user }}" state: present shell: /bin/bash
    handlers:
    • name: 重新加载nginx systemd: name: nginx state: reloaded
undefined

Advanced Playbook

高级Playbook

yaml
undefined
yaml
undefined

playbooks/deploy-app.yml

playbooks/deploy-app.yml


  • name: Deploy application hosts: webservers become: yes vars: app_name: myapp app_version: "{{ version | default('latest') }}" app_port: 8080 deploy_user: deployer
    pre_tasks:
    • name: Check if required variables are defined assert: that: - app_name is defined - app_version is defined fail_msg: "Required variables are not defined"
    tasks:
    • name: Create deployment directory file: path: "/opt/{{ app_name }}" state: directory owner: "{{ deploy_user }}" group: "{{ deploy_user }}" mode: '0755'
    • name: Download application artifact get_url: url: "https://artifacts.example.com/{{ app_name }}/{{ app_version }}/{{ app_name }}.jar" dest: "/opt/{{ app_name }}/{{ app_name }}-{{ app_version }}.jar" mode: '0644' register: download_result
    • name: Create systemd service template: src: templates/app.service.j2 dest: "/etc/systemd/system/{{ app_name }}.service" mode: '0644' notify:
      • Reload systemd
      • Restart application
    • name: Enable application service systemd: name: "{{ app_name }}" enabled: yes
    • name: Wait for application to start wait_for: port: "{{ app_port }}" delay: 5 timeout: 60 when: download_result.changed
    • name: Check application health uri: url: "http://localhost:{{ app_port }}/health" status_code: 200 retries: 3 delay: 5
    post_tasks:
    • name: Clean up old versions shell: | cd /opt/{{ app_name }} ls -t {{ app_name }}-*.jar | tail -n +4 | xargs -r rm args: executable: /bin/bash
    handlers:
    • name: Reload systemd systemd: daemon_reload: yes
    • name: Restart application systemd: name: "{{ app_name }}" state: restarted
undefined

  • name: 部署应用 hosts: webservers become: yes vars: app_name: myapp app_version: "{{ version | default('latest') }}" app_port: 8080 deploy_user: deployer
    pre_tasks:
    • name: 检查是否定义了必填变量 assert: that: - app_name is defined - app_version is defined fail_msg: "未定义必填变量"
    tasks:
    • name: 创建部署目录 file: path: "/opt/{{ app_name }}" state: directory owner: "{{ deploy_user }}" group: "{{ deploy_user }}" mode: '0755'
    • name: 下载应用制品 get_url: url: "https://artifacts.example.com/{{ app_name }}/{{ app_version }}/{{ app_name }}.jar" dest: "/opt/{{ app_name }}/{{ app_name }}-{{ app_version }}.jar" mode: '0644' register: download_result
    • name: 创建systemd服务文件 template: src: templates/app.service.j2 dest: "/etc/systemd/system/{{ app_name }}.service" mode: '0644' notify:
      • 重新加载systemd
      • 重启应用
    • name: 启用应用服务 systemd: name: "{{ app_name }}" enabled: yes
    • name: 等待应用启动 wait_for: port: "{{ app_port }}" delay: 5 timeout: 60 when: download_result.changed
    • name: 检查应用健康状态 uri: url: "http://localhost:{{ app_port }}/health" status_code: 200 retries: 3 delay: 5
    post_tasks:
    • name: 清理旧版本 shell: | cd /opt/{{ app_name }} ls -t {{ app_name }}-*.jar | tail -n +4 | xargs -r rm args: executable: /bin/bash
    handlers:
    • name: 重新加载systemd systemd: daemon_reload: yes
    • name: 重启应用 systemd: name: "{{ app_name }}" state: restarted
undefined

Conditionals and Loops

条件判断与循环

yaml
---
- name: Conditional and loop examples
  hosts: all
  tasks:
    - name: Install package (Debian)
      apt:
        name: "{{ item }}"
        state: present
      loop:
        - nginx
        - postgresql
        - redis
      when: ansible_os_family == "Debian"

    - name: Install package (RedHat)
      yum:
        name: "{{ item }}"
        state: present
      loop:
        - nginx
        - postgresql
        - redis
      when: ansible_os_family == "RedHat"

    - name: Create users
      user:
        name: "{{ item.name }}"
        state: present
        groups: "{{ item.groups }}"
      loop:
        - { name: 'alice', groups: 'wheel' }
        - { name: 'bob', groups: 'users' }
        - { name: 'charlie', groups: 'developers' }

    - name: Configure services
      systemd:
        name: "{{ item.name }}"
        state: "{{ item.state }}"
        enabled: "{{ item.enabled }}"
      loop:
        - { name: 'nginx', state: 'started', enabled: yes }
        - { name: 'postgresql', state: 'started', enabled: yes }
        - { name: 'redis', state: 'started', enabled: yes }

    - name: Set fact based on condition
      set_fact:
        environment_type: "{{ 'production' if inventory_hostname in groups['production'] else 'development' }}"

    - name: Debug conditional
      debug:
        msg: "This is a {{ environment_type }} server"
yaml
---
- name: 条件判断与循环示例
  hosts: all
  tasks:
    - name: 安装软件包(Debian系统)
      apt:
        name: "{{ item }}"
        state: present
      loop:
        - nginx
        - postgresql
        - redis
      when: ansible_os_family == "Debian"

    - name: 安装软件包(RedHat系统)
      yum:
        name: "{{ item }}"
        state: present
      loop:
        - nginx
        - postgresql
        - redis
      when: ansible_os_family == "RedHat"

    - name: 创建用户
      user:
        name: "{{ item.name }}"
        state: present
        groups: "{{ item.groups }}"
      loop:
        - { name: 'alice', groups: 'wheel' }
        - { name: 'bob', groups: 'users' }
        - { name: 'charlie', groups: 'developers' }

    - name: 配置服务
      systemd:
        name: "{{ item.name }}"
        state: "{{ item.state }}"
        enabled: "{{ item.enabled }}"
      loop:
        - { name: 'nginx', state: 'started', enabled: yes }
        - { name: 'postgresql', state: 'started', enabled: yes }
        - { name: 'redis', state: 'started', enabled: yes }

    - name: 根据条件设置事实变量
      set_fact:
        environment_type: "{{ 'production' if inventory_hostname in groups['production'] else 'development' }}"

    - name: 调试条件结果
      debug:
        msg: "这是一台{{ environment_type }}服务器"

Roles

角色

Role Structure

角色结构

roles/
└── webserver/
    ├── defaults/
    │   └── main.yml         # Default variables
    ├── files/
    │   └── app.conf         # Static files
    ├── handlers/
    │   └── main.yml         # Handlers
    ├── meta/
    │   └── main.yml         # Role metadata and dependencies
    ├── tasks/
    │   └── main.yml         # Main task list
    ├── templates/
    │   └── nginx.conf.j2    # Jinja2 templates
    ├── tests/
    │   └── test.yml         # Role tests
    └── vars/
        └── main.yml         # Role variables
roles/
└── webserver/
    ├── defaults/
    │   └── main.yml         # 默认变量
    ├── files/
    │   └── app.conf         # 静态文件
    ├── handlers/
    │   └── main.yml         # 处理器
    ├── meta/
    │   └── main.yml         # 角色元数据与依赖
    ├── tasks/
    │   └── main.yml         # 主任务列表
    ├── templates/
    │   └── nginx.conf.j2    # Jinja2模板
    ├── tests/
    │   └── test.yml         # 角色测试
    └── vars/
        └── main.yml         # 角色变量

Example Role

示例角色

yaml
undefined
yaml
undefined

roles/webserver/defaults/main.yml

roles/webserver/defaults/main.yml


nginx_port: 80 nginx_user: www-data document_root: /var/www/html

nginx_port: 80 nginx_user: www-data document_root: /var/www/html

roles/webserver/tasks/main.yml

roles/webserver/tasks/main.yml


  • name: Install nginx apt: name: nginx state: present update_cache: yes
  • name: Copy nginx configuration template: src: nginx.conf.j2 dest: /etc/nginx/nginx.conf mode: '0644' notify: Restart nginx
  • name: Create document root file: path: "{{ document_root }}" state: directory owner: "{{ nginx_user }}" mode: '0755'
  • name: Start nginx systemd: name: nginx state: started enabled: yes

  • name: 安装nginx apt: name: nginx state: present update_cache: yes
  • name: 复制nginx配置文件 template: src: nginx.conf.j2 dest: /etc/nginx/nginx.conf mode: '0644' notify: 重启nginx
  • name: 创建文档根目录 file: path: "{{ document_root }}" state: directory owner: "{{ nginx_user }}" mode: '0755'
  • name: 启动nginx systemd: name: nginx state: started enabled: yes

roles/webserver/handlers/main.yml

roles/webserver/handlers/main.yml


  • name: Restart nginx systemd: name: nginx state: restarted

  • name: 重启nginx systemd: name: nginx state: restarted

roles/webserver/templates/nginx.conf.j2

roles/webserver/templates/nginx.conf.j2

user {{ nginx_user }}; worker_processes auto;
events { worker_connections 1024; }
http { include /etc/nginx/mime.types; default_type application/octet-stream;
server {
    listen {{ nginx_port }};
    server_name {{ ansible_hostname }};

    root {{ document_root }};
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }
}
}
user {{ nginx_user }}; worker_processes auto;
events { worker_connections 1024; }
http { include /etc/nginx/mime.types; default_type application/octet-stream;
server {
    listen {{ nginx_port }};
    server_name {{ ansible_hostname }};

    root {{ document_root }};
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }
}
}

Use role in playbook

在Playbook中使用角色


  • name: Configure web servers hosts: webservers become: yes roles:
    • role: webserver vars: nginx_port: 8080 document_root: /var/www/myapp
undefined

  • name: 配置Web服务器 hosts: webservers become: yes roles:
    • role: webserver vars: nginx_port: 8080 document_root: /var/www/myapp
undefined

Role Dependencies

角色依赖

yaml
undefined
yaml
undefined

roles/app/meta/main.yml

roles/app/meta/main.yml


dependencies:
  • role: common
  • role: nginx vars: nginx_port: 8080
  • role: postgresql when: database_enabled | default(false)
undefined

dependencies:
  • role: common
  • role: nginx vars: nginx_port: 8080
  • role: postgresql when: database_enabled | default(false)
undefined

Templates (Jinja2)

模板(Jinja2)

jinja2
{# templates/app.conf.j2 #}
jinja2
{# templates/app.conf.j2 #}

Application configuration for {{ app_name }}

{{ app_name }}应用配置

Generated by Ansible on {{ ansible_date_time.iso8601 }}

由Ansible于{{ ansible_date_time.iso8601 }}生成

[server] host = {{ ansible_default_ipv4.address }} port = {{ app_port }} workers = {{ ansible_processor_vcpus }}
[database] host = {{ db_host }} port = {{ db_port }} name = {{ db_name }} user = {{ db_user }} password = {{ db_password }}
[cache] enabled = {{ cache_enabled | default(true) | lower }} {% if cache_enabled | default(true) %} backend = redis redis_host = {{ redis_host }} redis_port = {{ redis_port }} {% endif %}
[features] {% for feature, enabled in features.items() %} {{ feature }} = {{ enabled | lower }} {% endfor %}
{% if environment == 'production' %} [production] debug = false log_level = warning {% else %} [development] debug = true log_level = debug {% endif %}
undefined
[server] host = {{ ansible_default_ipv4.address }} port = {{ app_port }} workers = {{ ansible_processor_vcpus }}
[database] host = {{ db_host }} port = {{ db_port }} name = {{ db_name }} user = {{ db_user }} password = {{ db_password }}
[cache] enabled = {{ cache_enabled | default(true) | lower }} {% if cache_enabled | default(true) %} backend = redis redis_host = {{ redis_host }} redis_port = {{ redis_port }} {% endif %}
[features] {% for feature, enabled in features.items() %} {{ feature }} = {{ enabled | lower }} {% endfor %}
{% if environment == 'production' %} [production] debug = false log_level = warning {% else %} [development] debug = true log_level = debug {% endif %}
undefined

Variables and Facts

变量与事实

Variable Precedence (low to high)

变量优先级(从低到高)

  1. Role defaults
  2. Inventory file/script group vars
  3. Inventory group_vars/all
  4. Playbook group_vars/all
  5. Inventory group_vars/*
  6. Playbook group_vars/*
  7. Inventory file/script host vars
  8. Inventory host_vars/*
  9. Playbook host_vars/*
  10. Host facts
  11. Play vars
  12. Play vars_prompt
  13. Play vars_files
  14. Role vars
  15. Block vars
  16. Task vars
  17. Extra vars (-e flag)
  1. 角色默认变量
  2. 清单文件/脚本组变量
  3. 清单group_vars/all
  4. Playbook group_vars/all
  5. 清单group_vars/*
  6. Playbook group_vars/*
  7. 清单文件/脚本主机变量
  8. 清单host_vars/*
  9. Playbook host_vars/*
  10. 主机事实
  11. Play变量
  12. Play vars_prompt
  13. Play vars_files
  14. 角色变量
  15. 块变量
  16. 任务变量
  17. 额外变量(-e参数)

Using Variables

变量使用示例

yaml
---
- name: Variable examples
  hosts: all
  vars:
    app_name: myapp
    app_version: 1.0.0
  vars_files:
    - vars/common.yml
    - "vars/{{ environment }}.yml"

  tasks:
    - name: Load variables from file
      include_vars:
        file: "vars/{{ ansible_distribution }}.yml"

    - name: Set fact
      set_fact:
        full_app_name: "{{ app_name }}-{{ app_version }}"

    - name: Register output
      command: hostname
      register: hostname_output

    - name: Use registered variable
      debug:
        msg: "Hostname is {{ hostname_output.stdout }}"

    - name: Access facts
      debug:
        msg: |
          OS: {{ ansible_distribution }} {{ ansible_distribution_version }}
          Kernel: {{ ansible_kernel }}
          CPU: {{ ansible_processor_vcpus }} cores
          Memory: {{ ansible_memtotal_mb }} MB
          IP: {{ ansible_default_ipv4.address }}
yaml
---
- name: 变量使用示例
  hosts: all
  vars:
    app_name: myapp
    app_version: 1.0.0
  vars_files:
    - vars/common.yml
    - "vars/{{ environment }}.yml"

  tasks:
    - name: 从文件加载变量
      include_vars:
        file: "vars/{{ ansible_distribution }}.yml"

    - name: 设置事实变量
      set_fact:
        full_app_name: "{{ app_name }}-{{ app_version }}"

    - name: 注册命令输出
      command: hostname
      register: hostname_output

    - name: 使用注册的变量
      debug:
        msg: "主机名是{{ hostname_output.stdout }}"

    - name: 访问主机事实
      debug:
        msg: |
          操作系统: {{ ansible_distribution }} {{ ansible_distribution_version }}
          内核: {{ ansible_kernel }}
          CPU: {{ ansible_processor_vcpus }}核
          内存: {{ ansible_memtotal_mb }} MB
          IP地址: {{ ansible_default_ipv4.address }}

Error Handling

错误处理

yaml
---
- name: Error handling examples
  hosts: all
  tasks:
    - name: Task that might fail
      command: /bin/false
      ignore_errors: yes

    - name: Task with custom error handling
      block:
        - name: Try to start service
          systemd:
            name: myapp
            state: started
      rescue:
        - name: Log error
          debug:
            msg: "Failed to start myapp"

        - name: Try alternative
          systemd:
            name: myapp-fallback
            state: started
      always:
        - name: This always runs
          debug:
            msg: "Cleanup task"

    - name: Assert condition
      assert:
        that:
          - ansible_memtotal_mb >= 2048
          - ansible_processor_vcpus >= 2
        fail_msg: "Server does not meet minimum requirements"

    - name: Fail when condition
      fail:
        msg: "Production deployment requires version tag"
      when:
        - environment == 'production'
        - app_version == 'latest'

    - name: Changed when condition
      command: /usr/local/bin/check_status.sh
      register: result
      changed_when: "'updated' in result.stdout"
      failed_when: result.rc not in [0, 2]
yaml
---
- name: 错误处理示例
  hosts: all
  tasks:
    - name: 可能失败的任务
      command: /bin/false
      ignore_errors: yes

    - name: 自定义错误处理任务
      block:
        - name: 尝试启动服务
          systemd:
            name: myapp
            state: started
      rescue:
        - name: 记录错误信息
          debug:
            msg: "启动myapp失败"

        - name: 尝试备选方案
          systemd:
            name: myapp-fallback
            state: started
      always:
        - name: 始终执行的任务
          debug:
            msg: "清理任务"

    - name: 断言条件
      assert:
        that:
          - ansible_memtotal_mb >= 2048
          - ansible_processor_vcpus >= 2
        fail_msg: "服务器不满足最低要求"

    - name: 满足条件时触发失败
      fail:
        msg: "生产环境部署需要版本标签"
      when:
        - environment == 'production'
        - app_version == 'latest'

    - name: 自定义变更条件
      command: /usr/local/bin/check_status.sh
      register: result
      changed_when: "'updated' in result.stdout"
      failed_when: result.rc not in [0, 2]

Ansible Vault

Ansible Vault

bash
undefined
bash
undefined

Create encrypted file

创建加密文件

ansible-vault create secrets.yml
ansible-vault create secrets.yml

Edit encrypted file

编辑加密文件

ansible-vault edit secrets.yml
ansible-vault edit secrets.yml

Encrypt existing file

加密现有文件

ansible-vault encrypt vars/production.yml
ansible-vault encrypt vars/production.yml

Decrypt file

解密文件

ansible-vault decrypt vars/production.yml
ansible-vault decrypt vars/production.yml

View encrypted file

查看加密文件

ansible-vault view secrets.yml
ansible-vault view secrets.yml

Rekey (change password)

重新加密(修改密码)

ansible-vault rekey secrets.yml

```yaml
ansible-vault rekey secrets.yml

```yaml

secrets.yml (encrypted)

secrets.yml(已加密)


db_password: supersecret api_key: abc123xyz ssl_key: | -----BEGIN PRIVATE KEY----- ... -----END PRIVATE KEY-----

db_password: supersecret api_key: abc123xyz ssl_key: | -----BEGIN PRIVATE KEY----- ... -----END PRIVATE KEY-----

Use in playbook

在Playbook中使用


  • name: Deploy with secrets hosts: production vars_files:
    • secrets.yml tasks:
    • name: Configure database template: src: db.conf.j2 dest: /etc/db.conf no_log: yes # Don't log sensitive data

```bash

  • name: 使用敏感信息部署 hosts: production vars_files:
    • secrets.yml tasks:
    • name: 配置数据库 template: src: db.conf.j2 dest: /etc/db.conf no_log: yes # 不记录敏感数据

```bash

Run playbook with vault password

使用Vault密码运行Playbook

ansible-playbook playbook.yml --ask-vault-pass
ansible-playbook playbook.yml --ask-vault-pass

Use password file

使用密码文件

ansible-playbook playbook.yml --vault-password-file ~/.vault_pass
ansible-playbook playbook.yml --vault-password-file ~/.vault_pass

Use multiple vault IDs

使用多个Vault ID

ansible-playbook playbook.yml --vault-id prod@prompt --vault-id dev@~/.vault_dev
undefined
ansible-playbook playbook.yml --vault-id prod@prompt --vault-id dev@~/.vault_dev
undefined

Best Practices

最佳实践

Playbook Organization

Playbook组织

ansible-project/
├── ansible.cfg
├── inventory/
│   ├── production/
│   │   ├── hosts.yml
│   │   └── group_vars/
│   └── staging/
│       ├── hosts.yml
│       └── group_vars/
├── playbooks/
│   ├── site.yml
│   ├── webservers.yml
│   └── databases.yml
├── roles/
│   ├── common/
│   ├── nginx/
│   └── postgresql/
├── group_vars/
│   ├── all.yml
│   └── webservers.yml
├── host_vars/
└── files/
ansible-project/
├── ansible.cfg
├── inventory/
│   ├── production/
│   │   ├── hosts.yml
│   │   └── group_vars/
│   └── staging/
│       ├── hosts.yml
│       └── group_vars/
├── playbooks/
│   ├── site.yml
│   ├── webservers.yml
│   └── databases.yml
├── roles/
│   ├── common/
│   ├── nginx/
│   └── postgresql/
├── group_vars/
│   ├── all.yml
│   └── webservers.yml
├── host_vars/
└── files/

Idempotency

幂等性

yaml
undefined
yaml
undefined

❌ Not idempotent

❌ 非幂等

  • name: Add line to file shell: echo "server {{ ansible_hostname }}" >> /etc/hosts
  • name: 向文件添加行 shell: echo "server {{ ansible_hostname }}" >> /etc/hosts

✅ Idempotent

✅ 幂等

  • name: Add line to file lineinfile: path: /etc/hosts line: "server {{ ansible_hostname }}" state: present
undefined
  • name: 向文件添加行 lineinfile: path: /etc/hosts line: "server {{ ansible_hostname }}" state: present
undefined

Performance

性能优化

  • Use
    strategy: free
    for faster execution
  • Enable pipelining in ansible.cfg
  • Use
    async
    for long-running tasks
  • Disable fact gathering when not needed
  • Use
    serial
    for rolling updates
yaml
undefined
  • 使用
    strategy: free
    提升执行速度
  • 在ansible.cfg中启用管道传输
  • 对长时间运行的任务使用
    async
  • 不需要时禁用事实收集
  • 使用
    serial
    实现滚动更新
yaml
undefined

ansible.cfg

ansible.cfg

[defaults] forks = 20 host_key_checking = False pipelining = True gathering = smart fact_caching = jsonfile fact_caching_connection = /tmp/ansible_facts fact_caching_timeout = 86400
[defaults] forks = 20 host_key_checking = False pipelining = True gathering = smart fact_caching = jsonfile fact_caching_connection = /tmp/ansible_facts fact_caching_timeout = 86400

Playbook with performance optimizations

带性能优化的Playbook


  • name: Fast deployment hosts: webservers strategy: free gather_facts: no serial: 5 # Deploy to 5 hosts at a time
    tasks:
    • name: Long running task command: /usr/local/bin/build.sh async: 3600 poll: 0 register: build_job
    • name: Check build status async_status: jid: "{{ build_job.ansible_job_id }}" register: job_result until: job_result.finished retries: 60 delay: 30
undefined

  • name: 快速部署 hosts: webservers strategy: free gather_facts: no serial: 5 # 一次部署5台主机
    tasks:
    • name: 长时间运行的任务 command: /usr/local/bin/build.sh async: 3600 poll: 0 register: build_job
    • name: 检查构建状态 async_status: jid: "{{ build_job.ansible_job_id }}" register: job_result until: job_result.finished retries: 60 delay: 30
undefined

Security

安全

  • Use Ansible Vault for secrets
  • Use
    no_log: yes
    for sensitive tasks
  • Set proper file permissions
  • Use
    become
    sparingly
  • Validate SSL certificates
  • Use SSH keys, not passwords
  • 使用Ansible Vault存储敏感信息
  • 对敏感任务使用
    no_log: yes
  • 设置正确的文件权限
  • 谨慎使用
    become
  • 验证SSL证书
  • 使用SSH密钥而非密码

Testing

测试

Molecule (Role Testing)

Molecule(角色测试)

bash
undefined
bash
undefined

Install molecule

安装molecule

pip install molecule molecule-docker
pip install molecule molecule-docker

Initialize molecule

初始化molecule

cd roles/myapp molecule init scenario
cd roles/myapp molecule init scenario

Run tests

运行测试

molecule test
molecule test

Test workflow

测试流程

molecule create # Create test instances molecule converge # Run playbook molecule verify # Run tests molecule destroy # Cleanup

```yaml
molecule create # 创建测试实例 molecule converge # 运行Playbook molecule verify # 执行测试 molecule destroy # 清理资源

```yaml

molecule/default/molecule.yml

molecule/default/molecule.yml


dependency: name: galaxy driver: name: docker platforms:
  • name: ubuntu image: geerlingguy/docker-ubuntu2004-ansible pre_build_image: yes provisioner: name: ansible verifier: name: ansible
undefined

dependency: name: galaxy driver: name: docker platforms:
  • name: ubuntu image: geerlingguy/docker-ubuntu2004-ansible pre_build_image: yes provisioner: name: ansible verifier: name: ansible
undefined

Anti-Patterns to Avoid

需避免的反模式

Not using roles: Organize code in reusable roles ❌ Shell commands everywhere: Use modules when available ❌ Hardcoded values: Use variables ❌ No error handling: Use blocks, rescue, always ❌ Storing secrets in plaintext: Use Ansible Vault ❌ Not testing: Use molecule for role testing ❌ Ignoring idempotency: Tasks should be safe to run multiple times ❌ Complex playbooks: Break into smaller, focused playbooks
不使用角色:将代码组织为可复用的角色 ❌ 过度使用Shell命令:尽可能使用模块 ❌ 硬编码值:使用变量替代 ❌ 无错误处理:使用block、rescue、always ❌ 明文存储敏感信息:使用Ansible Vault ❌ 不进行测试:使用Molecule进行角色测试 ❌ 忽略幂等性:任务应可安全多次执行 ❌ 复杂Playbook:拆分为更小、聚焦的Playbook

Resources

资源