Puppet - 編碼風格



在 Puppet 中,編碼風格定義了在嘗試將機器配置上的基礎架構轉換為程式碼時需要遵循的所有標準。Puppet 使用資源來執行其所有定義的任務。

Puppet 的語言定義有助於以結構化的方式指定所有資源,這是管理任何需要管理的目標機器所必需的。Puppet 使用 Ruby 作為其編碼語言,它具有多個內建功能,這使得在程式碼端透過簡單的配置來完成任務變得非常容易。

基本單元

Puppet 使用多種易於理解和管理的基本編碼風格。以下是其中的一些。

資源

在 Puppet 中,資源被稱為用於管理或修改任何目標系統的基本建模單元。資源涵蓋了系統的方方面面,例如檔案、服務和包。Puppet 帶有一個內建功能,允許使用者或開發人員開發自定義資源,這有助於管理機器的任何特定單元。

在 Puppet 中,所有資源都透過使用“define”“classes”聚合在一起。這些聚合功能有助於組織模組。以下是一個示例資源,它包含多種型別、一個標題和一個屬性列表,Puppet 可以支援多個屬性。Puppet 中的每個資源都有自己的預設值,可以在需要時覆蓋。

檔案示例 Puppet 資源

在以下命令中,我們試圖為特定檔案指定許可權。

file {  
   '/etc/passwd': 
   owner => superuser, 
   group => superuser, 
   mode => 644, 
}

每當上述命令在任何機器上執行時,它都會驗證系統中的 passwd 檔案是否按描述配置。冒號之前的檔案是資源的標題,可以在 Puppet 配置的其他部分將其稱為資源。

除了標題之外,還指定本地名稱

file { 'sshdconfig': 
   name => $operaSystem ? { 
      solaris => '/usr/local/etc/ssh/sshd_config', 
      default => '/etc/ssh/sshd_config', 
   }, 
   owner => superuser, 
   group => superuser, 
   mode => 644, 
}

透過使用始終相同的標題,可以很容易地在配置中引用檔案資源,而無需重複與作業系統相關的邏輯。

另一個示例可能是使用依賴於檔案的服務。

service { 'sshd': 
   subscribe => File[sshdconfig], 
} 

透過此依賴項,sshd 服務將在 sshdconfig 檔案更改後始終重新啟動。這裡需要記住的是 File[sshdconfig] 是宣告為檔案,小寫,但如果我們將其更改為 FILE[sshdconfig],則它將是引用。

宣告資源時需要牢記的一個基本點是,每個配置檔案只能宣告一次。重複宣告同一個資源多次會導致錯誤。透過這個基本概念,Puppet 確保配置建模良好。

我們甚至能夠管理資源依賴關係,這有助於管理多個關係。

service { 'sshd': 
   require => File['sshdconfig', 'sshconfig', 'authorized_keys']
}   

元引數

元引數在 Puppet 中被稱為全域性引數。元引數的一個關鍵特性是,它適用於 Puppet 中的任何型別的資源。

資源預設值

當需要定義預設資源屬性值時,Puppet 提供了一組語法來實現它,使用沒有標題的大寫資源規範。

例如,如果我們想設定所有可執行檔案的預設路徑,可以使用以下命令。

Exec { path => '/usr/bin:/bin:/usr/sbin:/sbin' } 
exec { 'echo Testing mataparamaters.': } 

在上述命令中,第一個語句 Exec 將設定 exec 資源的預設值。Exec 資源需要一個完全限定路徑或看起來像可執行檔案的路徑。透過此,可以為整個配置定義單個預設路徑。預設值適用於 Puppet 中的任何資源型別。

預設值不是全域性值,但是,它們僅影響定義它們的範圍或緊隨其後的變數。如果要為完整配置定義default,則在下一節中定義default和類。

資源集合

聚合是將事物收集在一起的方法。Puppet 支援非常強大的聚合概念。在 Puppet 中,聚合用於對資源進行分組,資源是 Puppet 的基本單元。Puppet 中的這種聚合概念是透過使用兩種強大的方法實現的,稱為定義

類和定義

類負責對節點的基本方面進行建模。它們可以說節點是 Web 伺服器,並且此特定節點是其中之一。在 Puppet 中,程式設計類是單例,並且每個節點只能評估一次。

