3.1 核心概念概述

Ansible 的核心概念构成了整个自动化框架的基础。理解这些概念对于有效使用 Ansible 至关重要。

3.1.1 概念关系图

┌─────────────────────────────────────────────────────────────┐
│                    Ansible 核心架构                          │
├─────────────────────────────────────────────────────────────┤
│  Control Node (控制节点)                                     │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐          │
│  │  Playbooks  │  │ Inventory   │  │   Modules   │          │
│  │             │  │             │  │             │          │
│  │  ┌───────┐  │  │ ┌─────────┐ │  │ ┌─────────┐ │          │
│  │  │ Tasks │  │  │ │ Groups  │ │  │ │ Actions │ │          │
│  │  │       │  │  │ │         │ │  │ │         │ │          │
│  │  │ ┌───┐ │  │  │ │ ┌─────┐ │ │  │ │ ┌─────┐ │ │          │
│  │  │ │Var│ │  │  │ │ │Host │ │ │  │ │ │Args │ │ │          │
│  │  │ └───┘ │  │  │ │ └─────┘ │ │  │ │ └─────┘ │ │          │
│  │  └───────┘  │  │ └─────────┘ │  │ └─────────┘ │          │
│  └─────────────┘  └─────────────┘  └─────────────┘          │
│                           │                                  │
│                           ▼                                  │
│  ┌─────────────────────────────────────────────────────────┐│
│  │              SSH/WinRM 连接                              ││
│  └─────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────┘
                           │
                           ▼
┌─────────────────────────────────────────────────────────────┐
│                 Managed Nodes (被管理节点)                   │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐          │
│  │   Server1   │  │   Server2   │  │   Server3   │          │
│  │             │  │             │  │             │          │
│  │ ┌─────────┐ │  │ ┌─────────┐ │  │ ┌─────────┐ │          │
│  │ │ Python  │ │  │ │ Python  │ │  │ │ Python  │ │          │
│  │ │ SSH     │ │  │ │ SSH     │ │  │ │ SSH     │ │          │
│  │ └─────────┘ │  │ └─────────┘ │  │ └─────────┘ │          │
│  └─────────────┘  └─────────────┘  └─────────────┘          │
└─────────────────────────────────────────────────────────────┘

3.2 Inventory(主机清单)

3.2.1 Inventory 基本概念

Inventory 是 Ansible 中定义和组织被管理主机的文件,它告诉 Ansible 要管理哪些主机以及如何连接这些主机。

主要功能: - 定义主机列表 - 组织主机分组 - 设置连接参数 - 定义变量

3.2.2 静态 Inventory

INI 格式示例

# inventory/hosts
# 单独的主机
web1.example.com
web2.example.com ansible_host=192.168.1.11 ansible_port=2222

# 主机组
[webservers]
web1.example.com
web2.example.com
web3.example.com

[databases]
db1.example.com
db2.example.com

[cache]
redis1.example.com
redis2.example.com

# 主机组的组(父子关系)
[production:children]
webservers
databases
cache

[staging:children]
webservers

# 组变量
[webservers:vars]
http_port=80
max_clients=200
ansible_user=deploy

[databases:vars]
db_port=3306
ansible_user=dbadmin

# 主机范围
[test]
test[01:10].example.com
test[a:f].example.com

# 连接参数
[windows]
win1.example.com ansible_host=192.168.1.50 ansible_connection=winrm ansible_user=administrator

YAML 格式示例

# inventory/hosts.yml
all:
  children:
    production:
      children:
        webservers:
          hosts:
            web1.example.com:
            web2.example.com:
              ansible_host: 192.168.1.11
              ansible_port: 2222
            web3.example.com:
          vars:
            http_port: 80
            max_clients: 200
            ansible_user: deploy
        databases:
          hosts:
            db1.example.com:
            db2.example.com:
          vars:
            db_port: 3306
            ansible_user: dbadmin
        cache:
          hosts:
            redis1.example.com:
            redis2.example.com:
    staging:
      children:
        webservers:
          hosts:
            staging-web1.example.com:
            staging-web2.example.com:
    windows:
      hosts:
        win1.example.com:
          ansible_host: 192.168.1.50
          ansible_connection: winrm
          ansible_user: administrator

3.2.3 动态 Inventory

云平台动态 Inventory

