当前位置: 首页 > news >正文

使用Ansible批量管理+更新产品环境服务器配置

文章目录

  • 背景
  • 开发环境搭建
  • 创建Inventory文件
  • Play vs Task vs Playbook
  • 实战
    • 简单更新文件
      • 命令解释
        • 动态传入服务器名字
        • 在远程主机上以root用户执行命令
        • 使用copy模块复制文件到远程
      • 检查脚本
      • 运行命令
        • 并发执行命令
    • 在配置文件中新增配置
      • 命令解释
      • 运行命令
    • 更新配置并重启相关服务
      • 命令解释
        • 重启服务
      • 运行命令
    • 升级Linux内核修复CVE漏洞
      • 命令解释
        • 静态变量
        • 动态变量
        • 版本比较
        • 使用block进一步组织task中的步骤
        • 条件判断
        • 获取脚本task的输出并打印
        • 打印日志
      • 运行命令
  • 工程规范
  • 示例工程
  • 备注

背景

我司生产环境的服务器,每年有2-3次完整的批量重建所有服务器用于升级OS、JDK、Tomcat等。在2次重建服务器间隔期间,难免会有一些其他改动需要应用,例如:OS的漏洞补丁,一些特定服务的配置新增、修改、更新等。这些改动如果都要应用在所有服务器上,面对几十甚至几百台服务器的情况下,不可能人工登录到每台服务器上去应用改变,所以需要

  1. 把要应用的改动写成脚本并提交到git库方便后续的跟踪、审查
  2. 批量ssh到服务器上执行该脚本

Ansible就是干这个事的。最终我们的git库存储的就是一系列Ansible脚本

开发环境搭建

在本地写ansible脚本时,我推荐安装Ansible Development Tools + VSCode插件一起开发。由于还需要Python环境,我推荐使用Poetry来管理虚拟环境进行隔离。以Linux为例

  1. 安装poetry并初始化项目
    dnfinstallpipx pipxinstallpoetrymkdiransible-scripts&&cdansible-scriptsgitinit poetry init
  2. 进入虚拟环境并安装Ansible Development Tools
    eval$(poetryenvactivate)poetryaddansible-dev-tools adt--version
  3. 配置VSCode并配置ansible-lint检测ansible脚本中的不规范问题.先安装Ansible插件
    mkdir.vscode&&cd.vscodevimsettings.json
    settings.json内容如下
    {"ansible.python.interpreterPath":"/home/${USER}/.cache/pypoetry/virtualenvs/ansible-scripts-fiLmypOw-py3.14/bin/python","ansible.ansible.path":"/home/${USER}/.cache/pypoetry/virtualenvs/ansible-scripts-fiLmypOw-py3.14/bin/ansible","ansible.validation.lint.path":"/home/${USER}/.cache/pypoetry/virtualenvs/ansible-scripts-fiLmypOw-py3.14/bin/ansible-lint","ansible.ansibleNavigator.path":"/home/${USER}/.cache/pypoetry/virtualenvs/ansible-scripts-fiLmypOw-py3.14/bin/ansible-navigator","files.associations":{"*.yaml":"ansible"},"[ansible]":{"editor.defaultFormatter":"redhat.ansible","editor.formatOnSave":true,"editor.tabSize":2},"yaml.validate":true,"yaml.format.enable":true,"[yaml]":{"editor.defaultFormatter":"redhat.vscode-yaml","editor.formatOnSave":true,"editor.insertSpaces":true,"editor.tabSize":2},"ansible.validation.lint.enabled":true,"ansible.validation.enabled":true"ansible.lightspeed.enabled":false,"chat.disableAIFeatures":true}
    把USER换成自己的用户即可。我这里还禁用了VSCode的AI Chat功能

创建Inventory文件

在实际生产环境的几百台服务器中,有的可能是web应用服务器,有的可能是数据库服务器,还有的可能是Redis服务器等等。每种类型的服务器的数量从几台到几十台不等,所以第1步我们先把这些分好类的服务器写到/etc/ansible/hosts文件中,这些在逻辑上被分为一组的节点在Ansible中叫做Inventory