另一方面,定義可以在單個節點上多次使用。它們的工作原理類似於您使用語言建立自己的 Puppet 型別。它們被建立為多次使用,每次使用不同的輸入。這意味著您可以將變數值傳遞到定義中。

類和定義之間的區別

類和定義之間唯一的關鍵區別在於,在定義構建結構和分配資源時,類每個節點只評估一次,而另一方面,定義在同一個節點上多次使用。

Puppet 中的類是使用 class 關鍵字引入的,並且該特定類的內容包裝在花括號內,如下例所示。

class unix { 
   file { 
      '/etc/passwd': 
      owner => 'superuser', 
      group => 'superuser', 
      mode => 644; 
      '/etc/shadow': 
      owner => 'vipin', 
      group => 'vipin', 
      mode => 440; 
   } 
}

在以下示例中,我們使用了一些類似於上述內容的簡寫形式。

class unix { 
   file { 
      '/etc/passwd': 
      owner => 'superuser', 
      group => 'superuser', 
      mode => 644; 
   }  
   
   file {'/etc/shadow': 
      owner => 'vipin', 
      group => 'vipin', 
      mode => 440; 
   } 
} 

Puppet 類中的繼承

在 Puppet 中,預設支援 OOP 的繼承概念,其中類可以擴充套件以前的功能,而無需在新建立的類中再次複製貼上完整的程式碼段。繼承允許子類覆蓋父類中定義的資源設定。使用繼承時需要牢記的一件事是,一個類只能繼承一個父類的特性,不能超過一個。

class superclass inherits testsubclass { 
   File['/etc/passwd'] { group => wheel } 
   File['/etc/shadow'] { group => wheel } 
}

如果需要撤消父類中指定的某些邏輯,可以使用undef 命令

class superclass inherits testsubcalss { 
   File['/etc/passwd'] { group => undef } 
} 

使用繼承的另一種方法

class tomcat { 
   service { 'tomcat': require => Package['httpd'] } 
} 
class open-ssl inherits tomcat { 
   Service[tomcat] { require +> File['tomcat.pem'] } 
}

Puppet 中的巢狀類

Puppet 支援巢狀類的概念,它允許使用巢狀類,這意味著一個類在另一個類內部。這有助於實現模組化和作用域。

class testclass { 
   class nested { 
      file {  
         '/etc/passwd': 
         owner => 'superuser', 
         group => 'superuser', 
         mode => 644; 
      } 
   } 
} 
class anotherclass { 
   include myclass::nested 
} 

引數化類

在 Puppet 中,類可以擴充套件其功能以允許將引數傳遞到類中。

要將引數傳遞到類中,可以使用以下結構 -

class tomcat($version) { 
   ... class contents ... 
} 

在 Puppet 中需要記住的一點是,帶有引數的類不是使用 include 函式新增的,而是可以將生成的類新增為定義。

node webserver { 
   class { tomcat: version => "1.2.12" } 
}

類中作為引數的預設值

class tomcat($version = "1.2.12",$home = "/var/www") { 
   ... class contents ... 
} 

執行階段

Puppet 支援執行階段的概念,這意味著使用者可以根據需要新增多個階段,以便管理任何特定資源或多個資源。當用戶想要開發複雜目錄時,此功能非常有用。在複雜目錄中,您有大量需要編譯的資源,同時要記住定義的資源之間的依賴關係不應受到影響。

執行階段在管理資源依賴關係方面非常有用。這可以透過在定義的階段中新增類來完成,其中特定類包含資源的集合。透過執行階段,Puppet 保證定義的階段每次目錄執行並在任何 Puppet 節點上應用時都將按指定的可預測順序執行。

為了使用它,您需要宣告超出已存在階段的其他階段,然後可以配置 Puppet 以使用相同的資源關係語法在需要“->”“+>”之前按指定順序管理每個階段。然後,關係將保證與每個階段關聯的類的順序。

使用 Puppet 宣告性語法宣告其他階段

stage { "first": before => Stage[main] } 
stage { "last": require => Stage[main] } 

宣告階段後,可以使用 stage 將類與主階段以外的階段關聯。

class { 
   "apt-keys": stage => first; 
   "sendmail": stage => main; 
   "apache": stage => last; 
}

與類 apt-key 關聯的所有資源將首先執行。Sendmail 中的所有資源將是主類,而與 Apache 關聯的資源將是最後一個階段。