#!/usr/bin/env python3
# inventory/aws_inventory.py

import boto3
import json
import sys

def get_aws_inventory():
    ec2 = boto3.client('ec2')
    response = ec2.describe_instances()
    
    inventory = {
        'all': {
            'hosts': [],
            'vars': {}
        },
        '_meta': {
            'hostvars': {}
        }
    }
    
    for reservation in response['Reservations']:
        for instance in reservation['Instances']:
            if instance['State']['Name'] == 'running':
                instance_name = instance.get('PublicDnsName', instance['InstanceId'])
                inventory['all']['hosts'].append(instance_name)
                
                # 主机变量
                inventory['_meta']['hostvars'][instance_name] = {
                    'ansible_host': instance.get('PublicIpAddress'),
                    'instance_id': instance['InstanceId'],
                    'instance_type': instance['InstanceType'],
                    'availability_zone': instance['Placement']['AvailabilityZone']
                }
                
                # 根据标签创建组
                for tag in instance.get('Tags', []):
                    if tag['Key'] == 'Environment':
                        env = tag['Value']
                        if env not in inventory:
                            inventory[env] = {'hosts': []}
                        inventory[env]['hosts'].append(instance_name)
                    
                    if tag['Key'] == 'Role':
                        role = tag['Value']
                        if role not in inventory:
                            inventory[role] = {'hosts': []}
                        inventory[role]['hosts'].append(instance_name)
    
    return inventory

if __name__ == '__main__':
    if len(sys.argv) == 2 and sys.argv[1] == '--list':
        print(json.dumps(get_aws_inventory(), indent=2))
    elif len(sys.argv) == 3 and sys.argv[1] == '--host':
        print(json.dumps({}, indent=2))
    else:
        print("Usage: %s --list or %s --host <hostname>" % (sys.argv[0], sys.argv[0]))

数据库动态 Inventory

#!/usr/bin/env python3
# inventory/db_inventory.py

import mysql.connector
import json
import sys
from configparser import ConfigParser

def get_db_inventory():
    # 读取数据库配置
    config = ConfigParser()
    config.read('db_config.ini')
    
    # 连接数据库
    conn = mysql.connector.connect(
        host=config.get('database', 'host'),
        user=config.get('database', 'user'),
        password=config.get('database', 'password'),
        database=config.get('database', 'name')
    )
    
    cursor = conn.cursor(dictionary=True)
    
    # 查询主机信息
    cursor.execute("""
        SELECT 
            hostname,
            ip_address,
            environment,
            role,
            ssh_user,
            ssh_port
        FROM servers 
        WHERE status = 'active'
    """)
    
    servers = cursor.fetchall()
    
    inventory = {
        'all': {
            'hosts': [],
            'vars': {}
        },
        '_meta': {
            'hostvars': {}
        }
    }
    
    for server in servers:
        hostname = server['hostname']
        inventory['all']['hosts'].append(hostname)
        
        # 主机变量
        inventory['_meta']['hostvars'][hostname] = {
            'ansible_host': server['ip_address'],
            'ansible_user': server['ssh_user'],
            'ansible_port': server['ssh_port']
        }
        
        # 环境分组
        env = server['environment']
        if env not in inventory:
            inventory[env] = {'hosts': []}
        inventory[env]['hosts'].append(hostname)
        
        # 角色分组
        role = server['role']
        if role not in inventory:
            inventory[role] = {'hosts': []}
        inventory[role]['hosts'].append(hostname)
    
    cursor.close()
    conn.close()
    
    return inventory

if __name__ == '__main__':
    if len(sys.argv) == 2 and sys.argv[1] == '--list':
        print(json.dumps(get_db_inventory(), indent=2))
    elif len(sys.argv) == 3 and sys.argv[1] == '--host':
        print(json.dumps({}, indent=2))
    else:
        print("Usage: %s --list or %s --host <hostname>" % (sys.argv[0], sys.argv[0]))

3.2.4 Inventory 插件

使用内置插件

# inventory/aws_ec2.yml
plugin: amazon.aws.aws_ec2
regions:
  - us-east-1
  - us-west-2
keyed_groups:
  - key: tags
    prefix: tag
  - key: instance_type
    prefix: instance_type
  - key: placement.region
    prefix: aws_region
compose:
  ansible_host: public_ip_address
