在Ansible自动化运维中,面对多主机、多任务的复杂场景,如何让Playbook保持轻量、可维护且易于扩展,是每个运维工程师必须掌握的技能。本文将从任务文件拆分、静态导入、通用任务封装,到角色(Role)与内容集合(Collection)的实战应用,带你一步步构建企业级自动化体系。

一、复杂Playbook的任务拆分与模块化导入

当Playbook变得臃肿,任务逻辑相互纠缠时,维护成本会急剧上升。通过任务拆分模块化导入,你可以像在TypeScript中用模块组织代码一样,将Ansible任务按功能解耦,实现轻量化管理。

1. 环境准备与基础Playbook编写

首先,切换至实验工作目录,统一管理所有资源文件:

cd ~/projects-review

编写一个基础单文件Playbook(playbook.yml),实现web服务的基础部署——包括httpd安装、启动及配置文件拷贝,并指定目标主机:

cat playbook.yml << 'EOF'
---
- name: Install and configure web service
  hosts:
    - servera.lab.example.com
    - serverb.lab.example.com
    - serverc.lab.example.com
    - serverd.lab.example.com
  tasks:
    - name: Install httpd
      ansible.builtin.dnf:
        name: httpd
        state: latest
    - name: Enable and start httpd
      ansible.builtin.service:
        name: httpd
        enabled: true
        state: started
    - name: Tuning configuration installed
      ansible.builtin.copy:
        src: files/tune.conf
        dest: /etc/httpd/conf.d/tune.conf
        owner: root
        group: root
EOF

接着,利用Ansible内置变量inventory_hostname编写host-test.yml,测试通配符主机匹配规则,同时关闭事实收集以提升执行效率:

cat > host-test.yml << 'EOF'
---
- name: List inventory hostnames
  hosts: server*.lab.example.com
  gather_facts: no
  tasks:
    - name: List inventory hostnames
      ansible.builtin.debug:
        msg: "{{ inventory_hostname }}"
EOF

执行测试命令验证主机匹配结果:

ansible-navigator run -m stdout host-test.yml

最后,修改playbook.yml,使用通配符简化多主机配置,适配批量部署场景:

vim playbook.yml
# 替换hosts配置为通配符形式
hosts: server*.lab.example.com

2. 任务文件拆分与静态导入

将基础Playbook中的任务按功能拆分为独立文件,通过import_tasks实现静态导入,就像在Java中通过import组织类一样清晰。

  • 创建专属任务目录:
    mkdir tasks
  • 编写tasks/web_tasks.yml,封装httpd安装、启动及配置文件拷贝,并配置handler触发条件:
    cat > tasks/web_tasks.yml << "EOF"
    ---
    - name: Install httpd
      ansible.builtin.dnf:
        name: httpd
        state: latest
    - name: Enable and start httpd
      ansible.builtin.service:
        name: httpd
        enabled: true
        state: started
    - name: Tuning configuration installed
      ansible.builtin.copy:
        src: files/tune.conf
        dest: /etc/httpd/conf.d/tune.conf
        owner: root
        group: root
        mode: 0644
      notify:
        - restart httpd
    EOF
  • 重构Playbook,通过import_tasks导入web核心任务,新增防火墙部署任务,并定义handler实现配置变更后httpd重启:
    cat > playbook.yml << "EOF"
    ---
    - name: Install and configure web service
      hosts: server*.lab.example.com
      tasks:
        - name: Import the web_tasks.yml task file
          import_tasks: tasks/web_tasks.yml
        - name: Install firewalld
          ansible.builtin.dnf:
            name: firewalld
            state: latest
        - name: Enable and start the firewall
          ansible.builtin.service:
            name: firewalld
            enabled: true
            state: started
        - name: Open the port for http
          ansible.posix.firewalld:
            service: http
            immediate: true
            permanent: true
            state: enabled
      handlers:
        - name: restart httpd
          ansible.builtin.service:
            name: httpd
            state: restarted
    EOF

3. 全量任务拆分与多层级导入

