运维知识
悠悠
2026年5月8日

Puppet配置管理实战:让1000台服务器听话的秘密武器

今天就来分享一下我这几个月使用Puppet管理多服务器配置的实战经验,包括一些踩过的坑和解决方案。

Puppet基础概念快速入门

在开始实战之前,先简单说说Puppet的核心概念,这些理解了后面的操作就很容易了。

Puppet采用的是Master-Agent架构,Master节点存储配置清单(manifest),Agent节点定期向Master请求配置并应用。整个过程是声明式的,你只需要描述想要的最终状态,Puppet会自动计算如何达到这个状态。

几个重要概念:

  • Manifest:用Puppet DSL语言编写的配置文件,描述系统应该是什么样子
  • Module:可复用的配置单元,包含manifest、文件、模板等
  • Node:被管理的服务器节点
  • Catalog:Master为每个节点编译生成的具体配置指令

环境搭建实战

我的测试环境是3台CentOS 7服务器,1台Master,2台Agent。生产环境类似,只是Agent数量更多。

Master节点安装配置

# 安装Puppet Server
rpm -Uvh https://yum.puppet.com/puppet-release-el-7.noarch.rpm
yum install -y puppetserver

# 修改内存配置,默认2G太大了
vim /etc/sysconfig/puppetserver
JAVA_ARGS="-Xms512m -Xmx512m"

# 启动服务
systemctl enable puppetserver
systemctl start puppetserver

这里有个坑,Puppet Server默认内存配置是2G,小环境根本用不了这么多,而且会导致启动失败。我把它调到512M,测试环境够用了。

Agent节点配置

# 安装Puppet Agent
rpm -Uvh https://yum.puppet.com/puppet-release-el-7.noarch.rpm
yum install -y puppet-agent

# 配置Master地址
vim /etc/puppetlabs/puppet/puppet.conf
[main]
server = puppet-master.example.com

# 启动服务
systemctl enable puppet
systemctl start puppet

Agent第一次连接Master时需要证书认证,在Master上执行:

/opt/puppetlabs/bin/puppetserver ca list
/opt/puppetlabs/bin/puppetserver ca sign --all

编写第一个配置管理模块

我们从一个简单的nginx模块开始。在生产环境中,nginx的配置管理是个头疼的问题,不同环境的配置文件经常不一致。

创建nginx模块结构

cd /etc/puppetlabs/code/environments/production/modules
mkdir -p nginx/{manifests,files,templates,tests}

编写主配置文件

# nginx/manifests/init.pp
class nginx {
  package { 'nginx':
    ensure => installed,
  }

  service { 'nginx':
    ensure  => running,
    enable  => true,
    require => Package['nginx'],
  }

  file { '/etc/nginx/nginx.conf':
    ensure  => present,
    source  => 'puppet:///modules/nginx/nginx.conf',
    owner   => 'root',
    group   => 'root',
    mode    => '0644',
    notify  => Service['nginx'],
    require => Package['nginx'],
  }
}

这个配置很简单,确保nginx包安装、服务运行、配置文件正确。notify参数很重要,当配置文件变化时会自动重启服务。

准备配置文件模板

# 将标准的nginx配置放到files目录
cp /etc/nginx/nginx.conf nginx/files/

应用到节点

# site.pp
node 'web-server-01.example.com' {
  include nginx
}

node 'web-server-02.example.com' {
  include nginx
}

这样两台web服务器就会自动安装和配置nginx了。每次Agent运行时(默认30分钟),都会检查配置是否符合要求,不符合就自动修复。

高级配置管理技巧

使用变量和条件判断

实际环境中,不同服务器的配置往往有差异。比如内存大小不同,nginx worker进程数就应该不同:

class nginx {
  $worker_processes = $facts['processors']['count']

  package { 'nginx':
    ensure => installed,
  }

  file { '/etc/nginx/nginx.conf':
    ensure  => present,
    content => template('nginx/nginx.conf.erb'),
    owner   => 'root',
    group   => 'root',
    mode    => '0644',
    notify  => Service['nginx'],
    require => Package['nginx'],
  }

  service { 'nginx':
    ensure  => running,
    enable  => true,
    require => [Package['nginx'], File['/etc/nginx/nginx.conf']],
  }
}

对应的ERB模板文件:

# nginx/templates/nginx.conf.erb
worker_processes <%= @worker_processes %>;

events {
    worker_connections 1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;
  
    <% if @facts['memorysize_mb'].to_i > 4096 %>
    # 大内存服务器使用更多连接
    worker_connections 2048;
    <% end %>
}

参数化模块

为了让模块更灵活,可以使用参数:

class nginx (
  String $version = 'installed',
  Integer $worker_processes = $facts['processors']['count'],
  Boolean $enable_ssl = false,
) {
  package { 'nginx':
    ensure => $version,
  }

  if $enable_ssl {
    package { 'nginx-mod-ssl':
      ensure => installed,
    }
  }

  # 其他配置...
}

在site.pp中这样使用:

node 'web-server-01.example.com' {
  class { 'nginx':
    worker_processes => 8,
    enable_ssl      => true,
  }
}

使用Hiera进行数据分离

Hiera是Puppet的数据查找系统,可以把配置数据从代码中分离出来。这在管理多环境时特别有用。

# /etc/puppetlabs/code/environments/production/data/common.yaml
nginx::worker_processes: 4
nginx::enable_ssl: false

# /etc/puppetlabs/code/environments/production/data/nodes/web-server-01.yaml
nginx::worker_processes: 8
nginx::enable_ssl: true

模块中使用lookup函数获取数据:

class nginx {
  $worker_processes = lookup('nginx::worker_processes', Integer, 'first', $facts['processors']['count'])
  $enable_ssl = lookup('nginx::enable_ssl', Boolean, 'first', false)

  # 配置逻辑...
}

管理复杂应用配置

单个服务的配置相对简单,但实际生产环境往往需要管理整个应用栈。我们来看一个更复杂的例子:LAMP环境的配置。

创建profile模块

# profiles/manifests/lamp.pp
class profiles::lamp {
  include apache
  include mysql
  include php

  # 确保安装顺序
  Class['mysql'] -> Class['apache'] -> Class['php']
}

Apache模块

class apache (
  String $document_root = '/var/www/html',
  Array[String] $modules = ['rewrite', 'ssl'],
) {
  package { 'httpd':
    ensure => installed,
  }

  service { 'httpd':
    ensure => running,
    enable => true,
  }

  file { '/etc/httpd/conf/httpd.conf':
    ensure  => present,
    content => template('apache/httpd.conf.erb'),
    notify  => Service['httpd'],
    require => Package['httpd'],
  }

  $modules.each |$module| {
    exec { "enable-${module}-module":
      command => "/usr/bin/a2enmod ${module}",
      unless  => "/usr/bin/apache2ctl -M | grep ${module}",
      notify  => Service['httpd'],
      require => Package['httpd'],
    }
  }
}

使用角色分离

在大型环境中,不同服务器承担不同角色。可以创建role模块:

# roles/manifests/webserver.pp
class roles::webserver {
  include profiles::lamp
  include profiles::monitoring
  include profiles::backup
}

# roles/manifests/database.pp
class roles::database {
  include profiles::mysql
  include profiles::monitoring
  include profiles::backup
}

然后在site.pp中按角色分配:

node /^web-\d+\.example\.com$/ {
  include roles::webserver
}

node /^db-\d+\.example\.com$/ {
  include roles::database
}

配置文件管理最佳实践

使用文件片段拼接

有时候配置文件需要从多个模块贡献内容,比如nginx的虚拟主机配置:

# 主配置
concat { '/etc/nginx/nginx.conf':
  owner => 'root',
  group => 'root',
  mode  => '0644',
  notify => Service['nginx'],
}

concat::fragment { 'nginx-main':
  target  => '/etc/nginx/nginx.conf',
  content => template('nginx/nginx-main.conf.erb'),
  order   => '01',
}

# 虚拟主机配置
define nginx::vhost (
  String $document_root,
  String $server_name = $title,
) {
  concat::fragment { "nginx-vhost-${title}":
    target  => '/etc/nginx/nginx.conf',
    content => template('nginx/vhost.conf.erb'),
    order   => '10',
  }
}

敏感数据处理

生产环境中经常需要处理密码等敏感信息。Puppet支持加密的eyaml:

# 安装hiera-eyaml
/opt/puppetlabs/puppet/bin/gem install hiera-eyaml

创建加密的配置:

eyaml encrypt -s 'mysecretpassword'

在Hiera中使用:

mysql::root_password: >
  ENC[PKCS7,MIIBiQYJKoZIhvcNAQcDoIIBejCCAXYCAQAxggEhMIIBHQIBADAFMAACAQEwDQYJKoZIhvcNAQEBBQAE...]

大规模部署策略

管理几百台服务器时,需要考虑部署策略,避免同时更新所有服务器导致服务中断。

使用环境分离

# 创建不同环境
mkdir -p /etc/puppetlabs/code/environments/{development,staging,production}

Agent可以指定环境:

# puppet.conf
[agent]
environment = staging

分批次部署

可以使用Puppet的runinterval和splay参数控制Agent运行时间:

[agent]
runinterval = 1800  # 30分钟运行一次
splay = true        # 随机延迟,避免同时运行

也可以手动控制部署:

# 禁用自动运行
puppet agent --disable "maintenance window"

# 手动触发特定节点
mco puppet runonce -I web-server-01

配置验证

部署前最好验证配置语法:

# 验证语法
puppet parser validate manifests/init.pp

# 模拟运行
puppet agent --test --noop

监控和故障排除

生产环境中,监控Puppet运行状态很重要。我们可以从多个维度监控:

日志分析

Puppet的日志很详细,但需要重点关注几个方面:

# Agent日志
tail -f /var/log/puppetlabs/puppet/puppet.log

# Master日志
tail -f /var/log/puppetlabs/puppetserver/puppetserver.log

关键指标包括:

  • 配置编译时间
  • 资源应用成功率
  • 证书问题
  • 依赖关系错误

使用PuppetDB

PuppetDB可以存储所有节点的状态信息,方便查询和监控:

# 安装PuppetDB
yum install -y puppetdb puppetdb-termini

# 配置Master连接PuppetDB
vim /etc/puppetlabs/puppet/puppetdb.conf
[main]
server_urls = https://puppetdb.example.com:8081

查询节点状态:

# 查看失败的节点
curl -X GET http://puppetdb:8080/pdb/query/v4/reports \
  --data-urlencode 'query=["=", "status", "failed"]'

常见问题排查

我在使用过程中遇到过几个典型问题:

证书过期:Puppet证书默认5年有效期,过期后Agent无法连接Master。需要重新签发证书。

依赖关系循环:资源之间的依赖关系形成循环,导致配置无法应用。可以通过puppet agent --graph生成依赖图分析。

文件权限问题:特别是SELinux环境下,需要注意文件的安全上下文。

内存不足:Master编译大量配置时可能内存不足,需要调整JVM参数。

与其他工具集成

Puppet可以和很多运维工具集成,形成完整的自动化体系。

与Git集成

使用r10k或Code Manager可以实现Git驱动的部署:

# Puppetfile
mod 'puppetlabs-stdlib'
mod 'puppetlabs-concat'
mod 'nginx',
  :git => 'https://github.com/company/puppet-nginx.git',
  :branch => 'production'

与监控系统集成

可以在Puppet配置中自动部署监控:

class profiles::monitoring {
  package { 'zabbix-agent':
    ensure => installed,
  }

  file { '/etc/zabbix/zabbix_agentd.conf':
    ensure  => present,
    content => template('monitoring/zabbix_agentd.conf.erb'),
    notify  => Service['zabbix-agent'],
  }

  service { 'zabbix-agent':
    ensure => running,
    enable => true,
  }
}

性能优化经验

管理大量服务器时,性能优化很重要。我总结了几个优化点:

Master端优化

# /etc/puppetlabs/puppetserver/conf.d/puppetserver.conf
jruby-puppet: {
    max-active-instances: 4
    max-requests-per-instance: 100000
}

Agent端优化

[agent]
usecacheonfailure = false
report = false
pluginsync = false

对于只读节点,可以禁用一些不必要的功能。

网络优化

使用本地Package仓库可以大幅提升安装速度:

yumrepo { 'local-repo':
  baseurl => 'http://repo.internal.com/centos/7/',
  enabled => 1,
  gpgcheck => 0,
}

经过几个月的实践,我们的服务器配置管理效率提升了至少10倍,配置漂移问题基本消失。虽然初期学习成本比较高,但是长期收益非常明显。

特别是在云环境下,服务器经常弹性伸缩,Puppet的自动化配置管理能力显得更加重要。新服务器启动后几分钟就能自动完成所有配置,大大提升了运维效率。

当然Puppet也不是万能的,对于一些复杂的业务逻辑,还是需要结合其他工具。但作为基础设施配置管理,Puppet确实是个不错的选择。

如果你也在管理大量服务器,强烈建议试试Puppet。虽然学习曲线比较陡峭,但掌握后真的会让你的运维工作轻松很多。记住,好的工具能让运维工作事半功倍,而不是增加复杂度。


希望这篇文章对正在做配置管理的朋友有所帮助。如果你在使用Puppet过程中遇到问题,欢迎留言讨论。觉得有用的话,别忘了点赞转发哦~

关注@运维躬行录,获取更多实战干货!

公众号:耕云躬行录
个人博客:躬行笔记

文章目录

博主介绍

热爱技术的云计算运维工程师,Python全栈工程师,分享开发经验与生活感悟。
欢迎关注我的微信公众号@运维躬行录,领取海量学习资料

微信二维码