Orange的运维学习日记–48.Ansible进阶之Import与Role

实验环境

mkdir web && cd web

cat > ansible.cfg <<'EOF'
[defaults]
remote_user = example
inventory    = ./inventory

[privilege_escalation]
become            = True
become_user       = root
become_method     = sudo
become_ask_pass   = False
EOF

cat > inventory <<'EOF'
controller
node1
node2
node3
node4
EOF

Ansible 角色管理

角色介绍

Ansible 角色提供一种标准化目录结构,将任务、变量、文件、模板、处理程序及依赖打包为可复用组件

角色结构

执行 ansible-galaxy init <role_name> 可生成以下目录结构

<role_name>/
├── defaults/
│   └── main.yml
├── files/
├── handlers/
│   └── main.yml
├── meta/
│   └── main.yml
├── tasks/
│   └── main.yml
├── templates/
├── tests/
│   ├── inventory
│   └── test.yml
└── vars/
    └── main.yml
  • defaults:提供默认变量,可在清单变量或 play vars 中被覆盖
  • files:存放静态文件
  • handlers:定义处理程序(handlers)
  • meta:角色元信息(作者、许可证、平台、依赖等)
  • tasks:角色主要执行任务
  • templates:存放 Jinja2 模板
  • vars:定义角色内部变量,优先级高于清单和 play vars
  • tests:用于验证角色的测试清单和 playbook
  • README.md:记录角色用途、用法示例及非 Ansible 要求

在这里插入图片描述

角色目录位置

默认搜索路径(优先级从高到低)

  • ~/.ansible/roles
  • /usr/share/ansible/roles
  • /etc/ansible/roles

可在 ansible.cfg [defaults] 块中通过 roles_path 变量自定义

[defaults]
roles_path = ./roles:/etc/ansible/roles

创建角色

mkdir roles
ansible-galaxy init apache --init-path=./roles
- apache was created successfully

示例:Apache 角色

  • defaults/main.yml

    ---
    web_package: httpd
    web_service: httpd
    
  • tasks/main.yml

    ---
    - name: install web
      yum:
        name: "{{ web_package }}"
        state: latest
    
    - name: "start {{ web_service }}"
      service:
        name: "{{ web_service }}"
        state: started
        enabled: yes
    
    - name: prepare motd
      template:
        src: motd.j2
        dest: /etc/motd
    
    - name: prepare site config
      template:
        src: example.conf.j2
        dest: /etc/httpd/conf.d/example.conf
      notify:
        - restart_web
    
    - name: create DocumentRoot
      file:
        path: "/var/www/html/{{ ansible_hostname }}"
        state: directory
    
    - name: prepare index.html
      template:
        src: index.html.j2
        dest: "/var/www/html/{{ ansible_hostname }}/index.html"
    
  • handlers/main.yml

    ---
    - name: restart_web
      service:
        name: "{{ web_service }}"
        state: restarted
    
  • templates/motd.j2

    hello guys!
    Welcome to {{ ansible_fqdn }}!
    
  • templates/example.conf.j2

    ## {{ ansible_managed }}
    <VirtualHost *:80>
        ServerAdmin example@{{ ansible_fqdn }}
        ServerName {{ ansible_fqdn }}
        ErrorLog logs/{{ ansible_hostname }}-error.log
        CustomLog logs/{{ ansible_hostname }}-common.log common
        DocumentRoot /var/www/html/{{ ansible_hostname }}/
    
        <Directory /var/www/html/{{ ansible_hostname }}/>
           Options +Indexes +FollowSymlinks +Includes
           Order allow,deny
           Allow from all
        </Directory>
    </VirtualHost>
    
  • templates/index.html.j2

    Welcome to {{ ansible_fqdn }} !
    
  • meta/main.yml

    ---
    galaxy_info:
      author: example
      description: example web
      company: example world
      license: GPLv2
      min_ansible_version: 2.4
      platforms:
        - name: Fedora
          versions: [all, 25]
        - name: SomePlatform
          versions: [all]
      galaxy_tags: [apache, web]
    dependencies: []
    

