跳转至

第六章:Roles 角色

什么是 Roles?

Roles 是 Ansible 组织 Playbook 的标准化方式,将变量、任务、模板、文件等按目录结构组织,实现代码复用和模块化。

Roles 目录结构

roles/
└── nginx/
    ├── tasks/
    │   └── main.yml          # 主任务文件
    ├── handlers/
    │   └── main.yml          # 处理器
    ├── templates/
    │   └── nginx.conf.j2     # Jinja2 模板
    ├── files/
    │   └── index.html        # 静态文件
    ├── vars/
    │   └── main.yml          # 角色变量(高优先级)
    ├── defaults/
    │   └── main.yml          # 默认变量(低优先级)
    ├── meta/
    │   └── main.yml          # 角色依赖
    ├── library/              # 自定义模块
    ├── module_utils/         # 模块工具
    └── README.md             # 角色文档

创建 Role

使用 ansible-galaxy 创建

# 创建角色
ansible-galaxy init nginx

# 查看目录结构
tree nginx/

手动创建

# 创建目录
mkdir -p roles/nginx/{tasks,handlers,templates,files,vars,defaults,meta}

Role 组成部分

tasks/main.yml

# roles/nginx/tasks/main.yml
- name: Install nginx
  ansible.builtin.apt:
    name: nginx
    state: present
  tags: install

- name: Create web directory
  ansible.builtin.file:
    path: "{{ nginx_root }}"
    state: directory
    owner: www-data
    group: www-data
    mode: '0755'
  tags: config

- name: Deploy nginx config
  ansible.builtin.template:
    src: nginx.conf.j2
    dest: /etc/nginx/nginx.conf
    mode: '0644'
  notify: Reload nginx
  tags: config

- name: Deploy site config
  ansible.builtin.template:
    src: site.conf.j2
    dest: "/etc/nginx/sites-available/{{ server_name }}"
    mode: '0644'
  notify: Reload nginx
  tags: config

- name: Enable site
  ansible.builtin.file:
    src: "/etc/nginx/sites-available/{{ server_name }}"
    dest: "/etc/nginx/sites-enabled/{{ server_name }}"
    state: link
  notify: Reload nginx
  tags: config

- name: Start nginx
  ansible.builtin.service:
    name: nginx
    state: started
    enabled: yes
  tags: service

handlers/main.yml

# roles/nginx/handlers/main.yml
- name: Reload nginx
  ansible.builtin.service:
    name: nginx
    state: reloaded

- name: Restart nginx
  ansible.builtin.service:
    name: nginx
    state: restarted

defaults/main.yml

# roles/nginx/defaults/main.yml
# 默认变量(可被覆盖)
nginx_root: /var/www/html
nginx_user: www-data
nginx_worker_processes: auto
nginx_worker_connections: 1024
http_port: 80
server_name: localhost

vars/main.yml

# roles/nginx/vars/main.yml
# 角色变量(高优先级,通常不覆盖)
nginx_config_path: /etc/nginx
nginx_log_path: /var/log/nginx

templates/nginx.conf.j2

