SIer向け Puppetベタープラクティス（ディレクトリ構成編）

サーバの構成管理ツールPuppetでサーバの定義を書いてみて、マニフェストのディレクトリ構成をこうした方がよかったかもと反省をこめて書きます。[以前の構成](http://qiita.com/kijibato/items/b8ab6dc76684c68cb85f)がイマイチだったので、構成を見直しました。
**一般的なやり方と違う部分もあると思いますので、ご参考程度に。**

# 想定ケース
条件が違えば、最適なディレクトリ構成も変わってきますが、次のようなケースでPuppetを使う場合を書いています。ちなみに、modulesはPuppet Forgeは使わない場合です。

- 複数環境（開発、ステージング、プロダクションなど）がある
- 各環境に○○サーバはあるけど、台数構成が違う
- Puppetはエージェント/マスター構成

# ディレクトリ構成の基本ルール
メンテナンスのしやすさなどを考えて、次のようなルールで。

- **① サーバの定義は○○サーバとして書く。1サーバごとに書かない。**
- **② 環境ごとの違いは、上位の階層で分ける。**
- **③ 細かい差は、HieraかFacterでカバー。**
- **④ 各環境用のファイル構成は、できるだけ同じにする。**

できるだけ最小限のルールで、あとはどこに何があるかが直感的にわかるようにするのが目的です。

# 構成例
例です。

## 全体構成
/etc/puppetlabs/code/environmentsの下に、各環境用の定義をディレクトリで分けます。（② 環境ごとの違いは、上位の階層で分ける。）

```
/etc/puppetlabs/code
├── environments
│   ├── production           # プロダクション環境用の定義
│   │   ├── environment.conf
│   │   ├── manifests
│   │   ├── hieradata
│   │   └── modules
│   ├── staging              # ステージング環境用の定義
│   │   ├── environment.conf
│   │   ├── manifests
│   │   ├── hieradata
│   │   └── modules
│   └── development          # 開発環境用の定義
│       ├── environment.conf
│       ├── manifests
│       ├── hieradata
│       └── modules
├── hiera.yaml               # Hiera設定
└── modules                  # 空ディレクトリ(本来、全環境で使う共通モジュール)
```

- environment.conf : 環境ごとのpuppetの設定
- manifests : hieradataのファイルを読み込む定義のみ
- hieradata :  サーバごとのパラメータ定義
- modules : ミドルウェア定義（インストール、設定ファイルなど）

なお、どの環境定義を使うかは、puppet agent実行時のenvironmentオプションで指定します。

```
/opt/puppetlabs/bin/puppet agent -t -v --environment <env>
```

実行毎に指定が面倒なら、各サーバ（ノード）のpuppet.confに設定する方法もあります。

```/etc/puppetlabs/puppet/puppet.conf
[main]
environment = <env>
```

## manifests
manifestsには、hieradataのファイルを読み込むための定義のみ書きます。

```
/etc/puppetlabs/code/environments/production/manifests
└── site.pp
/etc/puppetlabs/code/environments/staging/manifests
└── site.pp
/etc/puppetlabs/code/environments/development/manifests
└── site.pp
```

Hieraを使わない場合、manifestsに各サーバで使うクラスの指定、パラメータ等を定義しますが、Hieraを使った方が便利なので、manifests側はホスト名に対応する、○○サーバの種類と、何番目のサーバかを指定して、hiera_includeするようにしています。myrole、myindexを使うあたりは、一般的ではない気もするので参考程度に。

```puppet:/etc/puppetlabs/code/environments/production/manifests/site.pp
$nodes = {
  production.db.01        => { myrole => 'db',        myindex => '01'},
  production.db.02        => { myrole => 'db',        myindex => '02'},
  production.web.front.01 => { myrole => 'web_front', myindex => '01'},
  production.web.front.02 => { myrole => 'web_front', myindex => '02'},
  production.web.front.03 => { myrole => 'web_front', myindex => '03'},
  production.web.front.04 => { myrole => 'web_front', myindex => '04'},
  production.web.local.01 => { myrole => 'web_local', myindex => '01'},
}

node default {
  $myrole = $nodes[$hostname]['myrole']
  $myindex = $nodes[$hostname]['myindex']
  hiera_include('classes')
}
```

```puppet:/etc/puppetlabs/code/environments/staging/manifests/site.pp
$nodes = {
  staging.db.01        => { myrole => 'db',        myindex => '01'},
  staging.db.02        => { myrole => 'db',        myindex => '02'},
  staging.web.front.01 => { myrole => 'web_front', myindex => '01'},
  staging.web.front.02 => { myrole => 'web_front', myindex => '02'},
  staging.web.local.01 => { myrole => 'web_local', myindex => '01'},
}

node default {
  $myrole = $nodes[$hostname]['myrole']
  $myindex = $nodes[$hostname]['myindex']
  hiera_include('classes')
}
```



```puppet:/etc/puppetlabs/code/environments/development/manifests/site.pp
$nodes = {
  development.db.01        => { myrole => 'db',        myindex => '01'},
  development.web.front.01 => { myrole => 'web_front', myindex => '01'},
}

node default {
  $myrole = $nodes[$hostname]['myrole']
  $myindex = $nodes[$hostname]['myindex']
  hiera_include('classes')
}
```

## hieradata
hieradataでは、サーバごとの利用クラス、パラメータを定義します。Hieraを使うと階層的に指定できます。環境ごとの定義ファイルは次のような構成にします。（① サーバの定義は○○サーバとして書く。1サーバごとに書かない。③ 細かい差は、HieraかFacterでカバー。）

```
/etc/puppetlabs/code/environments/<env>/hieradata
├── common.yaml                # 環境内の共通パラメータ設定
└── nodes
    ├── <○○サーバ>.yaml         # ○○サーバの共通パラメータ設定
    └── <○○サーバ>
        └── <何番目のサーバ>.yaml # ○○サーバの各サーバで異なるパラメータ設定
```

階層は、設定重複を減らす、ファイルの数を抑える、各ファイルの役割のわかりやすさを考えると、３階層ぐらいがよいです。大事なのは、共通的なパラメータは上位階層で設定して、どうしてもサーバごとに変えないといけないようなパラメータを一番下の階層で設定します。**まとめられるものはまとめて、差異があるところははっきりさせるです。**

例です。

フロント向けのWebサーバ、内部向けのWebサーバ、バックエンドのDBサーバがあるイメージです。ちなみに、developmentではフロント向けのWebサーバ、バックエンドのDBサーバは１台ずつしかないですが、productionで01.yamlに書いているパラメータは、developmentの01.yamlに書いた方が環境間の差異がdiffで調べやすいです。（④ 各環境用のファイル構成は、できるだけ同じにする。）できるだけ実際にサービスで使うプロダクション環境と同じファイル構成にした方がよいです。

```
/etc/puppetlabs/code/environments/production/hieradata
├── common.yaml
└── nodes
    ├── db.yaml
    ├── db
    │   ├── 01.yaml
    │   └── 02.yaml
    ├── web_front.yaml
    ├── web_front
    │   ├── 01.yaml
    │   ├── 02.yaml
    │   ├── 03.yaml
    │   └── 04.yaml
    └── web_local.yaml

/etc/puppetlabs/code/environments/staging/hieradata
├── common.yaml
└── nodes
    ├── db.yaml
    ├── db
    │   ├── 01.yaml
    │   └── 02.yaml
    ├── web_front.yaml
    ├── web_front
    │   ├── 01.yaml
    │   └── 02.yaml
    └── web_local.yaml

/etc/puppetlabs/code/environments/development/hieradata
├── common.yaml
└── nodes
    ├── db.yaml
    ├── db
    │   └── 01.yaml
    ├── web_front.yaml
    └── web_front
        └── 01.yaml
```

参考ですが、hiera.yamlのhierarcyを次のような定義にすると、上記のディレクトリ構成に対応できます。

```yaml:/etc/puppetlabs/code/hiera.yaml
---
:backends:
  - yaml
:yaml:
  :datadir: "/etc/puppetlabs/code/environments/%{environment}/hieradata"
:hierarchy:
  - "nodes/%{myrole}/%{myindex}"
  - "nodes/%{myrole}"
#  - "virtual/%{::virtual}"
#  - "osfamily/%{::osfamily}"
  - common
```

## modules
modulesには、ミドルウェア用のクラス定義（どのパッケージをインストール、どの設定ファイルを変えるか）(manifests)や配布する設定ファイル類(templates)やバイナリファイル(files)を置きます。

以前は、modulesを全環境で共通にした方が良いと思っていましたが、次の理由で環境ごとに分けてしまった方がよい気がしています。（manifestsのクラス定義で、環境ごとの条件分岐をしない、templates,filesの下を環境ごとのディレクトリで分けない）

- 環境ごとに設定リリース時期が違う場合がある
- modulesを共通化すると、ステージング、開発環境のための条件分岐や設定ファイルが、結果的に商用のプロダクション環境マニフェストに紛れ込むので、一番大事なプロダクション環境のメンテナンス性が悪くなる
- 上位ディレクトリのみで分けた方が、diffでの環境か差異が調べやすい
- すでに上位で環境ごとに分けているので、下の方で環境ごとに分けると見通し悪い

良い例が思いつかなかったですが、例です。環境ごと同じディレクトリ構成、ファイル名で定義を作ります。（② 環境ごとの違いは、上位の階層で分ける。④ 各環境用のファイル構成は、できるだけ同じにする。）

```
/etc/puppetlabs/code/environments/production/modules
├── apache
│   ├── files
│   ├── manifests
│   │   ├── config.pp
│   │   ├── install.pp
│   │   └── service.pp
│   └── templates
│       ├── front
│       │   └── httpd.conf.erb
│       ├── local
│       │   └── httpd.conf.erb
│       └── readme
└── mariadb
    ├── files
    ├── manifests
    │   ├── config.pp
    │   ├── install.pp
    │   └── service.pp
    └── templates
        └── db
            └── my.conf.erb

/etc/puppetlabs/code/environments/staging/modules
├── apache
│   ├── files
│   ├── manifests
│   │   ├── config.pp
│   │   ├── install.pp
│   │   └── service.pp
│   └── templates
│       ├── front
│       │   └── httpd.conf.erb
│       ├── local
│       │   └── httpd.conf.erb
│       └── readme
└── mariadb
    ├── files
    ├── manifests
    │   ├── config.pp
    │   ├── install.pp
    │   └── service.pp
    └── templates
        └── db
            └── my.conf.erb

/etc/puppetlabs/code/environments/development/modules
├── apache
│   ├── files
│   ├── manifests
│   │   ├── config.pp
│   │   ├── install.pp
│   │   └── service.pp
│   └── templates
│       ├── front
│       │   └── httpd.conf.erb
│       └── readme
└── mariadb
    ├── files
    ├── manifests
    │   ├── config.pp
    │   ├── install.pp
    │   └── service.pp
    └── templates
        └── db
            └── my.conf.erb
```

# Gitでの管理
Puppetのマニフェストは、Gitなどで管理した方がよいです。Gitへの格納単位は、/etc/puppetlabs/code/environments/<env>ごと（つまり環境ごと）が望ましいと思います。

今回の例では、

- /etc/puppetlabs/code/environments/production
- /etc/puppetlabs/code/environments/staging
- /etc/puppetlabs/code/environments/development

ごとに格納します。

ただ、格納方法としては、環境ごとにリポジトリで分けて格納するか、環境ごとにブランチで分けて格納するかは、ケースバイケースかと思います。リポジトリで分けた場合、直感的にわかりやすいけど、他環境の変更を取り込む(cherry-pick)とかやりにくかなと。ブランチで分けた場合は、その逆です。

# バージョン
今回の構成案は、次のバージョンを想定して書いています。

```
[root@puppetserver ~]# rpm -qa | grep  puppet      
puppetlabs-release-pc1-1.0.0-1.el7.noarch
puppet-agent-1.4.1-1.el7.x86_64
puppetserver-2.2.1-1.el7.noarch
```

# 最後に
サーバ台数がもっと少ない場合は、よりシンプルなディレクトリ構成で定義をべた書き。サーバ台数がもっと多い場合は、ロジック（条件分岐、ループ、カスタムfact）を使った方よいです。

あと、ひさしぶりにpuppetの公式サイトみると、見た目が変わっていて、ロゴも変わってました。