Ansible角色配置管理技能Skill ansible-roles

这个技能专注于使用Ansible角色来创建模块化、可重用的自动化代码,实现基础设施即代码(IaC)、配置管理和持续部署。关键词:Ansible、角色、自动化、DevOps、配置管理、基础设施即代码、模块化、可维护性、Ansible Galaxy、CI/CD。

DevOps 0 次安装 0 次浏览 更新于 3/25/2026

名称: ansible-roles 用户可调用: false 描述: 使用Ansible角色来结构化和重用代码,以实现模块化、可维护的自动化和配置管理。 允许工具: [Bash, Read]

Ansible 角色

使用Ansible角色来结构化和重用自动化代码,以实现模块化、可维护的基础设施。

角色目录结构

一个组织良好的Ansible角色遵循标准化的目录结构:

roles/
└── webserver/
    ├── README.md
    ├── defaults/
    │   └── main.yml
    ├── files/
    │   ├── nginx.conf
    │   └── ssl/
    │       ├── cert.pem
    │       └── key.pem
    ├── handlers/
    │   └── main.yml
    ├── meta/
    │   └── main.yml
    ├── tasks/
    │   ├── main.yml
    │   ├── install.yml
    │   ├── configure.yml
    │   └── security.yml
    ├── templates/
    │   ├── nginx.conf.j2
    │   └── site.conf.j2
    ├── tests/
    │   ├── inventory
    │   └── test.yml
    └── vars/
        └── main.yml

基本角色示例

tasks/main.yml

---
# 主要任务文件,用于webserver角色
- name: 包含操作系统特定变量
  include_vars: "{{ ansible_os_family }}.yml"

- name: 导入安装任务
  import_tasks: install.yml
  tags:
    - install
    - webserver

- name: 导入配置任务
  import_tasks: configure.yml
  tags:
    - configure
    - webserver

- name: 导入安全任务
  import_tasks: security.yml
  tags:
    - security
    - webserver

- name: 确保nginx正在运行
  service:
    name: "{{ nginx_service_name }}"
    state: started
    enabled: yes
  tags:
    - service
    - webserver

tasks/install.yml

---
# 用于webserver角色的安装任务
- name: 安装nginx及其依赖(Debian/Ubuntu)
  apt:
    name:
      - nginx
      - nginx-extras
      - python3-passlib
    state: present
    update_cache: yes
    cache_valid_time: 3600
  when: ansible_os_family == "Debian"

- name: 安装nginx及其依赖(RedHat/CentOS)
  yum:
    name:
      - nginx
      - nginx-mod-stream
      - python3-passlib
    state: present
    update_cache: yes
  when: ansible_os_family == "RedHat"

- name: 创建nginx目录
  file:
    path: "{{ item }}"
    state: directory
    owner: "{{ nginx_user }}"
    group: "{{ nginx_group }}"
    mode: '0755'
  loop:
    - "{{ nginx_conf_dir }}/sites-available"
    - "{{ nginx_conf_dir }}/sites-enabled"
    - "{{ nginx_log_dir }}"
    - "{{ nginx_cache_dir }}"
    - /var/www/html

- name: 安装certbot以支持SSL
  apt:
    name: certbot
    state: present
  when:
    - nginx_ssl_enabled
    - ansible_os_family == "Debian"

tasks/configure.yml

---
# 用于webserver角色的配置任务
- name: 部署主要nginx配置
  template:
    src: nginx.conf.j2
    dest: "{{ nginx_conf_dir }}/nginx.conf"
    owner: root
    group: root
    mode: '0644'
    validate: 'nginx -t -c %s'
    backup: yes
  notify:
    - Reload nginx
  tags:
    - config

- name: 部署站点配置
  template:
    src: site.conf.j2
    dest: "{{ nginx_conf_dir }}/sites-available/{{ item.name }}.conf"
    owner: root
    group: root
    mode: '0644'
    validate: 'nginx -t -c {{ nginx_conf_dir }}/nginx.conf'
  loop: "{{ nginx_sites }}"
  when: nginx_sites is defined
  notify:
    - Reload nginx