定義

在 Puppet 中,任何清單檔案中的資源集合是透過類或定義完成的。定義與 Puppet 中的類非常相似,但是它們是用define 關鍵字(而不是 class)引入的,並且它們支援引數而不是繼承。它們可以在同一系統上多次執行,並使用不同的引數。

例如,如果要建立一個控制原始碼儲存庫的定義,您正在嘗試在同一系統上建立多個儲存庫,則可以使用定義而不是類。

define perforce_repo($path) { 
   exec {  
      "/usr/bin/svnadmin create $path/$title": 
      unless => "/bin/test -d $path", 
   } 
} 
svn_repo { puppet_repo: path => '/var/svn_puppet' } 
svn_repo { other_repo: path => '/var/svn_other' }

這裡需要注意的關鍵點是如何將變數與定義一起使用。我們使用 ($) 美元符號變數。在上面,我們使用了 $title。定義可以同時具有 $title 和 $name,可以使用它們來表示名稱和標題。預設情況下,$title 和 $name 設定為相同的值,但您可以設定標題屬性並將不同的名稱作為引數傳遞。$title 和 $name 僅在定義中有效,在類或其他資源中無效。

模組

模組可以定義為所有配置的集合,Puppet master 將使用這些配置在任何特定 Puppet 節點(代理)上應用配置更改。它們也被稱為不同型別配置的可移植集合,這些配置是執行特定任務所必需的。例如,一個模組可能包含配置 Postfix 和 Apache 所需的所有資源。

節點

節點是非常簡單的剩餘步驟,即我們如何將我們定義的內容(“這是 Web 伺服器的樣子”)與選擇滿足這些指令的機器相匹配。

節點定義與類非常相似,包括支援繼承,但它們也有一些特殊之處:當一個節點(執行 Puppet 客戶端的受管理計算機)連線到 Puppet master 守護程序時,其名稱將在定義的節點列表中查詢。系統會評估節點的定義資訊,然後節點會發送相應的配置。

節點名稱可以是簡短的主機名或完全限定域名 (FQDN)。

node 'www.vipin.com' { 
   include common 
   include apache, squid 
}

上述定義建立了一個名為 www.vipin.com 的節點,幷包含了 common、Apache 和 Squid 類。

我們可以透過逗號分隔每個節點,將相同的配置傳送到不同的節點。

node 'www.testing.com', 'www.testing2.com', 'www3.testing.com' { 
   include testing 
   include tomcat, squid 
}

用於匹配節點的正則表示式

node /^www\d+$/ { 
   include testing 
}

節點繼承

節點支援有限的繼承模型。與類類似,節點只能繼承自另一個節點。

node 'www.testing2.com' inherits 'www.testing.com' { 
   include loadbalancer 
}

在上面的程式碼中,www.testing2.com 繼承了 www.testing.com 的所有功能,並額外添加了一個 loadbalancer 類。

高階支援功能

引用 - 在大多數情況下,我們不需要在 Puppet 中引用字串。任何以字母開頭的字母數字字串都無需加引號。但是,對於任何非負值,始終最佳實踐是為字串加引號。

帶引號的變數插值

到目前為止,我們已經從定義的角度提到了變數。如果需要在字串中使用這些變數,請使用雙引號,而不是單引號。單引號字串不會進行任何變數插值,而雙引號字串會進行。變數可以用{}括起來,這使得它們更容易一起使用,也更容易理解。

$value = "${one}${two}" 

最佳實踐是,對於所有不需要字串插值的字串,都應該使用單引號。

大寫

大寫是一個用於引用、繼承和設定特定資源的預設屬性的過程。基本上有兩種使用它的基本方法。

  • 引用 - 它是引用已建立資源的方式。它主要用於依賴目的,必須將資源的名稱大寫。例如,require => file [sshdconfig]

  • 繼承 - 從子類覆蓋父類的設定時,請使用資源名稱的大寫版本。使用小寫版本會導致錯誤。

  • 設定預設屬性值 - 使用沒有標題的大寫資源可以設定資源的預設值。

陣列

Puppet 允許在多個區域使用陣列 [One, two, three]。

一些型別成員,例如主機定義中的別名,在其值中接受陣列。具有多個別名的主機資源如下所示。

