第十章:Roles 角色管理

学习使用 Ansible Roles 组织 Playbook,实现代码复用和模块化。

最后更新: 2024-01-24
页面目录

Ansible Roles

Roles 是 Ansible 最强大的组织功能,允许你将 Playbook 分解为可复用、可共享的组件。

Roles 概述

┌─────────────────────────────────────────────────────────────────┐
                      Role 目录结构                                
├─────────────────────────────────────────────────────────────────┤
                                                                  
  role_name/                                                      
  ├── defaults/              # 默认变量(最低优先级)               │
     └── main.yml                                               
  ├── files/                 # 静态文件                           │
     ├── config.conf                                            
     └── script.sh                                                
  ├── handlers/               # 处理器                            │
     └── main.yml                                               
  ├── meta/                  # 依赖关系                           │
     └── main.yml                                               
  ├── tasks/                 # 任务列表                          │
     └── main.yml                                               
  ├── templates/             # Jinja2 模板                       │
     ├── config.conf.j2                                         
     └── app.yml.j2                                             
  ├── tests/                 # 测试                              │
     ├── inventory                                              
     └── test.yml                                               
  └── vars/                  # 变量(高于 defaults)              │
      └── main.yml                                               
                                                                  
└─────────────────────────────────────────────────────────────────┘

创建 Role

使用 ansible-galaxy

# 创建 role
ansible-galaxy init role_name

# 创建 role 到指定目录
ansible-galaxy init role_name --init-path ./roles/

手动创建目录

mkdir -p roles/nginx/defaults
mkdir -p roles/nginx/files
mkdir -p roles/nginx/handlers
mkdir -p roles/nginx/tasks
mkdir -p roles/nginx/templates
mkdir -p roles/nginx/vars

Role 组件

defaults/main.yml

---
# 默认变量(最低优先级)
# 可以在 playbook 或 inventory 中覆盖

nginx_version: "1.24.0"
nginx_port: 80
nginx_server_name: localhost
nginx_user: www-data
nginx_worker_processes: auto
nginx_worker_connections: 1024
nginx_keepalive_timeout: 65

# 包管理
nginx_packages:
  - nginx

# 配置选项
nginx_config_options: []
nginx_sites: []
nginx_remove_default_site: true

# 服务配置
nginx_service_enabled: yes
nginx_service_state: started

vars/main.yml

---
# 角色内部变量(较高优先级)
# 不应被 playbook 覆盖的内部配置

_nginx_os_packages:
  Debian: [nginx]
  RedHat: [nginx]
  CentOS: [nginx]

_nginx_default_conf_dir: /etc/nginx
_nginx_default_conf_path: "{{ _nginx_default_conf_dir }}/nginx.conf"
_nginx_pid_file: /run/nginx.pid

tasks/main.yml

---
# 任务列表

- name: Include OS-specific variables
  include_vars: "{{ ansible_os_family | lower }}.yml"

- name: Install Nginx
  package:
    name: "{{ nginx_packages }}"
    state: present

- name: Create nginx directories
  file:
    path: "{{ item }}"
    state: directory
    owner: "{{ nginx_user }}"
    mode: '0755'
  loop:
    - /var/cache/nginx
    - /var/log/nginx
    - /run/nginx

- name: Copy nginx configuration
  template:
    src: nginx.conf.j2
    dest: /etc/nginx/nginx.conf
    owner: root
    group: root
    mode: '0644'
    validate: nginx -t -c %s
  notify: Reload nginx

- name: Copy site configurations
  template:
    src: "{{ item.template | default('site.conf.j2') }}"
    dest: "/etc/nginx/sites-available/{{ item.name }}.conf"
  notify: Reload nginx
  loop: "{{ nginx_sites }}"
  when: nginx_sites | length > 0

- name: Enable sites
  file:
    src: "/etc/nginx/sites-available/{{ item.name }}.conf"
    dest: "/etc/nginx/sites-enabled/{{ item.name }}.conf"
    state: link
  loop: "{{ nginx_sites }}"
  when: nginx_sites | length > 0

- name: Remove default site
  file:
    path: /etc/nginx/sites-enabled/default
    state: absent
  when: nginx_remove_default_site | bool

- name: Ensure nginx is running
  service:
    name: nginx
    state: "{{ nginx_service_state }}"
    enabled: "{{ nginx_service_enabled }}"

handlers/main.yml

---
- name: Reload nginx
  service:
    name: nginx
    state: reloaded

- name: Restart nginx
  service:
    name: nginx
    state: restarted

templates/

# nginx.conf.j2
user {{ nginx_user }};
worker_processes {{ nginx_worker_processes }};
error_log /var/log/nginx/error.log;
pid {{ nginx_pid_file }};

events {
    worker_connections {{ nginx_worker_connections }};
}

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 /var/log/nginx/access.log main;

    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout {{ nginx_keepalive_timeout }};

    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}

files/

# 静态文件,直接复制到目标主机
# 使用 copy 或 script 模块引用

meta/main.yml

---
# 角色依赖

dependencies:
  - role: common
    vars:
      timezone: Asia/Shanghai

使用 Role

基本用法

# site.yml
---
- name: Deploy web application
  hosts: webservers
  become: yes

  roles:
    - nginx
    - mysql
    - app

