8.1 Roles 概述

8.1.1 什么是 Roles

Roles(角色)是 Ansible 中用于组织和重用代码的最佳方式。Role 将相关的任务、变量、文件、模板和处理器组织在一个标准的目录结构中,使得 Playbook 更加模块化、可维护和可重用。

Roles 的优势: - 模块化:将复杂的配置分解为独立的组件 - 重用性:可以在多个 Playbook 中重用 - 可维护性:标准化的目录结构便于维护 - 协作性:团队成员可以独立开发不同的 Role - 测试性:可以独立测试每个 Role - 分享性:可以通过 Ansible Galaxy 分享

8.1.2 Role 目录结构

# 标准 Role 目录结构
roles/
└── nginx/
    ├── README.md              # Role 说明文档
    ├── meta/
    │   └── main.yml           # Role 元数据和依赖
    ├── defaults/
    │   └── main.yml           # 默认变量(最低优先级)
    ├── vars/
    │   └── main.yml           # Role 变量(高优先级)
    ├── tasks/
    │   ├── main.yml           # 主要任务文件
    │   ├── install.yml        # 安装任务
    │   ├── configure.yml      # 配置任务
    │   └── service.yml        # 服务管理任务
    ├── handlers/
    │   └── main.yml           # 处理器
    ├── templates/
    │   ├── nginx.conf.j2      # Jinja2 模板
    │   └── site.conf.j2
    ├── files/
    │   ├── ssl_cert.pem       # 静态文件
    │   └── custom_config
    └── tests/
        ├── inventory          # 测试用的 Inventory
        └── test.yml           # 测试 Playbook

8.1.3 Role 加载顺序

# Role 中各组件的加载顺序
# 1. meta/main.yml(依赖关系)
# 2. defaults/main.yml(默认变量)
# 3. vars/main.yml(Role 变量)
# 4. tasks/main.yml(任务)
# 5. handlers/main.yml(处理器)
# 6. templates/ 和 files/(按需加载)

8.2 创建基础 Role

8.2.1 使用 ansible-galaxy 创建 Role

# 创建新的 Role
ansible-galaxy init nginx
ansible-galaxy init mysql
ansible-galaxy init common

# 在指定目录创建 Role
ansible-galaxy init --init-path ./roles nginx

# 查看创建的 Role 结构
tree roles/nginx/

8.2.2 简单的 Nginx Role 示例

# roles/nginx/meta/main.yml
# Role 元数据
galaxy_info:
  author: DevOps Team
  description: Nginx web server installation and configuration
  company: Example Corp
  license: MIT
  min_ansible_version: 2.9
  platforms:
    - name: Ubuntu
      versions:
        - 18.04
        - 20.04
        - 22.04
    - name: CentOS
      versions:
        - 7
        - 8
  galaxy_tags:
    - nginx
    - webserver
    - proxy

# Role 依赖
dependencies:
  - role: common
    vars:
      common_packages:
        - curl
        - wget
# roles/nginx/defaults/main.yml
# 默认变量(最低优先级)
---
# Nginx 基本配置
nginx_user: www-data
nginx_worker_processes: auto
nginx_worker_connections: 1024
nginx_keepalive_timeout: 65
nginx_client_max_body_size: 64m

# 端口配置
nginx_http_port: 80
nginx_https_port: 443

# 日志配置
nginx_access_log: /var/log/nginx/access.log
nginx_error_log: /var/log/nginx/error.log
nginx_log_level: warn

# SSL 配置
nginx_ssl_enabled: false
nginx_ssl_certificate: /etc/ssl/certs/nginx.crt
nginx_ssl_certificate_key: /etc/ssl/private/nginx.key
nginx_ssl_protocols:
  - TLSv1.2
  - TLSv1.3
nginx_ssl_ciphers: ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256

# 虚拟主机配置
nginx_sites:
  - name: default
    server_name: _
    root: /var/www/html
    index: index.html index.htm
    enabled: true

# 上游服务器配置
nginx_upstreams: []

# 额外的配置文件
nginx_extra_configs: []

# 服务配置
nginx_service_enabled: true
nginx_service_state: started

# 防火墙配置
nginx_firewall_enabled: true
# roles/nginx/vars/main.yml
# Role 变量(高优先级)
---
# 操作系统特定的变量
nginx_packages:
  RedHat:
    - nginx
  Debian:
    - nginx

nginx_service_name:
  RedHat: nginx
  Debian: nginx

nginx_config_dir:
  RedHat: /etc/nginx
  Debian: /etc/nginx

nginx_sites_available_dir:
  RedHat: /etc/nginx/conf.d
  Debian: /etc/nginx/sites-available