这里以应用服务器为例

[webservers] app00 app01 app02 ... app30

上述写法可以进一步简写成

[webservers] app[00:30]

官方把这种简写称为ranges of hosts

如何验证上述简写最终被ansible识别的服务器列表是我们所期望的呢? 使用简单的ansible命令加上–list-hosts参数即可

ansible webservers --list-hosts

输出如下

hosts(31)app00 app01... app30

对于Inventory的分组更复杂的例子,见官方文档

Play vs Task vs Playbook

官方文档中关于它们的定义

简单理解为,当要做个事时,例如部署1个网站,基本会分为几大步

  1. 部署数据库
  2. 部署web程序

这里的每1大步就叫做paly,它们结合起来的完整工作流就叫做play-book

----name:Deploy Databasehosts:dbtasks:-name:Install MySQL...-name:Deploy Webhosts:webtasks:-name:Install Nginx...

所以Paly就是:对哪些主机(hosts)执行什么任务(tasks)

这2大步又分为很多具体细节的小步骤

  1. 对于部署数据库而言,要安装数据库,配置用户、权限、创建表结构、写入一些基本的网站要用的SQL数据等
  2. 对于部署web程序而言,要安装nginx,配置nginx,部署代码等

而这些小步骤,就叫做play中的task

Playbook │ ├─ Play(db) │ ├─ Task │ ├─ Task │ └─ Task │ └─ Play(web) ├─ Task ├─ Task └─ Task

所以Task就是在目标主机上执行的一步操作

实战

简单更新文件

最简单的例子是把1个新的配置文件复制到远程,同时保证新文件的所属组合权限正确并备忘老文件

play脚本如下

----name:deploy jmx exporter configurationhosts:"{{ target_host | default('webservers') }}"become:yestasks:-name:copy jmx_exporter.yaml to target positionansible.builtin.copy:src:./jmx_exporter.yamldest:/etc/otelcol/jmx_exporter.yamlowner:rootgroup:rootmode:'0644'backup:yes

命令解释

动态传入服务器名字

这里的hosts我们没有写死,采取动态变量传入,为什么呢?因为在实际场景中,对于配置的改动,我们会采取渐进式应用,不会一下子全都应用。所以每次执行脚本时,远程服务器的名字都不是固定的

定义1个名为target_host变量,然后再执行命令时通过–extra-vars 参数传递

--extra-vars"target_host=app00,app01,app02"

假如我每次需要执行10台服务器,那传递变量岂不是要从app00一直写到app10,既然Inventory文件中的服务器列表可以简写,那这里是不是也可以简写?

官方提供了切片的匹配模式,即可以指定某个分组的开始下标和结束下标索引位置来指定服务器名字,上述命令就可以简写成

--extra-vars"target_host=app[00:10]"

这样就可以本次只更新app00到app10 这11台服务器,如果想更新appp15到25,则

--extra-vars"target_host=app[15:25]"

注意: 这里切片取的是某个分组服务器数组列表的索引而不是服务器名字。如果你的webservers分组没有app00,而是从app01开始的,那么在更新app01-05时,上述传递参数时,不要写成app[01:05],这会取成app02-06,正确写法是app[00:04]

在远程主机上以root用户执行命令

上述文件位置只有root用户才可以访问,所以需要我们当前用户ssh到远程后切换到root用户,这一动作通过使用become和become_user来实现。become是启用提升权限功能,become_user默认是root

当然前提是当前用户在远程服务器上属于root用户组

在执行命令时需要加入-k和-K选项, 小写的k是询问当前用户ssh到远程服务器密码,大写的K是询问在远程服务器上执行sudo输入的密码,它们一般是相同的,所以在执行命令时,提示输入密码后再次提示输入becom密码,则直接回车即可

-k-K
使用copy模块复制文件到远程

使用copy模块的参数说明见官方文档

刚开始接触ansible时,实现一个需求的时候不知道官方哪些模块支持,可以在Ansible 文档 > Collection Index页面查找,对于大部分简单需求,都可以在Ansible Builtin模块中找到