# roles/nginx/templates/nginx.conf.j2
user {{ nginx_user }};
worker_processes {{ nginx_worker_processes }};
error_log {{ nginx_log_path }}/error.log;
pid /run/nginx.pid;

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"';

    access_log {{ nginx_log_path }}/access.log main;

    sendfile on;
    keepalive_timeout 65;

    include /etc/nginx/sites-enabled/*;
}

templates/site.conf.j2

# roles/nginx/templates/site.conf.j2
server {
    listen {{ http_port }};
    server_name {{ server_name }};
    root {{ nginx_root }};
    index index.html index.htm;

    location / {
        try_files $uri $uri/ =404;
    }

    location ~ \.php$ {
        fastcgi_pass unix:/run/php/php-fpm.sock;
        fastcgi_index index.php;
        include fastcgi_params;
    }
}

meta/main.yml

# roles/nginx/meta/main.yml
galaxy_info:
  author: Your Name
  description: Nginx web server role
  company: Your Company
  license: MIT
  min_ansible_version: '2.9'
  platforms:
    - name: Ubuntu
      versions:
        - focal
        - jammy
  galaxy_tags:
    - nginx
    - web
    - server

dependencies:
  - role: common
    vars:
      some_var: value

使用 Role

在 Playbook 中使用

# site.yml
- name: Deploy web servers
  hosts: webservers
  become: yes
  roles:
    - nginx
    - role: mysql
      vars:
        mysql_root_password: secret
    - role: php
      tags: php

带条件的 Role

- name: Deploy web servers
  hosts: all
  become: yes
  roles:
    - role: nginx
      when: "'webservers' in group_names"
    - role: mysql
      when: "'dbservers' in group_names"

Role 依赖

# roles/nginx/meta/main.yml
dependencies:
  - role: common
  - role: selinux
    when: ansible_selinux.status == "enabled"

Ansible Galaxy

搜索 Role

# 搜索角色
ansible-galaxy search nginx

# 搜索特定作者
ansible-galaxy search nginx --author geerlingguy

安装 Role

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

# 安装指定版本
ansible-galaxy install geerlingguy.nginx,v1.0.0

# 安装到指定目录
ansible-galaxy install geerlingguy.nginx -p ./roles

# 从 Git 安装
ansible-galaxy install git+https://github.com/geerlingguy/ansible-role-nginx.git

requirements.yml

# requirements.yml
- src: geerlingguy.nginx
  version: v3.1.0
- src: geerlingguy.mysql
  version: v4.3.0
- src: https://github.com/user/ansible-role-custom.git
  name: custom-role
- src: git@github.com:user/private-role.git
  scm: git
  version: main
# 安装所有依赖
ansible-galaxy install -r requirements.yml

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

发布 Role

# 登录 Ansible Galaxy
ansible-galaxy login

# 导入角色
ansible-galaxy import github_user role_name

# 发布到 Galaxy
# 1. 在 GitHub 创建仓库
# 2. 添加 meta/main.yml
# 3. 在 Galaxy 网站导入

实战示例:完整 LAMP Stack

目录结构

lamp/
├── site.yml
├── inventory.ini
├── roles/
│   ├── common/
│   │   └── tasks/
│   │       └── main.yml
│   ├── nginx/
│   │   ├── tasks/
│   │   │   └── main.yml
│   │   ├── handlers/
│   │   │   └── main.yml
│   │   ├── templates/
│   │   │   ├── nginx.conf.j2
│   │   │   └── site.conf.j2
│   │   └── defaults/
│   │       └── main.yml
│   ├── mysql/
│   │   ├── tasks/
│   │   │   └── main.yml
│   │   ├── handlers/
│   │   │   └── main.yml
│   │   ├── templates/
│   │   │   └── my.cnf.j2
│   │   └── defaults/
│   │       └── main.yml
│   └── php/
│       ├── tasks/
│       │   └── main.yml
│       └── defaults/
│           └── main.yml
└── group_vars/
    └── all.yml

site.yml

- name: Deploy LAMP Stack
  hosts: webservers
  become: yes
  roles:
    - common
    - nginx
    - mysql
    - php

roles/common/tasks/main.yml

- name: Update apt cache
  ansible.builtin.apt:
    update_cache: yes
    cache_valid_time: 3600

- name: Install common packages
  ansible.builtin.apt:
    name:
      - vim
      - git
      - curl
      - wget
      - unzip
    state: present

- name: Set timezone
  community.general.timezone:
    name: "{{ timezone }}"

roles/mysql/tasks/main.yml

- name: Install MySQL
  ansible.builtin.apt:
    name:
      - mysql-server
      - mysql-client
      - python3-mysqldb
    state: present

- name: Start MySQL
  ansible.builtin.service:
    name: mysql
    state: started
    enabled: yes

- name: Set root password
  mysql_user:
    name: root
    password: "{{ mysql_root_password }}"
    login_unix_socket: /var/run/mysqld/mysqld.sock

- name: Create database
  mysql_db:
    name: "{{ mysql_database }}"
    state: present
    login_user: root
    login_password: "{{ mysql_root_password }}"

- name: Create user
  mysql_user:
    name: "{{ mysql_user }}"
    password: "{{ mysql_password }}"
    priv: "{{ mysql_database }}.*:ALL"
    state: present
    login_user: root
    login_password: "{{ mysql_root_password }}"

roles/php/tasks/main.yml

- name: Add PHP repository
  ansible.builtin.apt_repository:
    repo: ppa:ondrej/php
    state: present

- name: Install PHP
  ansible.builtin.apt:
    name:
      - php{{ php_version }}
      - php{{ php_version }}-fpm
      - php{{ php_version }}-mysql
      - php{{ php_version }}-curl
      - php{{ php_version }}-gd
      - php{{ php_version }}-mbstring
      - php{{ php_version }}-xml
    state: present

- name: Start PHP-FPM
  ansible.builtin.service:
    name: "php{{ php_version }}-fpm"
    state: started
    enabled: yes

group_vars/all.yml

timezone: Asia/Shanghai

# Nginx
nginx_root: /var/www/html
http_port: 80
server_name: localhost

# MySQL
mysql_root_password: "{{ vault_mysql_root_password }}"
mysql_database: myapp
mysql_user: myapp
mysql_password: "{{ vault_mysql_password }}"

# PHP
php_version: "8.1"

最佳实践

1. 角色命名

# 好的命名
nginx
mysql
php-fpm
docker-ce

# 不好的命名
my-role
test
role1

2. 使用 defaults

# defaults/main.yml
# 提供合理的默认值
nginx_port: 80
nginx_root: /var/www/html

# 允许用户覆盖
# 在 playbook 或 inventory 中设置

3. 使用 Tags

tasks:
  - name: Install nginx
    ansible.builtin.apt:
      name: nginx
      state: present
    tags:
      - install
      - nginx

4. 文档化

# roles/nginx/README.md
# Nginx Role

## Description
Installs and configures Nginx web server.

## Requirements
- Ubuntu 20.04/22.04

## Variables
| Variable | Default | Description |
|----------|---------|-------------|
| nginx_port | 80 | HTTP port |
| nginx_root | /var/www/html | Web root |

## Dependencies
- common

## Example Playbook
```yaml
- hosts: webservers
  roles:
    - nginx
```

小结

本章学习了:

  • ✅ Roles 概念和目录结构
  • ✅ 创建和使用 Roles
  • ✅ Ansible Galaxy 使用
  • ✅ 实战示例:LAMP Stack
  • ✅ 最佳实践

总结

通过这六章的学习,你已经掌握了:

  1. Ansible 基础 - 概念、架构、安装
  2. Inventory - 主机清单配置
  3. Ad-hoc 命令 - 临时命令执行
  4. Playbook - 自动化剧本编写
  5. Roles - 代码组织和复用

继续学习:Kubernetes 教程 - 容器编排和集群管理。