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 变量有明确的优先级顺序(从高到低):

  1. 命令行变量 (-e--extra-vars)
  2. 任务变量 (vars 在任务中)
  3. 块变量 (vars 在块中)
  4. 角色和包含变量
  5. Play 变量 (vars 在 play 中)
  6. 主机变量 (host_vars)
  7. 组变量 (group_vars)
  8. Inventory 变量
  9. 角色默认变量 (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 练习题

基础练习

  1. 变量定义和使用

    • 创建多层次的变量结构
    • 实现变量的条件赋值
    • 使用变量过滤器处理数据
  2. 模板编写

    • 编写 Nginx 配置模板
    • 创建应用程序配置模板
    • 实现条件和循环逻辑

进阶练习

  1. 复杂模板

    • 设计可重用的模板宏
    • 实现模板继承和包含
    • 创建动态配置生成
  2. 变量管理

    • 设计环境特定的变量结构
    • 实现变量加密和安全管理
    • 创建动态变量生成逻辑

实战练习

  1. 完整配置管理
    • 设计多环境配置管理方案
    • 实现配置模板的版本控制
    • 创建配置验证和测试机制

下一章第7章:Inventory 管理详解

返回目录Ansible 自动化运维教程