一、class 類
1)什么是類?
類是puppet中命名的代碼模塊,常用於定義一組通用目標的資源,可在puppet全局調用;
類可以被繼承,也可以包含子類;
具體定義的語法如下:
class NAME{
... puppet code ...
}
其中,在我們定義的時候,需要注意的是:
- 類的名稱只能以小寫字母開頭,可以包含小字字母、數字和下划線。
- 每個類都會引入一個新的變量scope ,這意味着在任何時候訪問類中的變量時,都得使用其完全限定名稱。
- 不過,在本地 scope 可以重新為 top scope 中的變量賦予一個新值。
下面,我們來看一個簡單的例子:
vim class1.pp
class redis { #定義一個類
package{'redis':
ensure => installed,
} ->
file{'/etc/redis.conf':
ensure => file,
source => '/root/manifests/file/redis.conf',
owner => 'redis',
group => 'root',
mode => '0640',
tag => 'redisconf'
} ~>
service{'redis':
ensure => running,
enable => true,
hasrestart => true,
hasstatus => true
}
}
include redis #調用類
注意:類只有被調用才會執行。include后可以跟多個類,直接用","隔開即可。
2)帶有參數的類
我們定義的類也可以進行參數設置,可以進行參數的傳遞。
具體語法如下所示:
class NAME(parameter1, parameter2) { #注意,大括號前有一個空格
...puppet code...
}
我們來看一個例子:
vim class2.pp
class instpkg($pkg) {
package{"$pkg":
ensure => installed,
}
}
class{"instpkg": #給參數傳入值
pkg => 'memcached',
}
注意:單個主機上不能被直接聲明兩次。
如果對應的參數未傳值的話,執行會報錯。
但是我們可以在定義形參的時候,設定一個默認值,這樣的話,我們不傳入值的話,就會自動調用默認值:
vim class3.pp
class instpkg($pkg='wget') {
package{"$pkg":
ensure => installed,
}
}
include instpkg
這樣的話,我們直接使用include
調用即可,就不需要給參數傳入值了。
由上,我們可以總結出,調用類的方式有兩種:
1. include CLASS_NAME1, CLASS_NAME2, ...
2. class{'CLASS_NAME':
attribute => value,
}
我們來看一個比較全面的例子:
首先,判斷我們系統的版本,是6還是7,由此來確定,是安裝mysql
還是mariadb
,同時,使用調用參數的方式來實現如上需求。
具體實現的代碼如下:
vim dbserver.pp
class dbserver($dbpkg='mariadb-server',$svc='mariadb') { #定義類並給參數賦值
package{"$dbpkg":
ensure => installed,
}
service{"$svc":
ensure => running,
enable => true,
hasrestart => true,
hasstatus => true,
}
}
if $operatingsystem == 'CentOS' {
if $operatingsystemmajrelease == '7' {
include dbserver #直接調用類
} else {
class{"dbserver": #調用類並對參數重新賦值
dbpkg => 'mysql-server',
svc => 'mysqld'
}
}
}
3)類的繼承
類似於其它編程語言中的類的功能,puppet 的Class 可以被繼承,也可以包含子類。
其定義的語法如下:
class SUB_CLASS_NAME inherits PARENT_CLASS_NAME {
...puppet code...
}
下面我們來看一個例子:
vim class4.pp
class redis { #定義class類
package{'redis':
ensure => installed,
}
service{'redis':
ensure => running,
enable => true,
}
}
class redis::master inherits redis { #調用父類
file {'/etc/redis.conf':
ensure => file,
source => '/root/manifests/file/redis-master.conf',
owner => 'redis',
group => 'root',
}
Service['redis'] { #定義依賴關系
subscribe => File['/etc/redis.conf']
}
}
class redis::slave inherits redis { #調用父類
file {'/etc/redis.conf':
ensure => file,
source => '/root/manifests/file/redis-slave.conf',
owner => 'redis',
group => 'root',
}
Service['redis'] { #定義依賴關系
subscribe => File['/etc/redis.conf']
}
}
一樣的,我們的類在調用的時候,可以實現修改原有值和額外新增屬性的功能。
1.新增屬性
我們的繼承父類的時候,可以定義一些父類原本沒有的屬性:

2.新增原有值
在繼承的類中,我們可以在屬性原有值的基礎上,使用 +> 進行新增修改:

3.修改原有值
在繼承的類中,我們可以直接把原有的值進行覆蓋修改,使用 =>
進行覆蓋即可:

4.整體調用父類,並重寫部分值
在繼承的類中,我們還可以在整體調用的基礎上,根據不同的需求,把父類中的部分值進行重寫修改:

二、模板
模板通常以erb
結尾。模板均使用erb
語法。
關於puppet
兼容的erb
語法,我們可以去官方文檔查看,下面附上官方文檔地址:https://docs.puppet.com/puppet/latest/reference/lang_template_erb.html
以下,附上部分重要內容:
<%= EXPRESSION %> — 插入表達式的值,進行變量替換
<% CODE %> — 執行代碼,但不插入值
<%# COMMENT %> — 插入注釋
<%% or %%> — 插入%
接着我們來看一個實例:
實例1:puppet 模板實現修改 redis 端口地址
我們使用puppet 模板來實現,將redis 監聽端口修改為本機的ip地址。
首先,我們先來定義一個file.pp
文件,在該文件中調用我們的模板:
vim file.pp
file{'/tmp/redis.conf': #僅用於測試模板是否生效,所以放在tmp目錄下
ensure => file,
content => template('/root/manifests/file/redis.conf.erb'), #調用模板文件
owner => 'redis',
group => 'root',
mode => '0640',
}
接着,我們去修改配置文件的源,也就是我們的模板文件:
vim file/redis.conf.erb
bind 127.0.0.1 <%= @ipaddress_eth0 %> #修改監聽端口
修改完成以后,我們就可以執行查看結果了:
puppet apply -v file.pp
然后,我們去查看一下/tmp/redis.conf
文件:
vim /tmp/redis.conf

可以看出,我們的變量替換已經成功。
三、模塊
1)什么是模塊?
實踐中,一般需要把manifest 文件分解成易於理解的結構,例如將類文件、配置文件甚至包括后面將提到的模塊文件等分類存放,並且通過某種機制在必要時將它們整合起來。
這種機制即模塊,它有助於以結構化、層次化的方式使用puppet,而puppet 則基於“模塊自動裝載器”。
從另一個角度來說,模塊實際上就是一個按約定的、預定義的結構存放了多個文件或子目錄的目錄,目錄里的這些文件或子目錄必須遵循其命名規范。
2)模塊的命名規范
模塊的目錄格式如下:

其中,每個文件夾中存放的內容及其要求如下:
- MODULE NAME:模塊名稱,模塊名只能以小寫字母開頭,可以包含小寫字母、數字和下划線;但不能使用"main"和"settings";
- manifests/:必須要有
- init.pp:必須一個類定義,類名稱必須與模塊名稱相同;
- files/:靜態文件;
- 其中,每個文件的訪問路徑遵循:
puppet:///modules/MODULE_NAME/FILE_NAME
;
- 其中,每個文件的訪問路徑遵循:
- templates/:
- 其中,每個文件的訪問路徑遵循:
tempate('MOD_NAME/TEMPLATE_FILE_NAME')
;
- 其中,每個文件的訪問路徑遵循:
- lib/:插件目錄,常用於存儲自定義的facts以及自定義類型;
- spec/:類似於tests目錄,存儲lib/目錄下插件的使用幫助和范例;
- tests/:當前模塊的使用幫助或使用范例文件;
實例:定義一個redis主從模塊
下面我們就來看一個實例來具體的了解應該如何定義一個模塊:
1)我們先來創建對應的目錄格式:
[root@master ~]# mkdir modules
[root@master ~]# cd modoules/
[root@master modules]# ls
[root@master modules]# mkdir -pv redis/{manifests,files,templates,tests,lib,spec}
mkdir: created directory ‘redis’
mkdir: created directory ‘redis/manifests’
mkdir: created directory ‘redis/files’
mkdir: created directory ‘redis/templates’
mkdir: created directory ‘redis/tests’
mkdir: created directory ‘redis/lib’
mkdir: created directory ‘redis/spec’
2)目錄格式創建完成之后,我們就可以來創建對應的父類子類文件了。
首先,我們來創建父類文件:
[root@master modules]# cd redis/
[root@master redis]# vim manifests/init.pp
class redis {
package{'redis':
ensure => installed,
} ->
service{'redis':
ensure => running,
enable => true,
hasrestart => true,
hasstatus => true,
require => Package['redis'],
}
}
創建完成后,我們再來創建對應的子類文件:
[root@master redis]# vim manifests/master.pp
class redis::master inherits redis {
file {'/etc/redis.conf':
ensure => file,
source => 'puppet:///modules/redis/redis-master.conf',
owner => 'redis',
group => 'root',
mode => '0640',
}
Package['redis'] -> File['/etc/redis.conf'] ~> Service['redis']
}
[root@master redis]# vim manifests/slave.pp
class redis::slave($master_ip,$master_port='6379') inherits redis {
file {'/etc/redis.conf':
ensure => file,
content => template('redis/redis-slave.conf.erb'),
owner => 'redis',
group => 'root',
mode => '0640',
}
Package['redis'] -> File['/etc/redis.conf'] ~> Service['redis']
}
3)准備文件:
現在我們需要把模板文件准備好,放入我們的templates
目錄下:
scp redis.conf.erb /root/modules/redis/templates/redis-slave.conf.erb
還有我們的靜態文件,也要放入我們的files
目錄下:
scp redis.conf /root/modules/redis/files/redis-master.conf
4)查看目錄結構,確定我們是否都已准備完成:
[root@master modules]# tree
.
└── redis
├── files
│ └── redis-master.conf
├── lib
├── manifests
│ ├── init.pp
│ ├── master.pp
│ └── slave.pp
├── spec
├── templates
│ └── redis-slave.conf.erb
└── tests
7 directories, 5 files
5)現在就可以把我們的准備好的模塊放入系統的模塊目錄下:
[root@master mdoules]# cp -rp redis/ /etc/puppet/modules/
注意,模塊是不能直接被調用的,只有放在/etc/puppet/modules
下,或/usr/share/puppet/modules
目錄下,使其生效才可以被調用。
我們可以來查看一下我們的模塊到底有哪些:
[root@master mdoules]# puppet module list
/etc/puppet/modules
└── redis (???)
/usr/share/puppet/modules (no modules installed)
可以看出,我們的模塊已經定義好了,現在我們就可以直接調用了。
6)調用模塊
我們可以直接命令行傳入參數來調用我們准備好的模塊:
[root@master modules]# puppet apply -v --noop -e "class{'redis::slave': master_ip => '192.168.37.100'}" #如果有多個參數,直接以逗號隔開即可
也可以把我們的調用的類賦值在.pp
文件中,然后運行該文件。
[root@master ~]# cd manifests/
[root@master manifests]# vim redis2.pp
class{'redis::slave':
master_ip => '192.168.37.100',
}
[root@master manifests]# puppet apply -e --noop redis2.pp
以上。實驗完成。
注意,以上實驗是我們在單機模式下進行的,如果是要在master/agent 模式下進行,步驟還會略有不同。
四、master/agent 模型
master/agent模型時通過主機名進行通信的,下面,就來看看 master-agent 模式的puppet運維自動化如何實現:
實現步驟
1、實現前准備
1)下載包
master 端:puppet.noarch
,puppet-server.noarch
agent 端:puppet.noarch