# inventory/gcp_compute.yml
plugin: google.cloud.gcp_compute
projects:
  - my-project-id
auth_kind: serviceaccount
service_account_file: /path/to/service-account.json
keyed_groups:
  - key: labels
    prefix: label
  - key: machineType
    prefix: machine_type
  - key: zone
    prefix: zone

3.3 Modules(模块)

3.3.1 模块基本概念

模块是 Ansible 的工作单元,每个模块负责执行特定的任务。Ansible 提供了数百个内置模块,涵盖了系统管理的各个方面。

模块特点: - 幂等性:多次执行相同结果 - 返回 JSON 格式结果 - 可以在任何支持 Python 的系统上运行 - 支持 check 模式(干运行)

3.3.2 常用系统模块

文件操作模块

# file 模块 - 文件和目录操作
- name: 创建目录
  file:
    path: /opt/myapp
    state: directory
    mode: '0755'
    owner: app
    group: app

- name: 创建文件
  file:
    path: /opt/myapp/config.txt
    state: touch
    mode: '0644'

- name: 创建符号链接
  file:
    src: /opt/myapp/current
    dest: /opt/myapp/releases/v1.2.3
    state: link

# copy 模块 - 复制文件
- name: 复制配置文件
  copy:
    src: files/nginx.conf
    dest: /etc/nginx/nginx.conf
    backup: yes
    owner: root
    group: root
    mode: '0644'
  notify: restart nginx

# template 模块 - 模板文件
- name: 生成配置文件
  template:
    src: templates/app.conf.j2
    dest: /opt/myapp/app.conf
    owner: app
    group: app
    mode: '0600'
  vars:
    db_host: "{{ database_host }}"
    db_port: "{{ database_port }}"

# fetch 模块 - 从远程主机获取文件
- name: 获取日志文件
  fetch:
    src: /var/log/myapp.log
    dest: logs/
    flat: yes

包管理模块

# package 模块 - 通用包管理
- name: 安装软件包
  package:
    name: "{{ item }}"
    state: present
  loop:
    - nginx
    - mysql-server
    - redis-server

# apt 模块 - Debian/Ubuntu 包管理
- name: 更新包缓存
  apt:
    update_cache: yes
    cache_valid_time: 3600

- name: 安装特定版本的包
  apt:
    name: nginx=1.18.0-1ubuntu1
    state: present

- name: 安装 .deb 包
  apt:
    deb: /tmp/myapp.deb

# yum 模块 - RedHat/CentOS 包管理
- name: 安装开发工具组
  yum:
    name: "@Development tools"
    state: present

- name: 从特定仓库安装
  yum:
    name: docker-ce
    state: present
    enablerepo: docker-ce-stable

# pip 模块 - Python 包管理
- name: 安装 Python 包
  pip:
    name:
      - django==3.2.0
      - requests
      - celery[redis]
    virtualenv: /opt/myapp/venv
    virtualenv_python: python3.8

服务管理模块

# service 模块 - 通用服务管理
- name: 启动并启用服务
  service:
    name: nginx
    state: started
    enabled: yes

# systemd 模块 - systemd 服务管理
- name: 重新加载 systemd 配置
  systemd:
    daemon_reload: yes

- name: 管理用户服务
  systemd:
    name: myapp
    state: started
    scope: user
    user: app

# cron 模块 - 定时任务管理
- name: 添加备份任务
  cron:
    name: "database backup"
    minute: "0"
    hour: "2"
    job: "/usr/local/bin/backup.sh"
    user: backup

- name: 删除定时任务
  cron:
    name: "old backup task"
    state: absent

3.3.3 网络和云模块

网络配置模块

# uri 模块 - HTTP 请求
- name: 检查 API 健康状态
  uri:
    url: "http://{{ inventory_hostname }}:8080/health"
    method: GET
    status_code: 200
  register: health_check

- name: 发送 POST 请求
  uri:
    url: "https://api.example.com/webhook"
    method: POST
    body_format: json
    body:
      message: "Deployment completed"
      server: "{{ inventory_hostname }}"
    headers:
      Authorization: "Bearer {{ api_token }}"

# get_url 模块 - 下载文件
- name: 下载应用程序
  get_url:
    url: "https://releases.example.com/myapp-{{ app_version }}.tar.gz"
    dest: "/tmp/myapp-{{ app_version }}.tar.gz"
    checksum: "sha256:{{ app_checksum }}"
    timeout: 30