同时在task中使用模块时,尽量都是以模块的全路径名即Fully Qualified Collection Name FQCN,这一点官方在最佳实践中也建议这么做。对于内置的模块,例如:copy模块,虽然可以不用加ansible.builtin前路径也可以被正常识别,但是最好加上

For builtin modules and plugins, use the ansible.builtin collection name as a prefix, for example, ansible.builtin.copy.

检查脚本

写完脚本后,先使用ansible-lint检查一下当前脚本是否满足规范

ansilbe-lint copy_jmx_config.yml

其他更多检查见文档

运行命令

并发执行命令

对于多台服务器,ansible是并发执行play脚本的,默认值是5. 也就说是,如果传入的target_host是00-14,那么ansible会分为3次循环执行完毕,如果想1次执行完毕,即可在传入命令时使用–forks参数

--forks15

也可以在ansible的配置文件中修改

有了上述知识,下面的命令应该就能看懂了:

以当前用户执行ansible-play命令,回车后,会提示输入 ssh到远程服务器的密码(-k选项),然后会提示输入 切换为root用户的sudo的密码(-K选项),直接默认回车和-k密码保持一致,然后脚本开始执行,一次并发直接对11台服务器同时执行上述逻辑

ansible-playbook /home/tomcat/ansible/eng-15996/copy_jmx_config.yml--forks11--extra-vars"target_host=app[00:10]"-k-K

在配置文件中新增配置

现在需求是要为Tomcat的server.xml中新增一个datasource,这个需求可以使用blockinfile模块实现,这是针对文本块的操作

----name:Add PostgreSQL Resource to Tomcat server.xmlhosts:"{{ target_host | default('webservers') }}"become:truebecome_user:tomcattasks:-name:Insert PostgreSQL Resource into GlobalNamingResourcesansible.builtin.blockinfile:path:/usr/local/tomcat/conf/server.xmlbackup:yesinsertbefore:"</GlobalNamingResources>"marker:"<!-- {mark} ANSIBLE MANAGED JDBC RESOURCE -->"block:|<Resource name="jdbc/prod_postgres" username="user_dml" password="test" auth="Container" driverClassName="org.postgresql.Driver" logAbandoned="true" maxActive="20" maxIdle="5" minIdle="2" maxWait="10000" suspectTimeout="60" removeAbandoned="true" removeAbandonedTimeout="120" abandonWhenPercentageFull="10" testOnBorrow="true" type="javax.sql.DataSource" factory="org.apache.tomcat.jdbc.pool.DataSourceFactory" url="jdbc:postgresql://test-pg-1.us-west-2.rds.amazonaws.com/prod?currentSchema=prod_app" validationInterval="60000" validationQuery="select 1" validationQueryTimeout="3" jdbcInterceptors="org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer;org.apache.tomcat.jdbc.pool.interceptor.ResetAbandonedTimer" defaultAutoCommit="true"/>

命令解释

写入位置是server.xml的</GlobalNamingResources>标签之前,写入内容block是一个多行内容,使用 | 标记,| 属于 yaml 多行字符串语法

运行命令

ansible-playbook /home/tomcat/ansible/adm-3888/add_tomcat_resource.yml--forks10--extra-vars"target_server=app[00:09]"-k-K

更新配置并重启相关服务

有时候仅仅修改配置还不够,我们需要在修改配置后使用systemd重启相关服务使其重新加载新的配置

----name:Add local probehosts:"{{ target_host | default('webservers') }}"become:truehandlers:-name:restart custom_metricsansible.builtin.systemd:name:custom_metricsstate:restarted-name:restart otelcolansible.builtin.systemd:name:otelcolstate:restartedtasks:-name:Update metrics.pyansible.builtin.copy:src:./metrics.pydest:/opt/common/python/custom_metrics/metrics.pyowner:rootgroup:rootmode:'0644'backup:truenotify:restart custom_metrics-name:Update custom_metrics.pyansible.builtin.copy:src:./custom_metrics.pydest:/opt/common/python/custom_metrics/custom_metrics.pyowner:rootgroup:rootmode:'0755'backup:truenotify:restart custom_metrics-name:Update otelcol to add jsp metrics filteransible.builtin.lineinfile:path:/etc/otelcol/config.yamlinsertafter:'^\s+-\s+top_processes_memory_usage'line:' - jsp_.*'state:presentbackup:truenotify:restart otelcol

