puppet系列(七)之mcollective框架
为了绝对puppet不能实时拉取配置信息的问题,我生产环境采用的是mcollective架构的形式。
一、mcollective介绍
1.1 mcollective简介
mcollective是一个与puppet密切相关的任务编排执行框架,虽然puppet kick命令的功能可以主动触发agent上执行同步信息操作,但是并不适合海量服务器场景。mcollective涵盖了puppet kick命令的全部功能,同时致力于以一种新颖独特的方式来满足海量服务器管理的需求,它将服务器上报的信息划分在不同的集群中进行管理,在这点上它的功能与Func、Fabric和Capistrano相类似。
1.2 mcollective的特点(优势就是去中心化存储,支持成千上万节点,使用RPC框架异步执行)
与同类软件Func等相比,mcollective有下面的特点:
能够与各种规模的服务器集群交互。
使用广播范式来进行请求分发,所有服务器同时受到请求,而只有与请求所附带的过滤器匹配的服务器才会去执行这些请求。
打破了以往用主机名作为身份验证手段的复杂命令规则。使用每台机器自身提供的丰富的目标数据来进行定位。
使用命令行调用远程代理。
能够写自定义的设备报告。
外部可插件化以实现本地需求。
中间件系统有着丰富的身份验证、授权模型与数据加密方式,保证连接安全。
1.3 mcollective工作流程
mcollective client : mcollective客户端(puppet的服务端)用来发送指令到中间件,mcollective server通过订阅中间件消息获取指令并在本机执行。
中间件:中间件是一种独立的系统软件或服务程序,mcollective借助中间件来搭建client与mcollective server连接的,目前常见的中间件包括ActiveMQ和RabbitMQ。
mcollective server : mcollective的服务端(puppet client端),要运行mcollectived守护进程来接收client通过中间件发送的指令,并在puppet agent上应用这些指令。
1.4 中间件介绍
中间件是一个分布软件层,屏蔽了底层分布环境(网络、主机、操作系统和编程语言)的复杂性和异构性,主要解决异构网络环境下分布应用软件之间的互联、互通和互操作问题。如ActiveMQ和RabbitMQ来实现异步消息的发布和订阅。
mcollective本身使用Stomp(流文本定向协议)来发送和接收消息,所以理论上任何一个实现健壮Stomp监听服务的消息中间件都可以与mcollective协同工作,不过ActiveMQ和RabbitMQ使用的比较广泛。
RabbitMQ是一个实现了高级消息排队协议(AMQP)的消息队列服务。RabbitMQ基于OTP进行构建,并使用Erlang语言和运行环境来实现。
1.5 ActiveMQ介绍
ActiveMQ由Apache官方维护,它是一款流行且功能强劲的开源消息总线,运行在JVM环境上。ActiveMQ完全支持JMS1.1和J2EE 1.4规范的JMS Provider实现。
ActiveMQ的特性如下:
可以使用多种语言和协议编写客户端。应用协议:OpenWire、Stomp、REST、WS Notification、XMPP和AMQP等。
完全支持JMS1.1 和J2EE 1.4规范。
支持Spring,ActiveMQ可以很容易内嵌到使用Spring的系统里面去,而且也支持Spring 2.0的特性。
通过了常见J2EE服务器(Geronimo、JBoss 4、GlassFish和WebLogic)的测试,其中通过JCA 1.5 resource adaptors的配置,可以让ActiveMQ自动部署到任何兼容J2EE 1.4的商业服务器上。
支持多种传送协议,如in-VM,TCP,SSL,NIO,UDP,JGroups和JXTA等。
支持通过JDBC和journal方式提供高速的消息持久化。
从设计上保证了高性能的集群、客户端到服务器或者点对点。
支持Ajax。
支持与Axis的整合。
可以很容易的调用内嵌JMS provider进行测试。
二、ActiveMQ的安装
这里ActiveMQ我们安装到一台单独的服务器,IP地址为:192.168.1.108.
# yum install activemq -y #yum源就是puppet的源。MQ的版本要5.5或更改版本。
博文来自:www.51niux.com
配置ActiveMQ:
# cat /etc/activemq/activemq.xml
<persistenceAdapter>
<kahaDB directory="${activemq.data}/kahadb"/>
</persistenceAdapter>
<plugins> #在<persistenceAdapter>标签下面添加一个<plugins>标签
<simpleAuthenticationPlugin>
<users>
<authenticationUser username="${activemq.username}" password="${activemq.password}" groups="admins,everyone"/>
<authenticationUser username="mcollective" password="secret" groups="mcollective,admins,everyone"/> #设置了用户名为mcollective,密码为secret
</users>
</simpleAuthenticationPlugin>
<authorizationPlugin>
<map>
<authorizationMap>
<authorizationEntries>
<authorizationEntry queue=">" write="admins" read="admins" admin="admins" />
<authorizationEntry topic=">" write="admins" read="admins" admin="admins" />
<authorizationEntry topic="mcollective.>" write="mcollective" read="mcollective" admin="mcollective" />
<authorizationEntry topic="mcollective.>" write="mcollective" read="mcollective" admin="mcollective" />
<authorizationEntry topic="ActiveMQ.Advisory.>" read="everyone" write="everyone" admin="everyone"/>
</authorizationEntries>
</authorizationMap>
</map>
</authorizationPlugin>
</plugins>
<transportConnectors> #将<transportConnectors>标签修改成下面的内容
<transportConnector name="openwire" uri="tcp://0.0.0.0:61616"/>
<transportConnector name="stomp" uri="stomp+nio://0.0.0.0:61613"/> #确认ActiveMQ的61613端口在配置文件中处于开启状态
</transportConnectors>
启动ActiveMQ的守护进程:
# service activemq start
# netstat -lntup|grep 61613
tcp 0 0 :::61613 :::* LISTEN 9582/java
三、mcollective的安装与配置
3.1 mcollective的安装(puppet的客户端和服务端都执行一样的操作)
# yum install mco* -y #应该还有很多辅助组件要安装,当然要严格的话,就是客户端执行:yum install mcollective-common mcollective-client,服务端执行:yum install mcollective mcollective-common,但是这样其他的组件还是要另外安装的。
#yum install rubygem-stomp -y #需要1.2.2或更高版本,如果上面是mco*,这里一般会被关联安装上。
3.2 mcollective客户端的配置(也就是puppet master端,我们这里是192.168.1.107)
# cat /etc/mcollective/client.cfg #现在使用的是最简单的用户密码认证,是明文,安全性较差,应该是在安全的网络里运行。
#subcollectives部分
main_collective = mcollective #main_collective参数:默认请求策略。
collectives = mcollective #collectives参数:分类请求测试,默认情况下,所有的server属于单一广播域,可以追加bj_mcollective,sh_mcollective来以地域划分接收请求。
#基本配置部分
libdir = /usr/libexec/mcollective #libdir参数:插件搜索路径。mcollective在不同操作系统中插件的位置也不一样,当然可以通过libdir参数改变插件存放的位置。
logger_type = file #logger_type参数:mcollective守护进程日志记录方式,默认为file(文件记录),可选参数syslog与console。
logfile = /var/log/mcollective.log #logfile参数:指定log写入文件地址。
loglevel = warn #loglevel参数:记录日志级别,默认为info。可选fatal、error、warn、info和debug。
#安全部分
securityprovider = psk #securityprovider参数:使用哪种安全插件,默认值为psk,它是一个共享的密码,用于服务器间交换数据凭证。可选值还有ssl和aes_security。
plugin.psk = unset #plugin.psk参数 : unset是共享密码的意思。
#MQ消息队列连接设置部分
connector = activemq #connector参数 : 连接的中间件,默认为ActiveMQ,可选参数RabbitMQ.
plugin.activemq.pool.size = 1 #plugin.activemq.pool.size参数:为ActiveMQ的池子ID号,值为1,以下配置参数均对池子ID1生效。
plugin.activemq.pool.1.host = 192.168.1.108 #plugin.activemq.pool.1.host参数:指定中间件的服务器地址。
plugin.activemq.pool.1.port = 61613 #plugin.activemq.pool.1.port参数:根据中间件的配置指定连接的端口。
plugin.activemq.pool.1.user = mcollective #plugin.activemq.pool.1.user参数:指定访问中间件的账户名。
plugin.activemq.pool.1.password = secret #plugin.activemq.pool.1.password参数:指定中间件的账户密码。
#Facts部分
factsource = yaml #factsource 参数:指定Facts插件,默认为yaml。
plugin.yaml = /etc/mcollective/facts.yaml #plugin.yaml参数:缓存Facts的配置文件。
fact_cache_time = 300 #fact_cache_time参数:Facts缓存时间,单位秒。
3.3 mcollective服务端的配置(也就是puppet agent端,我们这里是192.168.1.103,192.168.1.104,192.168.1.105)
# cat /etc/mcollective/server.cfg
main_collective = mcollective
collectives = mcollective
libdir = /usr/libexec/mcollective
logger_type = file
logfile = /var/log/mcollective.log
loglevel = info
daemonize = 1 #是否在后台运行mcollective守护进程
securityprovider = psk
plugin.psk = unset
connector = activemq
plugin.activemq.pool.size = 1
plugin.activemq.pool.1.host = 192.168.1.108
plugin.activemq.pool.1.port = 61613
plugin.activemq.pool.1.user = mcollective
plugin.activemq.pool.1.password = secret
factsource = yaml
plugin.yaml = /etc/mcollective/facts.yaml
fact_cache_time = 300
# service mcollective start #mcollective的服务端,也就是我们所有的puppet agent端都要启动此服务用来接收来自activemq发来的消息。
# ps -ef|grep mco #此服务是没有端口的,是一个进程。
root 49405 1 0 5月03 ? 00:00:06 /usr/bin/ruby /usr/sbin/mcollectived --config=/etc/mcollective/server.cfg --pidfile=/var/run/mcollective.pid --no-daemonize
3.4 mcollective客户端(也就是puppet mastet端)执行命令进行测试:
# mco ping #通过mcollective自带mco命令的ping指明了进行测试。另外这里面任何的主机名都可以重复出现,比如localhost.localdomain可以出现N次。
# mco find -v #find可以找到有哪些mcollective服务端,也就是有哪些puppet client端可以再我们的主动管理之下。加-v可以看详细的过程,在排错的时候可以用到。
博文来自:www.51niux.com
四、mcollective的TLS加密认证
上面已经说过了。那种明文账号密码认证的方式并不是太安全。这里记录一下TLS安全传输层协议的配置方法,也是我线上使用的方法。
安全传输层协议(TLS)用于在两个通信应用程序之间提供保密性和数据完整性。此方式的优势是安全性较高,适用于安全性较差的网络环境,劣势是配置比较复杂。
第一步:activemq服务器上面的操作。
如果我们这台服务器已经跟puppetmaster服务器进行了通信,本身已经有证书了,就可以使用自身的证书来产生对应的证书文件就可以了。
创建证书并生成相关的证书:
# mkdir /opt/mq_cert
# cd /opt/mq_cert/
# keytool -import -alias "51niux.com" -file /var/lib/puppet/ssl/certs/ca.pem -keystore truststore.jks #生成truststore.jks
# cat /var/lib/puppet/ssl/private_keys/activemq.puppet.pem /var/lib/puppet/ssl/certs/activemq.puppet.pem > temp.pem
# openssl pkcs12 -export -in temp.pem -out activemq.p12 -name activemq.51niux.com
# keytool -importkeystore -destkeystore keystore.jks -srckeystore activemq.p12 -srcstoretype PKCS12 -alias activemq.51niux.com
# keytool -list -keystore truststore.jks #验证此证书和密码是否正确,密码我们是51niux.com
# keytool -list -keystore keystore.jks #验证keystore.jks
# cp *.jks /etc/activemq/
修改配置文件,并重启服务:
# vim /etc/activemq/activemq.xml #修改配置文件,<transportConnectors>标签修改为一行,并在其上方添加ssl标签
<sslContext>
<sslContext
keyStore="keystore.jks" keyStorePassword="51niux.com"
trustStore="truststore.jks" trustStorePassword="51niux.com"
/>
</sslContext>
<transportConnectors>
<transportConnector name="stomp" uri="stomp+ssl://0.0.0.0:61614?needClientAuth=true"/>
</transportConnectors>
# /etc/init.d/activemq restart
# netstat -lntup|grep 61614
tcp 0 0 :::61614 :::* LISTEN 4412/java
第二步:mcollective客户端,也就是puppet mater端的操作(如果CA服务独立出去了,要在CA服务器上面进行操作)
创建证书并将证书:
#puppet cert generate mcollective-servers #生成共享证书
#cp /var/lib/puppet/ssl/private_keys/mcollective-servers.pem /etc/mcollective/server_private.pem
# cp /var/lib/puppet/ssl/public_keys/mcollective-servers.pem /etc/mcollective/server_public.pem
# chmod 644 /etc/mcollective/server*
#mkdir /etc/mcollective/clients/
#cp /var/lib/puppet/ssl/certs/master.puppet.pem /etc/mcollective/clients/ #如果是在CA服务器上面操作,这里就不是master,就是类似于ca.puppet.pem之类的。
# chmod 644 /etc/mcollective/clients/
发送证书到puppet的客户端也就是发送共享证书到mcollective的服务端(测试先手工传一下):
#scp -r /etc/mcollective/*.pem 192.168.1.102:/etc/mcollective/
#scp -r /etc/mcollective/clients 192.168.1.102:/etc/mcollective/
修改配置文件(官方文档:https://docs.puppet.com/mcollective/configure/client.html):
# cat /etc/mcollective/client.cfg
main_collective=mcollective
collectives=mcollective
libdir=/usr/libexec/mcollective
logfile=/var/log/mcollective.log
loglevel=info
daemonize=1
#ActiveMQconnectorsettings:
connector=activemq
direct_addressing=1
plugin.activemq.pool.size=1
plugin.activemq.pool.1.host=192.168.1.108
plugin.activemq.pool.1.port=61614 #端口发生了变化
plugin.activemq.pool.1.user=mcollective
plugin.activemq.pool.1.password=secret #这里密码还是对应的账户密码的那个明文密码。
plugin.activemq.pool.1.ssl=1
plugin.activemq.pool.1.ssl.ca=/var/lib/puppet/ssl/certs/ca.pem #这里都是puppet master端自身的证书,或者是说作为mcollective客户端节点自己的证书
plugin.activemq.pool.1.ssl.cert=/var/lib/puppet/ssl/certs/master.puppet.pem
plugin.activemq.pool.1.ssl.key=/var/lib/puppet/ssl/private_keys/master.puppet.pem
plugin.activemq.pool.1.ssl.fallback=0
securityprovider=ssl
plugin.ssl_server_public=/var/lib/puppet/ssl/certs/mcollective-servers.pem #这里是共享证书的位置,这个证书包括下面的两个证书,如果是想让其他机器做mco的客户端,就要把这三个证书发送到那台机器指定目录。
plugin.ssl_client_public=/var/lib/puppet/ssl/certs/master.puppet.pem #这里还是puppet master端自身的证书
plugin.ssl_client_private=/var/lib/puppet/ssl/private_keys/master.puppet.pem #这里还是puppet master端自身的证书
#Facts
factsource=yaml
plugin.yaml=/etc/mcollective/facts.yaml
第三步:mcollective服务端也就是puppet agent端的操作:
证书都已经发过来了,那么就剩修改配置文件并重启服务了。
# cat /etc/mcollective/server.cfg
# ActiveMQ connector settings:
connector=activemq
direct_addressing=1
plugin.activemq.pool.size=1
plugin.activemq.pool.1.host=192.168.1.111
plugin.activemq.pool.1.port=61614
plugin.activemq.pool.1.user=mcollective
plugin.activemq.pool.1.password=secret
plugin.activemq.pool.1.ssl = 1 #连接activemq是否启用ssl
plugin.activemq.pool.1.ssl.ca=/var/lib/puppet/ssl/certs/ca.pem #CA的地址,用puppet客户端自己的ca证书就可以。
plugin.activemq.pool.1.ssl.cert=/var/lib/puppet/ssl/certs/smaster.puppet.pem #用agent端自己的证书
plugin.activemq.pool.1.ssl.key=/var/lib/puppet/ssl/private_keys/smaster.puppet.pem
plugin.activemq.pool.1.ssl.fallback=0
# SSL security plugin settings:
securityprovider = ssl #应用层验证方式,ssl
plugin.ssl_client_cert_dir=/etc/mcollective/clients #存放客户端证书的目录,已经从puppet master端发送过来了
plugin.ssl_server_private=/etc/mcollective/server_private.pem #共享的server key,也已经从puppet master端发送过来了
plugin.ssl_server_public=/etc/mcollective/server_public.pem
# Facts, identity, and classes:
identity = smaster.puppet #主机标注,DQDN,这里设置什么,mco find的时候就显示什么。
factsource=yaml
plugin.yaml=/etc/mcollective/facts.yaml
classesfile = /var/lib/puppet/state/classes.txt #puppet类文件,此文件记录上次获取的catalog中都有哪些类
# No additional subcollectives:
main_collective=mcollective
collectives=mcollective
# Registration:
registerinterval=600
# Auditing (optional):
rpcaudit = 1 #启用审计功能
rpcauditprovider = logfile
plugin.rpcaudit.logfile = /var/log/mcollective-audit.log
# Logging:
logger_type = file
logfile=/var/log/mcollective.log #很有用,可以可以通过日志查看我们是否成功连接activemq服务器等问题。
loglevel=info
keeplogs = 5
max_log_size = 2097152
logfacility = user
# Platform defaults:
libdir=/usr/libexec/mcollective
daemonize=1
# service mcollective restart #重启mcollective服务端的服务。
第四步:puppet master端再次进行测试(192.168.1.102的主机名为foreman1.puppet)。
另外:url连接:http://activemq服务器的IP地址:8161/admin/ 就可以web端进行一些管理和查看。
另外、如果CA是独立的服务器呢,并未跟master在一起?
生成mcollective-servers共享证书的操作就不在puppet master端操作了,就要在CA服务器上操作了。
#cp /var/lib/puppet/ssl/certs/ca.puppet.pem /etc/mcollective/clients/ #clients里面就要是ca.puppet.pem文件了。
# cat /etc/mcollective/client.cfg #有两个地方就需要ca服务器的证书了。
plugin.ssl_client_public=/var/lib/puppet/ssl/certs/ca.puppet.pem
plugin.ssl_client_private=/var/lib/puppet/ssl/private_keys/ca.puppet.pem
五、mco命令
# mco --help #可以查看可以使用哪些命令
# mco shell --help #如我们可以查看mco shell的具体用法
不过命令一大堆,我们实际用到的也就三个命令:
#mco find|wc -l #可以查看现在有哪些主机在我们的mco的管理之下。
#mco facts #使用facts显示报告信息
# mco shell #最重要的就是这个mco shell命令了,这样我们就可以控制所有的puppet agent去执行puppet agent -t命令了。
5.1 mco插件简单举例
# mco inventory foreman1.puppet #查询foreman1.puppet都载入了哪些插件
# mco puppet --noop --verbose status #默认是查询所有的puppet节点,守护进程我一般都是关闭状态,可以用这个看下还有哪些客户端的puppet守护进程是没有关闭,也给关闭掉.
博文来自:www.51niux.com
5.2 mco shell插件,管理神器。
5.2.1 介绍
首先介绍下shell插件,简单说就是控制远程主机去执行shell命令,这就屌了,可以远程控制所有的puppet客户端去执行#puppet agent -t,或者让所有客户端远程是执行命令,或者还可以通过正则表达式去选择一部分机器去执行我们""里面指定的命令。这样我们将所有服务器的管理做到了主动更新操作,而不是等待定时任务什么的。
然后就是Centos6系列的时候,官方还没有shell插件,所以呢要手工编写shell插件来进行mco shell的管理。这是生产使用的一种方式,因为没有系统兼容性的问题。
然后就是Centos7系列,puppet官网再官网yum源里面也推出了shell工具包,没有深入研究,不是很好用,存在的一个很要命的问题,就是执行格式是:mco shell run(等参数)的形式,但是呢Centos 6本身是没有这个插件的,客户端安装了Centos7的shell工具包,在执行的时候也是不兼容的。
foreman1.puppet: ERROR: #<MCollective::RPC::Result:0x000000021d2b88 @agent="shell", @action="run", @results={:sender=>"foreman1.puppet", :statuscode=>5, :statusmsg=>"undefined mr=>nil}}
#这个问题我也没有深入的去探究如何解决,应该是改改执行文件就可以吧。
5.2.2 线上用的shell工具(就是用的编写的插件比较方便和灵活)
mco客户端,一般也就是puppet master端,上面的操作:
这个插件来自于:https://github.com/puppetlabs/mcollective-shell-agent
# vi /usr/libexec/mcollective/mcollective/application/shell.rb #编写shell插件文件
class MCollective::Application::Shell < MCollective::Application description "MCollective Distributed Shell" usage <<-EOF mco shell <CMD> The CMD is a string EXAMPLES: mco shell uptime EOF def post_option_parser(configuration) if ARGV.size == 0 raise "You must pass a command!" end if ARGV.size > 1 raise "Please specify the command as one argument in single quotes." else command = ARGV.shift configuration[:command] = command end end def validate_configuration(configuration) if MCollective::Util.empty_filter?(options[:filter]) print "Do you really want to send this command unfiltered? (y/n): " STDOUT.flush # Only match letter "y" or complete word "yes" ... exit! unless STDIN.gets.strip.match(/^(?:y|yes)$/i) end end def main $0 = "mco" command = configuration[:command] mc = rpcclient("shell") mc.agent_filter(configuration[:agent]) mc.discover :verbose => true puts "\n" mc.execute(:cmd => command) do |node| sender = node[:senderid] exitcode = node[:body][:data][:exitcode] output = node[:body][:data][:stdout] error = node[:body][:data][:stderr] if (output.nil?) puts "Host: #{sender}" puts "Exitcode: #{exitcode}" end if !(output.nil?) puts "====================================================================================" puts "Host: #{sender}" puts "Exitcode: #{exitcode}" puts "====================================================================================" puts "Output:" puts output puts "====================================================================================" end if !(error.nil?) puts "====================================================================================" puts "Host: #{sender}" puts "Exitcode: #{exitcode}" puts "====================================================================================" puts "Error:" puts error puts "====================================================================================" end puts "\n" end mc.disconnect end end
注意:
原插件里面用的是empty?表示是否为空,我这里改为了nil?,因为我在用empty?的时候报错,所以替换了一下,如果你不报错的话,就直接用git提供的插件便可以了。
用empty报错内容:
The mco application failed to run: undefined method `empty?' for nil:NilClass
# vim /usr/libexec/mcollective/mcollective/agent/shell.ddl
metadata :name => "Shell Command", :description => "Remote execution of bash commands", :author => "Jeremy Carroll", :license => "Apache v.2", :version => "1.0", :url => "http://www.51niux.com", :timeout => 300 ["execute"].each do |act| action act, :description => "#{act.capitalize} a command" do display :always input :cmd, :prompt => "Command", :description => "The name of the command to #{act}", :type => :string, :validation => '^.+$', :optional => false, :maxlength => 300 output :output, :description => "Command Output", :display_as => "Output" output :error, :description => "Command Error", :display_as => "Error" output :exitcode, :description => "Exit code of the shell process", :display_as => "Exit Code" end end
# vim /usr/libexec/mcollective/mcollective/agent/shell.rb
module MCollective module Agent class Shell<RPC::Agent metadata :name => "Shell Command", :description => "Remote execution of bash commands", :author => "Jeremy Carroll", :license => "Apache v.2", :version => "1.0", :url => "http://www.51niux.com", :timeout => 300 action "execute" do validate :cmd, String out = [] err = "" begin status = run("#{request[:cmd]}", :stdout => out, :stderr => err, :chomp => true) rescue Exception => e reply.fail e.to_s end reply[:exitcode] = status reply[:stdout] = out.join(" ") reply[:stderr] = err reply.fail err if status != 0 end end end end
mco服务器端,也就是所有的puppet agent端的操作:
只需要两个配置文件(拷贝到所有的agent客户端):
/usr/libexec/mcollective/mcollective/agent/shell.ddl
/usr/libexec/mcollective/mcollective/agent/shell.rb
5.2.3 测试以及mco shell的使用:
# mco shell "puppet agent -t" #控制mco服务器,也就是puppet agent节点,都去执行“puppet agent -t”命令。
# mco shell "/etc/init.d/iptables stop"
# mco shell "puppet agent -t" -I 192.168.1.104 #-I 后面跟主机,是指定某一台机器去执行命令,切记,这里的主机名称是此主机/etc/mcollective/server.cfg 文件里面identity定义的名称。
# mco shell "uptime" -I /192.168.1.*/ #既然有指定单独的主机,那肯定能指定批量的主机了,这里就是指定了所有/etc/mcollective/server.cfg 文件里面identity定义的名称为192.168.1为前缀的主机去执行命令。
# mco shell "uptime" -I /"[p|a]".*puppet$/ #再写一个正则,我这里是让所有以p开头或者以a开头的以puppet结尾的主机,来执行这个命令。
小结:可以指定所有机器,可以指定单个机器,可以根据正则指定一类的机器,这样我们可以很灵活的去控制让那些机器去执行哪些操作,我们线上的机器基本也是以机房业务类型编号的形式命名的主机名,这样很灵活的主动管理。
5.3 mco facter插件的第一种形式:
# mco facts hostname #然后mco的客户端执行查看所有节点的hostname操作,发现主机没有返回结果。是因为puppet 客户端也就是mco 服务端没有facts此插件。或者配置文件设置不对。
No values found for fact hostname
Finished processing 2 / 2 hosts in 90.27 ms
博文来自:www.51niux.com
5.3.1 mco服务端也就是puppet客户端的操作:
# yum install -y mcollective-facter-facts #部署mcollective-facts插件,需要执行这个操作,当然你可能成功不了的,因为现在官方yum源没有这个包组。如果不成功就执行下面的操作。
# vim /usr/libexec/mcollective/mcollective/facts/facter_facts.rb
module MCollective module Facts # A factsource for Puppet Labs Facter # # This plugin by default works with puppet facts loaded via pluginsync # and the deprecated factsync. If your facts are in a custom location, or # you use non-standard puppet dirs, then set plugin.facter.facterlib # in the server.cfg class Facter_facts<Base def load_facts_from_source begin require 'facter' rescue LoadError=> e raise LoadError, 'Could not load facts from fact source. Missing fact library: facter' end ENV['FACTERLIB'] = Config.instance.pluginconf.fetch('facter.facterlib', nil) || '/var/lib/puppet/lib/facter:/var/lib/puppet/facts' Log.debug("Loading facts from FACTERLIB: #{ENV['FACTERLIB']}") Facter.reset facts = Facter.to_hash Log.info("Loaded #{facts.keys.size} facts from Facter") facts end end end end
# vim /usr/libexec/mcollective/mcollective/facts/facter_facts.ddl
metadata :name => "facter", :description => "Puppetlabs Facter facts plugin", :author => "P. Loubser <pieter.loubser@puppetlabs.com>", :license => "ASL 2.0", :version => "1.0.0", :url => "http://projects.puppetlabs.com/projects/mcollective-plugins/wiki/FactsFacter", :timeout => 1 requires :mcollective => "2.2.1"
修改mco服务端也就是puppet 客户端的mco的server.cfg文件:
# vi /etc/mcollective/server.cfg
#factsource=yaml #注释掉这一行
factsource=facter #改为这一行,为什么现在才改为这一行呢,是因为现在你已经有了/usr/libexec/mcollective/mcollective/facts/facter_facts.rb文件,不然mcollective启动会报错。
# service mcollective restart #重启mcollective服务
5.3.2 mco客户端的再次测试
# mco facts hostname #测试结果显示刚才更改的那个puppet的客户端已经可以使用facts了。而另外一个puppet 客户端因为没有做操作,没有显示主机名称。
5.4 mco facter插件的第二种形式:
现在这种方式是我生产用的一种形式,比较稳定,但是也还是有问题的。
第一步:puppet master端的操作:
class mcollective::facter { file{"/etc/mcollective/facts.yaml": owner => root, group => root, mode => 0440, content => inline_template('<%= scope.to_hash.reject { |k,v| k.to_s =~ /(uptime.*|path|timestamp|free|.*password.*|.*psk.*|.*key)/ }.to_yaml %>'), } }
#上面就是写一个类,当pupept客户端,执行#puppet agent -t的时候,会引用inline_template函数,将本机的facter信息,写入到/etc/mcollective/facts.yaml文件,这样MCollective客户端只需要每次读取这个文件中的facter变量即可。这样做是为了解决通过facter插件获取节点facter变量信息不是很稳定的问题。
mco服务端。也就是puppet客户端,不用修改service.cfg,也不用编写facter的插件也就是/usr/libexec/mcollective/mcollective/facts目录下面不用有facter_facts.ddl和/opt/facter_facts.rb。
# cat /etc/mcollective/server.cfg #也还是以前的配置
factsource=yaml
plugin.yaml=/etc/mcollective/facts.yaml
#第二步:mco客户端的操作
# mco shell "puppet agent -t" #先要执行一下这个命令,让所有的mco服务器端/etc/mcollective/facts.yaml的文件里面都有本机的facter信息。这样以后就可以在执行mco shell的时候结合facter的值使用了
下面是这样更改前,facts.yaml里面的信息:
# cat /etc/mcollective/facts.yaml
---
mcollective: 1
下面是这样更改后,facts.yaml里面的信息:
# cat /etc/mcollective/facts.yaml #只截取了部分信息。
---
blockdevice_sr0_model: "VMware IDE CDR10"
processorcount: "4"
caller_module_name: ""
netmask_eno16777736: "255.255.255.0"
augeasversion: "1.4.0"
module_name: ""
rubyversion: "2.0.0"
kernelmajversion: "3.10"
processor3: "Intel(R) Xeon(R) CPU X5670 @ 2.93GHz"
第三步:mco客户端测试一下:
# mco shell "uptime" -F operatingsystemmajrelease=7 #测试一下,只让操作系统是大版本7的去执行uptime
小结:开头就已经说了,这种提前把数据存放在文件中,然后mco读取文件来供我们-F使用的形式,虽然稳定,但是实时性方面就存在问题了。比如说,在mco服务没死的情况下,我们一台节点可能涉及主机名的更换,但是你/etc/mcollective/facts.yaml文件里面存的还是旧的主机名,那么我mco client端不知道啊,所以我mco客户端在执行mco shell "uptime" -F hostname=//的过滤的时候,可能还会把我这台已经更改过的主机一起算上,当然执行完这次之后,这台主机的/etc/mcollective/facts.yaml信息又是最新的了,下次再mco 过滤的时候就不会再出现误判了。你可以尝试着更换主机名试下,操作一下就可能更加理解这个问题。
所以如果你涉及更改主机名等一些更改信息操作的时候,记得更换完毕后,手工执行一下#puppet agent -t 命令,毕竟这种操作还是比较少的。或者你就要自动化,可以mco要执行我们新编写的模块之前,先执行下# mco shell "puppet agent -t" ,让本地先更新下/etc/mcollective/facts.yaml文件。mcollective::facter这个类在puppet master端肯定是一直开着的。
5.5 mco 插件的混合使用
# mco shell "uptime" -F ipaddress=/192.168.1.1/
#这是让所有的IP地址是192.168.1.1开头的主机都去执行uptime,-F 就是引用了facts里面变量的意思,注意这里是正则表达式,所以可以是192.168.1.1,192.168.1.10-192.168.1.19,192.168.1.100-192.168.1.199.
# mco shell "uptime" -F ipaddress=/192.168.1.1$/
#而这种就是让IP地址是192.168.1.1的主机去执行uptime.
# mco shell "puppet agent -t" -F ipaddress=/192.168.1.*/
#而这种形式是我们生产比较常用的,指定某个网段去执行puppet agent -t,然后我们可以在puppet服务器端根据node节点来判断此此网段里面的主机,因为我们一般node就是主机名嘛,可以根据node也就是根据所代表的业务,让其执行不同的资源。
# mco shell "yum install net-snmp -y" -F operatingsystem=CentOS -F operatingsystemmajrelease=6
#让所有系统是Centos并且大版本是6的,多条件用多个-F指定。来执行此操作。这样我集群里面是Centos7的系统就不会去执行命令。
# mco shell "mkdir /opt/scripts && cd /opt/scripts && wget ftp.51niux.com/disk_check.sh" -F ipaddress=/192.168.1.*/ -F is_virtual=false
#因为物理机的硬盘使用长了之后难免会出现问题,为了做到硬盘的预警,我们可以通过mco shell,让所有的mco服务器端也基本就是我们通过-F执行的某一个网段的机器 并且不是虚拟化的机器,然后去执行我们双括号里面的命令。
这样,我们可以结合-F里面指定的facter变量,根据不同的变量让去执行shell里面的命令,如根据operatingsystemmajrelease 判断是Centos6还是Centos7的办法,再执行命令的时候有所区分啊,又或者可以去批量更改某一个网段的用户密码,说道密码多说一句,这里有些特殊字符是不支持的,如!,切记双引号里面就是一段要在mco服务端要去执行的一串字符串,不支持变量的形式。
六、puppet客户端上面的mcollective的部署
上面为了对这个mcollective的部署过程有个清楚的认识,做了一个详细的操作过程记录,生产环节中,肯定不这么玩,一般都是在puppet master服务端上面做一个mcollective模块来进行安装。
下面我就来分析下我的mcollective模块结果:
#cd /etc/puppet/environments/production/modules/mcollective
6.1 files结构
#ls -lR files/ #只截图了部分有用的
drwxr-xr-x agent #首先files目录下面是两个目录,一个agent目录一个pem目录
drwxr-xr-x pem
files/agent: #files/agent目录下面是我们上面说到了两个shell插件文件
-rw-r--r-- 1 shell.ddl
-rw-r--r-- 1 shell.rb
files/pem: #files/pem目录下面是一个clients目录和两个共享证书文件
drwxr-xr-x clients
-rw-r--r-- server_private.pem
-rw-r--r-- server_public.pem
files/pem/clients: #files/pem/clients目录下面是ca服务器的证书文件,因为ca证书是跟puppet的服务器是一起的没有分离出去。如ca服务器单独共享证书是在ca服务器上面创建的,这里该是ca.puppet.pem
-rw-r--r-- master.puppet.pem
6.2 manfests目录下的pp文件:
# cat /etc/puppet/environments/production/modules/mcollective/manifests/init.pp #偷点懒就写一个简单的。
class mcollective( $activemq_server, #这里有两个变量等着被传参 $mcollective_password) { package { #因为我们好多mco的插件用不到,所以也不用全装。 ['mcollective','mcollective-puppet-agent', 'mcollective-service-agent',]: ensure => installed, } service { 'mcollective': ensure => running, enable => true, require => Package['mcollective'], } file { '/etc/mcollective': ensure => directory, source => 'master.puppet:///modules/mcollective/pem', mode => '0640', recurse => remote, notify => Service['mcollective'], } file { '/etc/mcollective/facts.yaml': ensure => file, mode => 400, replace => yes, content => inline_template('<%= scope.to_hash.reject { |k,v| k.to_s =~ /(uptime.*|path|timestamp|free|.*password.*|.*psk.*|.*key)/ }.to_yaml %>'), } file { '/etc/mcollective/server.cfg': ensure => file, mode => 400, content => template("mcollective/server.cfg.erb"), notify => Service['mcollective'], } file { '/usr/libexec/mcollective/mcollective/agent': ensure => directory, source => 'master.puppet:///modules/mcollective/agent', recurse => remote, notify => Service['mcollective'], require => Package['mcollective','mcollective-puppet-agent','mcollective-service-agent'], } }
6.3 模板目录下的模板文件编写:
#cat /etc/puppet/environments/production/modules/mcollective/templates/server.cfg.erb
<% ssldir = '/var/lib/puppet/ssl' %> #Activemq connector = activemq direct_addressing = 1 plugin.activemq.pool.size = 1 plugin.activemq.pool.1.host = <%= @activemq_server %> plugin.activemq.pool.1.port = 61614 plugin.activemq.pool.1.user = mcollective plugin.activemq.pool.1.password = <%= @mcollective_password %> plugin.activemq.pool.1.ssl = 1 plugin.activemq.pool.1.ssl.ca = <%= ssldir %>/certs/ca.pem plugin.activemq.pool.1.ssl.cert = <%= ssldir %>/certs/<%= scope.lookupvar('::clientcert')%>.pem plugin.activemq.pool.1.ssl.key = <%= ssldir %>/private_keys/<%= scope.lookupvar('::clientcert')%>.pem plugin.activemq.pool.1.ssl.fallback = 0 #SSL Plugin: securityprovider = ssl plugin.ssl_client_cert_dir = /etc/mcollective/clients plugin.ssl_server_private = /etc/mcollective/server_private.pem plugin.ssl_server_public = /etc/mcollective/server_public.pem plugin.puppet.resource_allow_managed_resources = true plugin.puppet.resource_type_whitelist = exec,file #Facts: identity = <%= scope.lookupvar('::fqdn') %> factsource = yaml plugin.yaml = /etc/mcollective/facts.yaml classesfile = /var/lib/puppet/state/classes.txt #No additional subcollectives: main_collective = mcollective collectives = mcollective #Plugin: registerinterval = 600 #Auditing: rpcaudit = 1 rpcauditprovider = logfile plugin.rpcaudit.logfile = /var/log/mcollective-audit.log #Logging: logger_type = file loglevel = info logfile = /var/log/mcollective.log keeplogs = 5 max_log_size = 2097152 logfacility = user #Platform defaults: libdir = /usr/libexec/mcollective daemonize = 1
6.4 然后就是最后的manifests目录下面的node文件,去执行此类
#cat /etc/puppet/environments/production/manifests/nodes/tianjing.pp #可以这样以市区的机房来命令,又或者什么根据自己的实际情况吧。
node /tjidc-(agent|cache|web|kvm)-\d+\.sjhl\.ys$/ #这就是定义了匹配的节点为tjidc开头的-包含agent或者cache或者web或者kvm的-数字编号.sjhl.ys结尾的node节点
{
class { 'mcollective':
activemq_server => 'activemq.puppet', #将这两个值传递给mcollective类里面的那两个变量。然后用于templates/server.cfg.erb模板里面的<%= @activemq_server %>和 <%= @mcollective_password %>的值的替换。
mcollective_password => '51niux.com',
}
}
6.5 puppet 客户端:
通过部署工具将机器系统创建完毕,环境操作完毕之后,控制此机器执行# puppet agent -t ,证书也验证下发了,mco服务端也配置好了。根本不用人工去干预,干预的话也是puppet master端的操作。