Ansible 变量与模板:灵活配置管理

Ansible 变量与模板详解:灵活配置管理

变量基础

定义变量

# 在 Playbook 中定义
vars:
  app_name: myapp
  app_port: 8080
  app_config:
    debug: true
    log_level: info

# 在 Inventory 中定义
[webservers]
web1 ansible_host=192.168.1.10 app_port=8080
web2 ansible_host=192.168.1.11 app_port=8081

# 在变量文件中定义
# vars/common.yml
---
app_name: myapp
app_port: 8080

使用变量

tasks:
  - name: Display simple variable
    debug:
      msg: "App name: {{ app_name }}"
  
  - name: Display nested variable
    debug:
      msg: "Log level: {{ app_config.log_level }}"
  
  - name: Use variable in task
    file:
      path: "/opt/{{ app_name }}"
      state: directory

变量优先级

Ansible 变量的优先级从低到高(后面的会覆盖前面的):

  1. Role defaults(角色默认值)
  2. Inventory file or script group vars
  3. Inventory group_vars/all
  4. Inventory group_vars/*
  5. Inventory file or script host vars
  6. Inventory host_vars/*
  7. Host facts / cached set_facts
  8. Play vars
  9. Play vars_prompt
  10. Play vars_files
  11. Role vars
  12. Block vars
  13. Task vars
  14. Include_vars
  15. Set_facts / registered vars
  16. Role params
  17. Extra vars(命令行参数)

变量文件

创建变量文件

group_vars/webservers.yml:

---
http_port: 80
https_port: 443
document_root: /var/www/html
worker_processes: 4

group_vars/all.yml:

---
ansible_user: ansible
ansible_ssh_private_key_file: ~/.ssh/id_rsa
ansible_python_interpreter: /usr/bin/python3

host_vars/web1.yml:

---
http_port: 8080
server_name: web1.example.com

在 Playbook 中使用

---
- name: Use variable files
  hosts: webservers
  vars_files:
    - vars/common.yml
    - vars/{{ ansible_os_family }}.yml
  
  tasks:
    - name: Display variables
      debug:
        msg: "HTTP Port: {{ http_port }}"

Jinja2 模板

基础语法

# 变量输出
{{ variable_name }}

# 条件判断
{% if condition %}
  content
{% elif condition %}
  content
{% else %}
  content
{% endif %}

# 循环
{% for item in list %}
  {{ item }}
{% endfor %}

# 过滤器
{{ variable | filter_name }}

# 注释
{# comment #}

模板示例

templates/nginx.conf.j2:

worker_processes {{ worker_processes | default(4) }};

events {
    worker_connections 1024;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;
    
    server {
        listen {{ http_port | default(80) }};
        server_name {{ server_name | default('localhost') }};
        
        root {{ document_root }};
        index index.html index.htm;
        
        location / {
            try_files $uri $uri/ =404;
        }
    }
}

templates/app.conf.j2:

{% if debug_mode %}
DEBUG = True
{% else %}
DEBUG = False
{% endif %}

LOG_LEVEL = "{{ log_level | default('INFO') }}"

{% for server in servers %}
SERVERS.append({
    'host': '{{ server.host }}',
    'port': {{ server.port }}
})
{% endfor %}

使用模板

tasks:
  - name: Copy Nginx config from template
    template:
      src: nginx.conf.j2
      dest: /etc/nginx/nginx.conf
      owner: root
      group: root
      mode: '0644'
      backup: yes
    notify: Restart Nginx
  
  - name: Copy app config
    template:
      src: app.conf.j2
      dest: /opt/myapp/config.conf

常用过滤器

# 字符串过滤器
{{ "hello" | upper }}              # HELLO
{{ "HELLO" | lower }}              # hello
{{ "  hello  " | trim }}           # hello
{{ "hello world" | replace(' ', '_') }}  # hello_world
{{ var | default('default_value') }}

# 数字过滤器
{{ 3.14159 | round(2) }}           # 3.14
{{ 10 | int }}                     # 10
{{ "10" | int }}                   # 10

# 列表过滤器
{{ [1, 2, 3] | length }}           # 3
{{ list | first }}                  # 第一个元素
{{ list | last }}                   # 最后一个元素
{{ list | sort }}                   # 排序
{{ list | unique }}                 # 去重
{{ list | join(', ') }}             # 用逗号连接

# 字典过滤器
{{ dict | items() }}                # 获取所有键值对

# IP 地址过滤器
{{ "192.168.1.1" | ipaddr }}       # 验证 IP 地址
{{ "192.168.1.0/24" | ipaddr('network') }}  # 192.168.1.0

动态变量

用户输入

---
- name: Prompt for variables
  hosts: all
  vars_prompt:
    - name: "app_name"
      prompt: "Enter application name"
      private: no
    
    - name: "db_password"
      prompt: "Enter database password"
      private: yes
      confirm: yes
  
  tasks:
    - name: Display inputs
      debug:
        msg: "App: {{ app_name }} Pass: {{ db_password }}"

注册变量

tasks:
  - name: Get disk usage
    command: df -h /
    register: disk_usage
  
  - name: Display disk usage
    debug:
      var: disk_usage.stdout_lines
  
  - name: Check if service is running
    command: systemctl status nginx
    register: service_status
    failed_when: false
  
  - name: Display service status
    debug:
      msg: "Service is {{ 'running' if service_status.rc == 0 else 'not running' }}"

Set Facts

tasks:
  - name: Set fact
    set_fact:
      app_configured: true
      install_path: "/opt/{{ app_name }}"
  
  - name: Use fact
    debug:
      msg: "Install path: {{ install_path }}"

高级技巧

动态变量名

tasks:
  - name: Set dynamic variables
    set_fact:
      "{{ item }}_value": "{{ item }}"
    loop:
      - foo
      - bar
  
  - name: Display dynamic variables
    debug:
      msg: "{{ foo_value }} {{ bar_value }}"

组合过滤器

# 多个过滤器组合使用
{{ long_list | default([]) | unique | sort | join(', ') }}

条件模板

{% if nginx_ssl_enabled %}
listen {{ nginx_ssl_port }} ssl;
ssl_certificate {{ ssl_cert_path }};
ssl_certificate_key {{ ssl_key_path }};
{% else %}
listen {{ nginx_http_port }};
{% endif %}

最佳实践

  • 分层管理: 使用 group_vars 和 host_vars 管理变量
  • 默认值: 使用 default 过滤器提供默认值
  • 模板化: 使用 Jinja2 模板生成配置文件
  • 敏感信息: 使用 Ansible Vault 加密敏感变量
  • 命名规范: 使用清晰的变量名

总结

变量和模板是 Ansible 配置管理的核心。通过本文的学习,你已经掌握了变量定义、变量优先级、Jinja2 模板、过滤器等知识。灵活使用变量和模板可以让你的 Playbook 更加灵活和可维护。接下来可以深入学习 Roles、条件判断等内容。

发表回复

后才能评论