命令解释

对于文件的改动,除了之前copy模块外,这次还需要在配置文件中的某一行后写入1行新配置,对于文本中某一行的改动不使用blockinfile,而是使用lineinfile

重启服务

通过Handler来实现,先创建handler并命名,然后在task中使用notify来通知handler执行重启服务的任务

运行命令

ansible-playbook /home/tomcat/ansible/adm-5252/add_local_probe.yml--forks7--extra-vars"target_host=app[03:09]"-K-k

升级Linux内核修复CVE漏洞

对于最近的Linux内核漏洞CVE-2026-31431及其后续漏洞,官方出了patch之后,需要及时应用到生产环境中。我们使用的是Rocky8,漏洞修复的内核版本为4.18.0-553.124

----name:Kernel Update and System Cleanup for CVE-2026-31431hosts:"{{ target_host | default('all') }}"become:yesvars:# CVE-2026-31431 is fixed in kernel 4.18.0-553.123 or higherfixed_kernel_version:"4.18.0-553.124"tasks:-name:Get current kernel version# Get the kernel version from ansible_factsset_fact:current_kernel:"{{ ansible_facts['ansible_kernel'] }}"-name:Display current kernel versionansible.builtin.debug:msg:"Current kernel version is {{ current_kernel }}"-name:Check if kernel is affected by CVE-2026-31431# Compare current version with the fixed versionset_fact:is_affected:"{{ current_kernel is version(fixed_kernel_version, '<') }}"-name:Perform updates if the system is affectedblock:-name:Update all packages except temurin-17-jdk# dnf -y --exclude=temurin-17-jdk updateansible.builtin.dnf:name:"*"state:latestexclude:temurin-17-jdkupdate_cache:yes-name:Remove setroubleshoot packages# dnf -y remove setroubleshoot*ansible.builtin.dnf:name:"setroubleshoot*"state:absent-name:Run dnf autoremove# dnf -y autoremoveansible.builtin.dnf:autoremove:yes-name:Identify the latest kernel installed on disk# Query RPM to see the newest kernel version that will load after rebootansible.builtin.shell:"rpm -q kernel --queryformat '%{VERSION}-%{RELEASE}.%{ARCH}\n' | tail -n 1"register:installed_kernelchanged_when:false-name:Final status report and reboot requirementansible.builtin.debug:msg:-"------------------------------------------------"-"PATCHING SUMMARY:"-"1. Currently Active Kernel: {{ current_kernel }}"-"2. Newly Installed Kernel: {{ installed_kernel.stdout }}"-"------------------------------------------------"-"ACTION REQUIRED:"-"reboot the server"-"------------------------------------------------"when:is_affected-name:Notify if no action is requiredansible.builtin.debug:msg:"System kernel is already at or above the secure version. No patching needed."when:not is_affected

命令解释

静态变量

使用vars定义静态变量fixed_kernel_version,代表内核版本为此版本的服务器不需要升级内核

动态变量

我们即使重新创建服务器也没有特意跟踪个每台服务器的内核版本,所以每一台服务器的具体内核版本是未知的,需要在脚本中动态获取。对于这种系统级的信息,ansible也默认提供了工具叫facts,它的结构包含的内容非常多,具体信息可参考facts文档。也可以在本机执行

ansible localhost-msetup

查看当前机器的ansible facts的所有信息。既然ansible提供了这些信息,直接根据它的语法去拿即可

拿到之后通过set_fact把需要的信息设置到变量current_kernel中

版本比较

对于这种条件比较,ansible同样提供了内置工具。不得不说,ansible还是太全面了.
工具是verstion_test,具体用法可语法课参考文档链接

使用block进一步组织task中的步骤

如果内核需要更新,那么需要执行4个子任务,这种task中的task可以通过block搭配条件判断来实现,即block中的task要么都执行要么都不执行

条件判断

使用最基本的when来进行判断

获取脚本task的输出并打印