# wait_for 模块 - 等待条件
- name: 等待端口开放
  wait_for:
    host: "{{ inventory_hostname }}"
    port: 8080
    delay: 10
    timeout: 300
    state: started

- name: 等待文件存在
  wait_for:
    path: /opt/myapp/ready
    state: present
    timeout: 600

云平台模块

# AWS EC2 模块
- name: 启动 EC2 实例
  amazon.aws.ec2_instance:
    name: "web-server-{{ ansible_date_time.epoch }}"
    image_id: ami-0abcdef1234567890
    instance_type: t3.micro
    key_name: my-key
    security_groups:
      - web-servers
    tags:
      Environment: production
      Role: webserver
    wait: yes
    wait_timeout: 600
  register: ec2_result

# AWS S3 模块
- name: 上传文件到 S3
  amazon.aws.s3_object:
    bucket: my-app-backups
    object: "backups/{{ ansible_date_time.date }}/database.sql"
    src: /tmp/database.sql
    mode: put
    encrypt: yes

# Azure 虚拟机模块
- name: 创建 Azure VM
  azure.azcollection.azure_rm_virtualmachine:
    resource_group: myResourceGroup
    name: myVM
    vm_size: Standard_B1s
    admin_username: azureuser
    ssh_password_enabled: false
    ssh_public_keys:
      - path: /home/azureuser/.ssh/authorized_keys
        key_data: "{{ ssh_public_key }}"
    image:
      offer: UbuntuServer
      publisher: Canonical
      sku: 18.04-LTS
      version: latest

3.3.4 自定义模块

Python 自定义模块

#!/usr/bin/python
# library/custom_service_check.py

from ansible.module_utils.basic import AnsibleModule
import requests
import time

def check_service_health(url, timeout, retries):
    """检查服务健康状态"""
    for attempt in range(retries):
        try:
            response = requests.get(url, timeout=timeout)
            if response.status_code == 200:
                return True, f"Service is healthy (attempt {attempt + 1})"
            else:
                time.sleep(2)
        except requests.RequestException as e:
            if attempt == retries - 1:
                return False, f"Service check failed: {str(e)}"
            time.sleep(2)
    
    return False, "Service check failed after all retries"

def main():
    module = AnsibleModule(
        argument_spec=dict(
            url=dict(type='str', required=True),
            timeout=dict(type='int', default=10),
            retries=dict(type='int', default=3),
            expected_status=dict(type='int', default=200)
        ),
        supports_check_mode=True
    )
    
    url = module.params['url']
    timeout = module.params['timeout']
    retries = module.params['retries']
    
    if module.check_mode:
        module.exit_json(changed=False, msg="Check mode - would check service health")
    
    success, message = check_service_health(url, timeout, retries)
    
    if success:
        module.exit_json(changed=False, msg=message, url=url)
    else:
        module.fail_json(msg=message, url=url)

if __name__ == '__main__':
    main()

使用自定义模块

# 使用自定义模块
- name: 检查应用程序健康状态
  custom_service_check:
    url: "http://{{ inventory_hostname }}:8080/health"
    timeout: 15
    retries: 5
  register: health_result

- name: 显示健康检查结果
  debug:
    msg: "{{ health_result.msg }}"

3.4 Playbooks(剧本)

3.4.1 Playbook 基本结构

Playbook 是 Ansible 的配置、部署和编排语言。它们可以描述你希望远程系统执行的策略,或者一般 IT 流程中的一系列步骤。

# basic-playbook.yml
---
# Playbook 头部信息
- name: 基础 Web 服务器配置
  hosts: webservers
  become: yes
  gather_facts: yes
  vars:
    http_port: 80
    max_clients: 200
  
  # 前置任务
  pre_tasks:
    - name: 更新包缓存
      apt:
        update_cache: yes
        cache_valid_time: 3600
  
  # 主要任务
  tasks:
    - name: 安装 Nginx
      package:
        name: nginx
        state: present
    
    - name: 启动并启用 Nginx
      service:
        name: nginx
        state: started
        enabled: yes
    
    - name: 配置防火墙
      ufw:
        rule: allow
        port: "{{ http_port }}"
        proto: tcp
  
  # 处理器
  handlers:
    - name: restart nginx
      service:
        name: nginx
        state: restarted
  
  # 后置任务
  post_tasks:
    - name: 验证 Nginx 运行状态
      uri:
        url: "http://{{ inventory_hostname }}"
        status_code: 200

