如何用Puppet和Augeas管理Linux配置

来源:互联网 发布:js 对象访问器 编辑:程序博客网 时间:2024/06/05 10:23

puppet运维自动化 

一、简介

当服务器数量达到一定的规模时,仅依靠人为完成批量部署服务器个资源的配置,运维工作将变得繁琐且容易出错,为了解决这一问题,我们应该怎么办呢?我们可以引入一批工具,这批工具可编写相应的manifests代码,运行它便可以自动完成所有的工作,目前比较流行的运维工具主要有:puppet,ansible, slackstack等,在这我们主要以puppet来展开话题。在一些大型互联网企业中,运维自动化管理着几百甚至上千台服务器,它可以针对多台服务器进行统一操作,例如部署统一软件、进行统一上线维护等,而且能够快速完成上线部署,减少人力及人力误操作风险。

二、Puppet的工作原理

puppet的目的是让系统管理员只集中于要管理的目标服务器,而忽略实现的细节。puppet既可以在单机上使用,也可以C/S结构使用,在大规模部署puppet的情况下,通常我们会使用C/S结构,在这种结构下,服务端运行puppet-master程序客户端运行puppet-client服务程序

具体的工作流程图如下所示:

对于puppet的的掌握,理解puppet的工作原理是一个必要的的阶段,只有在了解了puppet的工作原理后才能更好应用puppet,下面让我们一起了解学习puppet的工作原理:

说到puppet的工作原理,不得不从以下四个方面来说到,如下所示:

(1)定义:使用Puppet特定的语言定义基础配置信息。通常我们把这些信息写在Modules中。

(2)模板:在配置执行之前检测代码,但并不真正执行。

(3)执行:定义的配置自动部署。检测并记录下所发生变化的部分。

(4)报告:将期待的变化、实际发生的变化及任何修改发送给报告系统。

如下所示为puppet的工作数据流示意图

wKiom1cDLgKy1XY6AABjtukUGU4580.png

数据流说明:

首先所有的节点(Node)Node节点将Facts和本机信息发送给Master

Master告诉Node节点应该如何配置,将这些信息写入Catalog后传给Node。

Node节点在本机进行代码解析验证并执行,将结果反馈给Master。

Master通过API将数据发给分析工具。报告完全可以通过开放API或与其他系统集成。

整个数据流的走向是基于SSL安全协议的,如下图所示:

wKioL1cDM_mhIfxdAAC_01l6yr4158.png

模板文件处理过程说明如下:

Puppet通过编译Manifest中的内容 (即模板中内容),将编译好的代码存入Catalog。在执行前先进行代码的验证,再执行,完成最开始所定义好的状态。代码编译过程如图所示:

wKiom1cDUvDxzvTpAACnEvSauho578.png

如下所示为整个puppet自动部署过程中agent和master的详细的交互过程:

wKiom1cDWDvT6ao2AADZUDsO3KY688.png

过程说明:

1. Puppet客户端Agent将节点名与facts信息发送给Master。

2. Puppet服务端Master通过分类判断请求的客户端是谁,它将要做什么。这个判断是通过site.pp中包含的Node.pp配置文件定义的。

3. Puppet服务端Master将所需要的Class类信息进行编译后存入Catalog并发送给Puppet客户端Agent,到此完成第一次交互。

4. Puppet客户端Agent对Catalog进行代码验证(语法检查及错误检查)并执行。主要是代码的验证,并将执行过程的信息及结果写入日志。

5. Puppet客户端Agent最终达到最开始所定义的状态,并且将结果及任何执行数据通过开放API的形式发送给Puppet服务端Master。

 

以上就是puppet的工作原理需要注意是:因为整个过程中都是基于ssl实现的,所以首要的是保证agent和master间可以基于ssl通讯!

 

以上内容参考:http://blief.blog.51cto.com/6170059/1760439

 

三、puppet常用资源及配置实例

实例一: 创建centos用户为普通用户,且uid为4000,gid为3000,所属组为centos,附加组为mygrp