将防火墙任务也拆分为独立文件,形成Playbook → 核心任务文件 → 功能任务文件的多层级结构,进一步提升可维护性。

  • 编写tasks/firewall_tasks.yml,封装防火墙安装、启动及http端口开放任务:
    cat > tasks/firewall_tasks.yml << "EOF"
    ---
    - name: Install firewalld
      ansible.builtin.dnf:
        name: firewalld
        state: latest
    - name: Enable and start the firewall
      ansible.builtin.service:
        name: firewalld
        enabled: true
        state: started
    - name: Open the port for http
      ansible.posix.firewalld:
        service: http
        immediate: true
        permanent: true
        state: enabled
    EOF
  • 修改playbook.yml,通过import_tasks分别导入web和防火墙任务文件:
    cat > playbook.yml << "EOF"
    ---
    - name: Install and configure web service
      hosts: server*.lab.example.com
      tasks:
        - name: Import the web_tasks.yml task file
          import_tasks: tasks/web_tasks.yml
        - name: Import the firewall_tasks.yml task file
          import_tasks: tasks/firewall_tasks.yml
      handlers:
        - name: restart httpd
          ansible.builtin.service:
            name: httpd
            state: restarted
    EOF

4. 通用任务封装与变量传参

提取软件安装 + 服务启动的通用逻辑,封装为可复用任务文件,通过变量传参适配多服务场景。这就像在Go中定义通用函数,传入不同参数即可复用。

  • 编写tasks/install_and_enable.yml,定义packageservice通用变量:
    cat > tasks/install_and_enable.yml << "EOF"
    ---
    - name: Install {{ package }}
      ansible.builtin.dnf:
        name: "{{ package }}"
        state: latest
    - name: Enable and start {{ service }}
      ansible.builtin.service:
        name: "{{ service }}"
        enabled: true
        state: started
    EOF
  • 修改tasks/web_tasks.yml,导入通用任务并传递httpd变量:
    cat > tasks/web_tasks.yml << "EOF"
    ---
    - name: Install and start httpd
      import_tasks: install_and_enable.yml
      vars:
        package: httpd
        service: httpd
    - name: Tuning configuration installed
      ansible.builtin.copy:
        src: files/tune.conf
        dest: /etc/httpd/conf.d/tune.conf
        owner: root
        group: root
        mode: 0644
      notify:
        - restart httpd
    EOF
  • 修改tasks/firewall_tasks.yml,导入通用任务并传递firewalld变量:
    cat > tasks/firewall_tasks.yml << "EOF"
    ---
    - name: Install and start firewalld
      import_tasks: install_and_enable.yml
      vars:
        package: firewalld
        service: firewalld
    - name: Open the port for http
      ansible.posix.firewalld:
        service: http
        immediate: true
        permanent: true
        state: enabled
    EOF

5. 语法检查与执行

完成封装后,先进行语法校验,再执行Playbook实现批量部署:

  • 语法检查:
    ansible-navigator run -m stdout playbook.yml --syntax-check
  • 执行Playbook:
    ansible-navigator run -m stdout playbook.yml

二、使用角色和内容集合简化Playbook

Ansible角色(Role)和内容集合(Collection)是企业级自动化标准化的核心。角色封装完整功能逻辑,集合管理角色与资源,大幅简化Playbook编写——这类似于在C++中通过库和命名空间组织代码。

1. 环境准备与集合安装

  • 切换至角色实验目录:
    cd ~/role-review
  • redhat.rhel_system_roles集合安装到项目本地collections/目录:
    ansible-galaxy collection install -p collections/ redhat-rhel_system_roles-1.19.3.tar.gz
  • 验证安装结果:
    ansible-galaxy collection list

2. 基础角色化Playbook编写

编写web_dev_server.yml,指定目标主机组,并开启强制处理程序:

vim web_dev_server.yml << 'EOF'
---
- name: Configure Dev Web Server
  hosts: dev_webserver
  force_handlers: yes
EOF

进行语法检查与基础执行:

# 语法检查
ansible-navigator run -m stdout web_dev_server.yml --syntax-check
# 基础执行
ansible-navigator run -m stdout web_dev_server.yml

3. 从Git仓库拉取现成角色

通过requirements.yml配置Git仓库中的现成角色,实现自动化拉取与安装,提升开发效率。

  • 创建角色目录:
    mkdir -v roles
  • 编写roles/requirements.yml,指定Git仓库地址、版本控制系统及角色版本:
    vim roles/requirements.yml << 'EOF'
    - name: infra.apache
      src: git@workstation.lab.example.com:infra/apache
      scm: git
      version: v1.4
    EOF
  • 拉取并安装角色到roles/目录:
    ansible-galaxy install -r roles/requirements.yml -p roles

4. 自定义角色创建与依赖配置

