packer本身不执行补丁更新,真正补丁安装必须由ansible在provision阶段完成;直接在模板中用shell provisioner运行yum update是错误做法,因其缺乏幂等性、无法处理重启、排除包、日志归档及跨os适配能力。

Packer 本身不执行补丁更新,它只负责“启动一个临时虚拟机 → 运行 provisioner(比如 ansible)→ 打包关机”,真正的补丁安装必须由 Ansible 在 provision 阶段完成。直接在 Packer 模板里写 yum update 命令是常见错误起点——它可能失败、不可控、无法验证,也绕过了 Ansible 的幂等性和状态管理能力。
为什么不能用 shell provisioner 跑 yum/apt?
补丁更新不是简单执行一条命令:需要区分 OS 类型、处理依赖冲突、跳过黑名单包、控制重启行为、记录变更日志。用 shell provisioner 硬编码 yum update -y 会遇到这些典型问题:
-
yum update在最小化安装的镜像中可能因缓存过期而卡住,ansible可通过update_cache: yes显式控制 - 某些安全补丁要求 reboot,
shell无法优雅处理 reboot 后继续 provision,而ansible可配合reboot模块分阶段执行 - 无法按条件跳过特定包(如内核升级),
ansible的exclude参数可精准控制 - 没有失败重试、日志归档、结果注册(
register)机制,调试困难
packer.json 中正确调用 ansible 的关键配置
必须使用 ansible provisioner(非 ansible-local),且确保目标镜像已预装 Python 和 SSH 服务。关键点:
-
type必须设为"ansible",不是"ansible-local"—— 后者在构建机本地跑 Ansible,无法复现真实云平台环境行为 -
playbook_file指向一个完整 Playbook,不要用extra_arguments拼接参数 - 必须设置
host_alias或inventory_file,否则ansibleprovisioner 无法识别目标主机(默认是127.0.0.1,但 Packer 启动的是新 VM,IP 是动态分配的) - 推荐显式指定
extra_arguments: ["-e", "ansible_python_interpreter=/usr/bin/python3"],避免 CentOS/Rocky 8+ 默认无/usr/bin/python
示例片段(packer.json):
{
"type": "ansible",
"playbook_file": "./playbooks/hardened-base.yml",
"host_alias": "default",
"extra_arguments": ["-e", "ansible_python_interpreter=/usr/bin/python3"]
}
Ansible Playbook 里怎么安全地打补丁?
补丁逻辑必须分层设计,不能一股脑 yum update。重点考虑兼容性与可控性:
- 用
gather_facts: yes获取ansible_distribution和ansible_distribution_major_version,再用when分支控制不同发行版行为 - CentOS/RHEL 系列优先用
yum模块的state: latest,而非command;Debian/Ubuntu 用apt模块的upgrade: dist,并加autoremove: yes - 强制排除内核包:
exclude: ["kernel", "kernel-core"](避免镜像启动失败) - 补丁后验证关键服务是否仍可启动:
systemctl is-active --quiet sshd,失败则中断构建 - 把补丁日志重定向到固定路径(如
/var/log/packer-ansible-patch.log),便于后续审计
Playbook 片段示例:
- name: Update all packages (excluding kernel)
yum:
name: '*'
state: latest
exclude: ["kernel", "kernel-core"]
update_cache: yes
when: ansible_distribution in ["CentOS", "Rocky", "AlmaLinux"]
register: yum_result
- name: Verify sshd is active after patch command: systemctl is-active --quiet sshd changed_when: false
云平台镜像构建最易被忽略的三个细节
很多人能跑通流程,但产出的镜像在云平台上线后出问题,根源常在这三点:
-
cloud-init必须保留且未被覆盖:Packer 构建时若清空了/var/lib/cloud/instances或禁用了cloud-init服务,ECS/EC2 实例首次启动将无法注入用户数据、SSH 密钥、主机名 - 网卡命名规则需兼容云平台:
net.ifnames=0 biosdevname=0这类内核参数不能硬编码进镜像,应交由云平台 metadata 驱动控制;否则在阿里云或 AWS 上可能识别不到 eth0 - 镜像大小要主动裁剪:
ansible补丁后残留的yum cache、apt archives、journal logs会膨胀镜像体积,必须在 Playbook 末尾清理,例如:shell: yum clean all && rm -rf /var/cache/yum