host { 'one.vipin.com': 
   alias => [ 'satu', 'dua', 'tiga' ], 
   ip => '192.168.100.1', 
   ensure => present, 
}

上面的程式碼將主機‘one.brcletest.com’新增到主機列表中,並添加了三個別名‘satu’ ‘dua’ ‘tiga’。如果要將多個資源新增到一個資源中,可以按照以下示例進行操作。

resource { 'baz': 
   require => [ Package['rpm'], File['testfile'] ], 
}

變數

Puppet 支援多個變數,就像大多數其他程式語言一樣。Puppet 變數用$表示。

$content = 'some content\n' 
file { '/tmp/testing': content => $content } 

如前所述,Puppet 是一種宣告式語言,這意味著它的作用域和賦值規則與命令式語言不同。主要區別在於,不能在單個作用域內更改變數,因為它們依賴於檔案中的順序來確定變數的值。順序在宣告式語言中無關緊要。

$user = root 
file {  
   '/etc/passwd': 
   owner => $user, 
} 

$user = bin 
   file {  
      '/bin': 
      owner => $user, 
      recurse => true, 
   }

變數作用域

變數作用域定義了所有定義的變數是否有效。根據最新的功能,Puppet 目前是動態作用域的,在 Puppet 術語中,這意味著所有定義的變數都在其作用域內進行評估,而不是在其定義的位置進行評估。

$test = 'top' 
class Testclass { 
   exec { "/bin/echo $test": logoutput => true } 
} 

class Secondtestclass { 
   $test = 'other' 
   include myclass 
} 

include Secondtestclass 

限定變數

Puppet 支援在類或定義內部使用限定變數。當用戶希望在其他類中使用相同的變數時,這非常有用,無論這些類是他已定義的還是將要定義的。

class testclass { 
   $test = 'content' 
} 

class secondtestclass { 
   $other = $myclass::test 
} 

在上面的程式碼中,$other 變數的值會評估其內容。

條件語句

條件語句是指使用者希望在滿足定義的條件或所需條件時執行一組語句或程式碼的情況。Puppet 支援兩種型別的條件語句。

選擇器條件只能在定義的資源內使用,以選擇機器的正確值。

語句條件在清單中使用更為廣泛,有助於包含使用者希望在同一清單檔案中包含的其他類。在類中定義一組不同的資源,或做出其他結構性決策。

選擇器

當用戶希望根據事實或其他變數指定與預設值不同的資源屬性和變數時,選擇器非常有用。在 Puppet 中,選擇器索引的工作方式類似於多值三元運算子。選擇器還能夠在清單中定義的無值中定義自定義預設值,並匹配條件。

$owner = $Sysoperenv ? { 
   sunos => 'adm', 
   redhat => 'bin', 
   default => undef, 
}

在 Puppet 0.25.0 的後續版本中,選擇器可以用作正則表示式。

$owner = $Sysoperenv ? { 
   /(Linux|Ubuntu)/ => 'bin', 
   default => undef, 
}

在上面的示例中,如果選擇器$Sysoperenv的值匹配 Linux 或 Ubuntu,則 bin 將是選定的結果,否則使用者將設定為未定義。

語句條件

語句條件是 Puppet 中另一種型別的條件語句,它與 Shell 指令碼中的 switch case 條件非常相似。在此,定義了多組 case 語句,並將給定的輸入值與每個條件進行匹配。

與給定輸入條件匹配的 case 語句將被執行。此 case 語句條件沒有任何返回值。在 Puppet 中,條件語句的一個非常常見的用例是根據底層作業系統執行一組程式碼塊。

case $ Sysoperenv { 
   sunos: { include solaris }  
   redhat: { include redhat }  
   default: { include generic}  
}

Case 語句還可以透過逗號分隔來指定多個條件。

case $Sysoperenv { 
   development,testing: { include development } testing,production: { include production }
   default: { include generic }  
} 

If-Else 語句

Puppet 支援基於條件的操作的概念。為了實現這一點,If/else 語句根據條件的返回值提供分支選項。如下例所示 -

if $Filename { 
   file { '/some/file': ensure => present } 
} else { 
   file { '/some/other/file': ensure => present } 
} 

最新版本的 Puppet 支援變量表達式,其中 if 語句還可以根據表示式的值進行分支。