3.4.2 多 Play Playbook

# multi-play-playbook.yml
---
# 第一个 Play - 配置数据库服务器
- name: 配置数据库服务器
  hosts: databases
  become: yes
  vars:
    mysql_root_password: "{{ vault_mysql_root_password }}"
  
  tasks:
    - name: 安装 MySQL
      package:
        name: mysql-server
        state: present
    
    - name: 配置 MySQL
      template:
        src: my.cnf.j2
        dest: /etc/mysql/my.cnf
      notify: restart mysql
    
    - name: 启动 MySQL
      service:
        name: mysql
        state: started
        enabled: yes
  
  handlers:
    - name: restart mysql
      service:
        name: mysql
        state: restarted

# 第二个 Play - 配置 Web 服务器
- name: 配置 Web 服务器
  hosts: webservers
  become: yes
  vars:
    app_version: "1.2.3"
  
  tasks:
    - name: 安装应用程序依赖
      package:
        name:
          - python3
          - python3-pip
          - nginx
        state: present
    
    - name: 部署应用程序
      unarchive:
        src: "https://releases.example.com/myapp-{{ app_version }}.tar.gz"
        dest: /opt/
        remote_src: yes
        creates: "/opt/myapp-{{ app_version }}"
    
    - name: 创建符号链接
      file:
        src: "/opt/myapp-{{ app_version }}"
        dest: /opt/myapp/current
        state: link

# 第三个 Play - 配置负载均衡器
- name: 配置负载均衡器
  hosts: loadbalancers
  become: yes
  
  tasks:
    - name: 安装 HAProxy
      package:
        name: haproxy
        state: present
    
    - name: 配置 HAProxy
      template:
        src: haproxy.cfg.j2
        dest: /etc/haproxy/haproxy.cfg
      notify: restart haproxy
  
  handlers:
    - name: restart haproxy
      service:
        name: haproxy
        state: restarted

3.4.3 条件执行和循环

# conditional-loops.yml
---
- name: 条件执行和循环示例
  hosts: all
  become: yes
  
  tasks:
    # 条件执行
    - name: 仅在 Ubuntu 上安装 apt 包
      apt:
        name: "{{ item }}"
        state: present
      loop:
        - curl
        - wget
        - vim
      when: ansible_distribution == "Ubuntu"
    
    - name: 仅在 CentOS 上安装 yum 包
      yum:
        name: "{{ item }}"
        state: present
      loop:
        - curl
        - wget
        - vim
      when: ansible_distribution == "CentOS"
    
    # 复杂条件
    - name: 在生产环境的 Web 服务器上配置 SSL
      template:
        src: ssl.conf.j2
        dest: /etc/nginx/conf.d/ssl.conf
      when:
        - environment == "production"
        - "'webservers' in group_names"
        - ssl_enabled | default(false)
    
    # 字典循环
    - name: 创建多个用户
      user:
        name: "{{ item.name }}"
        groups: "{{ item.groups }}"
        shell: "{{ item.shell | default('/bin/bash') }}"
        state: present
      loop:
        - { name: "alice", groups: "developers", shell: "/bin/zsh" }
        - { name: "bob", groups: "operators" }
        - { name: "charlie", groups: "developers,operators" }
    
    # 条件循环
    - name: 安装开发工具(仅在开发环境)
      package:
        name: "{{ item }}"
        state: present
      loop:
        - git
        - nodejs
        - docker
      when: environment == "development"
    
    # 注册变量和条件
    - name: 检查服务状态
      command: systemctl is-active nginx
      register: nginx_status
      failed_when: false
      changed_when: false
    
    - name: 启动 Nginx(如果未运行)
      service:
        name: nginx
        state: started
      when: nginx_status.stdout != "active"
    
    # 文件存在性检查
    - name: 检查配置文件是否存在
      stat:
        path: /etc/myapp/config.yml
      register: config_file
    
    - name: 创建默认配置(如果不存在)
      template:
        src: default-config.yml.j2
        dest: /etc/myapp/config.yml
      when: not config_file.stat.exists