带参数使用

# site.yml
---
- name: Deploy web application
  hosts: webservers
  become: yes

  roles:
    - role: nginx
      vars:
        nginx_version: "1.24.0"
        nginx_port: 8080
        nginx_sites:
          - name: myapp
            template: myapp.conf.j2

    - role: mysql
      vars:
        db_port: 3307
        db_name: myapp_db

    - role: app

使用 import_role

tasks:
  - name: Include nginx role
    import_role:
      name: nginx
    vars:
      nginx_port: 8080

使用 include_role

tasks:
  - name: Include role dynamically
    include_role:
      name: "{{ item }}"
    loop:
      - nginx
      - mysql
      - app

Role 依赖

meta/main.yml

---
# 角色依赖关系
# 这些角色会在当前角色之前执行

dependencies:
  - role: common
    vars:
      timezone: Asia/Shanghai

  - role: ssl
    vars:
      ssl_cert_path: /etc/ssl/certs

依赖链

site.yml
├── role: webserver
│   ├── dependencies:
│   │   └── role: common
│   │       └── dependencies: (none)
│   └── tasks/main.yml
└── tasks/main.yml

Role 测试

测试结构

tests/
├── inventory
   ├── hosts
   └── group_vars/
       └── all.yml
└── test.yml

test.yml

---
- name: Test nginx role
  hosts: testhosts
  become: yes

  roles:
    - role: nginx
      vars:
        nginx_port: 80

运行测试

# 使用 molecule 测试
molecule test

# 手动测试
ansible-playbook -i tests/inventory tests/test.yml --syntax-check
ansible-playbook -i tests/inventory tests/test.yml

完整示例:Web 应用 Role

目录结构

roles/
└── webapp/
    ├── defaults/
       └── main.yml
    ├── files/
       └── init.sh
    ├── handlers/
       └── main.yml
    ├── meta/
       └── main.yml
    ├── tasks/
       ├── main.yml
       ├── install.yml
       ├── configure.yml
       └── deploy.yml
    ├── templates/
       ├── app.conf.j2
       └── systemd.service.j2
    └── vars/
        └── main.yml

defaults/main.yml

---
webapp_name: myapp
webapp_version: "1.0.0"
webapp_user: www-data
webapp_group: www-data
webapp_home: /opt/{{ webapp_name }}
webapp_port: 8080
webapp_environment: production
webapp_git_repo: ""
webapp_git_version: HEAD
webapp_requirements: []

webapp_service_state: started
webapp_service_enabled: yes

webapp_db_host: localhost
webapp_db_port: 3306
webapp_db_name: myapp
webapp_db_user: myapp
webapp_db_password: ""

webapp_redis_host: localhost
webapp_redis_port: 6379

tasks/main.yml

---
- name: Include install tasks
  include_tasks: install.yml

- name: Include configure tasks
  include_tasks: configure.yml

- name: Include deploy tasks
  include_tasks: deploy.yml

tasks/install.yml

---
- name: Create application user
  user:
    name: "{{ webapp_user }}"
    group: "{{ webapp_group }}"
    system: yes
    create_home: no
    shell: /bin/false

- name: Create application directories
  file:
    path: "{{ item }}"
    state: directory
    owner: "{{ webapp_user }}"
    group: "{{ webapp_group }}"
    mode: '0755'
  loop:
    - "{{ webapp_home }}"
    - "{{ webapp_home }}/config"
    - "{{ webapp_home }}/data"
    - "{{ webapp_home }}/logs"
    - "{{ webapp_home }}/tmp"

- name: Install Python packages
  pip:
    name: "{{ webapp_requirements }}"
    virtualenv: "{{ webapp_home }}/venv"
    virtualenv_command: python3 -m venv
  when: webapp_requirements | length > 0

tasks/configure.yml

---
- name: Copy configuration file
  template:
    src: app.conf.j2
    dest: "{{ webapp_home }}/config/app.conf"
    owner: "{{ webapp_user }}"
    group: "{{ webapp_group }}"
    mode: '0600'
  notify: Restart app

- name: Copy systemd service file
  template:
    src: systemd.service.j2
    dest: /etc/systemd/system/{{ webapp_name }}.service
  notify: Reload systemd

- name: Reload systemd
  systemd:
    daemon_reload: yes

tasks/deploy.yml

---
- name: Clone or update repository
  git:
    repo: "{{ webapp_git_repo }}"
    dest: "{{ webapp_home }}/current"
    version: "{{ webapp_git_version }}"
    force: yes
    accept_hostkey: yes
  when: webapp_git_repo | length > 0
  notify: Restart app

- name: Set permissions
  file:
    path: "{{ webapp_home }}"
    owner: "{{ webapp_user }}"
    group: "{{ webapp_group }}"
    recurse: yes

- name: Ensure service is started
  service:
    name: "{{ webapp_name }}"
    state: "{{ webapp_service_state }}"
    enabled: "{{ webapp_service_enabled }}"

handlers/main.yml

---
- name: Restart app
  systemd:
    name: "{{ webapp_name }}"
    state: restarted

- name: Reload systemd
  systemd:
    daemon_reload: yes

下一步

现在你已经掌握了 Roles 的使用。接下来让我们学习常用模块。

👉 常用模块详解