if $machine == 'production' { 
   include ssl 
} else { 
   include nginx 
}

為了在程式碼中實現更多多樣性並執行復雜的條件操作,Puppet 支援巢狀的 if/else 語句,如下面的程式碼所示。

if $ machine == 'production' { 
   include ssl 
} elsif $ machine == 'testing' { 
   include nginx
} else { 
   include openssl 
} 

虛擬資源

虛擬資源是指除非實現,否則不會發送到客戶端的資源。

以下是 Puppet 中使用虛擬資源的語法。

@user { vipin: ensure => present } 

在上面的示例中,使用者 vipin 是虛擬定義的,要實現定義,可以在集合中使用它。

User <| title == vipin |>

註釋

註釋用於任何程式碼塊中,以對一組程式碼行及其功能進行額外說明。在 Puppet 中,目前支援兩種型別的註釋。

  • Unix shell 風格的註釋。它們可以位於獨立的行上,也可以位於下一行上。
  • 多行 C 風格註釋。

以下是一個 shell 風格註釋的示例。

# this is a comment

以下是一個多行註釋的示例。

/* 
This is a comment 
*/ 

運算子優先順序

Puppet 的運算子優先順序符合大多數系統中的標準優先順序,從最高到最低。

以下是表示式的列表

  • ! = 非
  • / = 乘法和除法
  • - + = 減法、加法
  • << >> = 左移和右移
  • == != = 不等於、等於
  • >= <= > < = 大於等於、小於等於、大於、小於

比較表示式

當用戶希望在滿足給定條件時執行一組語句時,會使用比較表示式。比較表示式包括使用 == 表示式進行相等性測試。

if $environment == 'development' { 
   include openssl 
} else { 
   include ssl 
} 

不相等示例

if $environment != 'development' { 
   $otherenvironment = 'testing' 
} else { 
   $otherenvironment = 'production' 
} 

算術表示式

$one = 1 
$one_thirty = 1.30 
$two = 2.034e-2 $result = ((( $two + 2) / $one_thirty) + 4 * 5.45) - 
   (6 << ($two + 4)) + (0×800 + -9)

布林表示式

可以使用 or、and、& not 來實現布林表示式。

$one = 1 
$two = 2 
$var = ( $one < $two ) and ( $one + 1 == $two ) 

正則表示式

Puppet 使用 =~(匹配)和!~(不匹配)支援正則表示式匹配。

if $website =~ /^www(\d+)\./ { 
   notice('Welcome web server #$1') 
}

與 case 和選擇器類似,正則表示式匹配為每個正則表示式建立有限作用域的變數。

exec { "Test": 
   command => "/bin/echo now we don’t have openssl installed on machine > /tmp/test.txt", 
   unless => "/bin/which php" 
}

類似地,我們可以使用 unless,unless 一直執行命令,除非 unless 下面的命令成功退出。

exec { "Test": 
   command => "/bin/echo now we don’t have openssl installed on machine > /tmp/test.txt", 
   unless => "/bin/which php" 
}

使用模板

當希望擁有一個預定義的結構,該結構將在 Puppet 中的多個模組中使用,並且這些模組將分佈在多臺機器上時,可以使用模板。使用模板的第一步是建立一個使用模板方法呈現模板內容的模板。

file { "/etc/tomcat/sites-available/default.conf": 
   ensure => "present", 
   content => template("tomcat/vhost.erb")  
}

為了強制執行組織和模組化,Puppet 在處理本地檔案時會做出一些假設。Puppet 在 modules 目錄內的 apache/templates 資料夾內查詢 vhost.erb 模板。

定義和觸發服務

在 Puppet 中,它有一個名為 service 的資源,該資源能夠管理在任何特定機器或環境上執行的所有服務的生命週期。服務資源用於確保服務已初始化並啟用。它們還用於服務重啟。

例如,在我們之前設定 apache 虛擬主機的 tomcat 模板中。如果希望確保在虛擬主機更改後重新啟動 apache,則需要使用以下命令為 apache 服務建立服務資源。

service { 'tomcat': 
   ensure => running, 
   enable => true 
}

在定義資源時,需要包含 notify 選項以觸發重啟。

file { "/etc/tomcat/sites-available/default.conf": 
   ensure => "present", 
   content => template("vhost.erb"), 
   notify => Service['tomcat']  
}
廣告