nginx_sites_enabled_dir:
  RedHat: /etc/nginx/conf.d
  Debian: /etc/nginx/sites-enabled

nginx_user_map:
  RedHat: nginx
  Debian: www-data

nginx_log_dir: /var/log/nginx
nginx_pid_file: /var/run/nginx.pid
# roles/nginx/tasks/main.yml
# 主要任务文件
---
- name: Include OS-specific variables
  include_vars: "{{ ansible_os_family }}.yml"
  when: ansible_os_family in ['RedHat', 'Debian']

- name: Install Nginx
  include_tasks: install.yml
  tags: [nginx, install]

- name: Configure Nginx
  include_tasks: configure.yml
  tags: [nginx, configure]

- name: Manage Nginx service
  include_tasks: service.yml
  tags: [nginx, service]

- name: Configure firewall
  include_tasks: firewall.yml
  when: nginx_firewall_enabled
  tags: [nginx, firewall]
# roles/nginx/tasks/install.yml
# 安装任务
---
- name: Update package cache (Debian/Ubuntu)
  apt:
    update_cache: yes
    cache_valid_time: 3600
  when: ansible_os_family == 'Debian'

- name: Install EPEL repository (RHEL/CentOS)
  yum:
    name: epel-release
    state: present
  when: ansible_os_family == 'RedHat'

- name: Install Nginx packages
  package:
    name: "{{ nginx_packages[ansible_os_family] }}"
    state: present
  notify: restart nginx

- name: Ensure Nginx directories exist
  file:
    path: "{{ item }}"
    state: directory
    owner: root
    group: root
    mode: '0755'
  loop:
    - "{{ nginx_config_dir[ansible_os_family] }}"
    - "{{ nginx_sites_available_dir[ansible_os_family] }}"
    - "{{ nginx_sites_enabled_dir[ansible_os_family] }}"
    - "{{ nginx_log_dir }}"

- name: Create Nginx user
  user:
    name: "{{ nginx_user_map[ansible_os_family] }}"
    system: yes
    shell: /bin/false
    home: /var/cache/nginx
    createhome: no
  when: ansible_os_family == 'RedHat'
# roles/nginx/tasks/configure.yml
# 配置任务
---
- name: Generate main Nginx configuration
  template:
    src: nginx.conf.j2
    dest: "{{ nginx_config_dir[ansible_os_family] }}/nginx.conf"
    owner: root
    group: root
    mode: '0644'
    backup: yes
  notify: reload nginx
  tags: [config]

- name: Remove default site (Debian/Ubuntu)
  file:
    path: "{{ nginx_sites_enabled_dir[ansible_os_family] }}/default"
    state: absent
  when: ansible_os_family == 'Debian'
  notify: reload nginx

- name: Configure virtual hosts
  template:
    src: site.conf.j2
    dest: "{{ nginx_sites_available_dir[ansible_os_family] }}/{{ item.name }}.conf"
    owner: root
    group: root
    mode: '0644'
  loop: "{{ nginx_sites }}"
  notify: reload nginx
  tags: [sites]

- name: Enable virtual hosts (Debian/Ubuntu)
  file:
    src: "{{ nginx_sites_available_dir[ansible_os_family] }}/{{ item.name }}.conf"
    dest: "{{ nginx_sites_enabled_dir[ansible_os_family] }}/{{ item.name }}.conf"
    state: link
  loop: "{{ nginx_sites }}"
  when: 
    - ansible_os_family == 'Debian'
    - item.enabled | default(true)
  notify: reload nginx
  tags: [sites]

- name: Configure upstream servers
  template:
    src: upstream.conf.j2
    dest: "{{ nginx_config_dir[ansible_os_family] }}/conf.d/upstream.conf"
    owner: root
    group: root
    mode: '0644'
  when: nginx_upstreams | length > 0
  notify: reload nginx
  tags: [upstream]

- name: Copy SSL certificates
  copy:
    src: "{{ item.src }}"
    dest: "{{ item.dest }}"
    owner: root
    group: root
    mode: "{{ item.mode | default('0644') }}"
  loop:
    - { src: "{{ nginx_ssl_certificate }}", dest: "{{ nginx_ssl_certificate }}", mode: '0644' }
    - { src: "{{ nginx_ssl_certificate_key }}", dest: "{{ nginx_ssl_certificate_key }}", mode: '0600' }
  when: nginx_ssl_enabled
  notify: reload nginx
  tags: [ssl]

- name: Copy extra configuration files
  copy:
    src: "{{ item.src }}"
    dest: "{{ nginx_config_dir[ansible_os_family] }}/conf.d/{{ item.name }}"
    owner: root
    group: root
    mode: '0644'
  loop: "{{ nginx_extra_configs }}"
  notify: reload nginx
  tags: [extra_config]

