news 2026/10/29 16:03:06

90DaysOfDevOps Ansible 实战:Tags、Variables、Inventory 与 MySQL 数据库服务器自动化配置

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
90DaysOfDevOps Ansible 实战:Tags、Variables、Inventory 与 MySQL 数据库服务器自动化配置
  • 文档/教程

【免费下载链接】90DaysOfDevOps

This repository started out as a learning in public project for myself and has now become a structured learning map for many in the community. We have 3 years under our belt covering all things DevOps, including Principles, Processes, Tooling and Use Cases surrounding this vast topic.

项目地址:https://gitcode.com/gh_mirrors/90/90DaysOfDevOps
点击查看免费下载

本篇技术指南围绕 90DaysOfDevOps 项目第 68 天的 Ansible 自动化实战展开,系统讲解 Tags 标签机制、两类变量的定义与优先级、Inventory 清单文件的按环境拆分,以及如何从零编写 MySQL role 完成数据库服务器的自动化部署。读完本文,你将能够用--tags精准控制大规模 playbook 的执行范围,用group_vars/host_vars让 playbook 与配置解耦,并通过 roles 模板化的方式一键部署并验证一个 MySQL 数据库节点。

背景与实验环境

本篇对应的实验发生在 Configmgmt 目录 下的 Vagrant 多机环境中(Vagrantfile 定义了 webservers、proxy、database 等若干虚拟机),控制节点上运行 Ansible,通过默认的/etc/ansible/hosts定义主机分组。前几天的实验已完成 webservers 与 loadbalancer(nginx 反向代理)的 playbook,day68.md 是这一系列的延续:先完善 playbook 的标签与变量管理,再补上最后一台尚未启动的数据库服务器 db01。

一、Tags:让大型 Playbook 按需执行

1.1 为什么需要 Tags

前面的 playbook 一旦运行,会把webservers与proxy两个 play 从头到尾全部执行。当 playbook 变得庞大复杂时,往往只希望针对其中一部分 plays 或 tasks 做变更。Ansible 的 tags 机制正是为此设计的:它可以把 plays 和 tasks 打上标签,再由命令行按标签筛选执行范围。

以场景 5 的 playbook5.yml 为例,两个 play 分别被打上了web与proxy标签:

- hosts: webservers become: yes vars: http_port: 8000 https_port: 4443 html_welcome_msg: "Hello 90DaysOfDevOps - Welcome to Day 66!" roles: - common - apache2 tags: web - hosts: proxy become: yes roles: - common - nginx tags: proxy

1.2 查看与按标签执行

先列出 playbook 中定义过的所有标签,确认标签名称无误:

ansible-playbook playbook5.yml --list-tags

只针对 proxy 组执行:

ansible-playbook playbook5.yml --tags proxy

此时只有proxyplay 及其关联的common、nginxroles 会被执行,webservers相关任务全部跳过。

1.3 Tags 的粒度:task 级标签与always

除了给 play 打标签,tag 还可以加在单个 task 上,实现更细粒度的控制。例如可以按用途给 tasks 打上installation、configuration、removal等应用型标签,运行时按需挑选。

特别值得记住的是tag: always:任何被标记为always的 task,无论命令行传入什么--tags值,都会被执行。这对那些"任何场景都必须先跑"的基础任务(如安装公共依赖)非常有用。

1.4 多标签组合与多标签定义

多个标签可以一起使用,命令行以逗号分隔:

ansible-playbook playbook5.yml --tags proxy,web

上面这条命令会执行所有带proxy或web标签的 plays——在当前 playbook 中恰好等价于执行整个 playbook,但当 playbook 新增了其他 plays 时,这种组合筛选的价值就体现出来了。另外,一个 play 或 task 也可以同时定义多个标签(YAML 列表形式,如后文 scenario7 的写法)。

二、Variables:Ansible Facts 与用户自定义变量

Ansible 中有两大类变量:Ansible Facts(自动采集的系统事实)与User created(用户自定义变量)。

