ansible-expert
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseAnsible 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
undefinedbash
undefinedUsing 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
undefinedansible --version
undefinedInventory
清单
Basic Inventory (INI format)
基础清单(INI格式)
ini
undefinedini
undefinedinventory/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
undefinedYAML Inventory
YAML格式清单
yaml
undefinedyaml
undefinedinventory/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
undefinedall:
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
undefinedDynamic Inventory
动态清单
python
#!/usr/bin/env python3python
#!/usr/bin/env python3inventory/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 inventoryif name == 'main':
print(json.dumps(get_inventory(), indent=2))
undefinedimport 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 inventoryif name == 'main':
print(json.dumps(get_inventory(), indent=2))
undefinedPlaybooks
Playbook
Basic Playbook
基础Playbook
yaml
undefinedyaml
undefinedplaybooks/webserver.yml
playbooks/webserver.yml
-
name: Configure web servers hosts: webservers become: yes vars: app_port: 8080 app_user: webapptasks:
-
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: webapptasks:
-
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
-
undefinedAdvanced Playbook
高级Playbook
yaml
undefinedyaml
undefinedplaybooks/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: deployerpre_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: deployerpre_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
undefinedConditionals 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 variablesroles/
└── 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
undefinedyaml
undefinedroles/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
undefinedRole Dependencies
角色依赖
yaml
undefinedyaml
undefinedroles/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)
undefineddependencies:
- role: common
- role: nginx vars: nginx_port: 8080
- role: postgresql when: database_enabled | default(false)
undefinedTemplates (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 %}
undefinedVariables and Facts
变量与事实
Variable Precedence (low to high)
变量优先级(从低到高)
- Role defaults
- Inventory file/script group vars
- Inventory group_vars/all
- Playbook group_vars/all
- Inventory group_vars/*
- Playbook group_vars/*
- Inventory file/script host vars
- Inventory host_vars/*
- Playbook host_vars/*
- Host facts
- Play vars
- Play vars_prompt
- Play vars_files
- Role vars
- Block vars
- Task vars
- Extra vars (-e flag)
- 角色默认变量
- 清单文件/脚本组变量
- 清单group_vars/all
- Playbook group_vars/all
- 清单group_vars/*
- Playbook group_vars/*
- 清单文件/脚本主机变量
- 清单host_vars/*
- Playbook host_vars/*
- 主机事实
- Play变量
- Play vars_prompt
- Play vars_files
- 角色变量
- 块变量
- 任务变量
- 额外变量(-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
undefinedbash
undefinedCreate 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
```yamlansible-vault rekey secrets.yml
```yamlsecrets.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 # 不记录敏感数据
```bashRun 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
undefinedansible-playbook playbook.yml --vault-id prod@prompt --vault-id dev@~/.vault_dev
undefinedBest 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
undefinedyaml
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
undefinedPerformance
性能优化
- Use for faster execution
strategy: free - Enable pipelining in ansible.cfg
- Use for long-running tasks
async - Disable fact gathering when not needed
- Use for rolling updates
serial
yaml
undefined- 使用提升执行速度
strategy: free - 在ansible.cfg中启用管道传输
- 对长时间运行的任务使用
async - 不需要时禁用事实收集
- 使用实现滚动更新
serial
yaml
undefinedansible.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 timetasks:
-
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
-
undefinedSecurity
安全
- Use Ansible Vault for secrets
- Use for sensitive tasks
no_log: yes - Set proper file permissions
- Use sparingly
become - Validate SSL certificates
- Use SSH keys, not passwords
- 使用Ansible Vault存储敏感信息
- 对敏感任务使用
no_log: yes - 设置正确的文件权限
- 谨慎使用
become - 验证SSL证书
- 使用SSH密钥而非密码
Testing
测试
Molecule (Role Testing)
Molecule(角色测试)
bash
undefinedbash
undefinedInstall 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
```yamlmolecule create # 创建测试实例
molecule converge # 运行Playbook
molecule verify # 执行测试
molecule destroy # 清理资源
```yamlmolecule/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
undefineddependency:
name: galaxy
driver:
name: docker
platforms:
- name: ubuntu image: geerlingguy/docker-ubuntu2004-ansible pre_build_image: yes provisioner: name: ansible verifier: name: ansible
undefinedAnti-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
资源
- Ansible Documentation: https://docs.ansible.com/
- Ansible Galaxy: https://galaxy.ansible.com/
- Molecule: https://molecule.readthedocs.io/
- Best Practices: https://docs.ansible.com/ansible/latest/user_guide/playbooks_best_practices.html
- Ansible官方文档:https://docs.ansible.com/
- Ansible Galaxy:https://galaxy.ansible.com/
- Molecule文档:https://molecule.readthedocs.io/
- 最佳实践:https://docs.ansible.com/ansible/latest/user_guide/playbooks_best_practices.html