Roles and Profiles Pattern in Puppet

一開始寫 Puppet 就是 node definition 直接寫寫寫…後來就會開始把重複的 resource, file 等等拆成 modules…。不過當機器越來越多,發現還是有許多重複的地方,例如有好多台 web server, 但是他們有些又有些許的不同…。

Original Style

先看看傳統的寫法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
node web {
  include users
  include nginx
  include rails
}

node worker {
  include users
  include rails
  include redis
}

node db {
  include users
  include mysql
}

node web-qa {
  include users
  include nginx
  include rails
  include mysql
  include redis
}

四個看起來還好,但是當機器越來越多的時候,就會感到難以維護了。

Roles and Profiles Pattern

All problems in computer science can be solved by another level of indirection.

所有電腦科學領域的問題都可以用抽象化來解決(除了抽象太多層以外),以上的問題我們可以用現在常見的 “Roles and Profiles Pattern” 來做抽象。

Role

Role 很好理解,顧名思義就是「扮演的角色」,以上面的例子我們就有 web, db, worker,web 可能又分成 production 環境和 QA 環境。

這邊舉的例子可能太技術取向了,可以想想平常團隊在溝通時,在稱呼某台機器的時候都是怎麼稱呼的:「那台壓影片的」、「那台 QA 環境」…就很清楚明瞭了。

用這個思路來整理 node 會變成這樣:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
node web {
  include role::web::production
}

node worker {
  include role::worker
}

node db {
  include role::db
}

node web-qa {
  include role::web::qa
}

Role 本身大概會長這樣:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class role { 
  include profile::base
}
 
class role::web inherits role { 
  include profile::nginx
}
 
class role::web::production inherits role::web { 
  include profile::nginx::production
}

class role::web::qa inherits role::web { 
  include profile::nginx::qa
  include profile::db
  include profile::worker
}
 
class role::db inherits role { 
  include profile::mysql
}
 
class role::worker inherits role { 
  include profile::worker
}

Profile

Profile 則是用來抽象化「一組服務、設定」的。看上面 Role 的部份應該有點感覺了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class profile::base {
  include users
}

class profile::web {
  include nginx
  include rails
}

class profile::web::production inherits profile::web {
  ::nginx::file { 'production.conf':
      content => ...,
  }
}

class profile::web::qa inherits profile::web {
  ::nginx::file { 'qa.conf':
      content => ...,
  }
}

class profile::db inherits { 
  include profile::mysql
}

class profile::worker {}
  include rails
  include redis
}

這樣就可以把重複的程式碼減少,同時又保留彈性。

Tips

  • 一個 node 只 include 一個 role。如果這兩個 role 很像,但又有些微不同,那就是一個新 role。
  • 一個 role include 一個或多個 profile,而且 只能 include profile

Further Reading

更多詳細細節、優缺點以及不同的設計方式可以參考以下的幾篇連結:

Comments