- name: 启用站点
  file:
    src: "{{ nginx_conf_dir }}/sites-available/{{ item.name }}.conf"
    dest: "{{ nginx_conf_dir }}/sites-enabled/{{ item.name }}.conf"
    state: link
  loop: "{{ nginx_sites }}"
  when:
    - nginx_sites is defined
    - item.enabled | default(true)
  notify:
    - Reload nginx

- name: 禁用默认站点
  file:
    path: "{{ nginx_conf_dir }}/sites-enabled/default"
    state: absent
  when: nginx_disable_default_site
  notify:
    - Reload nginx

- name: 配置日志轮转
  template:
    src: logrotate.j2
    dest: /etc/logrotate.d/nginx
    owner: root
    group: root
    mode: '0644'

tasks/security.yml

---
# 用于webserver角色的安全任务
- name: 生成dhparam文件
  command: openssl dhparam -out {{ nginx_conf_dir }}/dhparam.pem 2048
  args:
    creates: "{{ nginx_conf_dir }}/dhparam.pem"
  when: nginx_ssl_enabled

- name: 设置dhparam的安全权限
  file:
    path: "{{ nginx_conf_dir }}/dhparam.pem"
    owner: root
    group: root
    mode: '0600'
  when: nginx_ssl_enabled

- name: 配置防火墙规则(ufw)
  ufw:
    rule: allow
    port: "{{ item }}"
    proto: tcp
  loop:
    - "80"
    - "443"
  when:
    - nginx_configure_firewall
    - ansible_os_family == "Debian"

- name: 配置防火墙规则(firewalld)
  firewalld:
    service: "{{ item }}"
    permanent: yes
    state: enabled
    immediate: yes
  loop:
    - http
    - https
  when:
    - nginx_configure_firewall
    - ansible_os_family == "RedHat"

- name: 创建基本认证文件
  htpasswd:
    path: "{{ nginx_conf_dir }}/.htpasswd"
    name: "{{ item.username }}"
    password: "{{ item.password }}"
    owner: root
    group: "{{ nginx_group }}"
    mode: '0640'
  loop: "{{ nginx_basic_auth_users }}"
  when: nginx_basic_auth_users is defined
  no_log: yes

角色变量

defaults/main.yml

---
# 用于webserver角色的默认变量
# 这些变量可以在playbooks或inventory中被覆盖

# 包和服务名称
nginx_package_name: nginx
nginx_service_name: nginx

# 用户和组
nginx_user: www-data
nginx_group: www-data

# 目录
nginx_conf_dir: /etc/nginx
nginx_log_dir: /var/log/nginx
nginx_cache_dir: /var/cache/nginx
nginx_pid_file: /var/run/nginx.pid

# 主要配置
nginx_worker_processes: auto
nginx_worker_connections: 1024
nginx_keepalive_timeout: 65
nginx_client_max_body_size: 10m

# 性能调优
nginx_sendfile: on
nginx_tcp_nopush: on
nginx_tcp_nodelay: on
nginx_gzip: on
nginx_gzip_types:
  - text/plain
  - text/css
  - application/json
  - application/javascript
  - text/xml
  - application/xml

# 安全
nginx_ssl_enabled: no
nginx_ssl_protocols: "TLSv1.2 TLSv1.3"
nginx_ssl_ciphers: "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256"
nginx_ssl_prefer_server_ciphers: on
nginx_disable_default_site: yes
nginx_configure_firewall: yes
nginx_server_tokens: off

# 站点配置
nginx_sites: []
# 示例:
# nginx_sites:
#   - name: example.com
#     server_name: example.com www.example.com
#     root: /var/www/example.com
#     enabled: yes
#     ssl: yes

# 基本认证
# nginx_basic_auth_users:
#   - username: admin
#     password: secretpassword

vars/main.yml

---
# 不应被覆盖的变量
nginx_conf_path: "{{ nginx_conf_dir }}/nginx.conf"
nginx_error_log: "{{ nginx_log_dir }}/error.log"
nginx_access_log: "{{ nginx_log_dir }}/access.log"

# 通过include_vars加载的操作系统特定覆盖

vars/Debian.yml

---
nginx_user: www-data
nginx_group: www-data
nginx_conf_dir: /etc/nginx
nginx_service_name: nginx

vars/RedHat.yml

---
nginx_user: nginx
nginx_group: nginx
nginx_conf_dir: /etc/nginx
nginx_service_name: nginx

