6.1 变量系统概述
6.1.1 变量的重要性
Ansible 的变量系统是其灵活性和可重用性的核心。变量允许你: - 在不同环境间重用相同的 Playbook - 动态配置系统参数 - 存储和传递数据 - 实现条件逻辑 - 简化复杂配置
6.1.2 变量类型
# variable-types.yml
---
- name: 变量类型示例
hosts: localhost
vars:
# 字符串变量
app_name: "myapp"
version: "1.2.3"
# 数字变量
port: 8080
timeout: 30
max_connections: 1000
# 布尔变量
debug_mode: true
ssl_enabled: false
# 列表变量
packages:
- nginx
- python3
- git
# 字典变量
database:
host: localhost
port: 3306
name: myapp
user: app_user
# 复杂嵌套结构
environments:
development:
debug: true
database:
host: dev-db.example.com
pool_size: 5
cache:
enabled: false
production:
debug: false
database:
host: prod-db.example.com
pool_size: 20
cache:
enabled: true
ttl: 3600
# 多行字符串
config_template: |
server {
listen {{ port }};
server_name {{ app_name }}.example.com;
root /var/www/{{ app_name }};
}
# 折叠字符串
description: >
这是一个很长的描述文本,
会被折叠成一行,
适合用于配置文件注释。
tasks:
- name: 显示不同类型的变量
debug:
msg: |
应用名称: {{ app_name }}
端口: {{ port }}
调试模式: {{ debug_mode }}
包列表: {{ packages | join(', ') }}
数据库: {{ database.host }}:{{ database.port }}
生产环境调试: {{ environments.production.debug }}
6.2 变量定义和作用域
6.2.1 变量优先级
Ansible 变量有明确的优先级顺序(从高到低):
- 命令行变量 (
-e
或--extra-vars
) - 任务变量 (
vars
在任务中) - 块变量 (
vars
在块中) - 角色和包含变量
- Play 变量 (
vars
在 play 中) - 主机变量 (
host_vars
) - 组变量 (
group_vars
) - Inventory 变量
- 角色默认变量 (
defaults
)
# variable-precedence.yml
---
- name: 变量优先级示例
hosts: all
# Play 级别变量 (优先级: 5)
vars:
message: "来自 Play 变量"
environment: "development"
# 变量文件 (优先级: 5)
vars_files:
- vars/common.yml
tasks:
# 任务级别变量 (优先级: 2)
- name: 显示消息(任务变量)
debug:
msg: "{{ message }}"
vars:
message: "来自任务变量"
# 块级别变量 (优先级: 3)
- name: 块变量示例
block:
- name: 显示块变量
debug:
msg: "环境: {{ environment }}, 调试: {{ debug_mode }}"
vars:
environment: "production"
debug_mode: false
# 包含任务变量 (优先级: 4)
- name: 包含任务
include_tasks: tasks/show-vars.yml
vars:
included_var: "包含任务变量"
# 注册变量
- name: 设置事实变量
set_fact:
custom_fact: "自定义事实"
calculated_value: "{{ ansible_memtotal_mb | int // 1024 }}GB"
- name: 显示事实变量
debug:
msg: "{{ custom_fact }}, 内存: {{ calculated_value }}"
6.2.2 变量作用域
# variable-scope.yml
---
- name: 变量作用域示例
hosts: all
vars:
global_var: "全局变量"
shared_var: "Play 级别"
tasks:
# 主机级别变量
- name: 设置主机变量
set_fact:
host_specific_var: "主机 {{ inventory_hostname }} 的变量"
shared_var: "主机级别覆盖"
# 任务组变量
- name: 任务组 1
block:
- name: 显示块内变量
debug:
msg: |
全局变量: {{ global_var }}
共享变量: {{ shared_var }}
块变量: {{ block_var }}
主机变量: {{ host_specific_var }}
- name: 修改块变量
set_fact:
block_var: "修改后的块变量"
vars:
block_var: "块级别变量"
- name: 块外访问变量
debug:
msg: |
全局变量: {{ global_var }}
共享变量: {{ shared_var }}
块变量: {{ block_var | default('未定义') }}
主机变量: {{ host_specific_var }}
# 循环变量作用域
- name: 循环变量示例
debug:
msg: "处理 {{ item.name }}, 值: {{ item.value }}"
loop:
- { name: "var1", value: "value1" }
- { name: "var2", value: "value2" }
loop_control:
loop_var: current_item
# 包含文件变量作用域
- name: 包含任务文件
include_tasks: tasks/scoped-tasks.yml
vars:
include_var: "包含文件变量"
# tasks/scoped-tasks.yml
---
- name: 显示包含文件中的变量
debug:
msg: |
全局变量: {{ global_var }}
包含变量: {{ include_var }}
主机变量: {{ host_specific_var }}
- name: 设置包含文件变量
set_fact:
include_file_var: "在包含文件中设置的变量"
6.3 变量文件管理
6.3.1 组织变量文件
# 推荐的变量文件结构
inventory/
├── group_vars/
│ ├── all/
│ │ ├── common.yml # 所有主机的通用变量
│ │ ├── secrets.yml # 加密的敏感信息
│ │ └── versions.yml # 软件版本信息
│ ├── webservers/
│ │ ├── main.yml # Web 服务器组变量
│ │ └── nginx.yml # Nginx 特定配置
│ ├── databases/
│ │ ├── main.yml # 数据库组变量
│ │ └── mysql.yml # MySQL 特定配置
│ └── production.yml # 生产环境组变量
├── host_vars/
│ ├── web1.example.com.yml # 特定主机变量
│ ├── web2.example.com.yml
│ └── db1.example.com.yml
└── vars/
├── common.yml # 通用变量
├── development.yml # 开发环境变量
├── staging.yml # 测试环境变量
└── production.yml # 生产环境变量
# group_vars/all/common.yml
---
# 应用程序配置
app_name: myapp
app_user: appuser
app_group: appgroup
app_home: "/opt/{{ app_name }}"
# 系统配置
system_timezone: "Asia/Shanghai"
system_locale: "zh_CN.UTF-8"
# 网络配置
default_ports:
http: 80
https: 443
ssh: 22
# 日志配置
log_level: info
log_rotation:
size: "100M"
count: 10
compress: true
# 备份配置
backup_retention_days: 30
backup_schedule: "0 2 * * *" # 每天凌晨2点
# group_vars/webservers/main.yml
---
# Web 服务器特定配置
web_server: nginx
web_port: "{{ default_ports.http }}"
ssl_port: "{{ default_ports.https }}"
# Nginx 配置
nginx_worker_processes: "{{ ansible_processor_vcpus }}"
nginx_worker_connections: 1024
nginx_keepalive_timeout: 65
# SSL 配置
ssl_enabled: true
ssl_cert_path: "/etc/ssl/certs/{{ app_name }}.crt"
ssl_key_path: "/etc/ssl/private/{{ app_name }}.key"
# 虚拟主机配置
vhosts:
- name: "{{ app_name }}.example.com"
root: "{{ app_home }}/public"
index: "index.html index.php"
ssl: "{{ ssl_enabled }}"
- name: "api.{{ app_name }}.example.com"
root: "{{ app_home }}/api/public"
index: "index.php"
ssl: "{{ ssl_enabled }}"
upstream: "api_backend"
# 上游服务器配置
upstreams:
api_backend:
- server: "127.0.0.1:8080"
weight: 1
- server: "127.0.0.1:8081"
weight: 1
# group_vars/databases/main.yml
---
# 数据库服务器配置
db_engine: mysql
db_version: "8.0"
db_port: 3306
db_root_password: "{{ vault_db_root_password }}"
# MySQL 配置
mysql_config:
innodb_buffer_pool_size: "{{ (ansible_memtotal_mb * 0.7) | int }}M"
max_connections: 200
query_cache_size: "64M"
slow_query_log: 1
long_query_time: 2
# 数据库和用户
databases:
- name: "{{ app_name }}"
encoding: utf8mb4
collation: utf8mb4_unicode_ci
- name: "{{ app_name }}_test"
encoding: utf8mb4
collation: utf8mb4_unicode_ci
db_users:
- name: "{{ app_name }}_user"
password: "{{ vault_app_db_password }}"
priv: "{{ app_name }}.*:ALL"
host: "%"
- name: "{{ app_name }}_readonly"
password: "{{ vault_readonly_db_password }}"
priv: "{{ app_name }}.*:SELECT"
host: "%"
# 备份配置
db_backup_dir: "/backup/mysql"
db_backup_script: "/usr/local/bin/mysql-backup.sh"
# host_vars/web1.example.com.yml
---
# 主机特定配置
server_id: 1
server_role: primary
# 网络配置
network_interfaces:
- name: eth0
ip: 192.168.1.10
netmask: 255.255.255.0
gateway: 192.168.1.1
- name: eth1
ip: 10.0.1.10
netmask: 255.255.255.0
# 内部网络,无网关
# 存储配置
storage_mounts:
- device: /dev/sdb1
path: /var/www
fstype: ext4
opts: defaults,noatime
- device: /dev/sdc1
path: /var/log
fstype: ext4
opts: defaults,noatime
# 性能调优
performance_tuning:
vm_swappiness: 10
net_core_somaxconn: 65535
fs_file_max: 100000
6.3.2 环境特定变量
# vars/development.yml
---
environment: development
debug_mode: true
log_level: debug
# 开发环境数据库
database:
host: dev-db.example.com
port: 3306
name: "{{ app_name }}_dev"
user: dev_user
password: dev_password
pool_size: 5
timeout: 30
# 缓存配置
cache:
enabled: false
type: memory
ttl: 300
# 外部服务
external_services:
api_url: "https://api-dev.example.com"
cdn_url: "https://cdn-dev.example.com"
payment_gateway: "sandbox"
# 开发工具
dev_tools:
- xdebug
- composer
- nodejs
- npm
# 邮件配置
mail:
driver: log
host: localhost
port: 1025
encryption: null
# vars/production.yml
---
environment: production
debug_mode: false
log_level: warning
# 生产环境数据库
database:
host: prod-db.example.com
port: 3306
name: "{{ app_name }}"
user: "{{ vault_prod_db_user }}"
password: "{{ vault_prod_db_password }}"
pool_size: 20
timeout: 10
ssl_mode: required
# 缓存配置
cache:
enabled: true
type: redis
host: prod-redis.example.com
port: 6379
password: "{{ vault_redis_password }}"
ttl: 3600
cluster: true
# 外部服务
external_services:
api_url: "https://api.example.com"
cdn_url: "https://cdn.example.com"
payment_gateway: "production"
# 性能配置
performance:
opcache_enabled: true
opcache_memory: 256
max_execution_time: 30
memory_limit: "512M"
# 安全配置
security:
ssl_only: true
hsts_enabled: true
csrf_protection: true
rate_limiting: true
# 监控配置
monitoring:
enabled: true
metrics_endpoint: "/metrics"
health_endpoint: "/health"
log_aggregation: true
# 邮件配置
mail:
driver: smtp
host: smtp.example.com
port: 587
encryption: tls
username: "{{ vault_mail_username }}"
password: "{{ vault_mail_password }}"
6.4 变量过滤器和测试
6.4.1 常用过滤器
# filters.yml
---
- name: 变量过滤器示例
hosts: localhost
vars:
sample_string: " Hello World "
sample_list: ["apple", "banana", "cherry", "apple"]
sample_dict:
name: "John Doe"
age: 30
email: "john@example.com"
sample_number: 42.7
sample_json: '{"name": "Alice", "age": 25}'
sample_yaml: |
name: Bob
age: 35
tasks:
# 字符串过滤器
- name: 字符串过滤器
debug:
msg: |
原始字符串: '{{ sample_string }}'
去除空格: '{{ sample_string | trim }}'
转大写: '{{ sample_string | upper }}'
转小写: '{{ sample_string | lower }}'
首字母大写: '{{ sample_string | title }}'
替换: '{{ sample_string | replace("World", "Ansible") }}'
长度: {{ sample_string | length }}
反转: '{{ sample_string | reverse }}'
# 列表过滤器
- name: 列表过滤器
debug:
msg: |
原始列表: {{ sample_list }}
排序: {{ sample_list | sort }}
反转: {{ sample_list | reverse }}
去重: {{ sample_list | unique }}
长度: {{ sample_list | length }}
第一个: {{ sample_list | first }}
最后一个: {{ sample_list | last }}
随机: {{ sample_list | random }}
连接: {{ sample_list | join(', ') }}
选择包含'a'的: {{ sample_list | select('match', '.*a.*') | list }}
# 字典过滤器
- name: 字典过滤器
debug:
msg: |
原始字典: {{ sample_dict }}
键列表: {{ sample_dict | list }}
值列表: {{ sample_dict | dict2items | map(attribute='value') | list }}
键值对: {{ sample_dict | dict2items }}
提取名称: {{ sample_dict | json_query('name') }}
# 数字过滤器
- name: 数字过滤器
debug:
msg: |
原始数字: {{ sample_number }}
绝对值: {{ sample_number | abs }}
向上取整: {{ sample_number | round(0, 'ceil') }}
向下取整: {{ sample_number | round(0, 'floor') }}
四舍五入: {{ sample_number | round(1) }}
转整数: {{ sample_number | int }}
转字符串: '{{ sample_number | string }}'
# 格式化过滤器
- name: 格式化过滤器
debug:
msg: |
JSON 解析: {{ sample_json | from_json }}
YAML 解析: {{ sample_yaml | from_yaml }}
转 JSON: {{ sample_dict | to_json }}
美化 JSON: {{ sample_dict | to_nice_json }}
转 YAML: {{ sample_dict | to_nice_yaml }}
Base64 编码: {{ sample_string | b64encode }}
Base64 解码: {{ (sample_string | b64encode) | b64decode }}
# 日期时间过滤器
- name: 日期时间过滤器
debug:
msg: |
当前时间戳: {{ ansible_date_time.epoch }}
格式化日期: {{ ansible_date_time.epoch | int | strftime('%Y-%m-%d %H:%M:%S') }}
ISO 格式: {{ ansible_date_time.iso8601 }}
相对时间: {{ (ansible_date_time.epoch | int - 3600) | strftime('%Y-%m-%d %H:%M:%S') }}
# 路径过滤器
- name: 路径过滤器
vars:
file_path: "/home/user/documents/file.txt"
debug:
msg: |
完整路径: {{ file_path }}
目录名: {{ file_path | dirname }}
文件名: {{ file_path | basename }}
扩展名: {{ file_path | splitext | last }}
无扩展名: {{ file_path | splitext | first }}
# 网络过滤器
- name: 网络过滤器
vars:
ip_address: "192.168.1.100"
cidr: "192.168.1.0/24"
debug:
msg: |
IP 地址: {{ ip_address }}
是否私有: {{ ip_address | ipaddr('private') }}
网络地址: {{ cidr | ipaddr('network') }}
广播地址: {{ cidr | ipaddr('broadcast') }}
主机数量: {{ cidr | ipaddr('size') }}
6.4.2 自定义过滤器
# filter_plugins/custom_filters.py
#!/usr/bin/env python3
class FilterModule(object):
"""自定义过滤器模块"""
def filters(self):
return {
'reverse_string': self.reverse_string,
'calculate_percentage': self.calculate_percentage,
'format_bytes': self.format_bytes,
'extract_domain': self.extract_domain,
'generate_password': self.generate_password,
'mask_sensitive': self.mask_sensitive,
}
def reverse_string(self, string):
"""反转字符串"""
return string[::-1]
def calculate_percentage(self, value, total):
"""计算百分比"""
if total == 0:
return 0
return round((value / total) * 100, 2)
def format_bytes(self, bytes_value):
"""格式化字节数"""
for unit in ['B', 'KB', 'MB', 'GB', 'TB']:
if bytes_value < 1024.0:
return f"{bytes_value:.1f} {unit}"
bytes_value /= 1024.0
return f"{bytes_value:.1f} PB"
def extract_domain(self, email):
"""从邮箱地址提取域名"""
if '@' in email:
return email.split('@')[1]
return ''
def generate_password(self, length=12):
"""生成随机密码"""
import random
import string
characters = string.ascii_letters + string.digits + '!@#$%^&*'
return ''.join(random.choice(characters) for _ in range(length))
def mask_sensitive(self, value, mask_char='*', visible_chars=4):
"""遮蔽敏感信息"""
if len(value) <= visible_chars:
return mask_char * len(value)
visible_start = visible_chars // 2
visible_end = visible_chars - visible_start
masked_length = len(value) - visible_chars
masked_part = mask_char * masked_length
return value[:visible_start] + masked_part + value[-visible_end:]
# custom-filters.yml
---
- name: 自定义过滤器示例
hosts: localhost
vars:
test_string: "Hello Ansible"
disk_usage: 1536
total_disk: 2048
file_size_bytes: 1073741824
user_email: "admin@example.com"
secret_key: "abc123def456ghi789"
tasks:
- name: 使用自定义过滤器
debug:
msg: |
反转字符串: {{ test_string | reverse_string }}
磁盘使用率: {{ disk_usage | calculate_percentage(total_disk) }}%
文件大小: {{ file_size_bytes | format_bytes }}
邮箱域名: {{ user_email | extract_domain }}
随机密码: {{ '' | generate_password(16) }}
遮蔽密钥: {{ secret_key | mask_sensitive }}
6.4.3 变量测试
# variable-tests.yml
---
- name: 变量测试示例
hosts: localhost
vars:
test_string: "hello"
test_number: 42
test_list: [1, 2, 3]
test_dict: {"key": "value"}
test_bool: true
test_none: null
test_file: "/etc/passwd"
test_url: "https://example.com"
test_email: "user@example.com"
test_ip: "192.168.1.1"
tasks:
# 基本类型测试
- name: 基本类型测试
debug:
msg: |
是否定义: {{ test_string is defined }}
是否未定义: {{ undefined_var is undefined }}
是否为空: {{ test_none is none }}
是否不为空: {{ test_string is not none }}
是否为字符串: {{ test_string is string }}
是否为数字: {{ test_number is number }}
是否为列表: {{ test_list is sequence }}
是否为字典: {{ test_dict is mapping }}
是否为布尔: {{ test_bool is boolean }}
# 数字测试
- name: 数字测试
debug:
msg: |
是否为偶数: {{ test_number is even }}
是否为奇数: {{ test_number is odd }}
是否可被3整除: {{ test_number is divisibleby(3) }}
是否大于40: {{ test_number is gt(40) }}
是否小于等于50: {{ test_number is le(50) }}
# 字符串测试
- name: 字符串测试
debug:
msg: |
是否全小写: {{ test_string is lower }}
是否全大写: {{ test_string is upper }}
是否匹配正则: {{ test_email is match('.*@.*\..*') }}
是否包含子串: {{ test_string is search('ell') }}
# 文件和路径测试
- name: 文件测试
debug:
msg: |
是否为绝对路径: {{ test_file is abs }}
文件是否存在: {{ test_file is file }}
是否为目录: {{ '/etc' is directory }}
是否为链接: {{ '/usr/bin/python' is link }}
ignore_errors: yes
# 网络测试
- name: 网络测试
debug:
msg: |
是否为有效URL: {{ test_url is url }}
是否为IP地址: {{ test_ip is ip }}
是否为IPv4: {{ test_ip is ipv4 }}
是否为私有IP: {{ test_ip is ip_private }}
# 版本比较测试
- name: 版本比较测试
vars:
version1: "1.2.3"
version2: "1.2.4"
debug:
msg: |
版本1 < 版本2: {{ version1 is version(version2, '<') }}
版本1 >= 版本2: {{ version1 is version(version2, '>=') }}
版本匹配: {{ version1 is version('1.2.*', 'match') }}
# 集合测试
- name: 集合测试
vars:
subset: [1, 2]
superset: [1, 2, 3, 4]
debug:
msg: |
是否为子集: {{ subset is subset(superset) }}
是否为超集: {{ superset is superset(subset) }}
是否包含元素: {{ 2 in subset }}
# 条件组合测试
- name: 条件组合测试
debug:
msg: "满足复杂条件"
when:
- test_number is defined
- test_number is number
- test_number is gt(40)
- test_string is defined
- test_string is not none
- test_list is sequence
- test_list | length > 0
6.5 Jinja2 模板系统
6.5.1 模板基础语法
{# templates/nginx.conf.j2 #}
# Nginx 配置文件
# 生成时间: {{ ansible_date_time.iso8601 }}
# 管理主机: {{ ansible_hostname }}
# 用户和组
user {{ nginx_user | default('www-data') }};
# 工作进程数
worker_processes {{ nginx_worker_processes | default(ansible_processor_vcpus) }};
# 错误日志
error_log {{ nginx_error_log | default('/var/log/nginx/error.log') }} {{ log_level | default('warn') }};
# PID 文件
pid {{ nginx_pid_file | default('/var/run/nginx.pid') }};
events {
# 每个工作进程的最大连接数
worker_connections {{ nginx_worker_connections | default(1024) }};
# 使用 epoll 事件模型
{% if ansible_kernel is version('2.6', '>=') %}
use epoll;
{% endif %}
# 允许一个工作进程同时接受多个连接
multi_accept on;
}
http {
# 基本设置
include /etc/nginx/mime.types;
default_type application/octet-stream;
# 日志格式
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
# 访问日志
access_log {{ nginx_access_log | default('/var/log/nginx/access.log') }} main;
# 性能优化
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout {{ nginx_keepalive_timeout | default(65) }};
types_hash_max_size 2048;
# 隐藏版本信息
server_tokens off;
# Gzip 压缩
{% if nginx_gzip_enabled | default(true) %}
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level {{ nginx_gzip_level | default(6) }};
gzip_types
text/plain
text/css
text/xml
text/javascript
application/json
application/javascript
application/xml+rss
application/atom+xml
image/svg+xml;
{% endif %}
# 上游服务器配置
{% for upstream_name, upstream_servers in upstreams.items() %}
upstream {{ upstream_name }} {
{% for server in upstream_servers %}
server {{ server.server }}{% if server.weight is defined %} weight={{ server.weight }}{% endif %}{% if server.max_fails is defined %} max_fails={{ server.max_fails }}{% endif %}{% if server.fail_timeout is defined %} fail_timeout={{ server.fail_timeout }}{% endif %};
{% endfor %}
# 负载均衡方法
{% if upstream_method is defined %}
{{ upstream_method }};
{% endif %}
}
{% endfor %}
# 虚拟主机配置
{% for vhost in vhosts %}
server {
# 监听端口
listen {{ vhost.port | default(80) }};
{% if vhost.ssl | default(false) %}
listen {{ ssl_port | default(443) }} ssl http2;
{% endif %}
# 服务器名称
server_name {{ vhost.name }}{% if vhost.aliases is defined %} {{ vhost.aliases | join(' ') }}{% endif %};
# 文档根目录
root {{ vhost.root }};
# 默认文件
index {{ vhost.index | default('index.html index.htm') }};
# SSL 配置
{% if vhost.ssl | default(false) %}
ssl_certificate {{ ssl_cert_path }};
ssl_certificate_key {{ ssl_key_path }};
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
# HSTS
{% if hsts_enabled | default(false) %}
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
{% endif %}
{% endif %}
# 访问日志
{% if vhost.access_log is defined %}
access_log {{ vhost.access_log }};
{% else %}
access_log /var/log/nginx/{{ vhost.name }}.access.log main;
{% endif %}
# 错误日志
{% if vhost.error_log is defined %}
error_log {{ vhost.error_log }};
{% else %}
error_log /var/log/nginx/{{ vhost.name }}.error.log;
{% endif %}
# 位置配置
{% if vhost.locations is defined %}
{% for location in vhost.locations %}
location {{ location.path }} {
{% if location.proxy_pass is defined %}
proxy_pass {{ location.proxy_pass }};
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
{% endif %}
{% if location.try_files is defined %}
try_files {{ location.try_files }};
{% endif %}
{% if location.deny is defined %}
{% for deny_rule in location.deny %}
deny {{ deny_rule }};
{% endfor %}
{% endif %}
{% if location.allow is defined %}
{% for allow_rule in location.allow %}
allow {{ allow_rule }};
{% endfor %}
{% endif %}
}
{% endfor %}
{% else %}
# 默认位置
location / {
try_files $uri $uri/ =404;
{% if vhost.upstream is defined %}
proxy_pass http://{{ vhost.upstream }};
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
{% endif %}
}
# PHP 处理
{% if vhost.php_enabled | default(false) %}
location ~ \.php$ {
fastcgi_pass {{ php_fpm_socket | default('unix:/var/run/php/php-fpm.sock') }};
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
{% endif %}
# 静态文件缓存
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
expires {{ static_files_expire | default('1y') }};
add_header Cache-Control "public, immutable";
}
{% endif %}
# 安全头
{% if security_headers | default(true) %}
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
{% endif %}
}
{% endfor %}
# 包含其他配置文件
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
6.5.2 模板控制结构
{# templates/application.conf.j2 #}
# 应用程序配置文件
# 环境: {{ environment | upper }}
# 生成时间: {{ ansible_date_time.iso8601 }}
[application]
name = {{ app_name }}
version = {{ app_version }}
environment = {{ environment }}
debug = {{ debug_mode | lower }}
# 条件配置
{% if environment == 'development' %}
# 开发环境特定配置
log_level = DEBUG
reload = true
workers = 1
{% elif environment == 'testing' %}
# 测试环境特定配置
log_level = INFO
reload = false
workers = 2
{% elif environment == 'production' %}
# 生产环境特定配置
log_level = WARNING
reload = false
workers = {{ ansible_processor_vcpus * 2 }}
{% else %}
# 默认配置
log_level = INFO
reload = false
workers = 4
{% endif %}
[database]
host = {{ database.host }}
port = {{ database.port }}
name = {{ database.name }}
user = {{ database.user }}
# 密码在运行时设置
# 连接池配置
{% if database.pool_size is defined %}
pool_size = {{ database.pool_size }}
{% endif %}
{% if database.max_overflow is defined %}
max_overflow = {{ database.max_overflow }}
{% endif %}
{% if database.timeout is defined %}
timeout = {{ database.timeout }}
{% endif %}
# SSL 配置
{% if database.ssl_mode is defined %}
ssl_mode = {{ database.ssl_mode }}
{% if database.ssl_cert is defined %}
ssl_cert = {{ database.ssl_cert }}
{% endif %}
{% if database.ssl_key is defined %}
ssl_key = {{ database.ssl_key }}
{% endif %}
{% if database.ssl_ca is defined %}
ssl_ca = {{ database.ssl_ca }}
{% endif %}
{% endif %}
# 缓存配置
{% if cache.enabled %}
[cache]
type = {{ cache.type }}
{% if cache.type == 'redis' %}
host = {{ cache.host }}
port = {{ cache.port }}
{% if cache.password is defined %}
# 密码在运行时设置
{% endif %}
db = {{ cache.db | default(0) }}
{% if cache.cluster is defined and cache.cluster %}
cluster = true
{% endif %}
{% elif cache.type == 'memcached' %}
servers = {% for server in cache.servers %}{{ server }}{% if not loop.last %},{% endif %}{% endfor %}
{% endif %}
ttl = {{ cache.ttl }}
{% endif %}
# 日志配置
[logging]
level = {{ log_level | upper }}
format = {{ log_format | default('%(asctime)s - %(name)s - %(levelname)s - %(message)s') }}
# 日志处理器
{% for handler_name, handler_config in log_handlers.items() %}
[logging.{{ handler_name }}]
type = {{ handler_config.type }}
{% if handler_config.type == 'file' %}
filename = {{ handler_config.filename }}
{% if handler_config.max_size is defined %}
max_size = {{ handler_config.max_size }}
{% endif %}
{% if handler_config.backup_count is defined %}
backup_count = {{ handler_config.backup_count }}
{% endif %}
{% elif handler_config.type == 'syslog' %}
facility = {{ handler_config.facility | default('local0') }}
{% if handler_config.address is defined %}
address = {{ handler_config.address }}
{% endif %}
{% endif %}
level = {{ handler_config.level | default(log_level) | upper }}
{% endfor %}
# 安全配置
[security]
secret_key_file = {{ security.secret_key_file | default('/etc/' + app_name + '/secret.key') }}
{% if security.csrf_protection is defined %}
csrf_protection = {{ security.csrf_protection | lower }}
{% endif %}
{% if security.ssl_only is defined %}
ssl_only = {{ security.ssl_only | lower }}
{% endif %}
{% if security.hsts_enabled is defined %}
hsts_enabled = {{ security.hsts_enabled | lower }}
{% endif %}
# 允许的主机
{% if security.allowed_hosts is defined %}
allowed_hosts = {% for host in security.allowed_hosts %}{{ host }}{% if not loop.last %},{% endif %}{% endfor %}
{% endif %}
# CORS 配置
{% if security.cors is defined %}
[security.cors]
allowed_origins = {% for origin in security.cors.allowed_origins %}{{ origin }}{% if not loop.last %},{% endif %}{% endfor %}
allowed_methods = {% for method in security.cors.allowed_methods %}{{ method }}{% if not loop.last %},{% endif %}{% endfor %}
allowed_headers = {% for header in security.cors.allowed_headers %}{{ header }}{% if not loop.last %},{% endif %}{% endfor %}
{% endif %}
# 外部服务配置
{% for service_name, service_config in external_services.items() %}
[external.{{ service_name }}]
{% for key, value in service_config.items() %}
{{ key }} = {{ value }}
{% endfor %}
{% endfor %}
# 功能开关
[features]
{% for feature_name, feature_enabled in features.items() %}
{{ feature_name }} = {{ feature_enabled | lower }}
{% endfor %}
# 性能配置
{% if performance is defined %}
[performance]
{% for key, value in performance.items() %}
{{ key }} = {{ value }}
{% endfor %}
{% endif %}
# 监控配置
{% if monitoring.enabled %}
[monitoring]
enabled = true
metrics_endpoint = {{ monitoring.metrics_endpoint }}
health_endpoint = {{ monitoring.health_endpoint }}
{% if monitoring.log_aggregation is defined %}
log_aggregation = {{ monitoring.log_aggregation | lower }}
{% endif %}
{% if monitoring.tracing is defined %}
tracing = {{ monitoring.tracing | lower }}
{% endif %}
{% endif %}
# 自定义配置段
{% for section_name, section_config in custom_config.items() %}
[{{ section_name }}]
{% for key, value in section_config.items() %}
{% if value is string %}
{{ key }} = {{ value }}
{% elif value is number %}
{{ key }} = {{ value }}
{% elif value is boolean %}
{{ key }} = {{ value | lower }}
{% elif value is sequence %}
{{ key }} = {% for item in value %}{{ item }}{% if not loop.last %},{% endif %}{% endfor %}
{% endif %}
{% endfor %}
{% endfor %}
6.5.3 模板宏和包含
{# templates/macros.j2 #}
{# 通用宏定义 #}
{# 生成配置文件头部 #}
{% macro config_header(title, description='') %}
#
# {{ title }}
{% if description %}
# {{ description }}
{% endif %}
# 生成时间: {{ ansible_date_time.iso8601 }}
# 管理主机: {{ ansible_hostname }}
# 操作系统: {{ ansible_distribution }} {{ ansible_distribution_version }}
#
{% endmacro %}
{# 生成数据库连接配置 #}
{% macro database_config(db_config, section_name='database') %}
[{{ section_name }}]
host = {{ db_config.host }}
port = {{ db_config.port }}
name = {{ db_config.name }}
user = {{ db_config.user }}
{% if db_config.charset is defined %}
charset = {{ db_config.charset }}
{% endif %}
{% if db_config.timezone is defined %}
timezone = {{ db_config.timezone }}
{% endif %}
{% endmacro %}
{# 生成日志配置 #}
{% macro logging_config(log_config) %}
[logging]
level = {{ log_config.level | upper }}
format = {{ log_config.format | default('%(asctime)s - %(name)s - %(levelname)s - %(message)s') }}
{% if log_config.handlers is defined %}
{% for handler_name, handler in log_config.handlers.items() %}
[logging.handler.{{ handler_name }}]
class = {{ handler.class }}
{% if handler.filename is defined %}
filename = {{ handler.filename }}
{% endif %}
{% if handler.maxBytes is defined %}
maxBytes = {{ handler.maxBytes }}
{% endif %}
{% if handler.backupCount is defined %}
backupCount = {{ handler.backupCount }}
{% endif %}
level = {{ handler.level | default(log_config.level) | upper }}
formatter = {{ handler.formatter | default('default') }}
{% endfor %}
{% endif %}
{% endmacro %}
{# 生成 Nginx 虚拟主机配置 #}
{% macro nginx_vhost(vhost) %}
server {
listen {{ vhost.port | default(80) }};
{% if vhost.ssl | default(false) %}
listen {{ vhost.ssl_port | default(443) }} ssl http2;
{% endif %}
server_name {{ vhost.server_name }};
root {{ vhost.document_root }};
index {{ vhost.index | default('index.html index.htm') }};
{% if vhost.ssl | default(false) %}
ssl_certificate {{ vhost.ssl_certificate }};
ssl_certificate_key {{ vhost.ssl_certificate_key }};
{% endif %}
{% if vhost.access_log is defined %}
access_log {{ vhost.access_log }};
{% endif %}
{% if vhost.error_log is defined %}
error_log {{ vhost.error_log }};
{% endif %}
{% if vhost.locations is defined %}
{% for location in vhost.locations %}
location {{ location.path }} {
{% if location.proxy_pass is defined %}
proxy_pass {{ location.proxy_pass }};
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
{% endif %}
{% if location.try_files is defined %}
try_files {{ location.try_files }};
{% endif %}
{% if location.return is defined %}
return {{ location.return }};
{% endif %}
}
{% endfor %}
{% else %}
location / {
try_files $uri $uri/ =404;
}
{% endif %}
}
{% endmacro %}
{# 生成防火墙规则 #}
{% macro firewall_rule(rule) %}
{% if rule.action == 'allow' %}
-A INPUT -p {{ rule.protocol | default('tcp') }} --dport {{ rule.port }} {% if rule.source is defined %}-s {{ rule.source }} {% endif %}-j ACCEPT
{% elif rule.action == 'deny' %}
-A INPUT -p {{ rule.protocol | default('tcp') }} --dport {{ rule.port }} {% if rule.source is defined %}-s {{ rule.source }} {% endif %}-j DROP
{% endif %}
{% endmacro %}
{# 生成系统服务配置 #}
{% macro systemd_service(service) %}
[Unit]
Description={{ service.description }}
{% if service.after is defined %}
After={{ service.after | join(' ') }}
{% endif %}
{% if service.requires is defined %}
Requires={{ service.requires | join(' ') }}
{% endif %}
[Service]
Type={{ service.type | default('simple') }}
User={{ service.user | default('root') }}
Group={{ service.group | default('root') }}
ExecStart={{ service.exec_start }}
{% if service.exec_reload is defined %}
ExecReload={{ service.exec_reload }}
{% endif %}
{% if service.exec_stop is defined %}
ExecStop={{ service.exec_stop }}
{% endif %}
{% if service.working_directory is defined %}
WorkingDirectory={{ service.working_directory }}
{% endif %}
{% if service.environment is defined %}
{% for env_var, env_value in service.environment.items() %}
Environment={{ env_var }}={{ env_value }}
{% endfor %}
{% endif %}
Restart={{ service.restart | default('on-failure') }}
RestartSec={{ service.restart_sec | default(5) }}
[Install]
WantedBy={{ service.wanted_by | default('multi-user.target') }}
{% endmacro %}
{# templates/nginx-site.conf.j2 #}
{# 导入宏 #}
{% from 'macros.j2' import config_header, nginx_vhost %}
{{ config_header('Nginx 站点配置', '自动生成的虚拟主机配置') }}
{% for site in sites %}
{{ nginx_vhost(site) }}
{% endfor %}
{# templates/app-config.ini.j2 #}
{# 导入宏 #}
{% from 'macros.j2' import config_header, database_config, logging_config %}
{{ config_header('应用程序配置文件') }}
[application]
name = {{ app_name }}
version = {{ app_version }}
environment = {{ environment }}
debug = {{ debug_mode | lower }}
{{ database_config(database) }}
{{ logging_config(logging) }}
{% if cache.enabled %}
[cache]
type = {{ cache.type }}
host = {{ cache.host }}
port = {{ cache.port }}
ttl = {{ cache.ttl }}
{% endif %}
6.6 模板使用实践
6.6.1 模板任务示例
# template-tasks.yml
---
- name: 模板使用示例
hosts: webservers
become: yes
vars:
app_name: mywebapp
app_version: "1.0.0"
environment: production
# Nginx 配置
nginx_sites:
- server_name: "{{ app_name }}.example.com"
document_root: "/var/www/{{ app_name }}"
port: 80
ssl: true
ssl_port: 443
ssl_certificate: "/etc/ssl/certs/{{ app_name }}.crt"
ssl_certificate_key: "/etc/ssl/private/{{ app_name }}.key"
locations:
- path: "/api/"
proxy_pass: "http://api_backend"
- path: "/static/"
try_files: "$uri $uri/ =404"
# 应用配置
database:
host: "{{ db_host }}"
port: 3306
name: "{{ app_name }}"
user: "{{ app_name }}_user"
charset: utf8mb4
logging:
level: warning
format: "%(asctime)s [%(levelname)s] %(name)s: %(message)s"
handlers:
file:
class: logging.handlers.RotatingFileHandler
filename: "/var/log/{{ app_name }}/app.log"
maxBytes: 10485760 # 10MB
backupCount: 5
level: info
syslog:
class: logging.handlers.SysLogHandler
address: "/dev/log"
facility: local0
level: warning
cache:
enabled: true
type: redis
host: "{{ redis_host }}"
port: 6379
ttl: 3600
tasks:
# 创建应用目录
- name: 创建应用目录
file:
path: "{{ item }}"
state: directory
owner: "{{ app_user | default('www-data') }}"
group: "{{ app_group | default('www-data') }}"
mode: '0755'
loop:
- "/var/www/{{ app_name }}"
- "/var/log/{{ app_name }}"
- "/etc/{{ app_name }}"
# 生成 Nginx 配置
- name: 生成 Nginx 站点配置
template:
src: nginx-site.conf.j2
dest: "/etc/nginx/sites-available/{{ app_name }}"
owner: root
group: root
mode: '0644'
backup: yes
notify: reload nginx
vars:
sites: "{{ nginx_sites }}"
# 启用 Nginx 站点
- name: 启用 Nginx 站点
file:
src: "/etc/nginx/sites-available/{{ app_name }}"
dest: "/etc/nginx/sites-enabled/{{ app_name }}"
state: link
notify: reload nginx
# 生成应用配置文件
- name: 生成应用配置文件
template:
src: app-config.ini.j2
dest: "/etc/{{ app_name }}/config.ini"
owner: "{{ app_user | default('www-data') }}"
group: "{{ app_group | default('www-data') }}"
mode: '0640'
backup: yes
notify: restart application
# 生成环境变量文件
- name: 生成环境变量文件
template:
src: environment.j2
dest: "/etc/{{ app_name }}/environment"
owner: "{{ app_user | default('www-data') }}"
group: "{{ app_group | default('www-data') }}"
mode: '0600'
notify: restart application
# 生成 systemd 服务文件
- name: 生成 systemd 服务文件
template:
src: systemd-service.j2
dest: "/etc/systemd/system/{{ app_name }}.service"
owner: root
group: root
mode: '0644'
notify:
- reload systemd
- restart application
vars:
service:
description: "{{ app_name | title }} Web Application"
user: "{{ app_user | default('www-data') }}"
group: "{{ app_group | default('www-data') }}"
exec_start: "/usr/bin/python3 /opt/{{ app_name }}/app.py"
working_directory: "/opt/{{ app_name }}"
environment:
CONFIG_FILE: "/etc/{{ app_name }}/config.ini"
LOG_LEVEL: "{{ logging.level | upper }}"
restart: always
restart_sec: 10
# 生成日志轮转配置
- name: 生成日志轮转配置
template:
src: logrotate.j2
dest: "/etc/logrotate.d/{{ app_name }}"
owner: root
group: root
mode: '0644'
vars:
log_files:
- "/var/log/{{ app_name }}/*.log"
rotation:
frequency: daily
rotate: 30
compress: true
delaycompress: true
missingok: true
notifempty: true
postrotate: "systemctl reload {{ app_name }}"
# 验证配置文件
- name: 验证 Nginx 配置
command: nginx -t
register: nginx_config_test
failed_when: nginx_config_test.rc != 0
changed_when: false
- name: 验证应用配置
command: python3 -c "import configparser; c=configparser.ConfigParser(); c.read('/etc/{{ app_name }}/config.ini')"
register: app_config_test
failed_when: app_config_test.rc != 0
changed_when: false
handlers:
- name: reload nginx
service:
name: nginx
state: reloaded
- name: reload systemd
systemd:
daemon_reload: yes
- name: restart application
service:
name: "{{ app_name }}"
state: restarted
6.6.2 模板最佳实践
# template-best-practices.yml
---
- name: 模板最佳实践示例
hosts: all
vars:
# 使用有意义的变量名
web_server_config:
server_name: "{{ inventory_hostname }}"
document_root: "/var/www/html"
error_log_level: warn
access_log_format: combined
# 提供默认值
app_settings:
port: "{{ app_port | default(8080) }}"
workers: "{{ app_workers | default(ansible_processor_vcpus) }}"
timeout: "{{ app_timeout | default(30) }}"
max_requests: "{{ app_max_requests | default(1000) }}"
# 使用结构化数据
database_pools:
primary:
host: "{{ primary_db_host }}"
port: 5432
database: "{{ app_name }}"
username: "{{ app_name }}_user"
pool_size: 20
max_overflow: 30
readonly:
host: "{{ readonly_db_host }}"
port: 5432
database: "{{ app_name }}"
username: "{{ app_name }}_readonly"
pool_size: 10
max_overflow: 20
tasks:
# 使用模板验证
- name: 生成配置文件(带验证)
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
backup: yes
validate: 'nginx -t -c %s'
notify: reload nginx
# 使用条件模板
- name: 生成环境特定配置
template:
src: "app-{{ environment }}.conf.j2"
dest: "/etc/{{ app_name }}/app.conf"
owner: "{{ app_user }}"
group: "{{ app_group }}"
mode: '0640'
when: environment in ['development', 'staging', 'production']
# 使用模板变量
- name: 生成数据库配置
template:
src: database.yml.j2
dest: "/etc/{{ app_name }}/database.yml"
owner: "{{ app_user }}"
group: "{{ app_group }}"
mode: '0600'
vars:
db_config: "{{ database_pools[db_pool_type | default('primary')] }}"
# 模板循环生成多个文件
- name: 生成虚拟主机配置
template:
src: vhost.conf.j2
dest: "/etc/nginx/sites-available/{{ item.name }}"
owner: root
group: root
mode: '0644'
loop: "{{ virtual_hosts }}"
notify: reload nginx
# 使用模板生成脚本
- name: 生成部署脚本
template:
src: deploy.sh.j2
dest: "/usr/local/bin/deploy-{{ app_name }}"
owner: root
group: root
mode: '0755'
vars:
script_config:
app_path: "/opt/{{ app_name }}"
backup_path: "/backup/{{ app_name }}"
service_name: "{{ app_name }}"
health_check_url: "http://localhost:{{ app_port }}/health"
6.6.3 模板调试技巧
# template-debugging.yml
---
- name: 模板调试示例
hosts: localhost
vars:
debug_template: true
complex_data:
users:
- name: alice
role: admin
permissions: [read, write, delete]
- name: bob
role: user
permissions: [read]
settings:
timeout: 30
retries: 3
endpoints:
api: "https://api.example.com"
auth: "https://auth.example.com"
tasks:
# 调试变量内容
- name: 显示复杂数据结构
debug:
var: complex_data
when: debug_template
# 调试模板渲染结果
- name: 预览模板渲染结果
debug:
msg: |
{% for user in complex_data.users %}
用户: {{ user.name }}
角色: {{ user.role }}
权限: {{ user.permissions | join(', ') }}
{% endfor %}
when: debug_template
# 使用临时文件调试模板
- name: 生成调试模板
template:
src: debug-template.j2
dest: /tmp/debug-output.txt
when: debug_template
- name: 显示调试模板内容
slurp:
src: /tmp/debug-output.txt
register: debug_content
when: debug_template
- name: 输出调试内容
debug:
msg: "{{ debug_content.content | b64decode }}"
when: debug_template
# 验证模板语法
- name: 验证模板语法
template:
src: complex-config.j2
dest: /tmp/syntax-check.conf
check_mode: yes
register: template_syntax
- name: 报告语法检查结果
debug:
msg: "模板语法检查通过"
when: template_syntax is succeeded
# 条件调试输出
- name: 条件调试信息
debug:
msg: |
环境: {{ environment | default('未设置') }}
调试模式: {{ debug_mode | default(false) }}
主机组: {{ group_names | join(', ') }}
变量定义状态:
- app_name: {{ 'defined' if app_name is defined else 'undefined' }}
- database: {{ 'defined' if database is defined else 'undefined' }}
- cache: {{ 'defined' if cache is defined else 'undefined' }}
when: debug_template or debug_mode | default(false)
{# templates/debug-template.j2 #}
{# 调试模板示例 #}
=== 模板调试输出 ===
生成时间: {{ ansible_date_time.iso8601 }}
主机信息: {{ inventory_hostname }} ({{ ansible_default_ipv4.address | default('N/A') }})
=== 变量信息 ===
{% for key, value in vars.items() %}
{% if not key.startswith('ansible_') and not key.startswith('hostvars') %}
{{ key }}: {{ value | to_nice_json }}
{% endif %}
{% endfor %}
=== 主机事实 ===
操作系统: {{ ansible_distribution }} {{ ansible_distribution_version }}
CPU 核心数: {{ ansible_processor_vcpus }}
内存总量: {{ ansible_memtotal_mb }} MB
磁盘信息:
{% for mount in ansible_mounts %}
{{ mount.mount }}: {{ mount.size_total | filesizeformat }} ({{ mount.fstype }})
{% endfor %}
=== 网络信息 ===
{% for interface in ansible_interfaces %}
{% set interface_facts = hostvars[inventory_hostname]['ansible_' + interface] %}
{% if interface_facts.ipv4 is defined %}
{{ interface }}: {{ interface_facts.ipv4.address }}/{{ interface_facts.ipv4.netmask }}
{% endif %}
{% endfor %}
=== 复杂数据处理 ===
{% if complex_data is defined %}
用户列表:
{% for user in complex_data.users %}
- {{ user.name }} ({{ user.role }}): {{ user.permissions | join(', ') }}
{% endfor %}
设置信息:
{% for key, value in complex_data.settings.items() %}
{{ key }}: {{ value }}
{% endfor %}
{% endif %}
=== 条件测试 ===
{% set test_conditions = [
('environment == "production"', environment == 'production'),
('debug_mode is true', debug_mode | default(false)),
('ansible_memtotal_mb > 1024', ansible_memtotal_mb > 1024),
('"webservers" in group_names', 'webservers' in group_names)
] %}
{% for condition, result in test_conditions %}
{{ condition }}: {{ result }}
{% endfor %}
=== 过滤器测试 ===
{% set test_string = "Hello World" %}
原始字符串: {{ test_string }}
大写: {{ test_string | upper }}
小写: {{ test_string | lower }}
反转: {{ test_string | reverse }}
长度: {{ test_string | length }}
Base64: {{ test_string | b64encode }}
{% set test_list = [3, 1, 4, 1, 5, 9, 2, 6] %}
原始列表: {{ test_list }}
排序: {{ test_list | sort }}
去重: {{ test_list | unique | sort }}
最大值: {{ test_list | max }}
最小值: {{ test_list | min }}
平均值: {{ (test_list | sum / test_list | length) | round(2) }}
=== 模板结束 ===
6.7 高级变量技巧
6.7.1 动态变量生成
# dynamic-variables.yml
---
- name: 动态变量生成示例
hosts: all
vars:
base_config:
app_name: myapp
version: "1.0.0"
port: 8080
environment_overrides:
development:
debug: true
log_level: debug
workers: 1
production:
debug: false
log_level: warning
workers: "{{ ansible_processor_vcpus * 2 }}"
tasks:
# 动态合并配置
- name: 生成环境特定配置
set_fact:
app_config: "{{ base_config | combine(environment_overrides[environment]) }}"
vars:
environment: "{{ target_environment | default('development') }}"
# 基于主机信息动态生成变量
- name: 生成主机特定配置
set_fact:
host_config:
hostname: "{{ inventory_hostname }}"
ip_address: "{{ ansible_default_ipv4.address }}"
memory_mb: "{{ ansible_memtotal_mb }}"
cpu_cores: "{{ ansible_processor_vcpus }}"
disk_space: "{{ ansible_mounts | selectattr('mount', 'equalto', '/') | map(attribute='size_total') | first }}"
recommended_workers: "{{ [ansible_processor_vcpus * 2, 8] | min }}"
max_memory_usage: "{{ (ansible_memtotal_mb * 0.8) | int }}M"
# 动态生成服务发现配置
- name: 生成服务发现配置
set_fact:
service_discovery:
web_servers: |
{%- set servers = [] -%}
{%- for host in groups['webservers'] | default([]) -%}
{%- set _ = servers.append({
'name': host,
'ip': hostvars[host]['ansible_default_ipv4']['address'],
'port': hostvars[host]['app_port'] | default(8080)
}) -%}
{%- endfor -%}
{{ servers }}
database_servers: |
{%- set servers = [] -%}
{%- for host in groups['databases'] | default([]) -%}
{%- set _ = servers.append({
'name': host,
'ip': hostvars[host]['ansible_default_ipv4']['address'],
'port': hostvars[host]['db_port'] | default(3306),
'role': hostvars[host]['db_role'] | default('replica')
}) -%}
{%- endfor -%}
{{ servers }}
# 动态生成负载均衡配置
- name: 生成负载均衡器配置
set_fact:
load_balancer_config:
upstream_servers: |
{%- set upstreams = {} -%}
{%- for group_name in ['webservers', 'api_servers', 'worker_servers'] -%}
{%- if groups[group_name] is defined -%}
{%- set servers = [] -%}
{%- for host in groups[group_name] -%}
{%- set server_config = {
'server': hostvars[host]['ansible_default_ipv4']['address'] + ':' + (hostvars[host]['app_port'] | default(8080) | string),
'weight': hostvars[host]['server_weight'] | default(1),
'max_fails': hostvars[host]['max_fails'] | default(3),
'fail_timeout': hostvars[host]['fail_timeout'] | default('30s')
} -%}
{%- set _ = servers.append(server_config) -%}
{%- endfor -%}
{%- set _ = upstreams.update({group_name: servers}) -%}
{%- endif -%}
{%- endfor -%}
{{ upstreams }}
# 显示动态生成的变量
- name: 显示动态配置
debug:
msg: |
应用配置: {{ app_config | to_nice_json }}
主机配置: {{ host_config | to_nice_json }}
服务发现: {{ service_discovery | to_nice_json }}
负载均衡: {{ load_balancer_config | to_nice_json }}
6.7.2 变量继承和覆盖
# variable-inheritance.yml
---
- name: 变量继承和覆盖示例
hosts: all
vars:
# 基础配置(最低优先级)
default_config:
app:
name: myapp
version: "1.0.0"
port: 8080
workers: 4
timeout: 30
database:
host: localhost
port: 3306
pool_size: 10
cache:
enabled: false
ttl: 300
logging:
level: info
format: standard
# 环境特定配置(中等优先级)
environment_config:
development:
app:
workers: 1
timeout: 60
database:
host: dev-db.example.com
pool_size: 5
cache:
enabled: false
logging:
level: debug
production:
app:
workers: 8
timeout: 15
database:
host: prod-db.example.com
pool_size: 20
cache:
enabled: true
ttl: 3600
logging:
level: warning
# 主机特定配置(最高优先级)
host_specific_config:
web1.example.com:
app:
port: 8081
workers: 6
web2.example.com:
app:
port: 8082
workers: 4
db1.example.com:
database:
port: 3307
pool_size: 30
tasks:
# 多层配置合并
- name: 合并配置层次
set_fact:
final_config: |
{%- set config = default_config -%}
{%- if environment is defined and environment in environment_config -%}
{%- set config = config | combine(environment_config[environment], recursive=True) -%}
{%- endif -%}
{%- if inventory_hostname in host_specific_config -%}
{%- set config = config | combine(host_specific_config[inventory_hostname], recursive=True) -%}
{%- endif -%}
{{ config }}
vars:
environment: "{{ target_environment | default('development') }}"
# 显示最终配置
- name: 显示最终配置
debug:
msg: "{{ final_config | to_nice_json }}"
# 基于角色的配置继承
- name: 基于角色的配置
set_fact:
role_config: |
{%- set config = {} -%}
{%- for role in group_names -%}
{%- if role in role_configurations -%}
{%- set config = config | combine(role_configurations[role], recursive=True) -%}
{%- endif -%}
{%- endfor -%}
{{ config }}
vars:
role_configurations:
webservers:
nginx:
worker_processes: "{{ ansible_processor_vcpus }}"
worker_connections: 1024
php:
memory_limit: 256M
max_execution_time: 30
databases:
mysql:
innodb_buffer_pool_size: "{{ (ansible_memtotal_mb * 0.7) | int }}M"
max_connections: 200
loadbalancers:
haproxy:
maxconn: 4096
timeout_connect: 5000
timeout_client: 50000
timeout_server: 50000
# 条件配置覆盖
- name: 条件配置覆盖
set_fact:
conditional_config: |
{%- set config = final_config -%}
{%- if ansible_memtotal_mb < 2048 -%}
{%- set memory_config = {
'app': {'workers': 2},
'database': {'pool_size': 5}
} -%}
{%- set config = config | combine(memory_config, recursive=True) -%}
{%- endif -%}
{%- if ansible_processor_vcpus < 2 -%}
{%- set cpu_config = {
'app': {'workers': 1}
} -%}
{%- set config = config | combine(cpu_config, recursive=True) -%}
{%- endif -%}
{{ config }}
6.8 本章总结
本章深入介绍了 Ansible 的变量和模板系统,主要内容包括:
- 变量系统概述:变量类型、优先级和作用域
- 变量文件管理:组织结构和环境特定配置
- 变量过滤器和测试:内置过滤器、自定义过滤器和变量测试
- Jinja2 模板系统:基础语法、控制结构和宏定义
- 模板使用实践:最佳实践、调试技巧和实际应用
- 高级变量技巧:动态生成、继承和覆盖
掌握变量和模板是编写灵活、可维护的 Ansible Playbook 的关键技能。
6.9 练习题
基础练习
变量定义和使用
- 创建多层次的变量结构
- 实现变量的条件赋值
- 使用变量过滤器处理数据
模板编写
- 编写 Nginx 配置模板
- 创建应用程序配置模板
- 实现条件和循环逻辑
进阶练习
复杂模板
- 设计可重用的模板宏
- 实现模板继承和包含
- 创建动态配置生成
变量管理
- 设计环境特定的变量结构
- 实现变量加密和安全管理
- 创建动态变量生成逻辑
实战练习
- 完整配置管理
- 设计多环境配置管理方案
- 实现配置模板的版本控制
- 创建配置验证和测试机制
返回目录:Ansible 自动化运维教程