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 练习题
基础练习
创建简单 Role
- 使用 ansible-galaxy 创建一个新的 Role
- 实现基本的软件安装和配置
- 编写相应的处理器和模板
Role 结构理解
- 分析现有 Role 的目录结构
- 理解各个目录的作用
- 练习变量优先级
进阶练习
多平台 Role
- 开发支持多个操作系统的 Role
- 实现条件逻辑和平台特定配置
- 编写平台检测和验证逻辑
Role 依赖
- 设计具有复杂依赖关系的 Role
- 实现条件依赖和版本控制
- 管理依赖冲突
实战练习
完整应用栈 Role
- 开发包含 Web 服务器、数据库、缓存的完整应用栈
- 实现高可用和负载均衡配置
- 添加监控和日志收集功能
Role 测试和 CI/CD
- 编写全面的 Role 测试
- 集成到 CI/CD 流水线
- 实现自动化发布流程
下一章:第9章:高级特性与技巧
返回目录:Ansible 自动化运维教程