介绍
模板自动化保留一个源文件,根据各主机输入生成配置。你将使用变量和事实渲染文件,通过内置 Jinja 过滤器转换响应头,选择条件分支,并通过处理程序加载经过验证的 nginx 配置。实际 HTTP 请求和被拒绝的无效候选文件,将展示源文本、受管文件与实时服务行为的区别。
将主机变量和事实渲染到受管内容
本步骤将创建一个页面模板,并在两个目标上从相同源文件渲染不同的真实文件。
预先准备的节点已安装 nginx 作为前置条件。你的工作是渲染并应用配置,无需再次安装软件包。模板在控制节点使用各目标变量上下文渲染,然后传输到受管目标。
进入控制节点项目并创建 templates 目录。mkdir -p 在需要时创建目录,目录已存在也不会失败:
cd /home/labex/project
mkdir -p templates
定义具有正确类型的主机输入。环境与监听端口不同,不带引号的 maintenance_mode 值是 YAML 布尔值:
cat > host_vars/web1.yml <<'YAML'
environment_label: development
service_port: 8081
maintenance_mode: true
YAML
cat > host_vars/web2.yml <<'YAML'
environment_label: production
service_port: 8082
maintenance_mode: false
YAML
双花括号中的 Jinja 表达式将值插入文本。.j2 后缀是模板源的实用约定,并非受管目标文件名的要求。创建一个源文件,使用当前清单名、环境变量和发现的发行版事实:
cat > templates/page.txt.j2 <<'JINJA'
host={{ inventory_hostname }}
environment={{ environment_label }}
distribution={{ ansible_facts.distribution }}
JINJA
使用 template 模块渲染。copy 通常直接传输源字节,不解释这些 Jinja 表达式。使用发行版值前先收集事实,并明确声明受管所有权与权限:
cat > templates.yml <<'YAML'
- name: Render host-specific website content
hosts: all
gather_facts: true
become: true
tasks:
- name: Prepare the rendered site directory
ansible.builtin.file:
path: /var/www/template-site
state: directory
owner: root
group: root
mode: "0755"
- name: Render each host's website content
ansible.builtin.template:
src: templates/page.txt.j2
dest: /var/www/template-site/index.html
owner: root
group: root
mode: "0644"
YAML
渲染两个受管页面:
ansible-playbook templates.yml --syntax-check
ansible-playbook templates.yml
读取实际目标文件:
ansible all -m ansible.builtin.command -a 'cat /var/www/template-site/index.html'
各页面应标识自身主机、匹配的 development/production 环境及 distribution=Ubuntu。控制节点仍只有一个可复用源,而两个受管输出不同。
添加内置过滤器与条件配置分支
本步骤将渲染有效的 nginx 配置,根据各主机输入改变 HTTP 行为。
Jinja 过滤器使用管道符转换值。upper 是将文本转为大写的内置过滤器,不是自定义插件。Jinja 控制语句使用百分号与花括号分隔符。下面的 if/else/endif 分支根据布尔输入选择一个维护响应头。
创建完整的 nginx 配置模板。端口、环境响应头和维护响应头均来自各主机数据。root 和 index 设置提供已渲染的页面:
cat > templates/nginx.conf.j2 <<'JINJA'
events {}
http {
server_tokens off;
server {
listen {{ service_port }};
root /var/www/template-site;
index index.html;
add_header X-Environment "{{ environment_label | upper }}" always;
{% if maintenance_mode %}
add_header X-Maintenance "enabled" always;
{% else %}
add_header X-Maintenance "disabled" always;
{% endif %}
}
}
JINJA
将配置和服务任务及具名重新加载处理程序追加到现有 playbook。template 任务的 validate 命令在替换受管配置前测试临时渲染的候选文件(%s)。更改后的有效配置通知处理程序;普通 service 任务保持 nginx 运行。
cat >> templates.yml <<'YAML'
- name: Render and validate the nginx candidate before replacement
ansible.builtin.template:
src: templates/nginx.conf.j2
dest: /etc/nginx/nginx.conf
owner: root
group: root
mode: "0644"
validate: /usr/sbin/nginx -t -c %s
notify: Reload nginx
- name: Keep the rendered site service running
ansible.builtin.service:
name: nginx
state: started
handlers:
- name: Reload nginx
ansible.builtin.service:
name: nginx
state: reloaded
YAML
应用完整 playbook:
ansible-playbook templates.yml
配置任务应更改两个主机,并触发重新加载处理程序。检查端口 8081 上的 web1 和端口 8082 上的 web2。curl -i 同时包含响应头与响应体:
ansible web1 -m ansible.builtin.command -a 'curl --fail --silent --show-error -i http://127.0.0.1:8081/'
ansible web2 -m ansible.builtin.command -a 'curl --fail --silent --show-error -i http://127.0.0.1:8082/'
web1 应返回 X-Environment: DEVELOPMENT 和 X-Maintenance: enabled;web2 则为 X-Environment: PRODUCTION 和 X-Maintenance: disabled。两个响应体都保留各主机的正确页面。响应头日期会变化;请比较由数据驱动的值及成功状态。
更改输入并拒绝无效候选文件
本步骤将更新持久输入而不编辑模板,观察实际实时结果,并证明验证能保护可用配置。
将 web1 保存的环境改为 staging,维护模式改为 false,保留现有端口:
cat > host_vars/web1.yml <<'YAML'
environment_label: staging
service_port: 8081
maintenance_mode: false
YAML
应用相同源模板:
ansible-playbook templates.yml
web1 的页面和配置应改变,通知其重新加载处理程序;web2 应保持未更改。再次检查 web1:
ansible web1 -m ansible.builtin.command -a 'curl --fail --silent --show-error -i http://127.0.0.1:8081/'
其响应体应显示 environment=staging,响应头为 X-Environment: STAGING 和 X-Maintenance: disabled。输入变化时模板仍保持可复用。
现在测试候选拒绝。cp 保存有效的控制节点源以便恢复。仅将 nginx 模板替换为故意无效的文本:
cp templates/nginx.conf.j2 templates/nginx.conf.j2.good
printf 'invalid nginx configuration;\n' > templates/nginx.conf.j2
下一次 playbook 运行预期候选验证失败。shell 的 || 仅在非零退出后运行消息,使故意失败的练习能继续:
ansible-playbook templates.yml || printf 'Expected candidate validation rejection\n'
阅读任务失败信息:nginx 应拒绝临时候选文件。受管可用配置必须保留,因此两个实时服务仍应正确响应:
ansible web1 -m ansible.builtin.command -a 'curl --fail --silent --show-error -i http://127.0.0.1:8081/'
ansible web2 -m ansible.builtin.command -a 'curl --fail --silent --show-error -i http://127.0.0.1:8082/'
mv 将保存的有效源文件恢复到原文件名。再次运行完整 playbook:
mv templates/nginx.conf.j2.good templates/nginx.conf.j2
ansible-playbook templates.yml
两台主机均应报告 changed=0、failed=0 和 unreachable=0,且未调度重新加载处理程序。验证保护了实时配置,未将无效候选副本作为期望最终状态。
总结
你通过 template 模块渲染了主机变量和事实,使用内置 upper 过滤器与布尔 Jinja 分支,并通过处理程序应用经过验证的配置。输入变化产生不同的实时输出,无需重写源文件。无效候选文件在替换前被拒绝,恢复后的有效模板收敛且无额外更改。


