4、批量配置工具:sed/awk批量修改GRUB配置、Ansible自动化管理启动参数、Puppet/Chef在启动参数管理中的应用

说实话,手动改GRUB配置这事,我干过太多次了。早期运维那会儿,几十台服务器要统一加个iommu=pt参数,我愣是一台台重启、进编辑模式、改、保存。后来想想,真是傻得可以。批量配置这件事,说白了就是「把重复劳动交给脚本,把决策留给自己」。

这一章,我带你看看怎么用sed/awk快速搞定单次批量修改,再用Ansible这类工具把启动参数管理纳入自动化体系。嗯,这里有个坑我先说——千万别在生产环境直接跑未经测试的sed命令,我曾经手滑把/etc/default/grub改成了空文件,那场面...不提了。

4.1 用sed/awk快速批量修改GRUB配置

sed和awk,是Linux运维的两把瑞士军刀。处理GRUB配置这种结构化文本,它们比Python更轻量,比Vim更高效。

4.1.1 sed实战:修改GRUB_CMDLINE_LINUX

最常见的场景,就是给GRUB_CMDLINE_LINUX这一行追加或替换内核参数。比如我要在所有机器上加一个mitigations=off(关闭CPU漏洞缓解,提升性能)。

# 在GRUB_CMDLINE_LINUX行尾追加参数
sed -i 's/^GRUB_CMDLINE_LINUX="\(.*\)"/GRUB_CMDLINE_LINUX="\1 mitigations=off"/' /etc/default/grub

# 验证修改结果
grep GRUB_CMDLINE_LINUX /etc/default/grub

这里有个细节:\(.*\)是sed的捕获组,把原有参数原封不动保留,再追加新参数。我习惯先不加-i跑一遍,确认输出正确再真正写入。

⚠️ 我曾经踩过的坑: 如果GRUB_CMDLINE_LINUX原本是空值(即GRUB_CMDLINE_LINUX=""),上面的正则匹配不到内容。需要单独处理空行情况。建议先检查:grep -c 'GRUB_CMDLINE_LINUX=""' /etc/default/grub

4.1.2 awk实战:按条件筛选并修改

awk更适合做「条件判断+修改」的场景。比如我只想修改包含quiet参数的主机,去掉quiet以便调试启动过程。

# 去掉GRUB_CMDLINE_LINUX中的quiet参数
awk '/^GRUB_CMDLINE_LINUX=/ {
    gsub(/quiet/, "", $0);
    # 去除多余空格
    gsub(/  +/, " ", $0);
    print $0;
    next
} {print}' /etc/default/grub > /tmp/grub_new && mv /tmp/grub_new /etc/default/grub

你看,awk的gsub函数直接做全局替换,比sed的s///g更直观。我个人习惯用awk处理多条件逻辑,比如「如果包含quiet且不包含debug,则去掉quiet」——这种逻辑用sed写起来就费劲了。

4.1.3 批量执行与更新GRUB

改完配置文件,别忘了更新GRUB。我一般写个脚本一条龙搞定:

#!/bin/bash
# 批量修改并更新GRUB
for host in $(cat server_list.txt); do
    ssh "$host" "
        sed -i 's/^GRUB_CMDLINE_LINUX=\"\(.*\)\"/GRUB_CMDLINE_LINUX=\"\1 mitigations=off\"/' /etc/default/grub
        grub2-mkconfig -o /boot/grub2/grub.cfg
        echo '配置已更新,请手动重启: shutdown -r now'
    "
done
💡 我的建议: 别在脚本里直接加reboot。先更新配置,等业务低峰期再统一重启。我曾经半夜被电话叫醒,就是因为脚本自动重启了数据库主库...

4.2 Ansible自动化管理启动参数

当服务器数量超过两位数,ssh循环脚本就不够看了。Ansible才是正经的批量配置工具。它天然支持幂等性——你跑100次和跑1次,结果一样。

4.2.1 用Ansible模块修改GRUB

Ansible有专门的lineinfile模块,比sed更安全:

---
- name: 批量配置GRUB启动参数
  hosts: all
  become: yes
  tasks:
    - name: 确保GRUB_CMDLINE_LINUX包含iommu=pt
      lineinfile:
        path: /etc/default/grub
        regexp: '^GRUB_CMDLINE_LINUX='
        line: 'GRUB_CMDLINE_LINUX="quiet splash iommu=pt"'
        backrefs: no
      notify: 更新GRUB配置

  handlers:
    - name: 更新GRUB配置
      command: grub2-mkconfig -o /boot/grub2/grub.cfg

这里用notify触发handler,只有文件真正被修改了才会执行grub2-mkconfig。嗯,这个设计很巧妙——避免每次跑playbook都重新生成GRUB配置。

