Ansible的jinja2 模板、Roles角色详解
目录
- 一、Jinja2 模板
- 1.1 在 Ansible 中的使用
- 1.2 Jinja2 模板语法
- 1.2.1 基础语法
- 1.2.2 支持的数据类型
- 1.2.3 支持的运算
- 1.3 Jinja2 模板实例
- 1.3.1 创建模板文件
- 1.3.2 在 Playbook 中使用模板
- 1.3.3 效果说明
- 1.4 流程控制
- 1.4.1 条件判断
- 1.4.2 多条件判断
- 1.4.3 循环
- 1.4.4 扩展功能
- 1.5 过滤器
- 1.6 其他控制结构
- 1.7 template 模块
- 1.7.1 copy 与 template 的区别
- 1.7.2 template 常用参数
- 1.7.3 使用示例
- 1.8 Jinja2 使用案例 —— 部署 Redis 服务
- 1.8.1 背景
- 1.8.2 本案例涉及的模块/功能
- 1. 创建 Redis 配置模板
- 2. 编写 Playbook
- 二、Roles(角色)
- 2.1 Roles 的介绍与优势
- 2.2 Roles 的目录结构
- 2.3 ansible-galaxy 命令
- 2.3.1 初始化 Role 目录结构
- 2.3.2 安装别人写好的 Role
- 2.3.3 手动下载 Role
- 2.3.4 总结
- 三、使用 Roles 部署 Nginx案例
- 3.1 流程概览(5 步)
- 3.2 定义任务文件
- 3.2 定义模板文件
- 3.3 定义变量
- 3.4 定义 Handler
- 3.5 定义调用 Role 的 Playbook
- 3.6 检查 YAML 文件语法
- 3.7 执行 Playbook
- 3.8 查看 Nginx 服务状态
一、Jinja2 模板
Jinja2 是 Python 的全功能模板引擎,在 Python Web 开发中被广泛应用。
在 Ansible 中,Jinja2 模板通常用于动态生成配置文件
1.1 在 Ansible 中的使用
在 Ansible 中,使用 template 模块来处理 Jinja2 模板:
- template 模块:可以将模板文件渲染后复制到远程主机。
- copy 模块:只是原封不动地复制文件。
示例对比:
模块 | 功能 |
---|---|
copy | 推送内容原样复制,例如 {{ ansible_fqdn }} 不会被替换 |
template | 会渲染模板变量,例如 {{ ansible_fqdn }} 会被替换为对应主机名 |
注意:Jinja2 模板中可以使用条件判断和循环,但 playbook 中不允许使用。
模板文件通常使用 .j2
后缀。
1.2 Jinja2 模板语法
1.2.1 基础语法
- 在 playbook 中使用 template 模块
- 模板变量:使用
{{ 变量名 }}
,例如{{ PORT }}
或使用 Ansible facts。 - 表达式:
{{ }}
支持 Python 表达式,如:
{{ 3 + 5 }}
{{ 3 in [1,2,3,4,5] }}
- 可以包含比较运算、算术运算、逻辑运算、成员运算等。
1.2.2 支持的数据类型
- 字符串:单引号或双引号
- 数字:整数、浮点数
- 列表:
[item1, item2, ...]
- 元组:
(item1, item2, ...)
- 字典:
{key1: value1, key2: value2, ...}
- 布尔型:
true / false
1.2.3 支持的运算
- 算术运算:
+
,-
,*
,/
,//
,%
,**
- 比较运算:
==
,!=
,>
,>=
,<
,<=
- 逻辑运算:
and
,or
,not
1.3 Jinja2 模板实例
模板通常通过 引用变量 来使用。
1.3.1 创建模板文件
首先,创建一个包含 Jinja2 模板的文件。
在模板中,使用 {{ }}
包裹变量或表达式。
示例:创建 my_template.j2
模板文件
# my_template.j2
ServerName {{ hostname }}
Listen {{ port }}
Debug {{ debug_mode }}
1.3.2 在 Playbook 中使用模板
在 Ansible Playbook 中,可以使用 template 模块 来加载模板文件,并传递变量。
示例:jinja.yml
---
- name: 使用Jinja2模板hosts: your_target_hostsvars:hostname: example.comport: 8080debug_mode: Truetasks:- name: 生成配置文件template:src: my_template.j2dest: /etc/ansible/yml/jinja2/myapp.conf
1.3.3 效果说明
- 模板文件 (my_template.j2) 定义了变量位置
- Playbook 通过
vars
传递变量值 - template 模块 渲染后生成目标配置文件,例如:
ServerName example.com
Listen 8080
Debug True
1.4 流程控制
Jinja2 模板支持流程控制语句:条件判断 和 循环。
1.4.1 条件判断
基本格式
{% if EXPR %}执行内容
{% else %}执行内容
{% endif %}
示例
{% if is_production %}# 生产环境配置DebugLevel: 0
{% else %}# 开发/测试环境配置DebugLevel: 2
{% endif %}
1.4.2 多条件判断
格式:
{% if EXPR %}执行内容
{% elif EXPR %}执行内容
{% else %}执行内容
{% endif %}
示例:
{% if env == "production" %}DebugLevel: 0
{% elif env == "staging" %}DebugLevel: 1
{% else %}DebugLevel: 2
{% endif %}
说明:根据变量 env
的值,模板会生成不同配置。
1.4.3 循环
基本格式
{% for i in EXPR %}执行内容
{% endfor %}
示例
{% for item in list_items %}- {{ item }}
{% endfor %}
说明:如果 list_items = ["nginx", "mysql", "redis"]
,渲染结果为:
- nginx
- mysql
- redis
1.4.4 扩展功能
默认 Jinja2 **不支持 **break**
和 ****continue**
。
如需启用,可在 /etc/ansible/ansible.cfg
中配置:
jinja2_extensions = jinja2.ext.do,jinja2.ext.i18n,jinja2.ext.loopcontrols
这样即可在循环中使用 break
、continue
控制流程。
1.5 过滤器
Jinja2 支持 过滤器,用于对变量进行处理。
语法:
{{ variable | filter_name }}
示例
- 设置默认值
{{ variable | default("default_value") }}
- 获取列表长度
The list has {{ list_items | length }} items.
1.6 其他控制结构
Jinja2 还支持一些高级控制结构:
- {% include %}:包含其他模板文件
- {% macro %} / {% call %}:定义和调用宏(类似函数)
注意:Jinja2 功能非常强大,还支持宏、继承、自定义过滤器等。
更多高级用法可参考 Jinja2 官方文档。
在 Ansible 中,可以利用这些模板特性实现灵活的配置文件生成和任务编排。
1.7 template 模块
template
模块和 copy
模块的用法类似,但 template
主要用于渲染 Jinja2 模板。
1.7.1 copy 与 template 的区别
模块 | 参数替换 | 用途 |
---|---|---|
copy | ❌ 不替代变量 | 文件原样复制 |
template | ✅ 替代变量 | 生成定制化配置 |
1.7.2 template 常用参数
参数 | 说明 |
---|---|
src | 指定本地 Jinja2 模板文件路径 |
dest | 指定远程主机的目标路径 |
backup | 是否备份目标文件,默认 no |
mode | 设置权限 |
user | 设置属主用户 |
group | 设置属主用户组 |
1.7.3 使用示例
- name: 部署应用配置hosts: webserverstasks:- name: 渲染并分发配置文件template:src: my_template.j2dest: /etc/myapp/config.confmode: '0644'backup: yes
在不同主机上,模板会自动渲染变量,实现 定制化配置。
1.8 Jinja2 使用案例 —— 部署 Redis 服务
1.8.1 背景
Redis 默认只监听 127.0.0.1
,其他主机无法访问。
通过 Jinja2 模板结合 Ansible template
模块,可以自定义配置文件,让 Redis 监听指定地址和端口。
1.8.2 本案例涉及的模块/功能
- Jinja2 模板:动态生成 redis 配置
- template 模块:渲染模板
- copy 模块:推送 repo 文件
- yum 模块:安装/卸载软件包
- shell 模块:检测 redis 是否安装
- service 模块:启动/停止/重启服务
- vars:定义变量
- register:变量注册
- ignore_errors:忽略错误
- tags:任务打标签,方便选择性执行
- when:条件判断
- notify / handlers:触发任务(如配置变更后重启 Redis)
1. 创建 Redis 配置模板
文件:redis_conf.j2
# 绑定的IP地址
# {{ ansible_host }} 会替换为当前被控主机的IP,127.0.0.1 表示本地回环
# 这样配置后,Redis 可以同时监听本机和该主机对外的IP
bind {{ ansible_host }} 127.0.0.1# Redis 服务端口,使用变量定义,便于灵活修改
port {{ redis_port }}# 是否启用保护模式(默认yes)。关闭后允许远程访问
protected-mode no# TCP连接队列大小,默认511
tcp-backlog 511# 客户端闲置超时时间(秒)。0 表示不限制
timeout 0# TCP 保活时间(秒),用于检测死连接
tcp-keepalive 300# 是否以守护进程方式运行
daemonize yes# 是否启用 supervisor 管理,no 表示不使用
supervised no# Redis 进程PID文件位置
pidfile /var/run/redis_6379.pid# 日志级别:debug、verbose、notice、warning
# notice 表示一般推荐级别
loglevel notice
bind {{ ansible_host }}
:动态绑定当前主机 IPport {{ redis_port }}
:通过变量定义 Redis 端口
2. 编写 Playbook
文件:redis.yml
---
# Playbook 名称
- name: 部署Redis服务hosts: web # 目标主机组,需在 inventory 里定义remote_user: root # 远程执行用户gather_facts: no # 是否收集被控端的系统信息,这里禁用以加快速度vars:redis_port: 6379 # 定义变量:Redis 服务端口tasks:# ========== 1. 上传 YUM/DNF 仓库文件 ==========- name: Upload base repocopy: src: /etc/yum.repos.d/openEuler.repo # 控制端本地 repo 文件路径dest: /etc/yum.repos.d/ # 目标主机存放 repo 的目录backup: no # 不备份目标文件tags: [upload_yum, upload_repo, redis_server] # 标签,可按需执行此任务# 上传 epel.repo(openEuler 默认可能不需要,可选)- name: Upload epel repo (可选)copy:src: /etc/yum.repos.d/epel.repo # 控制端本地 EPEL 源文件dest: /etc/yum.repos.d/backup: notags: [upload_epel, upload_repo, redis_server]# ========== 2. 检查 Redis 是否已安装 ==========- name: Check redis installshell: /usr/bin/rpm -q redis # 执行 rpm 查询命令register: redis_msg # 将命令执行结果保存到 redis_msg 变量ignore_errors: yes # 即使执行失败也不中断(比如没装 Redis)tags: [check_redis, redis_server]# ========== 3. 安装 Redis ==========- name: Install redis serveryum: name: redis # 指定软件包名state: present # 确保安装when: redis_msg.failed == true # 只有检查未安装时才执行tags: [install_redis, redis_server]# ========== 4. 上传 Redis 配置文件 ==========- name: Upload redis.conftemplate:src: redis_conf.j2 # Jinja2 模板文件dest: /etc/redis.conf # 目标配置文件路径notify: Restart Redis # 配置更新后通知 handlers 执行“Restart Redis”tags: [upload_redis.conf, redis_server]# ========== 5. 启动 Redis 服务并设置开机自启 ==========- name: Start Redis serverservice:name: redis # 服务名(openEuler 默认是 redis)state: started # 确保服务已启动enabled: yes # 设置开机自启tags: [start_redis, redis_server]# ========== 6. 手动重启 Redis ==========- name: Restart Redis serverservice:name: redisstate: restarted # 强制重启tags: [restart_redis]# ========== 7. 停止 Redis ==========- name: Stop Redis Serverservice: name: redisstate: stopped # 停止服务tags: [stop_redis, uninstall_redis]# ========== 8. 卸载 Redis ==========- name: Uninstall Redis Serveryum: name: redisstate: absent # 确保 Redis 被卸载tags: [uninstall_redis]# ========== handlers:处理程序 ==========handlers:- name: Restart Redisservice:name: redisstate: restarted # 当配置文件更新时自动触发服务重启
输出
[root@ansible-manager j2]# ansible-playbook redis.yml --syntax-checkplaybook: redis.yml
[root@ansible-manager j2]# ansible-playbook redis.yml --list-tasksplaybook: redis.ymlplay #1 (web_group): 部署Redis服务 TAGS: []tasks:Upload yum repo TAGS: [redis_server, upload_repo, upload_yum]Upload epel repo TAGS: [redis_server, upload_epel, upload_repo]Check redis install TAGS: [check_redis, redis_server]Install redis server TAGS: [install_redis, redis_server]Upload redis.conf TAGS: [redis_server, upload_redis.conf]Start Redis server TAGS: [redis_server, start_redis]Restart Redis server TAGS: [restart_redis]Stop Redis Server TAGS: [stop_redis, uninstall_redis]Uninstall Redis Server TAGS: [uninstall_redis]
[root@ansible-manager j2]# ansible-playbook redis.yml --list-tagsplaybook: redis.ymlplay #1 (web_group): 部署Redis服务 TAGS: []TASK TAGS: [check_redis, install_redis, redis_server, restart_redis, start_redis, stop_redis, uninstall_redis, upload_epel, upload_redis.conf, upload_repo, upload_yum]
[root@ansible-manager j2]# ansible-playbook redis.yml PLAY [部署Redis服务] **********************************************************************************************************************************************************TASK [Upload yum repo] ****************************************************************************************************************************************************
[WARNING]: Platform linux on host 10.0.0.11 is using the discovered Python interpreter at /usr/bin/python3, but future installation of another Python interpreter could
change this. See https://docs.ansible.com/ansible/2.9/reference_appendices/interpreter_discovery.html for more information.
ok: [10.0.0.11]
[WARNING]: Platform linux on host 10.0.0.13 is using the discovered Python interpreter at /usr/bin/python3, but future installation of another Python interpreter could
change this. See https://docs.ansible.com/ansible/2.9/reference_appendices/interpreter_discovery.html for more information.
ok: [10.0.0.13]
[WARNING]: Platform linux on host 10.0.0.12 is using the discovered Python interpreter at /usr/bin/python3, but future installation of another Python interpreter could
change this. See https://docs.ansible.com/ansible/2.9/reference_appendices/interpreter_discovery.html for more information.
ok: [10.0.0.12]# "stdout": "package redis is not installed" 没有被安装,说明之前没有安装过
TASK [Check redis install] ************************************************************************************************************************************************
[WARNING]: Consider using the yum, dnf or zypper module rather than running 'rpm'. If you need to use command because yum, dnf or zypper is insufficient you can add
'warn: false' to this command task or set 'command_warnings=False' in ansible.cfg to get rid of this message.
fatal: [10.0.0.13]: FAILED! => {"changed": true, "cmd": "/usr/bin/rpm -q redis", "delta": "0:00:00.006004", "end": "2025-09-13 16:47:06.386991", "msg": "non-zero return code", "rc": 1, "start": "2025-09-13 16:47:06.380987", "stderr": "", "stderr_lines": [], "stdout": "package redis is not installed", "stdout_lines": ["package redis is not installed"]}
...ignoring
fatal: [10.0.0.11]: FAILED! => {"changed": true, "cmd": "/usr/bin/rpm -q redis", "delta": "0:00:00.007042", "end": "2025-09-13 16:47:06.398061", "msg": "non-zero return code", "rc": 1, "start": "2025-09-13 16:47:06.391019", "stderr": "", "stderr_lines": [], "stdout": "package redis is not installed", "stdout_lines": ["package redis is not installed"]}
...ignoring
fatal: [10.0.0.12]: FAILED! => {"changed": true, "cmd": "/usr/bin/rpm -q redis", "delta": "0:00:00.006628", "end": "2025-09-13 16:47:06.404864", "msg": "non-zero return code", "rc": 1, "start": "2025-09-13 16:47:06.398236", "stderr": "", "stderr_lines": [], "stdout": "package redis is not installed", "stdout_lines": ["package redis is not installed"]}
...ignoringTASK [Install redis server] ***********************************************************************************************************************************************
changed: [10.0.0.13]
changed: [10.0.0.12]
changed: [10.0.0.11]TASK [Upload redis.conf] **************************************************************************************************************************************************
changed: [10.0.0.13]
changed: [10.0.0.11]
changed: [10.0.0.12]TASK [Start Redis server] *************************************************************************************************************************************************
changed: [10.0.0.12]
changed: [10.0.0.13]
changed: [10.0.0.11]TASK [Restart Redis server] ***********************************************************************************************************************************************
changed: [10.0.0.13]
changed: [10.0.0.11]
changed: [10.0.0.12]TASK [Stop Redis Server] **************************************************************************************************************************************************
changed: [10.0.0.13]
changed: [10.0.0.11]
changed: [10.0.0.12]TASK [Uninstall Redis Server] *********************************************************************************************************************************************
changed: [10.0.0.11]
changed: [10.0.0.13]
changed: [10.0.0.12]# 上面redis已经被卸载,
# 所以 提示""msg": "Could not find the requested service"没有发现该服务
RUNNING HANDLER [Restart Redis] *******************************************************************************************************************************************
fatal: [10.0.0.12]: FAILED! => {"changed": false, "msg": "Could not find the requested service redis: host"}
fatal: [10.0.0.13]: FAILED! => {"changed": false, "msg": "Could not find the requested service redis: host"}
fatal: [10.0.0.11]: FAILED! => {"changed": false, "msg": "Could not find the requested service redis: host"}NO MORE HOSTS LEFT ********************************************************************************************************************************************************PLAY RECAP ****************************************************************************************************************************************************************
10.0.0.11 : ok=8 changed=7 unreachable=0 failed=1 skipped=0 rescued=0 ignored=1
10.0.0.12 : ok=8 changed=7 unreachable=0 failed=1 skipped=0 rescued=0 ignored=1
10.0.0.13 : ok=8 changed=7 unreachable=0 failed=1 skipped=0 rescued=0 ignored=1
二、Roles(角色)
在 Ansible 中,Roles 是一种规范化机制,用于将复杂的 Playbook 分解为多个可复用、结构化的部分,从而简化 Playbook 的编写和维护。
建议:每个 Role 最好只包含一个 tasks
,方便调用并实现良好的解耦。
2.1 Roles 的介绍与优势
- 目录位置
- 默认写在
/etc/ansible/roles
。 - 也可以写在其他路径,但需要手动创建
roles
文件夹。
- 默认写在
- 问题与解决
- 如果不使用 Role,同时部署
web
、database
、keepalived
等不同服务,或者在不同服务器组合不同应用,就需要写多个 Playbook 文件,维护困难。 - Roles 通过层次化、结构化组织 Playbook,使得变量、任务和处理器可以自动装载,便于复用。
- 如果不使用 Role,同时部署
- 核心概念
- Role 将 Playbook 的不同内容分离为独立目录:
- 变量(vars)
- 文件(files)
- 模板(templates)
- 任务(tasks)
- 处理器(handlers)
- 模块(modules,可选)
- 元信息(meta)
- 使用 Role 时,只需在 Playbook 中通过
include_role
或roles:
指令调用即可。
- Role 将 Playbook 的不同内容分离为独立目录:
- 应用场景
- 基于主机构建服务(如部署 web 服务、数据库等)。
- 构建守护进程或其他系统服务。
2.2 Roles 的目录结构
创建一个示例 Role 目录:
mkdir -pv /etc/ansible/roles/{nginx,mysql,httpd}/{files,templates,vars,tasks,handlers,meta,default}
- 说明:
{nginx,mysql,httpd}
:Role 名称,可根据实际服务命名。files/
:存放静态文件。templates/
:存放 Jinja2 模板。vars/
:存放变量文件。tasks/
:存放任务文件(main.yml
)。handlers/
:存放处理器文件(main.yml
)。meta/
:存放 Role 元信息。default/
:存放默认变量文件(main.yml
)。
Role 的目录结构决定了 Ansible 自动查找变量、任务、处理器等文件的方式,使 Playbook 更加模块化和可复用。
yum install -y tree[root@ansible-manager j2]# tree /etc/ansible/roles
/etc/ansible/roles
├── httpd # Role 名称:用于部署或管理 httpd 服务
│ ├── default # 默认变量目录(main.yml),用于定义 Role 的默认值
│ ├── files # 存放静态文件,如配置文件、二进制文件等,供 tasks 调用 copy 模块使用
│ ├── handlers # 存放处理器文件(main.yml),如服务重启、刷新配置等操作
│ ├── meta # 存放 Role 元信息,如依赖的其他 Role、作者信息、最小 Ansible 版本等
│ ├── tasks # 存放任务文件(main.yml),定义该 Role 的具体操作步骤
│ ├── templates # 存放 Jinja2 模板文件,用于生成动态配置文件
│ └── vars # 存放变量文件(main.yml),用于定义强制变量或特定环境变量
├── mysql # Role 名称:用于部署或管理 MySQL 服务
│ ├── default # 默认变量目录(main.yml)
│ ├── files # 静态文件目录
│ ├── handlers # 处理器目录
│ ├── meta # Role 元信息目录
│ ├── tasks # 任务目录
│ ├── templates # Jinja2 模板目录
│ └── vars # 变量目录
└── nginx # Role 名称:用于部署或管理 Nginx 服务├── default # 默认变量目录(main.yml)├── files # 静态文件目录├── handlers # 处理器目录├── meta # Role 元信息目录├── tasks # 任务目录├── templates # Jinja2 模板目录└── vars # 变量目录
2.3 ansible-galaxy 命令
ansible-galaxy
是 Ansible 提供的 Role 管理工具,用于:
- 初始化 Role 目录结构;
- 下载或安装 Galaxy 上别人写好的 Role;
- 管理已有 Role。
2.3.1 初始化 Role 目录结构
使用 ansible-galaxy init
创建一个 Role 的标准目录:
# 初始化 Role 目录
[root@ansible-manager ~]# ansible-galaxy init /etc/ansible/roles/webserver
# 执行后,输出
- Role /etc/ansible/roles/webserver was created successfully
- 会在
/etc/ansible/roles/webserver
下创建完整目录结构:
[root@ansible-manager ~]# tree /etc/ansible/roles/webserver
/etc/ansible/roles/webserver
├── defaults
│ └── main.yml # 默认变量文件# 用于定义 Role 的默认变量# 优先级低,会被 vars/main.yml 或 Playbook 中变量覆盖
├── files # 静态文件目录# 存放 Role 中需要复制到目标主机的文件# 任务中可通过 copy: 模块引用
├── handlers
│ └── main.yml # 处理器文件# 用于定义触发操作,如服务重启、刷新配置# Playbook 或 tasks 中通过 notify 调用
├── meta
│ └── main.yml # Role 元信息文件# 可定义角色依赖、作者信息、许可证、最低 Ansible 版本# 例如:# dependencies:# - common
├── README.md # Role 说明文件# 介绍 Role 功能、使用方法、变量说明等
├── tasks
│ └── main.yml # Role 的主要任务文件# 定义该 Role 的执行步骤# 可 include 或 import 其他任务文件实现模块化
├── templates # 模板文件目录# 存放 Jinja2 模板,用于动态生成配置文件# 任务中通过 template: 模块引用
├── tests
│ ├── inventory # 测试主机清单文件
│ └── test.yml # 测试 Playbook 文件# 用于在本地或测试环境验证 Role 是否正确执行
└── vars└── main.yml # 强制变量文件# 优先级高于 defaults/main.yml# 用于定义必须的变量或特定环境变量,确保 Role 正常运行
- 这样你就可以直接在 Role 里写任务、变量和模板。
2.3.2 安装别人写好的 Role
可以从 Galaxy 下载别人已经写好的 Role,例如 MySQL:
ansible-galaxy role install -p /etc/ansible/roles tenequm.mysql
-p /etc/ansible/roles
:指定 Role 安装路径。tenequm.mysql
:Galaxy 上的 Role 名称。
可能遇到的问题:
[ERROR]: failed to download the file: <urlopen error timed out>
[WARNING]: - tenequm.mysql was NOT installed successfully.
- 原因:网络超时或 Galaxy 仓库访问失败。
2.3.3 手动下载 Role
当自动下载失败时,可以使用 wget
手动拉取:
# 下载 Role 压缩包
wget https://github.com/tenequm/ansible-mysql/archive/1.0.1.tar.gz# 解压
tar xf 1.0.1.tar.gz[root@ansible-manager roles]# tree ansible-mysql-1.0.1
ansible-mysql-1.0.1
├── defaults
│ └── main.yml # 默认变量文件# 用于定义 Role 的默认变量,如 MySQL 端口、用户名、密码等# 优先级低,会被 vars/main.yml 或 Playbook 中的变量覆盖
├── LICENSE # 授权许可文件# 说明 Role 的版权信息和使用许可
├── meta
│ └── main.yml # Role 元信息文件# 定义角色的依赖关系、作者信息、最小 Ansible 版本等# 例如:# dependencies:# - common
├── README.md # Role 说明文件# 描述 Role 功能、安装方法、变量说明、使用示例等
├── tasks
│ └── main.yml # 主要任务文件# 定义 Role 执行步骤,如安装 MySQL、初始化数据库、配置服务等# 可以 include 或 import 其他任务文件实现模块化
├── templates
│ └── my.cnf.j2 # Jinja2 模板文件# 用于动态生成 MySQL 配置文件# 可通过 `template:` 模块将配置渲染到目标主机
├── tests
│ ├── README.MD # 测试说明文件# 描述如何在测试环境验证该 Role
│ └── test.yml # 测试 Playbook 文件# 用于快速验证 Role 是否能够在目标主机正确执行# 列出已安装的 Roles
[root@ansible-manager roles]# ansible-galaxy list
# /etc/ansible/roles
- ansible-mysql-1.0.1, (unknown version)
- webserver, (unknown version)# 查看指定 Role 信息
[root@ansible-manager roles]# ansible-galaxy info tenequm.mysqlRole: tenequm.mysqldescription: Simply installs MySQL 5.7 on Xenial.commit: b3a7139ba44a91e9568345565e861e326e9d401ecommit_message: Added priveleges configs for users.created: 2023-05-08T20:18:24.338543Zdownload_count: 351github_branch: mastergithub_repo: ansible-mysqlgithub_user: tenequmid: 103imported: 2017-06-08T21:57:26.659770-04:00modified: 2023-10-10T00:48:33.420438Zpath: ('/root/.ansible/roles', '/usr/share/ansible/roles', '/etc/ansible/roles')upstream_id: 17029username: tenequm
- 解压后会生成目录:
ansible-mysql-1.0.1
。 - 该目录下包含完整 Role 结构(tasks、handlers、vars、templates 等)。
- 将解压后的目录移动到
/etc/ansible/roles/
即可使用。
2.3.4 总结
- ansible-galaxy init:创建 Role 目录结构,适合自定义开发 Role。
- ansible-galaxy role install:从 Galaxy 下载 Role,但可能受网络限制。
- 手动下载 + 解压:解决下载超时或网络问题。
- Role 下载完成后,可以直接在 Playbook 中通过
roles:
或include_role:
调用使用。
三、使用 Roles 部署 Nginx案例
3.1 流程概览(5 步)
- 定义任务文件(tasks)
- 定义模板文件(templates)生成配置文件
- 定义变量(vars)
- 定义触发器(handlers)
- 定义 Playbook 调用 Role(roles.yml)
然后执行以下操作:
- 检查 YAML 语法
- 执行
roles.yml
文件 - 查看 Nginx 服务状态
创建目录
mkdir -p /etc/ansible/roles/nginx/
mkdir -p /etc/ansible/roles/nginx/tasks
mkdir -p /etc/ansible/roles/nginx/templates
mkdir -p /etc/ansible/roles/nginx/vars
mkdir -p /etc/ansible/roles/nginx/handlers
mkdir -p /etc/ansible/roles/nginx/files
3.2 定义任务文件
文件路径:/etc/ansible/roles/nginx/tasks/main.yml
vim /etc/ansible/roles/nginx/tasks/main.yml
# 任务1:安装 wget 工具
- name: install wgetyum:name: wget # 安装的软件包名称state: present # 确保软件包已经安装(如果已安装则跳过)# 任务2:安装 Nginx 安装包
- name: copy nginx rpm package to remote hostcopy:src: /etc/ansible/roles/nginx/files/nginx-1.18.0-1.el7.ngx.x86_64.rpm # 本地文件在 playbook 运行机器的路径dest: /tmp/nginx-1.18.0-1.el7.ngx.x86_64.rpm # 目标主机上的路径mode: '0644'# 使用 shell 命令下载指定版本的 Nginx rpm 包# -O 指定保存路径为 roles 文件夹下的 files 目录# 注意:使用 command 模块,不会经过 shell,因此不能使用管道或重定向符号# 任务3:复制 Nginx rpm 包到目标路径
- name: cp nginxcopy:src: nginx-1.18.0-1.el7.ngx.x86_64.rpm # 源文件路径(相对于 tasks 执行路径,通常指 roles/files)dest: /tmp/nginx-1.18.0-1.el7.ngx.x86_64.rpm # 复制到目标主机的 /tmp 目录# 任务4:安装 Nginx
- name: install nginxyum:name: /tmp/nginx-1.18.0-1.el7.ngx.x86_64.rpm # 指定 rpm 包路径进行安装state: latest # 确保安装最新版本,如果已安装相同版本则跳过# 任务5:生成 Nginx 配置文件
- name: conftemplate:src: nginx.conf.j2 # 源模板文件(Jinja2 模板)dest: /etc/nginx/nginx.conf # 渲染后生成的目标配置文件路径tags: nginxconf # 任务标签,可在执行 playbook 时按标签筛选执行notify: new conf to reload # 当模板文件发生变化时触发 handler:重新加载 Nginx 服务# 任务6:启动并设置 Nginx 服务开机自启
- name: start serviceservice:name: nginx # 服务名称state: started # 确保服务已经启动enabled: true # 设置服务开机自启
注:使用 template
模块动态生成配置文件,并通过 notify
调用 handler。
3.2 定义模板文件
文件路径:/etc/ansible/roles/nginx/templates/nginx.conf.j2
关键配置示例:
vim /etc/ansible/roles/nginx/templates/nginx.conf.j2
# 设置 Nginx 运行的系统用户
user nginx;# 设置工作进程数,使用 Jinja2 变量 {{ ansible_processor_vcpus }} 自动获取目标主机 CPU 核心数
worker_processes {{ ansible_processor_vcpus }};# 错误日志路径及日志级别
error_log /var/log/nginx/error.log warn;# nginx 主进程 PID 文件路径
pid /var/run/nginx.pid;# 事件驱动模块配置
events {# 每个 worker 进程允许的最大连接数worker_connections 1024;
}# HTTP 模块配置
http {# 引入 MIME 类型映射文件include /etc/nginx/mime.types;# 默认内容类型default_type application/octet-stream;# 定义日志格式 mainlog_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;# 客户端与服务端的连接保持时间,单位:秒keepalive_timeout 65;# 定义 HTTP 服务中的 server 块server {# 监听端口,使用 Jinja2 变量 {{ nginxport }},可以在 vars 中自定义listen {{ nginxport }};# 服务名称或主机名server_name localhost;# location 配置location / {# 网站根目录root /usr/share/nginx/html;# 默认首页index index.html index.htm;}# 错误页面配置error_page 500 502 503 504 /50x.html;# 单独指定错误页面路径location = /50x.html {root /usr/share/nginx/html;}}# 引入 conf.d 目录下的所有额外配置文件include /etc/nginx/conf.d/*.conf;
}
使用 Jinja2 变量 {{ nginxport }}
动态生成监听端口。
3.3 定义变量
文件路径:/etc/ansible/roles/nginx/vars/main.yml
vim /etc/ansible/roles/nginx/vars/main.yml
nginxport: 9999
可以在 Playbook 中覆盖。
3.4 定义 Handler
文件路径:/etc/ansible/roles/nginx/handlers/main.yml
vim /etc/ansible/roles/nginx/handlers/main.yml
- name: new conf to reloadservice:name: nginxstate: restarted
当模板文件更新时,通过 notify
自动触发 Nginx 重启。
3.5 定义调用 Role 的 Playbook
文件路径:/etc/ansible/roles/nginx/roles.yml
vim /etc/ansible/roles/nginx/roles.yml
- hosts: webremote_user: rootroles:- nginx
3.6 检查 YAML 文件语法
ansible-playbook --syntax-check roles.yml
会自动检查 Role 内所有 yml 文件语法。
3.7 执行 Playbook
ansible-playbook roles.yml
3.8 查看 Nginx 服务状态
systemctl status nginx
确认 Nginx 已经启动,并监听指定端口。