通过register来获得当前task的输出

每个模块结束之后,都会返回一个结构化的数据,里面包含了很多项。这些数据可以随着register,注入到声明的变量中。stdout也包含在内,所以后续可以直接调用installed_kernel.stdout。 所有数据项见Return Values文档

打印日志

普通的打印日志,使用内置的debug模块

运行命令

ansible-playbook /home/tomcat/ansible/adm-5240/patch_kernel.yml --extra-vars"target_server=app[03:05]"-K-k

工程规范

对于更复杂的企业级项目来说,ansible官方也有对于工程目录的建议. 一些更通用的建议见Tips文档

示例工程

更详细的一个示例见源码仓库。 这个工程是我使用 ClaudeCode Agent + KIMI 大模型 + Superpowers做的。99%工作都由AI完成。

我做了一些需求的澄清、确认和最终工程部分结果的验证,按照文档说明即可启动,使用。可放心食用

备注

Ansible的东西非常多,在平时使用时,如果不清楚的地方可以直接问AI,或者干脆让AI来完成Ansible脚本的书写。我现在一般交给AI来写Ansible脚本了,只不过从个人学习的角度来看,还是要熟悉一下没有AI的日子是如何学习新东西的,这对以后更好的使用AI也有好处

http://www.cnnetsun.cn/news/2711363.html

相关文章:

  • 3步解决Windows 10 PL-2303串口驱动代码10错误:老旧硬件完美重生指南
  • c#软件开发学习笔记--面向对象
  • 营销自动化升级迫在眉睫:2024年仅剩37天窗口期,完成AI+CDP+CRM三端协议对齐的企业不足11%
  • 告别Unknown display:手把手教你为Ubuntu老旧或特殊显示器手动创建xorg.conf配置
  • 7、More examples of machine learning can and connot do?机器学习可行性事例
  • 你的大脑只能同时处理4件事:一天下来你什么事都没做完的原因
  • 【轴承故障诊断】基于SE-TCN和SE-TCN-SVM西储大学轴承故障诊断研究附Matlab代码
  • 别再只用COCO了!手把手教你用DOTA V1.5数据集搞定航拍小目标检测
  • Windows 11 LTSC系统安装微软商店:企业级稳定与个人便利的完美平衡
  • 项目经理,如何平衡工作中的大局观和细节把控?
  • 基于ESP8266的应急通信设备:三重混合加密与ESP-NOW点对点传输实践
  • 别再只会用线性回归了!用Python的sklearn实战Lasso回归,5分钟搞定特征选择
  • 图解Linux V4L2异步注册:waiting、done、subdev_list链表如何协同工作
  • 20个核心概念揭秘:彻底搞懂AI,从ChatGPT到AI Agent全解析!
  • 从‘连接不上’到完美点云:YDlidar X2雷达在ROS1/ROS2下的完整调试与可视化指南
  • Obsidian Projects:用纯文本重塑你的项目管理体验
  • 【MATLAB例程】基于扩展卡尔曼滤波(EKF)的正反向滤波,实时滤波,改善估计精度。附下载链接
  • 一屏透明化三维立体重构安全信息机构
  • 3Sum问题
  • 终极神界原罪2模组管理指南:告别游戏闪退的完整教程
  • 基于Arduino与MQTT的远程办公时间交互系统:硬件仪表盘设计
  • FastSpeech语音合成:非自回归架构如何实现实时高质TTS
  • Loop:macOS窗口管理终极解决方案,免费开源提升桌面效率300%
  • 从D435深度相机到2304电机:一份给软件工程师的无人机硬件入门指南
  • Ti AWR2944雷达开发板新玩法:用BPM模式实现毫米波微弱形变监测(保姆级教程)
  • 晶体管无稳态多谐振荡器:零编程实现LED流水灯效果
  • DIY人体工学键盘支架:低成本PVC管材改造方案详解
  • Arduino与WS2812B打造动态心脏线弦艺光效装置
  • 终极指南:如何用Fan Control免费软件精准掌控Windows风扇控制
  • 手机变开发机:用Termux在安卓上编译APK的完整踩坑实录(附ARM版SDK工具)