2.1 Ansible Facts:系统自动采集的"免费变量"

每次运行 playbook 时,第一阶段都会自动执行一个未在 playbook 中显式定义的 task——"Gathering Facts(收集事实)"。这些 facts 是目标主机的系统级信息,可以直接在 playbook、模板和条件判断中引用,用来驱动自动化决策。

把事实数据导出成文件,便于检索分析:

ansible proxy -m setup >> facts.json

setup模块会返回目标主机的大量 JSON 事实数据。本仓库中的 facts.json 就是 loadbalancer 主机的完整事实快照(共约 990 行),从中可以看到ansible_all_ipv4_addresses(如192.168.169.134、10.0.2.15)、ansible_architecture(x86_64)、ansible_bios_vendor(innotek GmbH/VirtualBox)等丰富信息,这些都可以在 playbook 中按需引用。

2.2 用 Facts 动态生成 nginx 负载均衡配置

场景 5 的 mysite.j2 最初是把两台 webserver 的 IP 硬编码进 nginx 的 upstream 块:

upstream webservers { server 192.168.169.131:8000; server 192.168.169.132:8000; }

硬编码的缺陷在于:新增或移除 webserver 时,代理配置不会自动跟随。可以利用 facts 与groups['webservers']循环,把 nginx 配置模板改为动态生成:

#Dynamic Config for server {{ ansible_facts['nodename'] }} upstream webservers { {% for host in groups['webservers'] %} server {{ hostvars[host]['ansible_facts']['nodename'] }}:8000; {% endfor %} } server { listen 80; location / { proxy_pass http://webservers; } }

要点解释:

  • groups['webservers']是 Ansible 内置的魔法变量,返回webservers主机组的所有成员;
  • hostvars[host]['ansible_facts']['nodename']用于跨主机访问组内每台机器的 fact,即取其主机名;
  • 运行效果与当前硬编码配置完全一致,但此后无论往webservers组里添加还是移除机器,重新渲染出的代理配置都会自动同步变化;
  • 前提是目标环境已配置好 name resolution(名称解析),否则主机名无法正确解析为 IP。

2.3 用户自定义变量:从 playbook 内嵌到 group_vars 解耦

用户自定义变量由我们自己定义。此前变量直接写在 playbook 的vars:块中,例如:

- hosts: webservers become: yes vars: http_port: 8000 https_port: 4443 html_welcome_msg: "Hello 90DaysOfDevOps - Welcome to Day 68!" roles: - common - apache2 tags: web

为了让 playbook 更干净、变量更易维护,可以把变量移到独立文件中。进入场景 6 目录 ansible-scenario6,创建目录结构group_vars/all/,并在其中新建 common_variables.yml:

http_port: 8000 https_port: 4443 html_welcome_msg: "Hello 90DaysOfDevOps - Welcome to Day 68!"

同时把 playbook 中的vars:块及其变量删除。由于放在group_vars/all/下,这些变量对所有主机组全局生效;还可以把 NTP、DNS 服务器等全局配置一并加进来。此时 playbook6.yml 变得非常简洁:

- hosts: webservers become: yes roles: - common - apache2 tags: web - hosts: proxy become: yes roles: - common - nginx tags: proxy

全局变量在模板中同样可用。把 mysite.j2 中硬编码的端口号替换为变量{{ http_port }}:

#Dynamic Config for server {{ ansible_facts['nodename'] }} upstream webservers { {% for host in groups['webservers'] %} server {{ hostvars[host]['ansible_facts']['nodename'] }}:{{ http_port }}; {% endfor %} } server { listen 80; location / { proxy_pass http://webservers; } }

同理,在 apache2 role 的 index.html.j2 中,既引用自定义变量html_welcome_msg,又引用 factansible_facts['nodename'],用于标识当前响应请求的是哪台 webserver:

<html> <h1>{{ html_welcome_msg }}! I'm webserver {{ ansible_facts['nodename'] }} </h1> </html>