调用角色

  • 语法一

    ---
    - hosts: servers
      roles:
        - apache
    
  • 语法二(传递变量)

    ---
    - hosts: servers
      roles:
        - role: apache
          var1: value1
        - { role: other_role, var2: value2 }
    

角色依赖

在 meta/main.yml 中定义依赖关系,被依赖角色将自动执行

dependencies:
  - role: firewall
    service: http

任务执行顺序

  1. pre_tasks
  2. roles 中的 tasks
  3. playbook 中的 tasks
  4. post_tasks

Handlers 执行顺序

  1. pre_tasks 触发的 handlers
  2. roles 中 tasks 触发的 handlers
  3. playbook tasks 触发的 handlers
  4. post_tasks 触发的 handlers

include_role 与 import_role

  • import_role:静态导入,执行前解析角色并展开
  • include_role:动态包含,运行时加载角色,可配合 when 条件

角色优势

  • 模块化复用,降低重复代码
  • 并行开发,提高协作效率
  • 易于管理大型项目

推荐做法

  • 不在角色中存储敏感信息,使用 Ansible Vault 或环境变量注入
  • 使用 ansible-galaxy init 并删除不需要的目录
  • 维护 README.md 和 meta/main.yml 文档
  • 拆分职责单一的角色,避免过度臃肿
  • 重用并重构已有角色,避免重复创建边缘角色
  • 将角色纳入版本控制

RHEL 系统角色概述

系统角色由 rhel-system-roles 软件包提供,自 RHEL 7.4 起可用,用于标准化 RHEL 6.10+ 和 RHEL 7/8 主机的配置
通过 Ansible 角色方式统一管理跨不同发行版的配置项,简化管理员维护多套配置文件的工作

安装与文档位置

  • 安装系统角色

    sudo yum install -y rhel-system-roles
    
  • 默认角色路径

    /usr/share/ansible/roles/
    
  • 文档存放目录

    /usr/share/doc/rhel-system-roles/
    

    每个角色目录下包含 README.md,说明角色用途、变量及示例用法

timesync 角色

用于统一配置 RHEL 6/7/8 的时间同步服务,无需手动维护 chronyd/ntpd 配置

关键变量
  • timesync_ntp_servers
    

    :列表格式,定义要同步的 NTP 服务器

    • hostname:服务器主机名或地址
    • minpoll:最小轮询间隔,默认 6
    • maxpoll:最大轮询间隔,默认 10
    • iburst:启动时快速同步,布尔值,默认 no
    • pool:将解析到的每个地址视为单独服务器,默认 no
示例 Playbook
- hosts: targets
  vars:
    timesync_ntp_servers:
      - hostname: foo..com
        iburst: yes
      - hostname: bar..com
        iburst: yes
      - hostname: baz..com
        iburst: yes
  roles:
    - rhel-system-roles.timesync
设置时区示例
- name: Time Synchronization
  hosts: node1
  vars:
    timesync_ntp_servers:
      - hostname: classroom..com
        iburst: yes
    timezone: "Asia/Shanghai"
  roles:
    - rhel-system-roles.timesync
  tasks:
    - name: Set timezone
      timezone:
        name: "{{ timezone }}"

selinux 角色

简化 SELinux 管理,通过变量驱动配置,无需编写重复任务

功能
  • 设置 SELinux 模式(enforcing 或 permissive)
  • 恢复文件上下文(restorecon)
  • 配置 SELinux 布尔值
  • 永久设置文件上下文(fcontexts)
  • 管理 SELinux 用户映射
  • 配置 SELinux 端口
示例 1:基础配置
- hosts: node1
  vars:
    selinux_state: permissive
    selinux_booleans:
      - name: httpd_enable_homedirs
        state: on
        persistent: yes
    selinux_fcontexts:
      - target: '/srv/www(/.*)?'
        setype: httpd_sys_content_t
        state: present
    selinux_restore_dirs:
      - /srv/www
    selinux_ports:
      - ports: 82
        setype: http_port_t
        proto: tcp
        state: present
  roles:
    - rhel-system-roles.selinux