使用ansible-galaxy init创建标准化自定义角色apache.developer_configs,并配置角色依赖。这就像在JavaScript中通过npm管理依赖包。

  • 自动生成角色目录结构:
    cd roles
    ansible-galaxy init apache.developer_configs
    cd ..
  • 修改元数据文件roles/apache.developer_configs/meta/main.yml,指定依赖的现成角色infra.apache
    vim roles/apache.developer_configs/meta/main.yml << 'EOF'
    dependencies:
      - name: infra.apache
        src: git@workstation.lab.example.com:infra/apache
        scm: git
        version: v1.4
    EOF
  • 填充业务相关任务和模板文件:
    # 覆盖角色任务文件
    mv -v developer_tasks.yml roles/apache.developer_configs/tasks/main.yml
    # 移动Jinja2模板文件到角色模板目录
    mv -v developer.conf.j2 roles/apache.developer_configs/templates/

5. 组变量配置与角色引用

通过组变量(group_vars)为目标主机组统一配置变量,在Playbook中引用自定义角色:

  • 创建组变量目录并配置变量:
    # 创建目标主机组的组变量目录
    mkdir -pv group_vars/dev_webserver
    # 移动变量文件到组变量目录
    mv -v web_developers.yml group_vars/dev_webserver/
  • 修改web_dev_server.yml,添加roles引用自定义角色apache.developer_configs
    vim web_dev_server.yml << 'EOF'
    ---
    - name: Configure Dev Web Server
      hosts: dev_webserver
      force_handlers: yes
      roles:
        - apache.developer_configs
    EOF
  • 语法检查与执行:
    # 语法检查
    ansible-navigator run -m stdout web_dev_server.yml --syntax-check
    # 执行Playbook
    ansible-navigator run -m stdout web_dev_server.yml

6. 前置任务与Block-Rescue异常处理

通过pre_tasks配置系统级前置检查,结合Block-Rescue异常处理结构,实现SELinux配置的容错性处理。

  • 修改web_dev_server.yml,添加pre_tasks节点:
    vim web_dev_server.yml << 'EOF'
    ---
    - name: Configure Dev Web Server
      hosts: dev_webserver
      force_handlers: yes
      pre_tasks:
        - name: Verify SELinux configuration
          block:
            - include_role:
                name: redhat.rhel_system_roles.selinux
          rescue:
            - name: Handle general failure
              ansible.builtin.fail:
                msg: "SELinux role failed."
              when: not selinux_reboot_required
            - name: Restart managed host
              ansible.builtin.reboot:
                msg: "Ansible rebooting system for updates."
            - name: Reapply SELinux role to complete changes
              include_role:
                name: redhat.rhel_system_roles.selinux
      roles:
        - apache.developer_configs
    EOF
  • 将SELinux变量文件移动到组变量目录:
    mv -v selinux.yml group_vars/dev_webserver/
  • 语法检查与最终执行:
    # 语法检查
    ansible-navigator run -m stdout web_dev_server.yml --syntax-check
    # 最终执行
    ansible-navigator run -m stdout web_dev_server.yml

7. 部署结果验证

通过curl命令访问目标主机的web服务,验证角色化部署效果:

curl servera
curl servera:9081
curl servera:9082
[AFFILIATE_SLOT_1]

三、核心知识点总结

通过本文的实战,我们掌握了以下关键技能:

  • 复杂Playbook管理:通过import_tasks实现任务文件静态拆分与多层级导入,提取通用逻辑并通过变量传参适配多服务场景,大幅提升可维护性。
  • Ansible内容集合:通过ansible-galaxy collection实现集合的本地安装与管理,实现自动化资源的隔离与复用,这是企业级Ansible开发的标准规范。
  • 角色(Role):通过ansible-galaxy init生成标准化角色目录,支持现成角色拉取和自定义角色开发,配置meta/main.yml实现角色依赖管理。
  • 变量与前置任务:通过组变量(group_vars)统一配置目标主机,通过pre_tasks实现系统级前置检查,结合force_handlers确保handler强制执行。
  • 异常处理:在pre_tasks中结合Block-Rescue结构实现异常捕获与容错处理,提升Playbook的健壮性。

提示:将这些技术应用于日常运维中,你会发现Playbook的管理变得像编写TypeScript、C++或Java代码一样清晰和模块化。当你的自动化体系足够健壮时,即使面对数百台主机的复杂场景,也能从容应对。

[AFFILIATE_SLOT_2]

进阶思考:接下来可以尝试将自定义角色发布到Ansible Galaxy,或集成CI/CD流水线实现自动化测试与部署。让Ansible成为你运维体系中的“瑞士军刀”,高效、可靠、可扩展。