今天就来分享一下我这几个月使用Puppet管理多服务器配置的实战经验包括一些踩过的坑和解决方案。Puppet基础概念快速入门在开始实战之前先简单说说Puppet的核心概念这些理解了后面的操作就很容易了。Puppet采用的是Master-Agent架构Master节点存储配置清单manifestAgent节点定期向Master请求配置并应用。整个过程是声明式的你只需要描述想要的最终状态Puppet会自动计算如何达到这个状态。几个重要概念Manifest用Puppet DSL语言编写的配置文件描述系统应该是什么样子Module可复用的配置单元包含manifest、文件、模板等Node被管理的服务器节点CatalogMaster为每个节点编译生成的具体配置指令环境搭建实战我的测试环境是3台CentOS 7服务器1台Master2台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 puppetAgent第一次连接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关键指标包括配置编译时间资源应用成功率证书问题依赖关系错误使用PuppetDBPuppetDB可以存储所有节点的状态信息方便查询和监控# 安装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。虽然学习曲线比较陡峭但掌握后真的会让你的运维工作轻松很多。记住好的工具能让运维工作事半功倍而不是增加复杂度。