示例 2:切换模式并重启
- hosts: node1,node2
  vars:
    selinux_state: enforcing
    selinux_booleans:
      - name: samba_enable_home_dirs
        state: on
  tasks:
    - block:
        - name: Apply SELinux role
          include_role:
            name: rhel-system-roles.selinux
      rescue:
        - name: Check for non-reboot failures
          fail:
          when: not selinux_reboot_required
        - name: Restart managed host
          reboot:
        - name: Reapply SELinux role
          include_role:
            name: rhel-system-roles.selinux

使用 Ansible Galaxy 管理角色

Ansible Galaxy 是社区仓库,提供大量角色和集合,方便自动化任务实现和复用

ansible-galaxy 常用命令

ansible-galaxy            // 管理角色与集合的入口命令
ansible-galaxy role -h    // 查看角色子命令
ansible-galaxy collection // 管理 Galaxy 集合

子命令示例:

  • init 初始化角色目录结构
  • install 安装角色(URL、tarball 或 Galaxy 名称)
  • remove 删除本地角色
  • delete 从 Galaxy 删除角色(不影响 GitHub 仓库)
  • list 列出本地已安装角色
  • search 按平台、标签、作者搜索角色
  • info 显示角色详细信息

本地角色操作

  • 列表

    ansible-galaxy list
    
  • 查看详细信息(离线)

    ansible-galaxy info apache --offline
    

在线搜索与安装

  • 搜索示例

    ansible-galaxy search --platforms=EL docker
    ansible-galaxy search --author geerlingguy haproxy
    
  • 安装单个角色

    ansible-galaxy install geerlingguy.haproxy
    
  • 批量安装(requires.yml)

    - name: docker
      src: https://github.com/geerlingguy/ansible-role-docker/archive/2.7.0.tar.gz
    - name: haproxy
      src: https://github.com/geerlingguy/ansible-role-haproxy/archive/1.1.2.tar.gz
    
    ansible-galaxy install -r requires.yml
    

角色管理其他命令

  • 登录 Galaxy:ansible-galaxy login
  • 注销 Galaxy:ansible-galaxy logout
  • 删除本地角色:ansible-galaxy remove <role_name> 或直接 rm -rf roles/<role_name>
  • 从 Galaxy 删除角色:ansible-galaxy delete <role_name>

综合案例:部署 Web 集群

环境需求
  • node3 作为 HAProxy 负载均衡
  • node1/node2 作为 Apache Web 服务器
步骤概要
  1. 编辑 inventory

    [loadbalancers]
    loadbalancer ansible_host=node3
    
    [webservers]
    node1
    node2
    
  2. 安装角色

    ansible-galaxy install geerlingguy.haproxy
    ansible-galaxy install geerlingguy.apache
    
  3. 查看角色变量说明

    vim roles/geerlingguy.haproxy/README.md
    
  4. 编写 Playbook

    deploy_web_cluster.yaml
    
    ---
    - name: deploy LB
      hosts: loadbalancers
      vars:
        haproxy_backend_servers:
          - name: node1
            address: 10.1.8.11:80
          - name: node2
            address: 10.1.8.12:80
      roles:
        - geerlingguy.haproxy
    
    - name: deploy web servers
      hosts: webservers
      roles:
        - geerlingguy.apache
      tasks:
        - name: prepare index.html
          copy:
            content: "Welcome to {{ ansible_fqdn }}\n"
            dest: /var/www/html/index.html
    
  5. 执行 Playbook

    ansible-playbook deploy_web_cluster.yaml
    
  6. 验证结果

    curl http://node3/   ## 轮询返回 node1 和 node2 欢迎页面
    

包含(include)与导入(import)

Ansible 支持通过 include/import 将 Playbook 或任务拆分成更小组件,提升复用性和可维护性

模块化方式

  • 动态包含:include_tasksinclude_role 等,在运行时加载内容
  • 静态导入:import_playbookimport_tasksimport_role 等,在解析阶段展开内容
  • 旧版 include 关键字将在 Ansible 2.12 中移除,建议使用新版 include/import

Playbook 级别:import_playbook

  • 用于导入完整 Playbook
  • 在主 Playbook 中按顺序执行多个子 Playbook