- name: Test Nginx configuration
  command: nginx -t
  register: nginx_config_test
  changed_when: false
  failed_when: nginx_config_test.rc != 0
  tags: [config, test]
# roles/nginx/tasks/service.yml
# 服务管理任务
---
- name: Start and enable Nginx service
  service:
    name: "{{ nginx_service_name[ansible_os_family] }}"
    state: "{{ nginx_service_state }}"
    enabled: "{{ nginx_service_enabled }}"
  tags: [service]

- name: Check if Nginx is running
  uri:
    url: "http://localhost:{{ nginx_http_port }}"
    method: GET
    status_code: [200, 301, 302, 403, 404]
  register: nginx_health_check
  retries: 3
  delay: 5
  tags: [service, health_check]

- name: Display Nginx status
  debug:
    msg: "Nginx is {{ 'running' if nginx_health_check.status is defined else 'not responding' }}"
  tags: [service, health_check]
# roles/nginx/tasks/firewall.yml
# 防火墙配置任务
---
- name: Configure firewall for HTTP (firewalld)
  firewalld:
    service: http
    permanent: yes
    state: enabled
    immediate: yes
  when: 
    - ansible_os_family == 'RedHat'
    - ansible_service_mgr == 'systemd'
  tags: [firewall]

- name: Configure firewall for HTTPS (firewalld)
  firewalld:
    service: https
    permanent: yes
    state: enabled
    immediate: yes
  when: 
    - ansible_os_family == 'RedHat'
    - ansible_service_mgr == 'systemd'
    - nginx_ssl_enabled
  tags: [firewall]

- name: Configure firewall for HTTP (ufw)
  ufw:
    rule: allow
    port: "{{ nginx_http_port }}"
    proto: tcp
  when: ansible_os_family == 'Debian'
  tags: [firewall]

- name: Configure firewall for HTTPS (ufw)
  ufw:
    rule: allow
    port: "{{ nginx_https_port }}"
    proto: tcp
  when: 
    - ansible_os_family == 'Debian'
    - nginx_ssl_enabled
  tags: [firewall]
# roles/nginx/handlers/main.yml
# 处理器
---
- name: restart nginx
  service:
    name: "{{ nginx_service_name[ansible_os_family] }}"
    state: restarted
  listen: restart nginx

- name: reload nginx
  service:
    name: "{{ nginx_service_name[ansible_os_family] }}"
    state: reloaded
  listen: reload nginx

- name: stop nginx
  service:
    name: "{{ nginx_service_name[ansible_os_family] }}"
    state: stopped
  listen: stop nginx

- name: start nginx
  service:
    name: "{{ nginx_service_name[ansible_os_family] }}"
    state: started
  listen: start nginx

8.2.3 Nginx 配置模板