执行ansible-playbook playbook6.yml后,轮询负载均衡器地址,会发现请求交替落在两台 webserver 上,页面中显示的nodename也随之切换,直观验证了变量与 facts 的联动效果。

更细粒度的场景:还可以创建host_vars目录并在其中放web01.yml,实现"按单台主机"定制消息或页面外观。

2.4 变量优先级与目录约定小结

  • group_vars/all/:对所有主机组生效的全局变量;
  • group_vars/<group>/:只对指定主机组生效;
  • host_vars/<host>.yml:只对单台主机生效,优先级高于 group_vars;
  • play 内联vars:的优先级高于上述基于目录的文件变量。
  • 这些目录需与 playbook 同级(即 playbook 所在的"根"目录),Ansible 会按目录位置自动加载。

三、Inventory:为不同环境准备独立主机清单

此前一直使用/etc/ansible/hosts这个默认清单文件。对于 production、staging 等多环境场景,可以为每个环境准备独立的 inventory 文件,运行 playbook 时通过-i指定:

ansible-playbook -i dev playbook.yml

Inventory 文件里除了定义主机与分组,同样可以声明变量。例如可以在 dev 清单中定义env: dev之类的环境变量,然后在负载均衡器网页模板中引用该变量,把环境名渲染到页面消息里,让访问者一眼看出当前处于哪个环境。

四、实战:自动化部署 MySQL 数据库服务器

4.1 启动虚拟机并打通 SSH

最后一台 db01 机器此前尚未启动。在存放 Vagrantfile 的目录下执行:

vagrant up db01

机器启动并可达后,把控制节点的 SSH 公钥复制过去,以便免密管理:

ssh-copy-id db01

4.2 初始化 MySQL role 骨架

进入场景 7 目录 ansible-scenario7,用ansible-galaxy生成名为 mysql 的 role 目录骨架:

ansible-galaxy init roles/mysql

该命令会自动创建tasks/、handlers/、templates/、defaults/、vars/、meta/、tests/等标准目录与 README,见 roles/mysql 目录结构。

4.3 在 playbook 中新增 database play

在 playbook7.yml 中追加第三个 play 块:目标组为/etc/ansible/hosts中定义的database组,挂载common与新建的mysql两个 roles,并打上database标签(此处采用列表形式定义标签):

- hosts: webservers become: yes roles: - common - apache2 tags: web - hosts: proxy become: yes roles: - common - nginx tags: proxy - hosts: database become: yes roles: - common - mysql tags: database

4.4 编写 handler:重启 MySQL 服务

handlers/main.yml 定义了一个名为restart mysql的 handler,由任务通过notify触发,仅在状态发生变化时才执行:

# handlers file for roles/mysql - name: restart mysql service: name: mysql state: restarted

4.5 编写 tasks:安装、建库、建用户

tasks/main.yml是纯指针文件,通过import_tasks依次引入两个任务文件(从 源码 可见):

# tasks file for roles/mysql - import_tasks: install_mysql.yml - import_tasks: setup_mysql.yml

install_mysql.yml负责安装 MySQL 相关软件包并确保服务运行,详见 install_mysql.yml:

- name: "Install Common packages" apt: name={{ item }} state=latest with_items: - python3-pip - mysql-client - python3-mysqldb - libmysqlclient-dev - name: Ensure mysql-server is installed latest version apt: name=mysql-server state=latest - name: Installing python module MySQL-python pip: name: PyMySQL - name: Ensure mysql-server is running service: name: mysql state: started

各 task 作用说明:

  • 安装python3-pip、mysql-client、python3-mysqldb、libmysqlclient-dev等依赖(with_items遍历列表,逐项执行apt);
  • 以state=latest安装最新版mysql-server;
  • 通过 pip 安装PyMySQL,它是后续mysql_user/mysql_db模块与 MySQL 通信所需的 Python 驱动;
  • 确保mysql服务处于started状态。