3.4.4 错误处理

# error-handling.yml
---
- name: 错误处理示例
  hosts: all
  
  tasks:
    # 忽略错误
    - name: 尝试停止可能不存在的服务
      service:
        name: optional-service
        state: stopped
      ignore_errors: yes
    
    # 自定义失败条件
    - name: 检查磁盘空间
      shell: df -h / | tail -1 | awk '{print $5}' | sed 's/%//'
      register: disk_usage
      failed_when: disk_usage.stdout | int > 90
    
    # 重试机制
    - name: 下载文件(带重试)
      get_url:
        url: "https://example.com/large-file.zip"
        dest: /tmp/large-file.zip
      register: download_result
      until: download_result is succeeded
      retries: 3
      delay: 10
    
    # 块和救援
    - name: 尝试配置应用程序
      block:
        - name: 停止应用程序
          service:
            name: myapp
            state: stopped
        
        - name: 更新配置
          template:
            src: app.conf.j2
            dest: /etc/myapp/app.conf
        
        - name: 启动应用程序
          service:
            name: myapp
            state: started
      
      rescue:
        - name: 恢复备份配置
          copy:
            src: /etc/myapp/app.conf.backup
            dest: /etc/myapp/app.conf
            remote_src: yes
        
        - name: 重启应用程序
          service:
            name: myapp
            state: restarted
        
        - name: 发送告警
          mail:
            to: admin@example.com
            subject: "应用程序配置失败"
            body: "在 {{ inventory_hostname }} 上配置应用程序失败,已恢复备份配置"
      
      always:
        - name: 清理临时文件
          file:
            path: /tmp/config-update.lock
            state: absent

3.5 Variables(变量)

3.5.1 变量类型和优先级

