简介:这份资源面向需要搭建 Puppet 自动化运维体系的工程师与 DevOps 学习者,围绕 Puppet Master、The Foreman 与 smart-proxy 的安装和管理展开,帮助解决多角色 Puppet 架构部署与升级控制的问题。资源包共 60 个文件,约 64KB,以 pp 清单、erb 模板、rb 脚本为主,辅以 yml 配置、json 元数据及 Gemfile、Rakefile 等依赖与任务文件,目录按 foreman、smartproxy、puppet 等模块划分,结构清晰。其特色在于每个软件栈均通过 RVM、git 仓库与 gems 配置,而非依赖 yum 包和系统默认 Ruby,从而获得更高的模块化程度与升级可控性,并支持 aio、catalog、ca 三种 Puppet Master 角色。目前已有 133 人学习,适合希望掌握 Ruby 安全补丁更新、摆脱系统 Ruby 限制的运维人员参考实践。
1. 从一台裸机到可管控的 Puppet 架构:puppet_stack 到底解决了什么
如果你维护过超过二十台服务器的配置,大概率经历过这种场景:手动改了一台机器的 Nginx 配置,过两周发现另外三台早就跑偏了,谁改的、改了什么,全靠翻聊天记录。Puppet 就是来解决这个问题的——用声明式代码描述“机器应该长什么样”,由 Agent 定期拉取并收敛。但 Puppet Master、Foreman、smart-proxy 这三件套的安装和互连,官方文档散落在不同章节,版本兼容性又经常翻车。puppet_stack 这个资源包的价值就在于:它把 Puppet Master 的证书签发、Foreman 的 ENC 集成、smart-proxy 的节点上报这条链路,压缩成一套可复现的安装流程。适合正在做配置管理选型、或者已经用上 Puppet 但被 Foreman 集成卡住的运维工程师。下面我按实际拆包顺序,把每一步的参数和坑讲清楚。
2. 拆开 puppet_stack:组件依赖与安装前的环境决策
2.1 三个组件各自负责什么,为什么必须一起装
Puppet Master 是证书签发和 catalog 编译的中心。所有被管节点(Agent)第一次连接时,要向 Master 提交证书签名请求(CSR),Master 签发后才允许拉取配置。Foreman 在这里扮演的是 ENC(External Node Classifier)和事实存储的角色——它不替代 Puppet Master,而是通过调用 Master 的 API 来获取节点列表、分配 Puppet class,同时把节点上报的 facts 存进自己的数据库,供报表和搜索使用。smart-proxy 则是 Foreman 的“触手”,它部署在目标网段里,负责 DHCP、DNS、TFTP 和 Puppet 证书的代理操作,让 Foreman 能跨网段完成裸机部署。
这三个组件必须一起装的原因在于证书信任链:Foreman 要调用 Puppet Master 的 API,必须持有 Master 签发的客户端证书;smart-proxy 要向 Foreman 注册,又必须持有 Foreman 签发的证书。如果只装 Puppet Master,你得到的是一个孤立的 CA;只装 Foreman,它连不上任何 Puppet 环境。puppet_stack 把这三者的证书互签流程串了起来,这是它比手动逐个安装省事的地方。
2.2 安装前的系统基线检查
在跑安装脚本之前,有几项基线必须确认,否则后面会出现“证书签了但连不上”的玄学问题。常见做法是先统一时间源,因为 Puppet 证书有有效期,时间偏差超过几分钟就会导致证书校验失败。然后确认主机名是 FQDN 格式,且能正向和反向解析——Foreman 和 smart-proxy 注册时都会用 FQDN 做标识,如果hostname -f返回的不是完整域名,后面改起来很麻烦。
# 检查时间同步状态,确保 NTP 或 chrony 正常 timedatectl status | grep -E "System clock|NTP" # 确认 FQDN 解析,正向和反向都要通 hostname -f dig +short $(hostname -f) dig +short -x $(hostname -f | awk -F. '{print $NF"."$(NF-1)}') # 检查防火墙端口,Puppet 用 8140,Foreman 用 443/80,smart-proxy 用 8443 ss -tlnp | grep -E "8140|443|8443"上面这段检查的逻辑是:时间同步决定证书是否有效,FQDN 解析决定组件之间能否互相识别,端口监听决定服务是否可达。参数上,Puppet Master 默认监听 8140,Foreman 的 Web 界面走 443,smart-proxy 的 API 走 8443。如果这些端口被占用,安装脚本不会报错,但服务启动后会静默失败,日志里只写“connection refused”,排查起来很费时间。
2.3 仓库配置与包版本锁定
puppet_stack 依赖的包来自 Puppet 官方仓库和 Foreman 官方仓库。直接yum install或apt install拿到的是发行版自带的旧版本,和 Foreman 的兼容性没有保证。我一般会先配好仓库,再锁定主版本号。比如 Puppet 7 和 Puppet 8 的证书 API 有差异,Foreman 3.x 对 Puppet 7 的支持更成熟,如果混用 Puppet 8 和 Foreman 3.0,ENC 调用会返回 404。
# 以 EL 系为例,配置 Puppet 7 仓库 rpm -Uvh https://yum.puppet.com/puppet7-release-el-8.noarch.rpm # 配置 Foreman 3.5 仓库 rpm -Uvh https://yum.theforeman.org/releases/3.5/el8/x86_64/foreman-release.rpm # 锁定版本,避免自动升级到不兼容的大版本 yum versionlock puppet-agent puppet-server foreman foreman-proxy这里的关键参数是仓库的 release 版本号。Puppet 7 的仓库路径里带puppet7,Foreman 3.5 的路径里带3.5。锁定版本用yum versionlock或apt-mark hold,防止某次yum update把 Puppet 从 7 升到 8,导致证书格式变化、所有 Agent 掉线。这个坑我在测试环境踩过一次,生产环境升级前一定要先在隔离环境验证。
3. 安装 Puppet Master 与证书签发:从 CSR 到签名完成
3.1 Puppet Master 的初始化配置
Puppet Master 安装完成后,第一件事是改/etc/puppetlabs/puppet/puppet.conf。默认配置里certname是主机名,dns_alt_names为空。如果 Foreman 和 smart-proxy 要用不同的域名访问 Master,必须把备用域名写进dns_alt_names,否则证书里不包含这些 SAN,TLS 握手会直接失败。
# /etc/puppetlabs/puppet/puppet.conf [main] certname = puppet-master.example.lab server = puppet-master.example.lab environment = production dns_alt_names = puppet,puppet-master,puppet-master.example.lab [master] vardir = /opt/puppetlabs/server/data/puppetserver logdir = /var/log/puppetlabs/puppetserver rundir = /var/run/puppetlabs/puppetserver pidfile = /var/run/puppetlabs/puppetserver/puppetserver.pid codedir = /etc/puppetlabs/codedns_alt_names这一行是重点。它决定了证书里包含哪些备用名称。Foreman 通过 API 调用 Master 时,如果 URL 里写的是puppet而不是完整 FQDN,证书里没有这个 SAN 就会报hostname mismatch。改完配置后重启 puppetserver,然后用puppet cert list --all确认 CA 已经生成。如果 CA 没生成,检查/etc/puppetlabs/puppet/ssl目录权限,puppet 用户必须有读写权限。
3.2 证书签发流程与自动签名策略
Puppet 的证书签发默认是手动模式:Agent 提交 CSR 后,Master 上执行puppet cert sign <certname>才签发。在节点数量少的时候没问题,但 Foreman 集成后,smart-proxy 会代表新节点自动提交 CSR,如果还用手动签名,每次扩容都要登 Master 操作,效率很低。常见做法是配置自动签名,但只对特定域名后缀生效,避免任意节点都能签进来。
# 查看待签名的 CSR 列表 puppet cert list # 手动签名单个节点 puppet cert sign agent01.example.lab # 配置自动签名:在 Master 上创建 autosign.conf cat > /etc/puppetlabs/puppet/autosign.conf <<'EOF' *.example.lab EOF # 重启 puppetserver 使自动签名生效 systemctl restart puppetserverautosign.conf里写的是域名匹配模式,*.example.lab表示所有以example.lab结尾的节点都自动签发。这个策略的风险在于:任何能解析到该域名后缀的机器都能拿到证书。所以生产环境一般会配合网络隔离,只允许受控网段访问 Master 的 8140 端口。如果不想用通配符,也可以写具体主机名,或者用外部脚本做更细粒度的审批。签名完成后,Agent 端执行puppet agent -t就能拉取 catalog 了。
3.3 验证 Master 与 Agent 的首次通信
证书签完不代表通信正常。Agent 第一次跑puppet agent -t时,会做三件事:用本地私钥和 Master 的 CA 证书做 TLS 握手、提交 facts、请求 catalog。任何一步失败都会在 Agent 端留下日志。我一般会先在 Master 上用puppet agent -t --noop自测,确认 Master 自己能编译 catalog,再让 Agent 连。
# 在 Master 上自测,--noop 表示只模拟不实际变更 puppet agent -t --noop # 在 Agent 上测试,--verbose 输出详细握手过程 puppet agent -t --verbose --server puppet-master.example.lab # 如果握手失败,在 Master 上查看 puppetserver 日志 tail -f /var/log/puppetlabs/puppetserver/puppetserver.log | grep -i "certificate\|ssl"--noop参数让 Puppet 只报告“将要做什么”,不实际执行变更,适合首次验证。--verbose会打印 TLS 握手的每一步,如果卡在certificate verify failed,说明 Agent 的 CA 证书和 Master 的不匹配,通常是 Agent 之前连过别的 Master,/etc/puppetlabs/puppet/ssl目录里有旧证书,清掉重新签即可。日志里如果出现Could not find class,那是 catalog 编译问题,和证书无关,需要检查 Foreman 的 ENC 配置。
4. Foreman 与 smart-proxy 集成:ENC 配置与节点上报
4.1 Foreman 安装后的初始配置
Foreman 安装完成后,Web 界面默认在 443 端口。第一次登录后,第一件事是配置 Puppet Master 的关联。在Infrastructure > Smart Proxies里,Foreman 自己会注册为一个 smart-proxy,但它的 Puppet 功能默认是关闭的。需要编辑这个 proxy,在Puppet选项卡里填入 Master 的地址和证书路径。
# /etc/foreman-proxy/settings.d/puppet.yml --- :enabled: true :puppet_server: puppet-master.example.lab :puppet_port: 8140 :ssl_certificate: /etc/foreman-proxy/ssl/certs/foreman-proxy.example.lab.pem :ssl_ca_file: /etc/foreman-proxy/ssl/certs/ca.pem :ssl_private_key: /etc/foreman-proxy/ssl/private/foreman-proxy.example.lab.key这里的:puppet_server必须和 Master 的certname一致,否则 Foreman 调用 API 时证书校验会失败。:ssl_certificate和:ssl_private_key是 Foreman 自己的证书,不是 Master 的。如果 Foreman 和 Master 装在同一台机器上,证书路径可能重叠,需要确认权限:foreman-proxy 用户要能读这些文件,但私钥权限不能太开放,一般设成 0640,属主是 foreman-proxy。
4.2 把 Foreman 配成 Puppet 的 ENC
ENC 的作用是让 Puppet Master 在编译 catalog 时,向 Foreman 查询这个节点应该包含哪些 class。配置方式是在 Master 的puppet.conf里加node_terminus = exec和external_nodes指向 Foreman 提供的脚本。
# /etc/puppetlabs/puppet/puppet.conf 追加 [master] node_terminus = exec external_nodes = /etc/puppetlabs/puppet/enc.sh# /etc/puppetlabs/puppet/enc.sh #!/bin/bash # 调用 Foreman 的 ENC API,传入 certname curl -s --cert /etc/puppetlabs/puppet/ssl/certs/puppet-master.example.lab.pem \ --key /etc/puppetlabs/puppet/ssl/private_keys/puppet-master.example.lab.pem \ --cacert /etc/puppetlabs/puppet/ssl/certs/ca.pem \ "https://foreman.example.lab/api/hosts/$1/enc"这个脚本的逻辑是:Puppet Master 收到 Agent 的 catalog 请求后,用$1(即 Agent 的 certname)去调 Foreman 的 ENC API,Foreman 返回 YAML 格式的 class 列表和参数,Master 再据此编译 catalog。--cert和--key是 Master 自己的证书,用来向 Foreman 证明身份。如果 Foreman 返回 401,说明 Master 的证书没有被 Foreman 信任,需要在 Foreman 的Settings > Authentication里把 Master 的 CA 证书加进信任列表。
4.3 smart-proxy 的注册与节点上报验证
smart-proxy 可以装在和 Foreman 不同的网段,负责代理 DHCP、DNS、TFTP 和 Puppet 证书操作。注册流程是在 Foreman 的Smart Proxies页面点New Smart Proxy,填入 proxy 的 FQDN 和端口(默认 8443),然后 Foreman 会尝试用 HTTPS 连接 proxy 的/features接口。如果连接失败,通常是 proxy 的证书不被 Foreman 信任。
# 在 smart-proxy 上查看已启用的功能 curl -k https://localhost:8443/features # 在 Foreman 上测试到 proxy 的连通性 curl --cert /etc/foreman/client_cert.pem \ --key /etc/foreman/client_key.pem \ --cacert /etc/foreman/ssl_ca.pem \ https://smart-proxy.example.lab:8443/features/features接口返回 JSON,列出 proxy 支持的功能模块。如果 Puppet 功能没启用,返回的列表里就没有puppet字段。这时候要检查 proxy 的puppet.yml里:enabled是否为 true,以及:puppet_server是否指向正确的 Master。节点上报的验证方式是:在 Foreman 的Hosts > All Hosts里看是否有 Agent 节点出现,如果没有,检查 proxy 的日志/var/log/foreman-proxy/proxy.log,常见错误是SSL certificate verify failed,说明 proxy 和 Foreman 之间的证书链不完整。
5. 避坑与排查:证书、端口和版本兼容的五个血泪经验
5.1 现象:Agent 报 “certificate verify failed”,但证书明明签了
原因通常是 Agent 本地残留了旧的 CA 证书。如果这台机器之前连过另一个 Puppet Master,/etc/puppetlabs/puppet/ssl/certs/ca.pem里存的是旧 CA,和新 Master 的 CA 不匹配。解决方法是清空 Agent 的 ssl 目录,重新跑puppet agent -t生成新的 CSR,再到 Master 上签名。清空之前确认没有其他服务依赖这个证书,否则会影响其他 TLS 连接。
5.2 现象:Foreman 的 ENC 调用返回 404,但 API 路径是对的
原因是 Foreman 里没有这个节点的记录。ENC API 只对已经在 Foreman 中注册的主机返回 class 列表,如果 Agent 的 certname 在 Foreman 的数据库里不存在,API 会返回 404。解决方法是先在 Foreman 里手动创建这个主机,或者配置 Puppet 的facts上报,让 Foreman 自动发现节点。自动发现的配置在Settings > Puppet里,勾选Automatic host creation。
5.3 现象:smart-proxy 注册成功,但 Puppet 证书操作超时
原因是 proxy 到 Master 的 8140 端口不通。smart-proxy 代理 Puppet 证书操作时,需要直接连 Master 的 8140。如果中间有防火墙,或者 Master 的auth.conf限制了来源 IP,proxy 的请求会被拒绝。检查方法是先在 proxy 上用openssl s_client -connect puppet-master.example.lab:8140测试 TLS 握手,如果握手失败,就是网络或防火墙问题;如果握手成功但 API 返回 403,就是auth.conf的权限规则问题。
5.4 现象:Puppet 7 和 Foreman 3.0 混用,ENC 返回的 YAML 解析失败
原因是 Foreman 3.0 的 ENC API 返回的 YAML 格式和 Puppet 7 的解析器不兼容。Foreman 3.5 之后才修复了这个问题。如果已经装了 Foreman 3.0,要么升级 Foreman,要么在 ENC 脚本里做格式转换。我一般会在脚本里加一层python -c "import yaml,sys; yaml.safe_load(sys.stdin)"做校验,如果解析失败就输出空 hash,避免 Master 编译 catalog 时直接崩溃。
5.5 现象:自动签名生效后,陌生节点也能签进来
原因是autosign.conf的通配符太宽,或者没有配合网络隔离。解决方法是把通配符改成具体域名后缀,并且在 Master 的防火墙里只允许受控网段访问 8140。如果安全要求更高,可以关掉autosign.conf,改用puppet cert sign手动审批,或者写一个外部审批脚本,调用 Foreman 的 API 确认节点是否已在 CMDB 中登记。
6. 进阶:用 Foreman 报表验证 Puppet 收敛状态与一个日常检查习惯
装完这套架构只是开始,真正要确认的是 Puppet 的收敛是否按预期执行。Foreman 的报表功能可以按主机、按 class、按资源类型查看每次 Puppet run 的结果。在Monitor > Reports里,能看到每个节点的applied、failed、skipped资源数量。如果某个节点的failed持续大于 0,说明 catalog 里有资源无法收敛,需要点进详情看具体是哪个 class 的哪个资源报错。
# 在 Master 上手动触发一次全量 run,用于验证报表是否正常上报 puppet agent -t --server puppet-master.example.lab # 在 Foreman 上通过 API 拉取最近一次报表 curl -s --cert /etc/foreman/client_cert.pem \ --key /etc/foreman/client_key.pem \ --cacert /etc/foreman/ssl_ca.pem \ "https://foreman.example.lab/api/reports?search=host=puppet-master.example.lab" \ | python -m json.tool | head -40这个 API 调用返回的是 JSON 格式的报表摘要,search参数支持按主机名、时间范围、状态过滤。我一般会把它写进日常巡检脚本,每天跑一次,如果failed数量超过阈值就发告警。参数上,search=host=后面的主机名必须是 Foreman 里注册的 certname,不是 IP。如果返回空列表,说明报表没有上报,检查 Puppet Master 的reports配置里是否启用了foreman后端。
还有一个容易被忽略的点:Puppet 的noop模式。在推新配置之前,我习惯先用--noop跑一遍,看 Foreman 报表里“将要变更”的资源列表。如果列表里出现了预期之外的资源,比如某个不该重启的服务被标记为restart,那就说明 class 的参数写错了。这个习惯帮我避免过好几次生产环境的意外重启。从那以后,我每次改完 Puppet 代码,都强制走一遍noop加报表核对,确认变更范围符合预期再正式 apply。希望帮到你。
本文还有配套的精品资源,点击获取