setup_mysql.yml负责生成配置文件、创建数据库用户与数据库(见 setup_mysql.yml):

- name: Create my.cnf configuration file template: src=templates/my.cnf.j2 dest=/etc/mysql/conf.d/mysql.cnf notify: restart mysql - name: Create database user with name 'devops' and password 'DevOps90' with all database privileges community.mysql.mysql_user: login_unix_socket: /var/run/mysqld/mysqld.sock login_user: "{{ mysql_user_name }}" login_password: "{{ mysql_user_password }}" name: "{{db_user}}" password: "{{db_pass}}" priv: '*.*:ALL' host: '%' state: present - name: Create a new database with name '90daysofdevops' mysql_db: login_user: "{{ mysql_user_name }}" login_password: "{{ mysql_user_password }}" name: "{{ db_name }}" state: present

各 task 关键点:

  • 用模板生成/etc/mysql/conf.d/mysql.cnf,并notify触发restart mysqlhandler;
  • community.mysql.mysql_user创建数据库用户:用户名/密码分别取db_user、db_pass,priv: '*.*:ALL'授予全部库全部权限,host: '%'允许从任意主机登录,state: present表示存在即幂等;
  • login_unix_socket: /var/run/mysqld/mysqld.sock用于通过本地 socket 连接 db01 上的 MySQL 实例——这是作者实测后补充的关键连接参数,否则无法成功连接;
  • mysql_db模块创建名为db_name的数据库。

4.6 变量集中管理与模板

上面任务引用的用户名、密码、库名等敏感配置全部来自 common_variables.yml:

http_port: 8000 https_port: 4443 html_welcome_msg: "Hello 90DaysOfDevOps - Welcome to Day 68!" mysql_user_name: root mysql_user_password: "vagrant" db_user: devops db_pass: DevOps90 db_name: 90DaysOfDevOps

模板目录中的 my.cnf.j2 内容如下(注意仓库实际模板比文中示例多了[client]段,内置了 root 登录凭据以便模块连接):

[mysql] bind-address = 0.0.0.0 [client] user=root password=vagrant

bind-address = 0.0.0.0让 MySQL 监听所有网络接口,从而允许从外部(如负载均衡器或控制节点)访问;[client]段为客户端工具提供默认登录凭据。

4.7 运行 playbook 与排错

全量执行(包含此前所有 webservers、proxy 的 plays)或只针对数据库组执行:

ansible-playbook playbook7.yml ansible-playbook playbook7.yml --tags database

只运行--tags database时,作者遇到了一个典型错误:提示未安装 pip3(Python)。修复方式是把 pip3 的安装补充进 common 角色的公共任务里,再重新运行 playbook,即可成功完成变更。

4.8 验证数据库服务器

从控制节点登录 db01:

ssh db01

以 root 身份连接 MySQL(提示时输入 root 的 vagrant 密码):

sudo /usr/bin/mysql -u root -p

确认用户是否已创建:

select user, host from mysql.user;

查看新数据库是否已就绪:

SHOW DATABASES;

还可以用刚创建的 DevOps 账号以同样方式登录验证(密码为DevOps90):

sudo /usr/bin/mysql -u devops -p

作者在验证过程中还发现一个待优化点:setup_mysql.yml中必须显式加上login_unix_socket: /var/run/mysqld/mysqld.sock才能成功连接 db01 的 MySQL 实例,但每次运行时创建用户的 task 都会报告"变更"(幂等性不理想),这正是把用户名、密码抽到group_vars管理后,进一步向 Ansible Vault 迁移敏感信息的动机所在。

五、小结

回顾本系列的技术要点:

主题核心命令/文件作用
Tags--list-tags、--tags proxy、--tags proxy,web、tag: always按 play/task 粒度控制执行范围
Factsansible -m setup、facts.json、ansible_facts['nodename']利用系统自动采集信息驱动模板与任务
用户变量group_vars/all/、host_vars/、vars:配置与 playbook 解耦,按组/按主机分层
Inventoryansible-playbook -i dev playbook.yml多环境独立清单,可内嵌变量
MySQL roleansible-galaxy init roles/mysql标准化目录骨架,一键安装、建库、建用户