{# roles/nginx/templates/nginx.conf.j2 #}
# Nginx 主配置文件
# 由 Ansible 自动生成,请勿手动修改

user {{ nginx_user_map[ansible_os_family] }};
worker_processes {{ nginx_worker_processes }};
pid {{ nginx_pid_file }};

# 错误日志配置
error_log {{ nginx_error_log }} {{ nginx_log_level }};

# 事件模块配置
events {
    worker_connections {{ nginx_worker_connections }};
    use epoll;
    multi_accept on;
}

# HTTP 模块配置
http {
    # 基本设置
    include /etc/nginx/mime.types;
    default_type application/octet-stream;
    
    # 日志格式
    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for"';
    
    # 访问日志
    access_log {{ nginx_access_log }} main;
    
    # 性能优化
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout {{ nginx_keepalive_timeout }};
    types_hash_max_size 2048;
    client_max_body_size {{ nginx_client_max_body_size }};
    
    # 隐藏版本信息
    server_tokens off;
    
    # Gzip 压缩
    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_types
        text/plain
        text/css
        text/xml
        text/javascript
        application/json
        application/javascript
        application/xml+rss
        application/atom+xml
        image/svg+xml;
    
{% if nginx_ssl_enabled %}
    # SSL 配置
    ssl_protocols {{ nginx_ssl_protocols | join(' ') }};
    ssl_ciphers {{ nginx_ssl_ciphers }};
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;
{% endif %}
    
    # 包含其他配置文件
    include {{ nginx_config_dir[ansible_os_family] }}/conf.d/*.conf;
{% if ansible_os_family == 'Debian' %}
    include {{ nginx_sites_enabled_dir[ansible_os_family] }}/*;
{% endif %}
}
{# roles/nginx/templates/site.conf.j2 #}
# 虚拟主机配置:{{ item.name }}
# 由 Ansible 自动生成,请勿手动修改

server {
    listen {{ nginx_http_port }}{% if item.default | default(false) %} default_server{% endif %};
{% if nginx_ssl_enabled and item.ssl | default(false) %}
    listen {{ nginx_https_port }} ssl{% if item.default | default(false) %} default_server{% endif %};
{% endif %}
    
    server_name {{ item.server_name | default('_') }};
    
{% if item.root is defined %}
    root {{ item.root }};
{% endif %}
{% if item.index is defined %}
    index {{ item.index }};
{% endif %}
    
{% if nginx_ssl_enabled and item.ssl | default(false) %}
    # SSL 配置
    ssl_certificate {{ nginx_ssl_certificate }};
    ssl_certificate_key {{ nginx_ssl_certificate_key }};
    
    # HSTS
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
{% endif %}
    
    # 安全头
    add_header X-Frame-Options DENY always;
    add_header X-Content-Type-Options nosniff always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    
{% 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.locations is defined %}
{% for location in item.locations %}
    location {{ location.path | default('/') }} {
{% if location.proxy_pass is defined %}
        proxy_pass {{ location.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;
{% endif %}
{% if location.try_files is defined %}
        try_files {{ location.try_files }};
{% endif %}
{% if location.return is defined %}
        return {{ location.return }};
{% endif %}
{% if location.deny is defined %}
        deny {{ location.deny }};
{% endif %}
{% if location.allow is defined %}
        allow {{ location.allow }};
{% endif %}
    }
{% endfor %}
{% else %}
    location / {
        try_files $uri $uri/ =404;
    }
{% endif %}
    
{% if item.error_pages is defined %}
{% for error_page in item.error_pages %}
    error_page {{ error_page.codes }} {{ error_page.page }};
{% endfor %}
{% endif %}
}

{% if nginx_ssl_enabled and item.ssl | default(false) and item.redirect_http | default(true) %}
# HTTP 到 HTTPS 重定向
server {
    listen {{ nginx_http_port }};
    server_name {{ item.server_name | default('_') }};
    return 301 https://$server_name$request_uri;
}
{% endif %}
{# roles/nginx/templates/upstream.conf.j2 #}
# 上游服务器配置
# 由 Ansible 自动生成,请勿手动修改

{% for upstream in nginx_upstreams %}
upstream {{ upstream.name }} {
{% if upstream.method is defined %}
    {{ upstream.method }};
{% endif %}
{% for server in upstream.servers %}
    server {{ server.address }}{% if server.weight is defined %} weight={{ server.weight }}{% endif %}{% if server.max_fails is defined %} max_fails={{ server.max_fails }}{% endif %}{% if server.fail_timeout is defined %} fail_timeout={{ server.fail_timeout }}{% endif %}{% if server.backup | default(false) %} backup{% endif %}{% if server.down | default(false) %} down{% endif %};
{% endfor %}
{% if upstream.keepalive is defined %}
    keepalive {{ upstream.keepalive }};
{% endif %}
}

{% endfor %}

8.3 Role 依赖管理

8.3.1 定义 Role 依赖

# roles/webapp/meta/main.yml
# Web 应用 Role 的依赖配置
galaxy_info:
  author: DevOps Team
  description: Web application deployment role
  min_ansible_version: 2.9
  platforms:
    - name: Ubuntu
      versions: [18.04, 20.04, 22.04]

# 依赖其他 Role
dependencies:
  # 基础系统配置
  - role: common
    vars:
      common_packages:
        - curl
        - wget
        - unzip
        - git
      common_users:
        - name: webapp
          shell: /bin/bash
          groups: [www-data]
  
  # 数据库
  - role: mysql
    vars:
      mysql_databases:
        - name: webapp_db
          encoding: utf8mb4
          collation: utf8mb4_unicode_ci
      mysql_users:
        - name: webapp_user
          password: "{{ vault_webapp_db_password }}"
          priv: "webapp_db.*:ALL"
    when: webapp_database_type == 'mysql'
  
  # Web 服务器
  - role: nginx
    vars:
      nginx_sites:
        - name: webapp
          server_name: "{{ webapp_domain }}"
          root: "{{ webapp_document_root }}"
          ssl: "{{ webapp_ssl_enabled }}"
          locations:
            - path: /
              try_files: $uri $uri/ /index.php?$query_string
            - path: ~ \.php$
              proxy_pass: http://php_backend
  
  # PHP-FPM
  - role: php
    vars:
      php_version: "{{ webapp_php_version }}"
      php_extensions:
        - mysql
        - gd
        - curl
        - xml
        - mbstring
        - zip
    when: webapp_backend_type == 'php'

8.3.2 条件依赖

# roles/monitoring/meta/main.yml
# 监控系统的条件依赖
dependencies:
  # 基础监控(总是安装)
  - role: node_exporter
  
  # Prometheus(仅在监控服务器上安装)
  - role: prometheus
    when: inventory_hostname in groups['monitoring_servers']
  
  # Grafana(仅在可视化服务器上安装)
  - role: grafana
    when: 
      - inventory_hostname in groups['grafana_servers']
      - monitoring_grafana_enabled | default(false)
  
  # AlertManager(仅在告警服务器上安装)
  - role: alertmanager
    when: 
      - inventory_hostname in groups['alertmanager_servers']
      - monitoring_alerting_enabled | default(true)
  
  # 日志收集(根据环境决定)
  - role: filebeat
    vars:
      filebeat_output: elasticsearch
      filebeat_elasticsearch_hosts: "{{ elasticsearch_hosts }}"
    when: 
      - log_shipping_enabled | default(false)
      - environment != 'development'

8.3.3 Role 版本管理

# requirements.yml
# 外部 Role 依赖管理
---
# 从 Ansible Galaxy 安装
- name: geerlingguy.nginx
  version: 3.1.4

- name: geerlingguy.mysql
  version: 4.3.4

- name: geerlingguy.php
  version: 4.9.3

# 从 Git 仓库安装
- name: custom_monitoring
  src: https://github.com/company/ansible-monitoring-role.git
  version: v2.1.0
  scm: git

- name: security_hardening
  src: git+ssh://git@gitlab.company.com/ansible/security-role.git
  version: main
  scm: git

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

# 从压缩包安装
- name: third_party_role
  src: https://releases.example.com/ansible-role-v1.0.tar.gz
# 安装 Role 依赖
ansible-galaxy install -r requirements.yml

# 强制重新安装
ansible-galaxy install -r requirements.yml --force

# 安装到指定目录
ansible-galaxy install -r requirements.yml -p ./roles

# 列出已安装的 Role
ansible-galaxy list

# 删除 Role
ansible-galaxy remove geerlingguy.nginx

8.4 高级 Role 开发

8.4.1 多平台支持

# roles/docker/vars/RedHat.yml
# RedHat 系列系统变量
---
docker_packages:
  - docker-ce
  - docker-ce-cli
  - containerd.io

docker_service_name: docker
docker_config_dir: /etc/docker
docker_data_dir: /var/lib/docker

docker_repo_url: https://download.docker.com/linux/centos/docker-ce.repo
docker_repo_gpg_key: https://download.docker.com/linux/centos/gpg

docker_users_group: docker

# 系统特定配置
docker_storage_driver: overlay2
docker_log_driver: json-file
docker_log_opts:
  max-size: 10m
  max-file: 3
# roles/docker/vars/Debian.yml
# Debian 系列系统变量
---
docker_packages:
  - docker-ce
  - docker-ce-cli
  - containerd.io
  - docker-compose-plugin

docker_service_name: docker
docker_config_dir: /etc/docker
docker_data_dir: /var/lib/docker

docker_repo_url: "deb [arch=amd64] https://download.docker.com/linux/{{ ansible_distribution | lower }} {{ ansible_distribution_release }} stable"
docker_repo_gpg_key: https://download.docker.com/linux/ubuntu/gpg

docker_users_group: docker

# 系统特定配置
docker_storage_driver: overlay2
docker_log_driver: json-file
docker_log_opts:
  max-size: 10m
  max-file: 3
# roles/docker/tasks/main.yml
# 主任务文件,支持多平台
---
- name: Include OS-specific variables
  include_vars: "{{ item }}"
  with_first_found:
    - "{{ ansible_distribution }}-{{ ansible_distribution_major_version }}.yml"
    - "{{ ansible_distribution }}.yml"
    - "{{ ansible_os_family }}.yml"
    - "default.yml"
  tags: [docker, variables]

- name: Validate supported operating system
  assert:
    that:
      - ansible_os_family in ['RedHat', 'Debian']
      - ansible_distribution_version is version('18.04', '>=')
    fail_msg: "This role only supports RHEL/CentOS 7+ and Ubuntu 18.04+"
  tags: [docker, validation]

- name: Install Docker (RedHat)
  include_tasks: install-redhat.yml
  when: ansible_os_family == 'RedHat'
  tags: [docker, install]

- name: Install Docker (Debian)
  include_tasks: install-debian.yml
  when: ansible_os_family == 'Debian'
  tags: [docker, install]

- name: Configure Docker
  include_tasks: configure.yml
  tags: [docker, configure]

- name: Manage Docker service
  include_tasks: service.yml
  tags: [docker, service]

- name: Configure Docker users
  include_tasks: users.yml
  when: docker_users | length > 0
  tags: [docker, users]

8.4.2 Role 参数化和灵活性

# roles/application/defaults/main.yml
# 高度参数化的应用部署 Role
---
# 应用基本信息
app_name: myapp
app_version: latest
app_environment: production

# 部署配置
app_deploy_strategy: rolling  # rolling, blue_green, canary
app_deploy_user: deploy
app_deploy_group: deploy
app_base_dir: /opt/apps
app_current_dir: "{{ app_base_dir }}/{{ app_name }}/current"
app_releases_dir: "{{ app_base_dir }}/{{ app_name }}/releases"
app_shared_dir: "{{ app_base_dir }}/{{ app_name }}/shared"

# 源码配置
app_source_type: git  # git, archive, docker
app_git_repo: ""
app_git_branch: main
app_git_key_file: ""

# 构建配置
app_build_enabled: true
app_build_tool: npm  # npm, yarn, maven, gradle, make
app_build_commands:
  - npm install
  - npm run build

# 服务配置
app_service_enabled: true
app_service_type: systemd  # systemd, supervisor, docker
app_service_user: "{{ app_deploy_user }}"
app_service_group: "{{ app_deploy_group }}"

# 健康检查
app_health_check_enabled: true
app_health_check_url: "http://localhost:{{ app_port }}/health"
app_health_check_timeout: 30
app_health_check_retries: 5

# 回滚配置
app_rollback_enabled: true
app_keep_releases: 5

# 通知配置
app_notifications_enabled: false
app_notification_channels: []

# 环境变量
app_environment_vars: {}

# 配置文件
app_config_files: []

# 依赖服务
app_dependencies: []

# 端口配置
app_port: 3000
app_management_port: 9000

# 日志配置
app_log_dir: "{{ app_shared_dir }}/logs"
app_log_level: info
app_log_rotation: true

# 监控配置
app_monitoring_enabled: true
app_metrics_enabled: true
app_metrics_port: 9090

# 安全配置
app_security_enabled: true
app_firewall_rules: []

# 备份配置
app_backup_enabled: false
app_backup_schedule: "0 2 * * *"
app_backup_retention: 7
# roles/application/tasks/main.yml
# 主任务文件
---
- name: Validate application configuration
  include_tasks: validate.yml
  tags: [app, validate]

- name: Setup application user and directories
  include_tasks: setup.yml
  tags: [app, setup]

- name: Deploy application
  include_tasks: "deploy-{{ app_source_type }}.yml"
  tags: [app, deploy]

- name: Build application
  include_tasks: build.yml
  when: app_build_enabled
  tags: [app, build]

- name: Configure application
  include_tasks: configure.yml
  tags: [app, configure]

- name: Setup application service
  include_tasks: "service-{{ app_service_type }}.yml"
  when: app_service_enabled
  tags: [app, service]

- name: Configure monitoring
  include_tasks: monitoring.yml
  when: app_monitoring_enabled
  tags: [app, monitoring]

- name: Configure backup
  include_tasks: backup.yml
  when: app_backup_enabled
  tags: [app, backup]

- name: Health check
  include_tasks: health_check.yml
  when: app_health_check_enabled
  tags: [app, health_check]

- name: Cleanup old releases
  include_tasks: cleanup.yml
  when: app_rollback_enabled
  tags: [app, cleanup]

- name: Send notifications
  include_tasks: notify.yml
  when: app_notifications_enabled
  tags: [app, notify]

8.4.3 Role 测试

# roles/nginx/tests/test.yml
# Role 测试 Playbook
---
- hosts: localhost
  remote_user: root
  vars:
    nginx_sites:
      - name: test
        server_name: test.example.com
        root: /var/www/test
        ssl: false
  roles:
    - nginx
  
  post_tasks:
    - name: Test Nginx configuration
      command: nginx -t
      register: nginx_config_test
      failed_when: nginx_config_test.rc != 0
    
    - name: Test Nginx service status
      service:
        name: nginx
        state: started
      register: nginx_service_status
    
    - name: Test HTTP response
      uri:
        url: http://localhost
        method: GET
        status_code: [200, 301, 302, 403, 404]
      register: http_response
    
    - name: Verify test results
      assert:
        that:
          - nginx_config_test.rc == 0
          - nginx_service_status.state == 'started'
          - http_response.status in [200, 301, 302, 403, 404]
        fail_msg: "Nginx role tests failed"
        success_msg: "All Nginx role tests passed"
# roles/nginx/tests/test.sh
#!/bin/bash
# Role 测试脚本

set -e

# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color

echo -e "${YELLOW}Starting Nginx role tests...${NC}"

# 检查 Ansible 是否安装
if ! command -v ansible-playbook &> /dev/null; then
    echo -e "${RED}Error: ansible-playbook not found${NC}"
    exit 1
fi

# 运行语法检查
echo -e "${YELLOW}Running syntax check...${NC}"
ansible-playbook test.yml --syntax-check
if [ $? -eq 0 ]; then
    echo -e "${GREEN}Syntax check passed${NC}"
else
    echo -e "${RED}Syntax check failed${NC}"
    exit 1
fi

# 运行测试(检查模式)
echo -e "${YELLOW}Running check mode...${NC}"
ansible-playbook test.yml --check
if [ $? -eq 0 ]; then
    echo -e "${GREEN}Check mode passed${NC}"
else
    echo -e "${RED}Check mode failed${NC}"
    exit 1
fi

# 运行实际测试
echo -e "${YELLOW}Running actual deployment...${NC}"
ansible-playbook test.yml
if [ $? -eq 0 ]; then
    echo -e "${GREEN}Deployment test passed${NC}"
else
    echo -e "${RED}Deployment test failed${NC}"
    exit 1
fi

# 幂等性测试
echo -e "${YELLOW}Running idempotency test...${NC}"
ansible-playbook test.yml | tee /tmp/idempotency.log
grep -q 'changed=0.*failed=0' /tmp/idempotency.log
if [ $? -eq 0 ]; then
    echo -e "${GREEN}Idempotency test passed${NC}"
else
    echo -e "${RED}Idempotency test failed${NC}"
    exit 1
fi

echo -e "${GREEN}All tests passed successfully!${NC}"

8.5 Role 最佳实践

8.5.1 Role 设计原则

# 1. 单一职责原则
# 每个 Role 只负责一个特定的功能

# 好的例子:
# roles/nginx/        - 只负责 Nginx 安装和配置
# roles/mysql/        - 只负责 MySQL 安装和配置
# roles/firewall/     - 只负责防火墙配置

# 不好的例子:
# roles/webserver/    - 包含 Nginx、PHP、MySQL、防火墙等多个功能

# 2. 幂等性原则
# 所有任务都应该是幂等的

# roles/example/tasks/main.yml
---
# 好的例子:使用 creates 参数
- name: Download application
  get_url:
    url: "{{ app_download_url }}"
    dest: "{{ app_install_dir }}/{{ app_filename }}"
    creates: "{{ app_install_dir }}/{{ app_filename }}"

# 好的例子:检查状态后执行
- name: Check if service is configured
  stat:
    path: /etc/systemd/system/myapp.service
  register: service_file

- name: Configure service
  template:
    src: myapp.service.j2
    dest: /etc/systemd/system/myapp.service
  when: not service_file.stat.exists
  notify: reload systemd

# 3. 可配置性原则
# 使用变量使 Role 可配置

# roles/example/defaults/main.yml
---
# 提供合理的默认值
app_port: 8080
app_user: app
app_group: app
app_log_level: info

# 允许用户覆盖配置
app_config:
  database:
    host: localhost
    port: 5432
    name: myapp
  cache:
    enabled: true
    ttl: 3600

8.5.2 变量命名规范

# roles/example/defaults/main.yml
# 变量命名最佳实践
---
# 1. 使用 Role 名称作为前缀
example_app_name: myapp
example_app_version: 1.0.0
example_service_port: 8080

# 2. 使用描述性的名称
example_database_connection_timeout: 30
example_log_rotation_max_files: 10
example_ssl_certificate_path: /etc/ssl/certs

# 3. 布尔值使用 enabled/disabled 后缀
example_ssl_enabled: false
example_monitoring_enabled: true
example_backup_enabled: false

# 4. 列表和字典使用复数形式
example_packages:
  - curl
  - wget

example_users:
  - name: admin
    shell: /bin/bash

example_config_files:
  - src: app.conf.j2
    dest: /etc/myapp/app.conf

# 5. 路径变量使用 _dir 或 _path 后缀
example_install_dir: /opt/myapp
example_config_dir: /etc/myapp
example_log_dir: /var/log/myapp
example_pid_file_path: /var/run/myapp.pid

# 6. 端口和超时使用数字
example_http_port: 80
example_https_port: 443
example_connection_timeout: 30
example_read_timeout: 60

8.5.3 文档和注释

<!-- roles/nginx/README.md -->
# Nginx Role

这个 Role 用于在 RHEL/CentOS 和 Ubuntu/Debian 系统上安装和配置 Nginx。

## 要求

- Ansible 2.9+
- 目标系统:Ubuntu 18.04+, CentOS 7+
- 权限:sudo 或 root

## Role 变量

### 必需变量

无必需变量,所有变量都有默认值。

### 可选变量

| 变量名 | 默认值 | 描述 |
|--------|--------|------|
| `nginx_user` | www-data (Ubuntu) / nginx (CentOS) | Nginx 运行用户 |
| `nginx_worker_processes` | auto | Worker 进程数 |
| `nginx_worker_connections` | 1024 | 每个 Worker 的连接数 |
| `nginx_http_port` | 80 | HTTP 端口 |
| `nginx_https_port` | 443 | HTTPS 端口 |
| `nginx_ssl_enabled` | false | 是否启用 SSL |

### 虚拟主机配置

```yaml
nginx_sites:
  - name: example
    server_name: example.com
    root: /var/www/example
    ssl: true
    locations:
      - path: /
        try_files: $uri $uri/ =404
      - path: /api
        proxy_pass: http://backend

依赖

  • common (可选)

示例 Playbook

- hosts: webservers
  become: yes
  vars:
    nginx_ssl_enabled: true
    nginx_sites:
      - name: mysite
        server_name: mysite.com
        root: /var/www/mysite
        ssl: true
  roles:
    - nginx

标签

  • nginx - 所有任务
  • install - 安装任务
  • configure - 配置任务
  • service - 服务管理任务
  • firewall - 防火墙配置任务

许可证

MIT

作者信息

这个 Role 由 DevOps 团队创建和维护。


### 8.5.4 版本控制和发布

```yaml
# roles/nginx/meta/main.yml
# 版本信息和元数据
galaxy_info:
  role_name: nginx
  author: DevOps Team
  description: Nginx web server installation and configuration
  company: Example Corp
  license: MIT
  min_ansible_version: 2.9
  
  # 版本信息
  version: 1.2.3
  
  # 支持的平台
  platforms:
    - name: Ubuntu
      versions:
        - 18.04
        - 20.04
        - 22.04
    - name: EL
      versions:
        - 7
        - 8
        - 9
  
  # Galaxy 标签
  galaxy_tags:
    - nginx
    - webserver
    - proxy
    - loadbalancer
    - ssl

# 依赖版本控制
dependencies:
  - role: common
    version: ">=1.0.0,<2.0.0"
  - role: firewall
    version: "~1.1.0"  # 1.1.x
# 版本发布脚本
#!/bin/bash
# scripts/release.sh

set -e

VERSION=$1
if [ -z "$VERSION" ]; then
    echo "Usage: $0 <version>"
    exit 1
fi

echo "Releasing version $VERSION"

# 更新版本号
sed -i "s/version: .*/version: $VERSION/" meta/main.yml

# 运行测试
echo "Running tests..."
./tests/test.sh

# 提交更改
git add .
git commit -m "Release version $VERSION"
git tag -a "v$VERSION" -m "Version $VERSION"

# 推送到远程仓库
git push origin main
git push origin "v$VERSION"

# 发布到 Galaxy
ansible-galaxy role import --api-key="$GALAXY_API_KEY" username rolename

echo "Version $VERSION released successfully!"

8.6 本章总结

本章详细介绍了 Ansible Roles 的各个方面:

  • Roles 概述:理解 Roles 的概念、优势和目录结构
  • 创建基础 Role:从简单的 Nginx Role 开始学习 Role 开发
  • 依赖管理:管理 Role 之间的依赖关系和版本控制
  • 高级开发:多平台支持、参数化设计和测试方法
  • 最佳实践:设计原则、命名规范、文档编写和版本管理

Roles 是 Ansible 中最重要的概念之一,掌握 Role 的开发和使用是成为 Ansible 专家的必经之路。

8.7 练习题

基础练习

  1. 创建简单 Role

    • 使用 ansible-galaxy 创建一个新的 Role
    • 实现基本的软件安装和配置
    • 编写相应的处理器和模板
  2. Role 结构理解

    • 分析现有 Role 的目录结构
    • 理解各个目录的作用
    • 练习变量优先级

进阶练习

  1. 多平台 Role

    • 开发支持多个操作系统的 Role
    • 实现条件逻辑和平台特定配置
    • 编写平台检测和验证逻辑
  2. Role 依赖

    • 设计具有复杂依赖关系的 Role
    • 实现条件依赖和版本控制
    • 管理依赖冲突

实战练习

  1. 完整应用栈 Role

    • 开发包含 Web 服务器、数据库、缓存的完整应用栈
    • 实现高可用和负载均衡配置
    • 添加监控和日志收集功能
  2. Role 测试和 CI/CD

    • 编写全面的 Role 测试
    • 集成到 CI/CD 流水线
    • 实现自动化发布流程

下一章第9章:高级特性与技巧

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