本文转载自朱双印个人日志:https://www.zsythink.net/archives/420
这篇文章总结了puppet资源间的依赖关系,以及引用资源的方法。
当我们在一个清单中配置多个资源时,这些资源之间往往存在着依赖关系,比如,当我们想要在服务器上安装nginx并且想要在nginx安装完成后启动nginx服务,那么我们将会使用到两个资源, package资源与service资源,而且package资源应该在service资源之前被执行,因为如果想要启动nginx那么则必须先安装了nginx才行,同样,当我们修改了nginx的配置文件时,对应nginx服务的配置应该被重载,换句话说就是,当”配置文件被修改”时,nginx服务应该被告知去重载配置文件了,所以,当多个资源之间存在这种依赖关系或者通知关系的时候,则需要管理员在清单中定义。
还记得我们之前说过的元参数(元属性)吗,我们通过几个常用的元属性,即可定义资源之间的依赖关系,或许你已经忘了元属性是什么,不过没关系,我们一起来回顾一下。
元参数,或者称为 “元属性”,英文原称为metaparameter,当我们为资源设置元属性时,目的往往是希望通过元属性影响puppet的行为,你可以这样理解,当我们定义了资源之间的依赖关系,则表示puppet会按照我们定义先后顺序调用资源,这就是所谓的”影响puppet的行为”,那么常用的、能够影响资源依赖关系或者调用顺序的元属性有哪些呢,不多,就4个,如下:
before 、require 、notify 、subscribe
其实,我们可以把上面四个元属性再次两两分组:
before/require:这两个元属性可以定义资源之间的依赖关系。
notify/subscribe:这两个元属性可以定义资源之间的通知关系。
其实只用文字描述,我写着特别费劲,你也难看懂,不如我们动手做一个小例子,估计你就秒懂了。
before & require
我们创建一个test.pp清单,在清单中配置如下资源:
user{"pengyuyan":
ensure => present,
gid => 666,
managehome => true,
}
可以看到,我们定义了一个user类型的资源,用户名称为pengyuyan,我们的目标是创建这个用户,并且指定了用户的gid为666。
好了清单定义完成了,我们尝试着执行一遍这个清单。
{{uploading-image-781283.png(uploading...)}}
可以从返回结果看出,当前系统中并不存在gid为666的组。
那么,我们修改一下清单,改为如下内容。
user{"pengyuyan":
ensure => present,
gid => 666,
managehome => true,
}
group{"linuxgroup":
ensure => present,
gid => 666,
}
我们在创建user的同时,创建一个gid为666的组,相当于在一个清单中,同时定义了两个资源,改成上图中的内容以后,再次执行清单,应该就不会提示相应的错误了,因为puppet会自动选择先创建组,但是,为了说明怎样定义依赖关系,我们仍然使用上述场景进行演示,假设,如果puppet不会自动选择先创建组,那么,则需要我们手动指明依赖关系,很明显,当创建pengyuyan这个用户时,需要事先存在linuxgroup这个组,所以,我们可以理解为,user资源依赖group资源,那么我们可以改成如下定义:
user{"pengyuyan":
ensure => present,
gid => 666,
managehome => true,
require => Group['linuxgroup'],
}
group{"linuxgroup":
ensure => present,
gid => 666,
}
资源定义改为如上图所示后,表示user资源依赖了一个group类型的资源,这个group资源的名称为”linuxgroup”。
没错,聪明如你一定看到了,require就是我们所说的元属性,我们可以通过require属性引用当前资源所依赖的资源。
但是使用require属性引用资源时需要注意,被引用的资源的资源类型需要首字母大写。
require => Group['linuxgroup'],
如上图所示,linuxgroup这个资源的类型为group,所以,当linuxgroup被引用时,需要写成Group[‘linuxgroup’]
正如你所看到的,引用资源的语法如下:
Type[‘resource name’]
在刚才的示例中,我们使用了require属性,设置了当前资源所依赖到的资源。其实,我们也可以在被依赖的资源中,定义依赖关系,如下图所示:
user{"pengyuyan":
ensure => present,
gid => 666,
managehome => true,
}
group{"linuxgroup":
ensure => present,
gid => 666,
before => User['pengyuyan'],
}
上图中的定义表示,”linuxgroup”资源被”pengyuyan”所依赖,所以,”linuxgroup”资源需要在”pengyuyan”资源之前被调用,同理,before属性后面,需要引用对应资源,也需要使用 Type[‘resource name’] 这种语法进行引用,由于被引用的资源为user类型,所以,下图中的U为大写。
before => User['pengyuyan']
好了,require 与 before 这两个元属性 ,我想我已经说明白了。
notify & subscribe
现在,我们再来聊聊notify 与 subscribe这两个元属性。
我们通过require 与 before 可以定义资源之间的依赖关系,而通过 notify 与 subscribe可以定义资源之间的通知机制。
比如,修改nginx的配置文件以后,需要通知nginx服务进行重载操作,存在这种需求时,应该怎样定义资源呢,示例如下。
file{"nginxconf":
path => '/etc/nginx/conf.d/default.conf',
ensure => present,
source => '/template/default.conf',
}
exec{"nginxreload":
command => "/usr/sbin/nginx/ -s reload",
subscribe => File["nginxconf"],
refreshonly => true,
}
从上图可以看出,在exec资源中,我们使用了subscribe属性引用了”nginxconf”资源,subscribe的英文原意为”订阅”,从字面上也能够理解,我们可以通过subscribe属性订阅对应资源,上图中,exec资源订阅了nginx的配置文件(nginxconf)资源,同时,exec资源中还设置了refreshonly属性,exec的refreshonly属性表示,只有在订阅的资源发生变化时,才执行exec资源对应的命令,通过这两个属性的结合,就可以满足我们刚才描述的场景中的要求,即当nginx的配置文件发生变化时,nginx服务就会自动重载配置。
还记的我们说过的notify吗,它与subscribe是对应的,它们之间的关系就像before与require之间的关系,notify是主动”通知”,subscribe是主动”订阅”,只不过notify属性与subscribe属性所在的资源位置不同而已,比如刚才上例中的场景,我们还能使用如下定义,效果是相同的。
上例中,我们将notify属性写入了nginxconf资源中,表示nginx的配置文件发生变化时,会主动通知exec资源,因为exec资源中的refreshonly属性的值为true,所以,只有在接收到nginxconf资源的通知时,对应的命令才会执行。
file{"nginxconf":
path => '/etc/nginx/conf.d/default.conf',
ensure => present,
source => '/template/default.conf',
notify => Exec["nginxreload"],
}
exec{"nginxreload":
command => "/usr/sbin/nginx/ -s reload",
refreshonly => true,
}
好了,我们已经通过配置示例,演示了怎样定义资源之间的依赖关系以及通知关系。
其实我们还能够使用另一种方法,定义资源之间的执行顺序,这种方法被称为”链”,用文字描述太无力,还是看图吧,示例如下。
file{"nginxconf":
path => '/etc/nginx/conf.d/default.conf',
ensure => present,
source => '/template/default.conf',
}
exec{"nginxreload":
command => "/usr/sbin/nginx/ -s reload",
refreshonly => true,
}
File['nginxconf'] ~> Exec['nginxreload']
上图中的示例任然是我们刚才描述的场景,当nignx的配置文件发生改变时,则重载服务配置,可是上图中,我们既没有在file资源中定义notify属性,也没有在exec资源中定义subscribe属性,而是通过一条”通知链”,定义了资源之间的通知关系,在这条”通知链”中,我们定义了由”nginxconf”资源通知”nginxreload”资源,细心如你一定发现了,”通知链”通过 “~>”连接。
上述例子中展示了”通知链”的用法,当使用”通知链”时,我们则不需要使用notify 与 subscribe这两个元属性了,那么有没有其他”链”能够代替require 与 before 这两个元属性呢,必须有啊,我们可以使用”依赖链”或者称为”次序链”,来代替require 与 before 两个属性,定义”次序链”的示例如下:
user{"pengyuyan":
ensure => present,
gid => 666,
managehome => true,
}
group{"linuxgroup":
ensure => present,
gid => 666,
}
Group['linuxgroup'] -> User['pengyuyan']
上图中,红线标注的部分即为”次序链”,表示”linuxgroup”资源需要先于”pengyuyan”资源被调用,也可以理解为”pengyuyan”资源依赖”linuxgroup”资源,细心如你一定发现了,当我们定义”次序链”时,通过”->”符号连接次序链,不管是次序链还是通知链,我们都可以通过实际情况定义,假设我们想要先安装一个httpd的软件包,然后设置其配置文件内容,最后启动httpd服务,那么我们可能会配置一个如下的”次序链”。
Package[‘httpd’] -> File[‘httpdconf’] -> Service[‘httpdsrv’]
好了,我们已经了解了4个常用于定义执行顺序或通知关系的元属性,并且还学会了用”链”的方式来定义这些关系,我们在以后的应用中,可能会经常用到它们。