user{'centos':name=>'centos',#定义用户名ensure=>present,#创建用户uid=>4000,#定用户idgroups=>'mygrp',#定义其他附加组require=>Group['mygrp']#此资源依赖于事先创建mygrps组}group{'mygrp':name=>'mygrp',#定义组名ensure=>present,#创建组,与此相反的是absent,删除组gid=>'3000',#组idsystem=>false,#是否为系统组,默认为false}总结:user常用属性:name:用户名,uid:用户id,gid:组id,groups:附加组,comment:注释信息expiry:过期时间,home:家目录,shell:默认的shell类型,system:是否为系统组,ensure:创建或删除用户即present、absent,password:加密后的密码group常用属性:ensure:创建或删除组即present、absent,name:组id,system:是否为系统组等[root@node1manifets]#puppetapply-vuser.pp#将此manifests清单编译为伪代码catalog,并执行,且返回执行结果至此单机模式的主机上Notice:Compiledcatalogfornode1.alren.cominenvironmentproductionin0.55secondsInfo:Applyingconfigurationversion'1480561275'Notice:/Stage[main]/Main/Group[mygrp]/ensure:createdNotice:/Stage[main]/Main/User[centos]/ensure:createdNotice:Finishedcatalogrunin0.11seconds[root@node1manifets]#idcentosuid=4000(centos)gid=4000(centos)groups=4000(centos),3000(mygrp)[root@node1manifets]#viuser.pp[root@node1manifets]#puppetapply-vuser.ppNotice:Compiledcatalogfornode1.alren.cominenvironmentproductionin0.58secondsInfo:Applyingconfigurationversion'1480561376'Notice:/Stage[main]/Main/Group[mygrp]/ensure:removedNotice:/Stage[main]/Main/User[centos]/ensure:removedNotice:Finishedcatalogrunin0.13seconds[root@node1manifets]#idcentosid:centos:nosuchuser[root@node1manifets]#

实例二:此manifests代码为安装httpd包,为其提供配置文件,并且启动服务

service{'httpd':ensure=>running,enable=>true,restart=>'systemctlrestarthttpd.service',require=>Package['httpd'],}package{'httpd':ensure=>installed,}file{'httpd.conf':path=>'/etc/httpd/conf/httpd.conf',source=>'/root/manifests/httpd.conf',ensure=>file,notify=>Service['httpd'],before=>Service['httpd'],}package常用属性:ensure:installed,present,latest,absentname:包名source:程序包来源,仅对不会自动下载的相关程序包的provider有用,例如rpm或dpkgservicec常用属性:ensure:running,stopped运行停止enable:开机是否启动restart:重启命令require:被依赖于事先安装程序包,file常用属性:path:文件需放置的位置所在处source:源文件在哪ensure:file,present,absent,directory,link...file指的是普通文件,link为连接文件,此需结合target一起使用directory:类型为目录,可通过source指向的路径复制生成,recuse属性指明是否为递归复制owner:属主group:属组mode:权限[root@node1manifets]#puppetapply-vweb.ppNotice:Compiledcatalogfornode1.alren.cominenvironmentproductionin1.48secondsInfo:Applyingconfigurationversion'1480563754'Info:Computingchecksumonfile/etc/httpd/conf/httpd.confInfo:/Stage[main]/Main/File[httpd.conf]:Filebucketed/etc/httpd/conf/httpd.conftopuppetwithsum42566ec31df37e3d44429b285d015e1dNotice:/Stage[main]/Main/File[httpd.conf]/content:contentchanged'{md5}42566ec31df37e3d44429b285d015e1d'to'{md5}8b01e334a6e975b659df5dd351923ccb'Info:/Stage[main]/Main/File[httpd.conf]:SchedulingrefreshofService[httpd]Notice:/Stage[main]/Main/Service[httpd]:Triggered'refresh'from1eventsNotice:Finishedcatalogrunin1.95seconds[root@node1manifets]#ss-tnlStateRecv-QSend-QLocalAddress:PortPeerAddress:PortLISTEN05192.168.122.1:53*:*LISTEN0128*:22*:*LISTEN0128127.0.0.1:631*:*LISTEN0100127.0.0.1:25*:*LISTEN032:::21:::*LISTEN0128:::22:::*LISTEN0128::1:631:::*LISTEN0128:::8088:::*LISTEN0100::1:25:::*[root@node1manifets]#

实例三:每三分钟同步下系统时间,写入定时任务

 

cron{'synctime':command=>'/usr/sbin/ntpdate10.1.0.1&>/dev/null',#须执行的命令ensure=>present,#创建任务计划minute=>'*/5',#过多长是时间执行user=>root,#以谁的身份运行}[root@node1testmanifests]#puppetapply-vcron.ppNotice:Compiledcatalogfornode1.alren.cominenvironmentproductionin0.18secondsInfo:Applyingconfigurationversion'1480593969'Notice:/Stage[main]/Main/Cron[synctime]/minute:minutechanged'*/3'to'*/5'Notice:Finishedcatalogrunin0.09seconds[root@node1testmanifests]#crontab-l#HEADER:Thisfilewasautogeneratedat2016-12-0120:06:10+0800bypuppet.#HEADER:Whileitcanstillbemanagedmanually,itisdefinitelynotrecommended.#HEADER:Noteparticularlythatthecommentsstartingwith'PuppetName'should#HEADER:notbedeleted,asdoingsocouldcauseduplicatecronjobs.#PuppetName:synctime*/5****/usr/sbin/ntpdate10.1.0.1&>/dev/null#查看到此任务计划存在[root@node1testmanifests]#

实例四:puppet之if条件判断

if$osfamily=~/(?i-mx:debian)/{$osfamily为facter中取得的内嵌变量$webserver='apache2'#自定义变量}else{$webserver='httpd'}package{"$webserver":ensure=>installed,before=>[File['httpd.conf'],Service['httpd']],}file{'httpd.conf':path=>'/etc/httpd/conf/httpd.conf',source=>'/root/testmanifests/httpd.conf',ensure=>file,}service{'httpd':ensure=>running,enable=>true,restart=>'systemctlrestarthttpd.service',subscribe=>File['httpd.conf'],#通知其他的资源进行刷新操作}[root@node1testmanifests]#puppetapply-vif.ppNotice:Compiledcatalogfornode1.alren.cominenvironmentproductionin1.53secondsInfo:Applyingconfigurationversion'1480594920'Info:Computingchecksumonfile/etc/httpd/conf/httpd.confInfo:FileBucketgotaduplicatefile{md5}8b01e334a6e975b659df5dd351923ccbInfo:/Stage[main]/Main/File[httpd.conf]:Filebucketed/etc/httpd/conf/httpd.conftopuppetwithsum8b01e334a6e975b659df5dd351923ccbNotice:/Stage[main]/Main/File[httpd.conf]/content:contentchanged'{md5}8b01e334a6e975b659df5dd351923ccb'to'{md5}42566ec31df37e3d44429b285d015e1d'Info:/Stage[main]/Main/File[httpd.conf]:SchedulingrefreshofService[httpd]Notice:/Stage[main]/Main/Service[httpd]:Triggered'refresh'from1eventsNotice:Finishedcatalogrunin1.97seconds[root@node1testmanifests]#ss-tnlStateRecv-QSend-QLocalAddress:PortPeerAddress:PortLISTEN05192.168.122.1:53*:*LISTEN0128*:22*:*LISTEN0128127.0.0.1:631*:*LISTEN0100127.0.0.1:25*:*LISTEN0128:::80:::*LISTEN032:::21:::*LISTEN0128:::22:::*LISTEN0128::1:631:::*LISTEN0100::1:25:::*[root@node1testmanifests]#

实例五:puppet之case语句

case$osfamily{"RedHat":{$webserver='httpd'}/(?i-mx:debian)/:{$webserver='apache2'}default:{$webserver='httpd'}}package{"$webserver":ensure=>installed,before=>[File['httpd.conf'],Service['httpd']],}file{'httpd.conf':ensure=>file,path=>'/etc/httpd/conf/httpd.conf',source=>'/root/testmanifests/httpd.conf',}service{'httpd':ensure=>running,enable=>true,restart=>'systemctlrestarthttpd.service',subscribe=>File['httpd.conf'],}[root@node1testmanifests]#puppetapply-vcase.ppNotice:Compiledcatalogfornode1.alren.cominenvironmentproductionin1.61secondsInfo:Applyingconfigurationversion'1480595667'Info:Computingchecksumonfile/etc/httpd/conf/httpd.confInfo:FileBucketgotaduplicatefile{md5}42566ec31df37e3d44429b285d015e1dInfo:/Stage[main]/Main/File[httpd.conf]:Filebucketed/etc/httpd/conf/httpd.conftopuppetwithsum42566ec31df37e3d44429b285d015e1dNotice:/Stage[main]/Main/File[httpd.conf]/content:contentchanged'{md5}42566ec31df37e3d44429b285d015e1d'to'{md5}3dfa14b023127a3766bddfe15fe14b9a'Info:/Stage[main]/Main/File[httpd.conf]:SchedulingrefreshofService[httpd]Notice:/Stage[main]/Main/Service[httpd]:Triggered'refresh'from1eventsNotice:Finishedcatalogrunin1.91seconds[root@node1testmanifests]##可根据自行的语言习惯选择适合的语法,如下所示同样可达到上诉的目的$webserver=$osfamily?{"Redhat"=>'httpd',/(?i-mx:debian)/=>'apache2',default=>'httpd',}package{"$webserver":ensure=>installed,before=>[File['httpd.conf'],Service['httpd']],}file{'httpd.conf':path=>'/etc/httpd/conf/httpd.conf',source=>'/root/testmanifests/httpd.conf',ensure=>file,}service{'httpd':ensure=>running,enable=>true,restart=>'systemctlrestarthttpd.service',subscribe=>File['httpd.conf'],}

四、puppet类与继承

puppet中命名的代码模块,经常需要被使用,如如重写则代码冗余,使用定义一组通用目标的资源,可在puppet全局调用,就能解决这类问题,类可以被继承,也可以包含子类。

类的语法格式有两种,调用类的方法常用的有三种,可以在类中传递参数等灵活的操作,如以下实例:

classwebserver{#类申明后续调用后才生效,调用的方法常用的有三种$webpkg=$operatingsystem?{/(?i-mx:(centos|redhat|fedora))/=>'httpd',/(?i-mx:(ubuntu|debian))/=>'apache2',default=>'httpd',}package{"$webpkg":ensure=>installed,}file{'/etc/httpd/conf/httpd.conf':ensure=>file,owner=>root,group=>root,source=>'/root/testmanifests/httpd.conf',require=>Package["$webpkg"],notify=>Service['httpd'],}service{'httpd':ensure=>running,enable=>true,}}includewebserver#使用include进行调用#申明调用类的第二种方法classweb($webserver='httpd'){#向类中传递参数package{"$webserver":ensure=>installed,before=>[File['httpd.conf'],Service['httpd']],}file{'httpd.conf':path=>'/etc/httpd/conf/httpd.conf',source=>'/root/testmanifests/httpd.conf',ensure=>file,}service{'httpd':ensure=>running,enable=>true,restart=>'systemctlrestarthttpd.service',subscribe=>File['httpd.conf'],}}class{'web':webserver=>'apache2',#调用类}[root@node1~]#puppetapply-v--noopclass2.ppNotice:Compiledcatalogfornode1.alren.cominenvironmentproductionin1.53secondsInfo:Applyingconfigurationversion'1480596978'Notice:/Stage[main]/Web/Package[apache2]/ensure:current_valueabsent,shouldbepresent(noop)Notice:/Stage[main]/Web/File[httpd.conf]/content:current_value{md5}3dfa14b023127a3766bddfe15fe14b9a,shouldbe{md5}8b01e334a6e975b659df5dd351923ccb(noop)Info:/Stage[main]/Web/File[httpd.conf]:SchedulingrefreshofService[httpd]Notice:/Stage[main]/Web/Service[httpd]:Wouldhavetriggered'refresh'from1eventsNotice:Class[Web]:Wouldhavetriggered'refresh'from3eventsNotice:Stage[main]:Wouldhavetriggered'refresh'from1eventsNotice:Finishedcatalogrunin0.65seconds[root@node1~]#

类的继承:子类可继承父类的资源属性,同时可定义父类不存在的额资源属性,一个父类可同时被多个子类所继承

classnginx{#定义父类信息package{'nginx':ensure=>installed,#安装程序包}service{'nginx':#启动程序ensure=>running,enable=>true,require=>Package['nginx'],}}classnginx::webinheritsnginx{#继承父类的原有属性,同时增加file资源属性file{'ngx-web.conf':path=>'/etc/nginx/conf.d/ngx-web.conf',ensure=>file,require=>Package['nginx'],source=>'/root/testmanifests/nginx/ngx-web.conf',}file{'nginx.conf':path=>'/etc/nginx/nginx.conf',ensure=>file,content=>template('/root/testmanifests/nginx.conf.erb'),#此模板需事先存在方可使用require=>Package['nginx'],}Service['nginx']{subscribe=>[File['ngx-web.conf'],File['nginx.conf']],#通知其他资源进行刷新操作}}includenginx::web#调用声明的类

五、puppet模板(此内容不过多解释,需自行加强)

模板是一个按照约定的、预定的结构存放了多个文件或子目录的目录,目录里面的这些文件或子目录必须遵循一定的格式的命名规范,puppet会在配置的路径下查找所需的资源模块。模块的名称通常是只能以小写字母开头,可以包含小写字母,数字下划线,但是不能使用main和settings。

模块的组成部分:

manifests/:资源清单

init.pp:必须定义一个类,类名必须与模块名相同;

files/:静态文件

templates/:模板文件

lib/:插件目录,常用于存储自定义的facts以及自定义类型

spec/:类似于tests目录,存储lib/目录下插件的使用帮助和范例;

tests/:当前模块的使用帮助或使用范例文件;

总结:运维工具有很多例如:Puppet,Ansible,slackstack等,puppet还是一个很好用的自动化运维工具,大大减轻的运维人员的重复操作,提高了工作效率,在运维过程中可根据业务需求选择不同的运维工具,在服务器数量不是很大的情形下可使用轻量级的ansible,在服务器数量达到一定的规模时使用重量级的puppet相对来说效率更高。当面临有得选择的时候想起一句话:箩卜白菜各有所爱,适合自己专注、精通一个运维工具比全会那么一点,解决问题更有优势。





如何用Puppet和Augeas管理Linux配置

本文导航

  • -Augeas是什么?08%
  • -这篇教程要达成什么目的?13%
  • -前置阅读17%
  • -不带Puppet的Augeas36%
  • -Puppet Augeas 示例41%
    • -管理 /etc/sudoers 文件43%
    • -向一个组中加入用户75%
  • -总结84%
  • -有用的链接89%

虽然Puppet是一个真正独特的有用工具,但在有些情况下你可以使用一点不同的方法来用它。比如,你要修改几个服务器上已有的配置文件,而且它们彼此稍有不同。Puppet实验室的人也意识到了这一点,他们在 Puppet 中集成了一个叫做Augeas的伟大的工具,它是专为这种使用情况而设计的。

Augeas可被认为填补了Puppet能力的空白,比如在其中一个指定对象的资源类型(例如用于维护/etc/hosts中的条目的主机资源)还不可用时。在这个文档中,您将学习如何使用Augeas来减轻你管理配置文件的负担。

Augeas是什么?

Augeas基本上就是一个配置编辑工具。它以他们原生的格式解析配置文件并且将它们转换成树。配置的更改可以通过操作树来完成,并可以以原生配置文件格式保存配置。

这篇教程要达成什么目的?

我们会针对我们之前构建的Puppet服务器安装并配置Augeas。我们会使用这个工具创建并测试几个不同的配置文件,并学习如何适当地使用它来管理我们的系统配置。

前置阅读

我们需要一台工作的Puppet服务器和客户端。如果你还没有,请先按照我先前的教程来。

Augeas安装包可以在标准CentOS/RHEL仓库中找到。不幸的是,Puppet用到的Augeas的ruby封装只在puppetlabs仓库中(或者EPEL)中才有。如果你系统中还没有这个仓库,请使用下面的命令:

在CentOS/RHEL 6.5上:

  1. # rpm ivh https://yum.puppetlabs.com/el/6.5/products/x86_64/puppetlabs­release­6­10.noarch.rpm

在CentOS/RHEL 7上:

  1. # rpm ivh https://yum.puppetlabs.com/el/7/products/x86_64/puppetlabs­release­7­10.noarch.rpm

在你成功地安装了这个仓库后,在你的系统中安装Ruby­Augeas:

  1. # yum install ruby­augeas

或者如果你是从我的上一篇教程中继续的,使用puppet的方法安装这个包。在/etc/puppet/manifests/site.pp中修改你的custom_utils类,在packages这行中加入“ruby­augeas”。

  1. class custom_utils {
  2. package { ["nmap","telnet","vim­enhanced","traceroute","ruby­augeas"]:
  3. ensure => latest,
  4. allow_virtual => false,
  5. }
  6. }

不带Puppet的Augeas

如我先前所说,最初Augeas并不是来自Puppet实验室,这意味着即使没有Puppet本身我们仍然可以使用它。这种方法可在你将它们部署到Puppet环境之前,验证你的修改和想法是否是正确的。要做到这一点,你需要在你的系统中安装一个额外的软件包。请执行以下命令:

  1. # yum install augeas

Puppet Augeas 示例

用于演示,这里有几个Augeas使用案例。

管理 /etc/sudoers 文件

  1. 给wheel组加上sudo权限。

这个例子会向你展示如何在你的GNU/Linux系统中为%wheel组加上sudo权限。

  1. # 安装sudo
  2. package { 'sudo':
  3. ensure => installed, # 确保sudo包已安装
  4. }
  5.  
  6. # 允许用户属于wheel组来使用sudo
  7. augeas { 'sudo_wheel':
  8. context => '/files/etc/sudoers', # 目标文件是 /etc/sudoers
  9. changes => [
  10. # 允许wheel用户使用sudo
  11. 'set spec[user = "%wheel"]/user %wheel',
  12. 'set spec[user = "%wheel"]/host_group/host ALL',
  13. 'set spec[user = "%wheel"]/host_group/command ALL',
  14. 'set spec[user = "%wheel"]/host_group/command/runas_user ALL',
  15. ]
  16. }

现在来解释这些代码做了什么:spec定义了/etc/sudoers中的用户段,[user]定义了数组中给定的用户,所有的定义放在该用户的斜杠( / ) 后那部分。因此在典型的配置中这个可以这么表达:

  1. user host_group/host host_group/command host_group/command/runas_user

这个将被转换成/etc/sudoers下的这一行:

  1. %wheel ALL = (ALL) ALL
  1. 添加命令别称

下面这部分会向你展示如何定义命令别名,它可以在你的sudoer文件中使用。

  1. # 创建新的SERVICE别名,包含了一些基本的特权命令。
  2. augeas { 'sudo_cmdalias':
  3. context => '/files/etc/sudoers', # 目标文件是 /etc/sudoers
  4. changes => [
  5. "set Cmnd_Alias[alias/name = 'SERVICES']/alias/name SERVICES",
  6. "set Cmnd_Alias[alias/name = 'SERVICES']/alias/command[1] /sbin/service",
  7. "set Cmnd_Alias[alias/name = 'SERVICES']/alias/command[2] /sbin/chkconfig",
  8. "set Cmnd_Alias[alias/name = 'SERVICES']/alias/command[3] /bin/hostname",
  9. "set Cmnd_Alias[alias/name = 'SERVICES']/alias/command[4] /sbin/shutdown",
  10. ]
  11. }

sudo命令别名的语法很简单:Cmnd_Alias定义了命令别名字段,[alias/name]绑定所有给定的别名,/alias/name SERVICES 定义真实的别名,alias/command 是属于该别名的所有命令的数组。以上将被转换如下:

  1. Cmnd_Alias SERVICES = /sbin/service , /sbin/chkconfig , /bin/hostname , /sbin/shutdown

关于/etc/sudoers的更多信息,请访问官方文档。

向一个组中加入用户

要使用Augeas向组中添加用户,你也许要添加一个新用户,不管是排在 gid 字段还是最后的用户 uid 之后。我们在这个例子中使用SVN组。这可以通过下面的命令达成:

在Puppet中:

  1. augeas { 'augeas_mod_group:
  2. context => '/files/etc/group', #目标文件是 /etc/group
  3. changes => [
  4. "ins user after svn/*[self::gid or self::user][last()]",
  5. "set svn/user[last()] john",
  6. ]
  7. }

使用 augtool:

  1. augtool> ins user after /files/etc/group/svn/*[self::gid or self::user][last()] augtool> set /files/etc/group/svn/user[last()] john

总结

目前为止,你应该对如何在Puppet项目中使用Augeas有点明白了。随意地试一下,你肯定需要浏览官方的Augeas文档。这会帮助你了解如何在你的个人项目中正确地使用Augeas,并且它会让你知道可以用它节省多少时间。

如有任何问题,欢迎在下面的评论中发布,我会尽力解答和向你建议。

有用的链接

  • http://www.watzmann.net/categories/augeas.html: 包含许多关于 Augeas 使用的教程。
  • http://projects.puppetlabs.com/projects/1/wiki/puppet_augeas: Puppet wiki 带有许多实例。

via: http://xmodulo.com/2014/09/manage-configurations-linux-puppet-augeas.html


原创粉丝点击