至此,90DaysOfDevOps 的 Ansible 自动化场景已覆盖 Web 服务器、负载均衡器与数据库服务器三大类节点,并引入了标签筛选、事实变量、分组变量与自定义 inventory 等核心工程实践。下一阶段可继续探索 第 69 天 的内容,把这套配置管理的知识体系推向更深的自动化场景。

  • 文档/教程

【免费下载链接】90DaysOfDevOps

This repository started out as a learning in public project for myself and has now become a structured learning map for many in the community. We have 3 years under our belt covering all things DevOps, including Principles, Processes, Tooling and Use Cases surrounding this vast topic.

项目地址:https://gitcode.com/gh_mirrors/90/90DaysOfDevOps
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/15 18:08:37

ISO 26262 ASIL 评级:为什么 D 只有 S3+E4+C3 这一个组合

ISO 26262 ASIL 评级&#xff1a;为什么 D 只有 S3E4C3 这一个组合 先说结论 ISO 26262-3 Table 4 一共 36 格&#xff0c;只有 S3E4C3 得出 ASIL D。S3E4C2、S3E3C3、S2E4C3 都只比 D 差一格&#xff0c;但结果全是 C。S0、E0、C0 不在表里——这三个等级无需分配 ASIL。三个最…

作者头像 李华
网站建设 2026/10/15 23:15:29

Python上机实验2|随机模拟与算法效率

实验简介&#xff1a; 本次上机实验围绕 random 随机库 交互式猜数字游戏&#xff0c;增加输入校验、记录猜测过程、连玩3局求平均次数&#xff1b; 实现思路&#xff1a; 交互式猜数字游戏 使用 random.randint(1,100) 生成1~100随机答案。用 try-except 捕获非整数输入&#…

作者头像 李华
网站建设 2026/10/19 22:54:41

中秋手抄报、手工灯笼要做了?材料先备这 3 样,不返工

中秋节快到了&#xff0c;很多学校会给孩子布置"中秋作业"&#xff1a;低年级做手工灯笼、画月亮&#xff0c;中年级画手抄报、写中秋主题的小作文。老师一声令下&#xff0c;家长群里就开始"求素材、求模板"了。别等节前两三天再抓瞎&#xff0c;材料先备…

作者头像 李华
网站建设 2026/10/15 20:34:01

NLP 基础到高级 09:序列到序列模型

序列到序列模型 两个 RNN 假装是翻译器。它们遇到的瓶颈正是注意力机制存在的原因。 类型: 构建 语言: Python 前置条件: Phase 5 08(CNN + RNN 处理文本),Phase 3 11(PyTorch 简介) 预计用时: ~75 分钟 问题 分类将可变长度序列映射到单个标签。翻译将可变长度序…

作者头像 李华
网站建设 2026/10/29 12:39:13

海口吊顶和柜体怎么接缝,两种做法的差别

吊顶与柜体的接缝有两种常见做法&#xff1a;柜体顶到吊顶的下沿&#xff0c;或者柜体穿过吊顶顶到楼板。两种做法在收口位置、可调节余量和后期检修上都不一样。选哪一种&#xff0c;取决于吊顶是否留了检修口、以及柜体到顶后的观感要求。落到工序上&#xff0c;海口整家定制…

作者头像 李华
网站建设 2026/10/16 19:56:57

【题解-洛谷】P1855 榨取kkksc03

题目&#xff1a;P1855 榨取kkksc03 题目描述 洛谷 2 的团队功能是其他任何 OJ 和工具难以达到的。借助洛谷强大的服务器资源&#xff0c;任何学校都可以在洛谷上零成本的搭建 OJ 并高效率的完成训练计划。 为什么说是搭建 OJ 呢&#xff1f;为什么高效呢&#xff1f; 因为&…

作者头像 李华