柴少鹏的官方网站 技术在分享中进步,水平在学习中升华

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次。
图片.png
# mco find -v   #find可以找到有哪些mcollective服务端,也就是有哪些puppet client端可以再我们的主动管理之下。加-v可以看详细的过程,在排错的时候可以用到。

图片.png

博文来自: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

图片.png

# keytool -list -keystore keystore.jks  #验证keystore.jks

图片.png

# 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)。

图片.png

图片.png

另外: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工具包,在执行的时候也是不兼容的。

UH}V[FT$6)ZZ1R65KEE%628.png

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
图片.png

# 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”命令。

图片.png

# mco shell "/etc/init.d/iptables stop"

图片.png

# 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为前缀的主机去执行命令。

图片.png

# mco shell "uptime"   -I  /"[p|a]".*puppet$/  #再写一个正则,我这里是让所有以p开头或者以a开头的以puppet结尾的主机,来执行这个命令。

图片.png

小结:可以指定所有机器,可以指定单个机器,可以根据正则指定一类的机器,这样我们可以很灵活的去控制让那些机器去执行哪些操作,我们线上的机器基本也是以机房业务类型编号的形式命名的主机名,这样很灵活的主动管理。

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 客户端因为没有做操作,没有显示主机名称。
图片.png

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端的操作。

作者:忙碌的柴少 分类:puppet系列 浏览:3884 评论:6
留言列表
絮儿
絮儿 您好,按照您的方式部署了MC,大部分server与client通信正常,但有几台机器通信是有问题的mco ping找不到,请问这种情况怎么处理呢?  回复
忙碌的柴少
忙碌的柴少 要不加QQ给你看看吧联系我里面有我的联系方式  回复
访客
访客 联系我 里面没有找到您的QQ,发了邮件给您。  回复
絮儿
絮儿 你好,发了封邮件给您,但还未收到回复。还有另一个问题想请教下,如何配置activemq,让mc使用它的hostname而不是ip呢?期待回复,谢谢。  回复
nancy
nancy 你好,我在用mco shell时,报错
warn 2019/04/18 22:44:06: agent.rb:243:in `metadata' Setting metadata in agents has been deprecated, DDL files are now being used for this information. Please update the 'shell.rb' agent
/opt/puppetlabs/puppet/lib/ruby/vendor_ruby/mcollective/pluginmanager.rb:98:in `rescue in create_instance': Could not create instance of plugin MCollective::Application::Shell: uninitialized constant MCollective::Application::Shell (RuntimeError)
Did you mean? MCollective::Shell
from /opt/puppetlabs/puppet/lib/ruby/vendor_ruby/mcollective/pluginmanager.rb:94:in `create_instance'
from /opt/puppetlabs/puppet/lib/ruby/vendor_ruby/mcollective/pluginmanager.rb:81:in `[]'
from /opt/puppetlabs/puppet/lib/ruby/vendor_ruby/mcollective/applications.rb:23:in `run'
from /opt/puppetlabs/puppet/bin/mco:33:in `'  回复
访客
访客 这个puppet已经好久不用了,你可以先跟着https://github.com/choria-legacy/mcollective-shell-agent/tree/master/lib/mcollective/agent 官网试试,如果可以了再根据我修改的地方看是否需要修改  回复
发表评论
来宾的头像