2)主機名解析
為了方便我們后期的操作,我們可以通過定義/etc/hosts
文件實現主機名的解析。如果機器很多的話,可以使用DNS進行解析。
[root@master ~]# vim /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.37.111 master.keer.com
192.168.37.122 server1.keer.com
注意,該操作需要在每一台主機上進行。
修改完成以后,我們可以來測試一下是否已經成功:
[root@master ~]# ping server1.keer.com

3)時間同步
[root@master ~]# systemctl start chronyd.service
所有機器上都開啟chronyd.service
服務來進行時間同步
開啟過后可以查看一下狀態:
[root@master ~]# systemctl status chronyd.service

我們可以使用chronyc sources
命令來查看時間源:

2、開啟 master 端的 puppet 服務
1)手動前台開啟,觀察服務開啟過程:
puppet master -v --no-daemonize #前台運行

整個過程都是自動完成的,其中,每一步的意思如下:
① 創建key 給CA
② 創建一個請求給CA
③ 自簽名證書
④ CA 創建完成
⑤ 創建證書吊銷列表
⑥ 為當前的master 主機簽署證書
⑦ master 的證書簽署完成
2)直接systemctl
開啟服務,監聽在8140端口。

3、在 agent 端開啟服務
1)在配置文件中指明server端的主機名:
[root@server1 ~]# vim /etc/puppet/puppet.conf
server = master.keer.com

接着,我們可以通過puppet config print
命令來打印輸出我們配置的參數:
[root@server1 ~]# puppet config print 顯示配置文件中的配置參數
[root@server1 ~]# puppet config print --section=main 顯示main 段的配置參數
[root@server1 ~]# puppet config print --section=agent 顯示agent 段的配置參數
[root@server1 ~]# puppet config print server 顯示server 的配置參數

2)開啟 agent 服務

我們可以發現,他會一直卡在這里等待CA頒發證書。
3)在 master 端簽署證書
[root@master ~]# puppet cert list
"server1.keer.com" (SHA256) B5:67:51:30:5C:FB:45:BA:7A:73:D5:C5:87:D4:E3:1C:D7:02:BE:DD:CC:7A:E2:F0:28:34:87:86:EF:E7:1D:E4
[root@master ~]# puppet cert sign server1.keer.com #頒發證書
Notice: Signed certificate request for server1.keer.com
Notice: Removing file Puppet::SSL::CertificateRequest server1.keer.com at '/var/lib/puppet/ssl/ca/requests/server1.keer.com.pem'
master 端管理證書部署的命令語法如下:
puppet cert <action> [–all|-a] [<host>]
action:
list 列出證書請求
sign 簽署證書
revoke 吊銷證書
clean 吊銷指定的客戶端的證書,並刪除與其相關的所有文件;
注意:某agent證書手工吊銷后重新生成一次;
On master host:
puppet cert revoke NODE_NAME
puppet cert clean NODE_NAME
On agent host:
重新生成的主機系統,直接啟動agent;
變換私鑰,建議先清理/var/lib/puppet/ssl/目錄下的文件
4)終止服務開啟,再次開啟
[root@server1 ~]# puppet agent -v --noop --no-daemonize

可以看出我們的服務開啟成功,但是由於master 端沒有配置站點清單,所以沒有什么動作。
4、配置站點清單,且測試agent 端是否實現
1)設置站點清單
① 查詢站點清單應存放的目錄,(可以修改,去配置文件修改)
[root@master ~]# puppet config print |grep manifest

[root@master ~]# cd /etc/puppet/manifests/
[root@master manifests]# vim site.pp
node 'server1.along.com' {
include redis::master
}
分析:就是簡單的調用模塊,只有模塊提前定義好就可以直接調用;我調用的是上邊的redis 模塊
2)給puppet 用戶授權
因為agent 端要來master 端讀取配置,身份是puppet
[root@master manifests]# chown -R puppet /etc/puppet/modules/redis/*
3)[root@server1 ~]# puppet agent -v --noop --no-daemonize 手動前台開啟agent 端服務

(4)直接開啟服務,agent 會自動去master 端獲取配置
[root@server1 ~]# systemctl start puppetagent 包已下載,服務也開啟了