示例主 Playbook:

- name: prepare the web server
  import_playbook: pre_web.yml

- name: prepare the vsftpd server
  import_playbook: pre_vsftpd.yml

- name: prepare the database server
  import_playbook: pre_db.yml

Task 级别:import_tasks 与 include_tasks

  • import_tasks:静态导入子任务,解析时展开
  • include_tasks:动态包含子任务,运行时加载

示例主 Playbook:

---
- name: Install web server
  hosts: node1
  tasks:
    - name: load task file
      import_tasks: tasks.yaml
      ## 或者
      ## include_tasks: tasks.yaml

示例 tasks.yaml:

- name: Install httpd
  yum:
    name: httpd
    state: present

- name: Start httpd
  service:
    name: httpd
    state: started

include_vars 模块

用于在任务中动态加载外部变量文件

---
- name: Install web application packages
  hosts: node1
  tasks:
    - name: include variables
      include_vars: variables.yml

    - name: show loaded variables
      debug:
        msg: "{{ packages.web_package }} and {{ packages.db_package }} are loaded"
## variables.yml
---
packages:
  web_package: httpd
  db_package: mariadb-server

动态包含 与 静态导入 差异

  • 选项应用时机不同:静态导入会复制父选项到子任务,动态包含仅在运行时生效
  • --list-tasks/--list-tags:动态包含内部的标签和任务不会显示
  • 句柄触发:动态包含无法通过 notify 触发内部 handler,只能触发包含本身;静态导入可直接触发内部 handler
  • 语法检查:静态导入在预解析阶段检测错误;动态包含仅在执行时检测
  • 循环支持:include 与 loop 联合可对每个元素多次加载子任务;import 不支持循环加载
  • 变量生命周期:静态导入前解析,动态包含在运行时解析
场景示例
列出任务与标签
  • 使用 include_tasks 时,子任务的标签不在 --list-tags 输出中
  • 使用 import_tasks 时,子任务标签会出现在列表中
Handler 触发
- name: install httpd
  yum:
    name: httpd
  notify: restart_httpd

handlers:
  - name: restart_httpd
    include_tasks: tasks.yaml
    ## 或 import_tasks: tasks.yaml
  • include_tasks 时,notify 只能触发包含整体
  • import_tasks 时,可触发子任务里的指定 handler
Syntax Check
  • ansible-playbook --syntax-check 会在解析阶段检测 import_tasks 中的语法错误
  • include_tasks 中的错误只有执行到包含时才会报
Loop 加载
- name: install services
  include_tasks: tasks.yaml
  loop:
    - httpd
    - vsftpd

tasks.yaml:

- name: install {{ item }}
  yum:
    name: "{{ item }}"
    state: present
- name: start {{ item }}
  service:
    name: "{{ item }}"
    state: restarted
条件包含与变量作用域
- name: debug before
  debug: msg="{{ ansible_os_family }}"

- include_tasks: tasks.yaml
  when: ansible_os_family == "RedHat"

- name: debug after
  debug: msg="{{ ansible_os_family }}"

包含整体

  • import_tasks 时,可触发子任务里的指定 handler
Syntax Check
  • ansible-playbook --syntax-check 会在解析阶段检测 import_tasks 中的语法错误
  • include_tasks 中的错误只有执行到包含时才会报
Loop 加载
- name: install services
  include_tasks: tasks.yaml
  loop:
    - httpd
    - vsftpd

tasks.yaml:

- name: install {{ item }}
  yum:
    name: "{{ item }}"
    state: present
- name: start {{ item }}
  service:
    name: "{{ item }}"
    state: restarted
条件包含与变量作用域
- name: debug before
  debug: msg="{{ ansible_os_family }}"

- include_tasks: tasks.yaml
  when: ansible_os_family == "RedHat"

- name: debug after
  debug: msg="{{ ansible_os_family }}"

tasks.yaml 中可 set_fact 覆盖变量,在动态包含后可见;静态导入无法跨任务修改父级变量

Logo

一站式 AI 云服务平台

更多推荐