角色处理器

handlers/main.yml

---
# 用于webserver角色的处理器
- name: 重新加载nginx
  service:
    name: "{{ nginx_service_name }}"
    state: reloaded
  listen: "reload nginx"

- name: 重启nginx
  service:
    name: "{{ nginx_service_name }}"
    state: restarted
  listen: "restart nginx"

- name: 验证nginx配置
  command: nginx -t
  changed_when: false
  listen: "validate nginx"

- name: 重新加载防火墙
  service:
    name: ufw
    state: reloaded
  when: ansible_os_family == "Debian"
  listen: "reload firewall"

角色模板

templates/nginx.conf.j2

# {{ ansible_managed }}
user {{ nginx_user }};
worker_processes {{ nginx_worker_processes }};
pid {{ nginx_pid_file }};

events {
    worker_connections {{ nginx_worker_connections }};
    use epoll;
    multi_accept on;
}

http {
    ##
    # 基本设置
    ##
    sendfile {{ nginx_sendfile | ternary('on', 'off') }};
    tcp_nopush {{ nginx_tcp_nopush | ternary('on', 'off') }};
    tcp_nodelay {{ nginx_tcp_nodelay | ternary('on', 'off') }};
    keepalive_timeout {{ nginx_keepalive_timeout }};
    types_hash_max_size 2048;
    server_tokens {{ nginx_server_tokens | ternary('on', 'off') }};
    client_max_body_size {{ nginx_client_max_body_size }};

    include {{ nginx_conf_dir }}/mime.types;
    default_type application/octet-stream;

    ##
    # SSL 设置
    ##
{% if nginx_ssl_enabled %}
    ssl_protocols {{ nginx_ssl_protocols }};
    ssl_ciphers {{ nginx_ssl_ciphers }};
    ssl_prefer_server_ciphers {{ nginx_ssl_prefer_server_ciphers | ternary('on', 'off') }};
    ssl_dhparam {{ nginx_conf_dir }}/dhparam.pem;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;
{% endif %}

    ##
    # 日志设置
    ##
    access_log {{ nginx_access_log }};
    error_log {{ nginx_error_log }};

    ##
    # Gzip 设置
    ##
    gzip {{ nginx_gzip | ternary('on', 'off') }};
{% if nginx_gzip %}
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_types {{ nginx_gzip_types | join(' ') }};
{% endif %}

    ##
    # 虚拟主机配置
    ##
    include {{ nginx_conf_dir }}/sites-enabled/*;
}

templates/site.conf.j2

# {{ ansible_managed }}
# 站点: {{ item.name }}

{% if item.ssl | default(false) %}
# 重定向HTTP到HTTPS
server {
    listen 80;
    listen [::]:80;
    server_name {{ item.server_name }};
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name {{ item.server_name }};

    ssl_certificate {{ item.ssl_cert | default('/etc/letsencrypt/live/' + item.name + '/fullchain.pem') }};
    ssl_certificate_key {{ item.ssl_key | default('/etc/letsencrypt/live/' + item.name + '/privkey.pem') }};
{% else %}
server {
    listen 80;
    listen [::]:80;
    server_name {{ item.server_name }};
{% endif %}

    root {{ item.root | default('/var/www/' + item.name) }};
    index {{ item.index | default('index.html index.htm') }};

{% if item.access_log is defined %}
    access_log {{ item.access_log }};
{% endif %}
{% if item.error_log is defined %}
    error_log {{ item.error_log }};
{% endif %}

{% if item.basic_auth | default(false) %}
    auth_basic "限制访问";
    auth_basic_user_file {{ nginx_conf_dir }}/.htpasswd;
{% endif %}

    location / {
{% if item.proxy_pass is defined %}
        proxy_pass {{ item.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;
{% else %}
        try_files $uri $uri/ =404;
{% endif %}
    }

{% if item.locations is defined %}
{% for location in item.locations %}
    location {{ location.path }} {
{% if location.proxy_pass is defined %}
        proxy_pass {{ location.proxy_pass }};
{% endif %}
{% if location.alias is defined %}
        alias {{ location.alias }};
{% endif %}
{% if location.return is defined %}
        return {{ location.return }};
{% endif %}
    }
{% endfor %}
{% endif %}

    # 安全头
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;
}

角色依赖

meta/main.yml

---
galaxy_info:
  role_name: webserver
  author: 您的组织
  description: 安装和配置nginx web服务器
  company: 您的公司
  license: MIT
  min_ansible_version: 2.9

  platforms:
    - name: Ubuntu
      versions:
        - focal
        - jammy
    - name: Debian
      versions:
        - buster
        - bullseye
    - name: EL
      versions:
        - 7
        - 8
        - 9

  galaxy_tags:
    - web
    - nginx
    - webserver
    - http

dependencies:
  - role: common
    vars:
      common_packages:
        - curl
        - wget

  - role: firewall
    when: nginx_configure_firewall

allow_duplicates: no

在Playbooks中使用角色

简单角色使用

---
- name: 配置web服务器
  hosts: webservers
  become: yes

  roles:
    - webserver

带变量的角色

---
- name: 使用自定义设置配置web服务器
  hosts: webservers
  become: yes

  roles:
    - role: webserver
      vars:
        nginx_worker_processes: 4
        nginx_ssl_enabled: yes
        nginx_sites:
          - name: example.com
            server_name: example.com www.example.com
            root: /var/www/example.com
            ssl: yes
            ssl_cert: /etc/ssl/certs/example.com.crt
            ssl_key: /etc/ssl/private/example.com.key

带标签的角色

---
- name: 配置基础设施
  hosts: all
  become: yes

  roles:
    - role: webserver
      tags:
        - web
        - nginx

    - role: database
      tags:
        - db
        - postgres

前和后任务

---
- name: 部署web应用程序
  hosts: webservers
  become: yes

  pre_tasks:
    - name: 宣布部署
      debug:
        msg: "开始部署到 {{ inventory_hostname }}"

    - name: 检查磁盘空间
      command: df -h /
      register: disk_space
      changed_when: false

  roles:
    - webserver
    - monitoring

  post_tasks:
    - name: 验证nginx是否响应
      uri:
        url: http://localhost
        status_code: 200
      retries: 3
      delay: 5

    - name: 通知完成
      debug:
        msg: "部署成功完成"

角色包含方法

静态导入

---
- name: 配置服务器
  hosts: all

  tasks:
    - name: 导入webserver角色
      import_role:
        name: webserver
      vars:
        nginx_worker_processes: 2
      tags:
        - webserver

动态包含

---
- name: 基于条件配置服务器
  hosts: all

  tasks:
    - name: 为web服务器包含webserver角色
      include_role:
        name: webserver
      when: "'webservers' in group_names"

    - name: 为数据库服务器包含database角色
      include_role:
        name: database
      when: "'databases' in group_names"

使用任务文件导入

---
- name: 自定义安装工作流
  hosts: webservers

  tasks:
    - name: 运行预安装检查
      import_role:
        name: webserver
        tasks_from: preflight

    - name: 安装nginx
      import_role:
        name: webserver
        tasks_from: install

    - name: 配置nginx
      import_role:
        name: webserver
        tasks_from: configure

使用Ansible Galaxy创建角色

初始化新角色

# 创建角色结构
ansible-galaxy init roles/myapp

# 使用自定义模板创建角色
ansible-galaxy init --init-path roles/ myapp

# 列出角色文件
tree roles/myapp

从Galaxy安装角色

# 安装角色
ansible-galaxy install geerlingguy.nginx

# 安装特定版本
ansible-galaxy install geerlingguy.nginx,2.8.0

# 从requirements文件安装
ansible-galaxy install -r requirements.yml

requirements.yml

---
# 从Ansible Galaxy安装
- name: geerlingguy.nginx
  version: 2.8.0

- name: geerlingguy.postgresql
  version: 3.4.0

# 从Git仓库安装
- name: custom-app
  src: https://github.com/yourorg/ansible-role-custom-app.git
  scm: git
  version: main

# 从本地路径安装
- name: internal-role
  src: /path/to/roles/internal-role

高级角色模式

带多个入口点的角色

# roles/webserver/tasks/main.yml
---
- name: 默认任务流
  import_tasks: "{{ webserver_task_flow | default('standard') }}.yml"
# roles/webserver/tasks/standard.yml
---
- import_tasks: install.yml
- import_tasks: configure.yml
- import_tasks: security.yml
# roles/webserver/tasks/minimal.yml
---
- import_tasks: install.yml
- import_tasks: configure.yml

条件角色执行

---
- name: 使用条件角色配置服务器
  hosts: all
  become: yes

  roles:
    - role: webserver
      when:
        - ansible_os_family == "Debian"
        - inventory_hostname in groups['webservers']

    - role: webserver-nginx
      when: webserver_type == "nginx"

    - role: webserver-apache
      when: webserver_type == "apache"

嵌套角色依赖

# roles/application/meta/main.yml
---
dependencies:
  - role: webserver
    vars:
      nginx_sites:
        - name: "{{ app_name }}"
          server_name: "{{ app_domain }}"
          proxy_pass: "http://localhost:{{ app_port }}"

  - role: database
    vars:
      db_name: "{{ app_db_name }}"
      db_user: "{{ app_db_user }}"

  - role: monitoring
    vars:
      monitor_services:
        - nginx
        - "{{ app_name }}"

何时使用此技能

使用ansible-roles技能当您需要:

  • 在多个playbooks和项目之间结构化和重用自动化代码
  • 实现模块化基础设施即代码,具有清晰的职责分离
  • 在团队或项目之间共享自动化逻辑
  • 为通用基础设施模式创建可分发的自动化包
  • 将复杂的playbooks组织成可管理、可测试的组件
  • 使用变量优先级实现基于角色的配置管理
  • 构建具有角色依赖的分层基础设施
  • 独立于playbooks进行版本控制自动化逻辑
  • 为一致性创建标准化的基础设施组件
  • 通过可重用角色实现安全和合规要求
  • 为您的组织构建内部自动化库
  • 从Ansible Galaxy贡献或使用社区自动化
  • 在集成之前独立测试基础设施组件
  • 为开发、测试和生产实现不同的配置
  • 通过角色元数据创建自文档化的基础设施

最佳实践

  1. 遵循标准目录结构 - 使用ansible-galaxy init以适当的组织创建角色
  2. 明智地使用默认值 - 将可覆盖的变量放在defaults/main.yml中,不可覆盖的放在vars/main.yml中
  3. 充分文档化 - 包括全面的README.md,带有使用示例和变量文档
  4. 保持角色专注 - 每个角色应具有单一、明确定义的目的
  5. 使用角色依赖 - 在meta/main.yml中声明依赖,而不是在playbooks中
  6. 适当标记 - 为任务应用有意义的标签,以便选择性执行
  7. 实现幂等性 - 确保角色可以安全运行多次
  8. 版本化您的角色 - 使用语义版本控制进行角色发布
  9. 独立测试角色 - 在tests/目录中包含测试playbooks
  10. 使用模板进行配置 - 为提高灵活性,优先使用Jinja2模板而非静态文件
  11. 实现操作系统检测 - 使用ansible_os_family实现跨平台兼容性
  12. 保护敏感数据 - 在角色变量中使用ansible-vault存储密码和秘密
  13. 正确使用处理器 - 仅当配置更改时通知处理器
  14. 验证配置 - 在template/copy模块中使用validate参数
  15. 清晰命名任务 - 使用描述性名称,解释每个任务的作用

常见陷阱

  1. 过于复杂的角色 - 试图让一个角色做太多事情
  2. 变量命名不当 - 使用与其他角色冲突的通用名称
  3. 缺少角色前缀 - 未在角色变量前添加角色名称
  4. 忽略变量优先级 - 不理解Ansible如何解决变量冲突
  5. 硬编码值 - 嵌入环境特定的值,而不是使用变量
  6. 缺少依赖声明 - 未在meta/main.yml中声明角色依赖
  7. 无验证 - 部署配置时没有验证检查
  8. 跳过测试 - 未包括测试playbooks或场景
  9. 处理器设计不当 - 不必要地或不重启服务
  10. 缺少操作系统支持 - 假设所有目标系统相同
  11. 无备份策略 - 更改前不备份配置
  12. 忽略幂等性 - 使用command/shell模块时没有适当的防护
  13. 缺少标签 - 未标记任务以便选择性执行
  14. 模板实践不当 - 在模板中使用复杂逻辑而非变量
  15. 无版本控制 - 不进行角色版本控制或跟踪更改

资源