實戰 —— 使用master-agent 模型完成完整的redis 主從架構
1)環境准備
機器名稱 | IP配置 | 服務角色 |
---|---|---|
puppet-master | 192.168.37.111 | puppet的master |
puppet-server1-master-redis | 192.168.37.122 | puppet的agent,redis 的master |
puppet-server2-slave-redis | 192.168.37.133 | puppet的agent,redis 的slave |
2)實驗前准備
1)下載包
master 端:puppet.noarch
,puppet-server.noarch
agent 端:puppet.noarch

2)主機名解析
為了方便我們后期的操作,我們可以通過定義/etc/hosts
文件實現主機名的解析。如果機器很多的話,可以使用DNS進行解析。
[root@master ~]# vim /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.37.111 master.keer.com
192.168.37.122 server1.keer.com
192.168.37.133 server2.keer.com
注意,該操作需要在每一台主機上進行。
修改完成以后,我們可以來測試一下是否已經成功:
[root@master ~]# ping server1.keer.com

3)時間同步
[root@master ~]# systemctl start chronyd.service
三台機器上都開啟chronyd.service
服務來進行時間同步
開啟過后可以查看一下狀態:
[root@master ~]# systemctl status chronyd.service

我們可以使用chronyc sources
命令來查看時間源:

3)開啟puppet 的master、agent 服務
(1)開啟服務
[root@master ~]# systemctl start puppetmaster
[root@server1 ~]# systemctl start puppetagent
[root@server2 ~]# systemctl start puppetagent
因為server2 是第一次連接,需master 端簽署證書
(2)master 簽署頒發證書
[root@master manifests]# puppet cert list
[root@master ~]# puppet cert sign server2.keer.com

4)配置站點清單
[root@master manifests]# cd /etc/puppet/manifests
[root@master manifests]# vim site.pp 直接調上邊完成的模塊
node 'server1.keer.com' {
include redis::master
}
node 'server2.keer.com' {
class{'redis::slave':
master_ip => 'server1.keer.com'
}
}
5)檢測主從架構
[root@server2 ~]# vim /etc/redis.conf

[root@server2 ~]# redis-cli -a keerya info Replication

6)再添加個模塊准備配置進站點清單
(1) 創建一個 chrony 模塊,前准備
[root@master ~]# cd modules/ 進入模塊工作目錄
[root@master modules]# mkdir chrony 創建chrony 的模塊
[root@master modules]# mkdir chrony/{manifests,files} -pv 創建模塊結構
(2)配置chrony 模塊
[root@master modules]# cd chrony/
[root@master chrony]# cp /etc/chrony.conf files/
[root@master puppet]# vim files/chrony.conf
# test #用於測試實驗結果

[root@master chrony]# vim manifests/init.pp
class chrony {
package{'chrony':
ensure => installed
} ->
file{'/etc/chrony.conf':
ensure => file,
source => 'puppet:///modules/chrony.conf',
owner => 'root',
group => 'root',
mode => '0644'
} ~>
service{'chronyd':
ensure => running,
enable => true,
hasrestart => true,
hasstatus => true
}
}
(3)puppet 添加這個模塊,並生效
[root@master modules]# cp -rp chrony/ /etc/puppet/modules/
[root@master modules]# puppet module list

7、再配置站點清單
[root@master ~]# cd /etc/puppet/manifests/
[root@master manifests]# vim site.pp
node 'base' {
include chrony
}
node 'server1.keer.com' inherits 'base' {
include redis::master
}
node 'server2.keer.com' inherits 'base' {
class{'redis::slave':
master_ip => 'server1.keer.com'
}
}
#node /cache[1-7]+\.keer\.com/ { #可以用正則匹配多個服務器使用模塊
# include varnish
#}
8、測試
我們現在直接去server2機器上,查看我們的配置文件是否已經生效,是否是我們添加過一行的內容:
[root@server2 ~]# vim /etc/chrony.conf

發現我們的實驗成功。