4.2.2 参数模板化与变量管理

不同环境需要不同参数?用变量分离:

# group_vars/production.yml
grub_extra_params: "mitigations=off iommu=pt"

# group_vars/testing.yml
grub_extra_params: "mitigations=off iommu=pt debug"

# 模板文件 templates/grub.j2
GRUB_CMDLINE_LINUX="quiet splash {{ grub_extra_params }}"

我习惯把quiet splash这种基础参数写在模板里,把环境差异参数放在变量文件。这样改一个环境,改一个变量文件就行,不用动模板。

4.2.3 滚动更新与回滚策略

批量改启动参数最怕什么?改完起不来。Ansible可以配合滚动更新:

---
- name: 滚动更新GRUB配置
  hosts: all
  serial: 1  # 一次只更新一台
  tasks:
    - name: 修改GRUB配置
      lineinfile: ...
    - name: 更新GRUB
      command: grub2-mkconfig -o /boot/grub2/grub.cfg
    - name: 重启并等待
      reboot:
        reboot_timeout: 300
      register: reboot_result
    - name: 验证启动参数
      command: cat /proc/cmdline
      register: cmdline
    - name: 回滚如果参数不对
      fail:
        msg: "参数验证失败,请手动检查"
      when: "'iommu=pt' not in cmdline.stdout"
🔑 关键点: serial: 1确保一次只处理一台机器。如果这台机器重启后参数不对,playbook会失败,不会继续祸害下一台。我管这叫「慢就是快」。

4.3 Puppet/Chef在启动参数管理中的应用

Ansible适合「推模式」,Puppet和Chef则是「拉模式」的代表。如果你的环境已经用了Puppet/Chef,没必要为了改个GRUB参数再引入Ansible。

4.3.1 Puppet管理GRUB配置

Puppet用augeas模块修改配置文件,比直接sed更安全:

class grub_config {
  augeas { 'modify_grub_cmdline':
    context => '/files/etc/default/grub',
    changes => [
      "set GRUB_CMDLINE_LINUX '\"quiet splash iommu=pt\"'",
    ],
  }

  exec { 'update-grub':
    command     => 'grub2-mkconfig -o /boot/grub2/grub.cfg',
    refreshonly => true,
    subscribe   => Augeas['modify_grub_cmdline'],
  }
}

Puppet的augeas模块会校验语法,如果改出来的配置不合法,它不会应用。我在项目中遇到过,有人用sed把引号搞丢了,Puppet直接报错,避免了灾难。

4.3.2 Chef管理GRUB配置

Chef用ruby_block或line资源:

ruby_block 'modify_grub_cmdline' do
  block do
    file = Chef::Util::FileEdit.new('/etc/default/grub')
    file.search_file_replace_line(
      /^GRUB_CMDLINE_LINUX=/,
      'GRUB_CMDLINE_LINUX="quiet splash iommu=pt"'
    )
    file.write_file
  end
  notifies :run, 'execute[update-grub]', :immediately
end

execute 'update-grub' do
  command 'grub2-mkconfig -o /boot/grub2/grub.cfg'
  action :nothing
end

Chef的ruby_block给了你完全的Ruby能力——你可以写正则、做条件判断、甚至调用外部API。但说实话,我建议尽量用line资源,少用ruby_block,因为后者可读性差,新人接手容易懵。

4.3.3 三种工具对比

工具 模式 幂等性 学习曲线 适用场景
sed/awk 脚本 需手动保证 低 临时批量修改、小规模
Ansible 推 天然支持 中 中大规模、无Agent环境
Puppet 拉 天然支持 高 大规模、已有Puppet基础设施
Chef 拉 天然支持 高 大规模、Ruby技术栈团队

4.4 本章知识体系

下面这张图,帮你理清批量配置启动参数的整体思路:

批量配置启动参数知识体系 GRUB启动参数配置 sed / awk 脚本 临时批量修改 小规模环境 Ansible 自动化 幂等性配置 滚动更新 模板化变量 回滚策略 Puppet / Chef 拉模式管理 Augeas模块 语法校验 大规模集群 选择原则:场景决定工具,工具服务于流程

说白了,选哪个工具取决于你的场景。临时改几台机器,sed/awk一把梭;管理几十上百台服务器,Ansible是首选;如果公司已经上了Puppet/Chef,那就别折腾,直接在现有体系里扩展。

我个人最常用的组合是:日常巡检用sed快速验证,正式变更走Ansible playbook。这样既灵活又可控。嗯,记住一点——无论用哪个工具,改完一定要验证/proc/cmdline,别信配置文件,信内核。

💡 验证命令: cat /proc/cmdline 查看当前生效的启动参数。如果和配置文件不一致,说明GRUB没更新或者重启没生效。

可可精品资料库