Ansible 变量有明确的优先级顺序(从低到高):

  1. 命令行变量(-e)
  2. 任务变量
  3. 块变量
  4. 角色和包含变量
  5. play 变量
  6. 主机事实
  7. 注册变量
  8. set_facts
  9. 角色默认变量
  10. inventory 文件或脚本组变量
  11. inventory 组_vars/all
  12. playbook 组_vars/all
  13. inventory 组_vars/*
  14. playbook 组_vars/*
  15. inventory 文件或脚本主机变量
  16. inventory host_vars/*
  17. playbook host_vars/*
  18. 主机事实 / 缓存的 set_facts
  19. play vars
  20. play vars_prompt
  21. play vars_files
  22. 角色变量(在角色/vars/main.yml 中定义)
  23. 块变量(仅适用于块中的任务)
  24. 任务变量(仅适用于任务)
  25. include_vars
  26. set_facts / 注册变量
  27. 角色(和 include_role)参数
  28. include 参数
  29. 额外变量(在命令行中使用 -e 传递)

3.5.2 变量定义方式

Inventory 变量

# inventory/hosts
[webservers]
web1.example.com http_port=8080 ssl_enabled=true
web2.example.com http_port=8081 ssl_enabled=false

[webservers:vars]
app_version=1.2.3
environment=production
max_connections=1000
# group_vars/webservers.yml
---
app_version: "1.2.3"
environment: production
max_connections: 1000

# 复杂数据结构
database:
  host: db.example.com
  port: 3306
  name: myapp
  user: app_user

# 列表变量
required_packages:
  - nginx
  - python3
  - redis-server

# 字典变量
ssl_config:
  enabled: true
  cert_path: /etc/ssl/certs/myapp.crt
  key_path: /etc/ssl/private/myapp.key
  protocols:
    - TLSv1.2
    - TLSv1.3
# host_vars/web1.example.com.yml
---
server_id: 1
special_config: true
local_storage_path: /opt/data

# 覆盖组变量
max_connections: 2000

Playbook 变量

# playbook-vars.yml
---
- name: 变量定义示例
  hosts: webservers
  vars:
    # 简单变量
    app_name: myapp
    app_version: "1.2.3"
    
    # 字典变量
    database:
      host: "{{ db_host | default('localhost') }}"
      port: 3306
      name: "{{ app_name }}"
    
    # 列表变量
    required_services:
      - nginx
      - mysql
      - redis
  
  vars_files:
    - vars/common.yml
    - "vars/{{ environment }}.yml"
  
  vars_prompt:
    - name: db_password
      prompt: "请输入数据库密码"
      private: yes
      encrypt: sha512_crypt
      confirm: yes
  
  tasks:
    - name: 显示变量
      debug:
        msg: |
          应用名称: {{ app_name }}
          版本: {{ app_version }}
          数据库: {{ database.host }}:{{ database.port }}/{{ database.name }}
          服务列表: {{ required_services | join(', ') }}

任务变量

# task-vars.yml
---
- name: 任务级变量示例
  hosts: all
  
  tasks:
    - name: 设置任务变量
      debug:
        msg: "当前用户: {{ current_user }}"
      vars:
        current_user: "{{ ansible_user | default('unknown') }}"
    
    - name: 循环中使用变量
      package:
        name: "{{ item.name }}"
        state: "{{ item.state | default('present') }}"
      vars:
        package_list:
          - { name: "nginx", state: "present" }
          - { name: "apache2", state: "absent" }
          - { name: "mysql-server" }
      loop: "{{ package_list }}"
    
    - name: 注册变量
      command: whoami
      register: current_user_result
      changed_when: false
    
    - name: 使用注册变量
      debug:
        msg: "命令输出: {{ current_user_result.stdout }}"
    
    - name: 设置事实
      set_fact:
        deployment_time: "{{ ansible_date_time.iso8601 }}"
        server_role: |
          {% if 'webservers' in group_names %}
          web
          {% elif 'databases' in group_names %}
          db
          {% else %}
          unknown
          {% endif %}
    
    - name: 使用设置的事实
      debug:
        msg: "服务器角色: {{ server_role }}, 部署时间: {{ deployment_time }}"

3.5.3 变量作用域和继承

# variable-scope.yml
---
- name: 全局变量
  hosts: all
  vars:
    global_var: "我是全局变量"
    common_config:
      timeout: 30
      retries: 3
  
  tasks:
    - name: 显示全局变量
      debug:
        var: global_var
    
    - name: 块级变量
      block:
        - name: 在块内使用变量
          debug:
            msg: "块变量: {{ block_var }}, 全局变量: {{ global_var }}"
        
        - name: 修改全局变量(仅在块内有效)
          set_fact:
            global_var: "块内修改的值"
        
        - name: 显示修改后的变量
          debug:
            var: global_var
      
      vars:
        block_var: "我是块变量"
        global_var: "块内覆盖的全局变量"
    
    - name: 块外检查变量
      debug:
        msg: "块外的全局变量: {{ global_var }}"
    
    - name: 包含任务文件
      include_tasks: tasks/subtasks.yml
      vars:
        include_var: "传递给包含文件的变量"
        common_config:
          timeout: 60  # 覆盖全局配置
          max_size: 100  # 新增配置
# tasks/subtasks.yml
---
- name: 在包含的任务中使用变量
  debug:
    msg: |
      包含变量: {{ include_var | default('未定义') }}
      全局变量: {{ global_var | default('未定义') }}
      合并配置: {{ common_config }}

- name: 设置包含文件中的变量
  set_fact:
    subtask_result: "子任务完成"

- name: 返回结果
  debug:
    var: subtask_result

3.5.4 变量过滤器和测试

# filters-tests.yml
---
- name: 变量过滤器和测试示例
  hosts: localhost
  vars:
    sample_string: "  Hello World  "
    sample_list: [1, 2, 3, 4, 5]
    sample_dict:
      name: "John Doe"
      age: 30
      email: "john@example.com"
    sample_number: 42
    empty_var: ""
    undefined_var: null
  
  tasks:
    # 字符串过滤器
    - name: 字符串操作
      debug:
        msg: |
          原始: '{{ sample_string }}'
          去空格: '{{ sample_string | trim }}'
          大写: '{{ sample_string | upper }}'
          小写: '{{ sample_string | lower }}'
          替换: '{{ sample_string | replace("World", "Ansible") }}'
          长度: {{ sample_string | length }}
    
    # 列表过滤器
    - name: 列表操作
      debug:
        msg: |
          原始列表: {{ sample_list }}
          第一个: {{ sample_list | first }}
          最后一个: {{ sample_list | last }}
          随机: {{ sample_list | random }}
          排序: {{ sample_list | sort }}
          反转: {{ sample_list | reverse }}
          唯一: {{ (sample_list + [1, 2]) | unique }}
          连接: {{ sample_list | join('-') }}
          过滤: {{ sample_list | select('>', 3) | list }}
    
    # 字典过滤器
    - name: 字典操作
      debug:
        msg: |
          键列表: {{ sample_dict | list }}
          值列表: {{ sample_dict | dict2items | map(attribute='value') | list }}
          键值对: {{ sample_dict | dict2items }}
    
    # 数学过滤器
    - name: 数学运算
      debug:
        msg: |
          绝对值: {{ -42 | abs }}
          四舍五入: {{ 3.14159 | round(2) }}
          最大值: {{ [1, 5, 3, 9, 2] | max }}
          最小值: {{ [1, 5, 3, 9, 2] | min }}
          求和: {{ [1, 2, 3, 4, 5] | sum }}
    
    # 默认值和条件
    - name: 默认值处理
      debug:
        msg: |
          默认值: {{ undefined_var | default('默认值') }}
          非空默认: {{ empty_var | default('非空默认', true) }}
          三元运算: {{ (sample_number > 40) | ternary('大于40', '小于等于40') }}
    
    # 变量测试
    - name: 变量测试
      debug:
        msg: |
          是否定义: {{ sample_string is defined }}
          是否未定义: {{ undefined_var is undefined }}
          是否为空: {{ empty_var is empty }}
          是否为数字: {{ sample_number is number }}
          是否为字符串: {{ sample_string is string }}
          是否为列表: {{ sample_list is iterable }}
          是否为字典: {{ sample_dict is mapping }}
    
    # 条件判断
    - name: 条件示例
      debug:
        msg: "变量已定义且不为空"
      when:
        - sample_string is defined
        - sample_string is not empty
        - sample_string | trim | length > 0
    
    # JSON 和 YAML 处理
    - name: 数据格式转换
      debug:
        msg: |
          JSON格式: {{ sample_dict | to_json }}
          YAML格式: {{ sample_dict | to_yaml }}
          美化JSON: {{ sample_dict | to_nice_json }}
          美化YAML: {{ sample_dict | to_nice_yaml }}
    
    # 日期时间过滤器
    - name: 日期时间处理
      debug:
        msg: |
          当前时间: {{ ansible_date_time.iso8601 }}
          格式化时间: {{ ansible_date_time.epoch | strftime('%Y-%m-%d %H:%M:%S') }}
          时间戳: {{ ansible_date_time.epoch }}
    
    # 文件路径过滤器
    - name: 路径操作
      vars:
        file_path: "/opt/myapp/config/app.conf"
      debug:
        msg: |
          目录名: {{ file_path | dirname }}
          文件名: {{ file_path | basename }}
          扩展名: {{ file_path | splitext | last }}
          无扩展名: {{ file_path | splitext | first }}

3.6 本章总结

本章深入介绍了 Ansible 的核心概念,主要内容包括:

  • Inventory(主机清单):静态和动态 Inventory 的创建和管理
  • Modules(模块):系统、网络、云平台模块的使用和自定义模块开发
  • Playbooks(剧本):Playbook 结构、多 Play 设计、条件执行和错误处理
  • Variables(变量):变量类型、作用域、优先级和高级用法

这些概念是 Ansible 自动化的基础,掌握它们对于编写高效、可维护的自动化脚本至关重要。

3.7 练习题

基础练习

  1. Inventory 管理

    • 创建包含 Web、数据库、缓存三个组的 Inventory
    • 将 Inventory 转换为 YAML 格式
    • 编写简单的动态 Inventory 脚本
  2. 模块使用

    • 使用 file 模块创建目录结构
    • 使用 template 模块生成配置文件
    • 使用 service 模块管理系统服务

进阶练习

  1. Playbook 编写

    • 编写多 Play Playbook 部署 LAMP 栈
    • 实现条件执行和循环
    • 添加错误处理和重试机制
  2. 变量管理

    • 设计多环境变量结构
    • 使用变量过滤器处理数据
    • 实现变量继承和覆盖

实战练习

  1. 自定义模块

    • 开发检查服务状态的自定义模块
    • 实现支持 check 模式的模块
    • 添加模块文档和测试
  2. 复杂场景

    • 设计支持滚动更新的 Playbook
    • 实现基于条件的服务发现
    • 创建可重用的任务模板

下一章第4章:Ad-hoc 命令详解

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