CL LAB

HOME > CL LAB > Chef > Knife-ZeroでInfrastructure as Codeを始めよう #getchef

Knife-ZeroでInfrastructure as Codeを始めよう #getchef

 ★ 32

この記事は1年以上前に投稿されました。情報が古い可能性がありますので、ご注意ください。

本稿はChef Advent Calendar 201424日目の記事です。

Chefとは

Chefとは、インフラの情報を集中管理するツールです。An Overview of Chefでは次のように述べられています。

Chef is a powerful automation platform that transforms complex infrastructure into code, bringing your servers and services to life. (略)

Chef is built around simple concepts: achieving desired state, centralized modeling of IT infrastructure, and resource primitives that serve as building blocks. (略)

[和訳] Chefは複雑なインフラをコードに変換し、サーバやサービスを生きた状態とする、強力な自動化プラットフォームです。(略)

Chefは単純な概念で成り立っています: 望ましい状態に到達させる。ITインフラの中央集権的なモデルとなる。リソースの根源的要素を組み立てブロックのように扱う。(略)

インフラの情報を集中管理し、インフラの各要素を細かい部品に分けて再利用できるようにし、各要素をあるべき状態に収束させることがChefの真価です。

Chefで見逃されがちなのが インフラの情報を集中管理 の部分です。ここを見ずに単なる自動セットアップツールとしてChefを用いてもよいけれど、それはもったないでしょう。

Chefのインストール

では、さっそくChefをインストールしてみましょう。今回はChef Serverは必要ありません。Ubuntu 12.04 LTSをワークステーションとして、クライアントパッケージのみをインストールします。
また、管理する対象のリモートノードもUbuntu 12.04 LTSとします。

Chef Development Kitのインストール

ワークステーション用のクライアントパッケージはChef Development Kit、通称Chef DKと呼ばれています。Chefのクライアントプログラムだけでなく、Chefで開発を行うための各種ツールや、Chefを動作させるためのRubyまで同梱しています。そのため、別途Rubyをインストールしたり、RubyGemsでChefをインストールする必要はまったくありません

Chef DKはChef Development Kit | Chef Downloads | Chefからダウンロードできます。OS、Chef DKのバージョン、OSのバージョンを選択し、ダウンロードしましょう。今回は chefdk_0.3.5-1_amd64.deb をダウンロードしました。早速インストールしましょう。

ubuntu@ws:~$ sudo dpkg -i chefdk_0.3.5-1_amd64.deb
以前に未選択のパッケージ chefdk を選択しています。
(データベースを読み込んでいます ... 現在 49102 個のファイルとディレクトリがインストールされています。)
(chefdk_0.3.5-1_amd64.deb から) chefdk を展開しています...
chefdk (0.3.5-1) を設定しています ...
Thank you for installing Chef Development Kit!
ubuntu@ws:~$

Knife-Zeroのインストール

ChefクライアントプログラムはChef DK付属のものではなく、Knife-Zeroを利用します。

ubuntu@ws:~$ chef gem install knife-zero --no-document
Fetching: knife-zero-1.1.0.gem (100%)
WARNING:  You don't have /home/ubuntu/.chefdk/gem/ruby/2.1.0/bin in your PATH,
      gem executables will not run.
Successfully installed knife-zero-1.1.0
1 gem installed
ubuntu@ws:~$

警告はKnife-Zeroには関係ないので無視してかまいません。

SSHキーペアの作成など下準備

Knife-ZeroはSSHを利用してリモートノードの操作を行うので、SSH秘密鍵・公開鍵のペアを作成しておきます。ここではパスフレーズなしの鍵ペアを作成しています。

ubuntu@ws:~$ ssh-keygen -N ''
Generating public/private rsa key pair.
Enter file in which to save the key (/home/ubuntu/.ssh/id_rsa):
Created directory '/home/ubuntu/.ssh'.
Your identification has been saved in /home/ubuntu/.ssh/id_rsa.
Your public key has been saved in /home/ubuntu/.ssh/id_rsa.pub.
The key fingerprint is:
9a:72:67:23:3a:5c:2e:e9:74:03:44:0f:5e:87:0e:67 ubuntu@ws.example.jp
The key's randomart image is:
+--[ RSA 2048]----+
|    o ...        |
|   o.+E.         |
|    o=.          |
|   .  .          |
|    .   S        |
|     ..o         |
|   .o+B +        |
|   .==.= .       |
|   .oo           |
+-----------------+
ubuntu@ws:~$

当然ながら、対象のリモートノードはSSHデーモンが動作している必要があるので起動しておきます。また、先に作成した公開鍵をリモートノードにあらかじめ配置します。

ubuntu@node:~$ mkdir .ssh
ubuntu@node:~$ cat > .ssh/authorized_keys
ssh-rsa (省略) ubuntu@ws.example.jp
ubuntu@node:~$

加えて簡便のために、パスワードなしでsudoを実行してroot権限を得られるように、ログインアカウントに設定しておきましょう。

作業環境を作る

Knife-Zeroを実行する環境とするディレクトリツリーを作り、gitの管理下に置いておきます。このディレクトリツリーはchef-repoと呼ばれ、インフラをコード化したものとなります。

ubuntu@ws:~$ chef generate repo chef-repo
Compiling Cookbooks...
Recipe: code_generator::repo
  * directory[/home/ubuntu/chef-repo] action create
    - create new directory /home/ubuntu/chef-repo
    :
    (diff output suppressed by config)
ubuntu@ws:~$

ubuntu@ws:~$ cd chef-repo
ubuntu@ws:~/chef-repo$ tree
.
├── LICENSE
├── README.md
├── Rakefile
├── certificates
│   └── README.md
├── chefignore
├── config
│   └── rake.rb
├── cookbooks
│   └── README.md
├── data_bags
│   └── README.md
├── environments
│   └── README.md
└── roles
    └── README.md

6 directories, 10 files
ubuntu@ws:~/chef-repo$

ubuntu@ws:~/chef-repo$ git init
Initialized empty Git repository in /home/ubuntu/chef-repo/.git/
ubuntu@ws:~/chef-repo$

これからKnife-Zeroで管理すると示す設定ファイルを準備しておきます。

ubuntu@ws:~/chef-repo$ mkdir .chef
ubuntu@ws:~/chef-repo$ echo 'local_mode true' > .chef/knife.rb
ubuntu@ws:~/chef-repo$ cat .chef/knife.rb
local_mode true
ubuntu@ws:~/chef-repo$

ノードへChefクライアントのインストール

リモートノードへChefクライアントをインストールし、Chefの管理対象とします。
knife zero bootstrapがコマンド、node.example.jpがノードのホスト名、-x ubuntuがログインユーザ名の指定、--sudoがsudoコマンドにてroot権限を取得するという意味となります。

ubuntu@ws:~/chef-repo$ knife zero bootstrap node.example.jp -x ubuntu --sudo
Connecting to node.example.jp
node.example.jp Installing Chef Client...
node.example.jp --2014-12-19 12:06:20--  https://www.opscode.com/chef/install.sh
node.example.jp www.opscode.com (www.opscode.com) をDNSに問いあわせています... 184.106.28.90
node.example.jp www.opscode.com (www.opscode.com)|184.106.28.90|:443 に接続しています... 接続しました。
node.example.jp HTTP による接続要求を送信しました、応答を待っています... 200 OK
node.example.jp 長さ: 18285 (18K) [application/x-sh]
node.example.jp `STDOUT' に保存中
node.example.jp
100%[======================================>] 18,285      --.-K/s   時間 0.002s
node.example.jp
node.example.jp 2014-12-19 12:06:21 (7.52 MB/s) - stdout へ出力完了 [18285/18285]
node.example.jp
node.example.jp Downloading Chef 11 for ubuntu...
node.example.jp downloading https://www.opscode.com/chef/metadata?v=11&prerelease=false&nightlies=false&p=ubuntu&pv=12.04&m=x86_64
node.example.jp   to file /tmp/install.sh.1100/metadata.txt
node.example.jp trying wget...
node.example.jp url https://opscode-omnibus-packages.s3.amazonaws.com/ubuntu/12.04/x86_64/chef_11.16.4-1_amd64.deb
node.example.jp md5 2ffff5b4d80e4dcffc917f8eb2003a31
node.example.jp sha256  28b08975e7e33ac46c888616ec7fa232a0c624aeeda81e58a6047d2c6b62edfb
node.example.jp downloaded metadata file looks valid...
node.example.jp downloading https://opscode-omnibus-packages.s3.amazonaws.com/ubuntu/12.04/x86_64/chef_11.16.4-1_amd64.deb
node.example.jp   to file /tmp/install.sh.1100/chef_11.16.4-1_amd64.deb
node.example.jp trying wget...
node.example.jp Comparing checksum with sha256sum...
node.example.jp Installing Chef 11
node.example.jp installing with dpkg...
node.example.jp 以前に未選択のパッケージ chef を選択しています。
(データベースを読み込んでいます ... 現在 49102 個のファイルとディレクトリがインストールされています。)
node.example.jp (.../chef_11.16.4-1_amd64.deb から) chef を展開しています...
node.example.jp chef (11.16.4-1) を設定しています ...
node.example.jp Thank you for installing Chef!
node.example.jp Starting first Chef Client run...
    :
node.example.jp Starting Chef Client, version 11.16.4
node.example.jp Creating a new client identity for node.example.jp using the validator key.
node.example.jp resolving cookbooks for run list: []
node.example.jp Synchronizing Cookbooks:
node.example.jp Compiling Cookbooks...
node.example.jp [2014-12-19T12:06:48+09:00] WARN: Node node.example.jp has an empty run list.
node.example.jp Converging 0 resources
node.example.jp
node.example.jp Running handlers:
node.example.jp Running handlers complete
node.example.jp Chef Client finished, 0/0 resources updated in 1.443860973 seconds
ubuntu@ws:~/chef-repo$

自動的にChefクライアントをダウンロードし、ノードにインストールされました。
そして、ノードをChef管理対象に置きました。Chef Serverではなく、ワークステーションのChef-Repoディレクトリ内に管理情報が格納される形となっています。

ubuntu@ws:~/chef-repo$ cat clients/node.example.jp.json | cat
{
  "name": "node.example.jp",
  "public_key": "-----BEGIN PUBLIC KEY-----\n(省略)\n-----END PUBLIC KEY-----\n"
}
ubuntu@ws:~/chef-repo$
ubuntu@ws:~/chef-repo$ cat nodes/node.example.jp.json
{
  "name": "node.example.jp",
  "normal": {
    "tags": [

    ]
  },
  "automatic": {
    "network": {
      "interfaces": {
        "lo": {
          "mtu": "16436",
          "flags": [
            "LOOPBACK",
            "UP",
            "LOWER_UP"
          ],
          "encapsulation": "Loopback",
          "addresses": {
            "127.0.0.1": {
              "family": "inet",
              "prefixlen": "8",
              "netmask": "255.0.0.0",
              "scope": "Node"
            },
            "::1": {
              "family": "inet6",
              "prefixlen": "128",
              "scope": "Node"
            }
          },
          "state": "unknown"
        },
        "eth0": {
          "type": "eth",
          "number": "0",
          "mtu": "1500",
          "flags": [
            "BROADCAST",
            "MULTICAST",
            "UP",
            "LOWER_UP"
          ],
          "encapsulation": "Ethernet",
          "addresses": {
            "52:54:00:8E:F7:2A": {
              "family": "lladdr"
            },
            "192.168.122.67": {
              "family": "inet",
              "prefixlen": "24",
              "netmask": "255.255.255.0",
              "broadcast": "192.168.122.255",
              "scope": "Global"
            },
            "fe80::5054:ff:fe8e:f72a": {
              "family": "inet6",
              "prefixlen": "64",
              "scope": "Link"
            }
          },
          "state": "up",
          "arp": {
            "192.168.122.209": "52:54:00:62:c3:07"
          },
    :
    "recipes": [

    ],
    "roles": [

    ]
  }
}
ubuntu@ws:~/chef-repo$

ついでにワークステーション自体も登録してしまいましょう。

ubuntu@ws:~/chef-repo$ knife zero bootstrap ws.example.jp -x ubuntu --sudo
Connecting to ws.example.jp
ws.example.jp Starting first Chef Client run...
    :
ws.example.jp Starting Chef Client, version 11.18.0.rc.1
ws.example.jp Creating a new client identity for ws.example.jp using the validator key.
ws.example.jp resolving cookbooks for run list: []
ws.example.jp Synchronizing Cookbooks:
ws.example.jp Compiling Cookbooks...
ws.example.jp [2014-12-19T12:33:12+09:00] WARN: Node ws.example.jp has an empty run list.
ws.example.jp Converging 0 resources
ws.example.jp
ws.example.jp Running handlers:
ws.example.jp Running handlers complete
ws.example.jp Chef Client finished, 0/0 resources updated in 1.627046827 seconds
ubuntu@ws:~/chef-repo$
ubuntu@ws:~/chef-repo$ ls -l clients
合計 8
-rw-rw-r-- 1 ubuntu ubuntu 511 1219 12:06 node.example.jp.json
-rw-rw-r-- 1 ubuntu ubuntu 509 1219 12:33 ws.example.jp.json
ubuntu@ws:~/chef-repo$ ls -l nodes
合計 64
-rw-rw-r-- 1 ubuntu ubuntu 31169 1219 12:23 node.example.jp.json
-rw-rw-r-- 1 ubuntu ubuntu 31157 1219 12:33 ws.example.jp.json
ubuntu@ws:~/chef-repo$

以降、Knife-Zeroによって、ノードを管理していきます。

knifeコマンドでノードを管理する

knifeコマンドで登録した情報の取得を行ってみます。knife searchを用います。次はノードのIPアドレスを取得する例です。

ubuntu@ws:~/chef-repo$ knife search 'name:*' --attribute ipaddress
2 items found

node.example.jp
:
  ipaddress
: 192.168.122.67

ws.example.jp
:
  ipaddress
: 192.168.122.209

ubuntu@ws:~/chef-repo$

これらはChef-Repoに格納されらデータから表示しています。

引数を操作することで、個々のノードの情報も表示ができます。

ubuntu@ws:~/chef-repo$ knife search 'name:node*' --attribute fqdn
1 items found

node.example.jp
:
  fqdn
: node.example.jp

ubuntu@ws:~/chef-repo$ knife search 'name:ws*' --attribute fqdn
1 items found

ws.example.jp
:
  fqdn
: ws.example.jp

ubuntu@ws:~/chef-repo$

knife sshで各ノードに対してリモートからコマンドを実行できます。

ubuntu@ws:~/chef-repo$ knife ssh 'name:*' 'uname -a'
node.example.jp Linux node.example.jp 3.2.0-74-generic #109-Ubuntu SMP Tue Dec 9 16:45:49 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
ws.example.jp   Linux ws.example.jp 3.2.0-74-generic #109-Ubuntu SMP Tue Dec 9 16:45:49 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
ubuntu@ws:~/chef-repo$

クックブックを作る

インフラをコード化してあるべき状態を記述したChefのファイルをクックブックと呼びます。クックブックはRubyを知らなくても書けます。クックブックがRubyの内部DSLというのは一旦忘れましょう。知るべきことはクックブックの書き方だけでいいのです。

パッケージを1つインストールする

ntpパッケージをインストールするクックブックを作ってみましょう。
chef generate cookbookコマンドを使うと、クックブックの雛形を用意してくれます。クックブックの名前は「ntp」とします。

ubuntu@ws:~/chef-repo$ chef generate cookbook cookbooks/ntp
Compiling Cookbooks...
Recipe: code_generator::cookbook
  * directory[/home/ubuntu/chef-repo/cookbooks/ntp] action create
    - create new directory /home/ubuntu/chef-repo/cookbooks/ntp
    :
    (diff output suppressed by config)
ubuntu@ws:~/chef-repo$
ubuntu@ws:~/chef-repo$ tree cookbooks/ntp
cookbooks/ntp
├── Berksfile
├── README.md
├── chefignore
├── metadata.rb
└── recipes
    └── default.rb

1 directory, 5 files
ubuntu@ws:~/chef-repo$

cookbooks/ntp/recipes/default.rbは「ntpクックブック」の「defaultレシピ」と言います。通常、クックブックとはレシピ以外を含めたさまざまなデータの集合体を表すものですが、おおざっぱに同一視してしまってもだいたい通じます。
では編集してntpパッケージをインストールするようにしましょう。

#
# Cookbook Name:: ntp
# Recipe:: default
#
# Copyright (c) 2014 The Authors, All Rights Reserved.

package 'ntp'

これだけです。元々あるコメントを除けば1行付け足しただけです。Ruby要素はどこにもありません。

このレシピを適用したいノードに登録します。

ubuntu@ws:~/chef-repo$ knife node run_list add node.example.jp 'recipe[ntp]'
node.example.jp:
  run_list: recipe[ntp]
ubuntu@ws:~/chef-repo$ knife node run_list add ws.example.jp 'recipe[ntp]'
ws.example.jp:
  run_list: recipe[ntp]
ubuntu@ws:~/chef-repo$

ノードでChefクライアントを実行して実際に適用する前に-Wオプションでwhy-run (dry-runのようなもの)してみます。

ubuntu@ws:~/chef-repo$ knife zero chef_client 'name:*' -x ubuntu --sudo -W
node.example.jp [2014-12-22T13:09:25+09:00] WARN:
    :
node.example.jp Starting Chef Client, version 11.16.4
node.example.jp resolving cookbooks for run list: ["ntp"]
node.example.jp Synchronizing Cookbooks:
node.example.jp   - ntp
node.example.jp Compiling Cookbooks...
node.example.jp Converging 1 resources
node.example.jp Recipe: ntp::default
node.example.jp   * package[ntp] action install
node.example.jp     - Would install version 1:4.2.6.p3+dfsg-1ubuntu3.1 of package ntp
node.example.jp [2014-12-22T13:09:26+09:00] WARN: In whyrun mode, so NOT performing node save.
node.example.jp
node.example.jp Running handlers:
node.example.jp Running handlers complete
node.example.jp Chef Client finished, 1/1 resources would have been updated
ws.example.jp   [2014-12-22T13:09:27+09:00] WARN:
    :
ws.example.jp   Starting Chef Client, version 11.18.0.rc.1
ws.example.jp   resolving cookbooks for run list: ["ntp"]
ws.example.jp   Synchronizing Cookbooks:
ws.example.jp     - ntp
ws.example.jp   Compiling Cookbooks...
ws.example.jp   Converging 1 resources
ws.example.jp   Recipe: ntp::default
ws.example.jp     * package[ntp] action install
ws.example.jp       - Would install version 1:4.2.6.p3+dfsg-1ubuntu3.1 of package ntp
ws.example.jp   [2014-12-22T13:09:28+09:00] WARN: In whyrun mode, so NOT performing node save.
ws.example.jp  
ws.example.jp   Running handlers:
ws.example.jp   Running handlers complete
ws.example.jp   Chef Client finished, 1/1 resources would have been updated
ubuntu@ws:~/chef-repo$

ntpパッケージがインストールされることがわかります。では-Wオプションを取り除いて実際にレシピを適用し、ノードをあるべき状態に収束します。

ubuntu@ws:~/chef-repo$ knife zero chef_client 'name:*' -x ubuntu --sudo
node.example.jp [2014-12-22T13:14:00+09:00] WARN:
    :
node.example.jp Starting Chef Client, version 11.16.4
node.example.jp resolving cookbooks for run list: ["ntp"]
node.example.jp Synchronizing Cookbooks:
node.example.jp   - ntp
node.example.jp Compiling Cookbooks...
node.example.jp Converging 1 resources
node.example.jp Recipe: ntp::default
node.example.jp   * package[ntp] action install
node.example.jp     - install version 1:4.2.6.p3+dfsg-1ubuntu3.1 of package ntp
node.example.jp
node.example.jp Running handlers:
node.example.jp Running handlers complete
node.example.jp Chef Client finished, 1/1 resources updated in 9.409252235 seconds
ws.example.jp   [2014-12-22T13:14:11+09:00] WARN:
    :
ws.example.jp   Starting Chef Client, version 11.18.0.rc.1
ws.example.jp   resolving cookbooks for run list: ["ntp"]
ws.example.jp   Synchronizing Cookbooks:
ws.example.jp     - ntp
ws.example.jp   Compiling Cookbooks...
ws.example.jp   Converging 1 resources
ws.example.jp   Recipe: ntp::default
ws.example.jp     * package[ntp] action install
ws.example.jp       - install version 1:4.2.6.p3+dfsg-1ubuntu3.1 of package ntp
ws.example.jp  
ws.example.jp   Running handlers:
ws.example.jp   Running handlers complete
ws.example.jp   Chef Client finished, 1/1 resources updated in 10.834032539 seconds
ubuntu@ws:~/chef-repo$

レシピの適用が成功し、1/1 resources updatedで表されている通り、ノードの1リソース中1つのリソースに変更があったことを示しています。べき等性の確認のためもう1回適用してみます。

ubuntu@ws:~/chef-repo$ knife zero chef_client 'name:*' -x ubuntu --sudo
node.example.jp [2014-12-22T13:18:52+09:00] WARN:
    :
node.example.jp Starting Chef Client, version 11.16.4
node.example.jp resolving cookbooks for run list: ["ntp"]
node.example.jp Synchronizing Cookbooks:
node.example.jp   - ntp
node.example.jp Compiling Cookbooks...
node.example.jp Converging 1 resources
node.example.jp Recipe: ntp::default
node.example.jp   * package[ntp] action install (up to date)
node.example.jp
node.example.jp Running handlers:
node.example.jp Running handlers complete
node.example.jp Chef Client finished, 0/1 resources updated in 0.991954497 seconds
ws.example.jp   [2014-12-22T13:18:55+09:00] WARN:
    :
ws.example.jp   Starting Chef Client, version 11.18.0.rc.1
ws.example.jp   resolving cookbooks for run list: ["ntp"]
ws.example.jp   Synchronizing Cookbooks:
ws.example.jp     - ntp
ws.example.jp   Compiling Cookbooks...
ws.example.jp   Converging 1 resources
ws.example.jp   Recipe: ntp::default
ws.example.jp     * package[ntp] action install (up to date)
ws.example.jp  
ws.example.jp   Running handlers:
ws.example.jp   Running handlers complete
ws.example.jp   Chef Client finished, 0/1 resources updated in 1.050684551 seconds
ubuntu@ws:~/chef-repo$

0/1 resources updatedで表されている通り、ノードの1リソース中どのリソースにも変更がなかったことを示しています。

さらにべき等性の確認のため、パッケージをアンインストールしてもう1回実行してみます。

ubuntu@ws:~/chef-repo$ knife ssh 'name:*' 'sudo apt-get purge -y ntp' -x ubuntu
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています                
状態情報を読み取っています... 完了
ws.example.jp   以下のパッケージが自動でインストールされましたが、もう必要とされていません:
ws.example.jp     libopts25 libcap2
ws.example.jp   これらを削除するには 'apt-get autoremove' を利用してください。
ws.example.jp   以下のパッケージは「削除」されます:
ws.example.jp     ntp*
ws.example.jp   アップグレード: 0 個、新規インストール: 0 個、削除: 1 個、保留: 1 個。
ws.example.jp   この操作後に 1,511 kB のディスク容量が解放されます。
(データベースを読み込んでいます ... 現在 100642 個のファイルとディレクトリがインストールされています。)
ws.example.jp   ntp を削除しています ...
ws.example.jp    * Stopping NTP server ntpd                              [ OK ]
ws.example.jp   ntp の設定ファイルを削除しています ...
ws.example.jp   man-db のトリガを処理しています ...
ws.example.jp   ureadahead のトリガを処理しています ...
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています                
状態情報を読み取っています... 完了
node.example.jp 以下のパッケージが自動でインストールされましたが、もう必要とされていません:
node.example.jp   libopts25 libcap2
node.example.jp これらを削除するには 'apt-get autoremove' を利用してください。
node.example.jp 以下のパッケージは「削除」されます:
node.example.jp   ntp*
node.example.jp アップグレード: 0 個、新規インストール: 0 個、削除: 1 個、保留: 1 個。
node.example.jp この操作後に 1,511 kB のディスク容量が解放されます。
(データベースを読み込んでいます ... 現在 61224 個のファイルとディレクトリがインストールされています。)
node.example.jp ntp を削除しています ...
node.example.jp  * Stopping NTP server ntpd                              [ OK ]
node.example.jp ntp の設定ファイルを削除しています ...
node.example.jp man-db のトリガを処理しています ...
node.example.jp ureadahead のトリガを処理しています ...
ubuntu@ws:~/chef-repo$
ubuntu@ws:~/chef-repo$ knife zero chef_client 'name:*' -x ubuntu --sudo
node.example.jp [2014-12-22T13:25:47+09:00] WARN:
    :
node.example.jp Starting Chef Client, version 11.16.4
node.example.jp resolving cookbooks for run list: ["ntp"]
node.example.jp Synchronizing Cookbooks:
node.example.jp   - ntp
node.example.jp Compiling Cookbooks...
node.example.jp Converging 1 resources
node.example.jp Recipe: ntp::default
node.example.jp   * package[ntp] action install
node.example.jp     - install version 1:4.2.6.p3+dfsg-1ubuntu3.1 of package ntp
node.example.jp
node.example.jp Running handlers:
node.example.jp Running handlers complete
node.example.jp Chef Client finished, 1/1 resources updated in 5.015853954 seconds
ws.example.jp   [2014-12-22T13:25:54+09:00] WARN:
    :
ws.example.jp   Starting Chef Client, version 11.18.0.rc.1
ws.example.jp   resolving cookbooks for run list: ["ntp"]
ws.example.jp   Synchronizing Cookbooks:
ws.example.jp     - ntp
ws.example.jp   Compiling Cookbooks...
ws.example.jp   Converging 1 resources
ws.example.jp   Recipe: ntp::default
ws.example.jp     * package[ntp] action install
ws.example.jp       - install version 1:4.2.6.p3+dfsg-1ubuntu3.1 of package ntp
ws.example.jp  
ws.example.jp   Running handlers:
ws.example.jp   Running handlers complete
ws.example.jp   Chef Client finished, 1/1 resources updated in 5.624764122 seconds
ubuntu@ws:~/chef-repo$

1/1 resources updatedで表されている通り、再びレシピの適用に成功してリソースの更新が行われました。

なお、Chef-Repoディレクトリのnodes/ディレクトリの情報は、Chefクライアントの実行などで更新が行われています。

ubuntu@ws:~/chef-repo$ git diff
diff --git a/nodes/node.example.jp.json b/nodes/node.example.jp.json
index d90e315..03bea39 100644
--- a/nodes/node.example.jp.json
+++ b/nodes/node.example.jp.json
    :
@@ -1207,11 +1208,11 @@
     },
     "current_user": "ubuntu",
     "root_group": "root",
-    "ohai_time": 1418959381.048563,
-    "uptime_seconds": 2566,
-    "uptime": "42 minutes 46 seconds",
-    "idletime_seconds": 2528,
-    "idletime": "42 minutes 08 seconds",
+    "ohai_time": 1419222348.6213446,
+    "uptime_seconds": 5229,
+    "uptime": "1 hours 27 minutes 09 seconds",
+    "idletime_seconds": 5154,
+    "idletime": "1 hours 25 minutes 54 seconds",
     "block_device": {
       "ram0": {
         "size": "131072",
@@ -1316,10 +1317,14 @@
       }
     },
     "recipes": [
-
+      "ntp",
+      "ntp::default"
     ],
     "roles": [
 
     ]
-  }
+  },
+  "run_list": [
+    "recipe[ntp]"
+  ]
 }
diff --git a/nodes/ws.example.jp.json b/nodes/ws.example.jp.json
index 09b10d6..099b1af 100644
--- a/nodes/ws.example.jp.json
+++ b/nodes/ws.example.jp.json
    :
@@ -1207,11 +1208,11 @@
     },
     "current_user": "ubuntu",
     "root_group": "root",
-    "ohai_time": 1418959992.8692613,
-    "uptime_seconds": 3175,
-    "uptime": "52 minutes 55 seconds",
-    "idletime_seconds": 3127,
-    "idletime": "52 minutes 07 seconds",
+    "ohai_time": 1419222354.9337227,
+    "uptime_seconds": 5235,
+    "uptime": "1 hours 27 minutes 15 seconds",
+    "idletime_seconds": 5117,
+    "idletime": "1 hours 25 minutes 17 seconds",
     "block_device": {
       "ram0": {
         "size": "131072",
@@ -1316,10 +1317,14 @@
       }
     },
     "recipes": [
-
+      "ntp",
+      "ntp::default"
     ],
     "roles": [
 
     ]
-  }
+  },
+  "run_list": [
+    "recipe[ntp]"
+  ]
 }
ubuntu@ws:~/chef-repo$

パッケージを複数インストールする

nginxパッケージとredis-serverパッケージをインストールするクックブックを作ってみましょう。

ubuntu@ws:~/chef-repo$ chef generate cookbook cookbooks/multi_pkgs
Compiling Cookbooks...
Recipe: code_generator::cookbook
  * directory[/home/ubuntu/chef-repo/cookbooks/multi_pkgs] action create
    - create new directory /home/ubuntu/chef-repo/cookbooks/multi_pkgs
    :
    (diff output suppressed by config)
ubuntu@ws:~/chef-repo$
ubuntu@ws:~/chef-repo$ vi cookbooks/multi_pkgs/recipes/default.rb
#
# Cookbook Name:: multi_pkgs
# Recipe:: default
#
# Copyright (c) 2014 The Authors, All Rights Reserved.

package 'nginx'
package 'redis-server'
ubuntu@ws:~/chef-repo$
ubuntu@ws:~/chef-repo$ knife node run_list add node.example.jp 'recipe[multi_pkgs]'
node.example.jp:
  run_list:
    recipe[ntp]
    recipe[multi_pkgs]
ubuntu@ws:~/chef-repo$ knife node run_list add ws.example.jp 'recipe[multi_pkgs]'
ws.example.jp:
  run_list:
    recipe[ntp]
    recipe[multi_pkgs]
ubuntu@ws:~/chef-repo$
ubuntu@ws:~/chef-repo$ knife zero chef_client 'name:*' -x ubuntu --sudo
node.example.jp [2014-12-22T16:23:51+09:00] WARN:
    :
node.example.jp Starting Chef Client, version 11.16.4
node.example.jp resolving cookbooks for run list: ["ntp", "multi_pkgs"]
node.example.jp Synchronizing Cookbooks:
node.example.jp   - ntp
node.example.jp   - multi_pkgs
node.example.jp Compiling Cookbooks...
node.example.jp Converging 3 resources
node.example.jp Recipe: ntp::default
node.example.jp   * package[ntp] action install (up to date)
node.example.jp Recipe: multi_pkgs::default
node.example.jp   * package[nginx] action install
node.example.jp     - install version 1.1.19-1ubuntu0.6 of package nginx
node.example.jp   * package[redis-server] action install
node.example.jp     - install version 2:2.2.12-1build1 of package redis-server
node.example.jp
node.example.jp Running handlers:
node.example.jp Running handlers complete
node.example.jp Chef Client finished, 2/3 resources updated in 14.823459658 seconds
ws.example.jp   [2014-12-22T16:24:07+09:00] WARN:
    :
ws.example.jp   Starting Chef Client, version 11.18.0.rc.1
ws.example.jp   resolving cookbooks for run list: ["ntp", "multi_pkgs"]
ws.example.jp   Synchronizing Cookbooks:
ws.example.jp     - ntp
ws.example.jp     - multi_pkgs
ws.example.jp   Compiling Cookbooks...
ws.example.jp   Converging 3 resources
ws.example.jp   Recipe: ntp::default
ws.example.jp     * package[ntp] action install (up to date)
ws.example.jp   Recipe: multi_pkgs::default
ws.example.jp     * package[nginx] action install
ws.example.jp       - install version 1.1.19-1ubuntu0.6 of package nginx
ws.example.jp     * package[redis-server] action install
ws.example.jp       - install version 2:2.2.12-1build1 of package redis-server
ws.example.jp  
ws.example.jp   Running handlers:
ws.example.jp   Running handlers complete
ws.example.jp   Chef Client finished, 2/3 resources updated in 15.721211671 seconds
ubuntu@ws:~/chef-repo$

インストールできました。ここで一旦アンインストールしておきます。

ubuntu@ws:~/chef-repo$ knife ssh 'name:*' 'sudo apt-get purge -y nginx redis-server' -x ubuntu
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています                
状態情報を読み取っています... 完了
ws.example.jp   以下のパッケージが自動でインストールされましたが、もう必要とされていません:
ws.example.jp     libjpeg-turbo8 libjpeg8 libxslt1.1 nginx-full nginx-common libgd2-noxpm
ws.example.jp   これらを削除するには 'apt-get autoremove' を利用してください。
ws.example.jp   以下のパッケージは「削除」されます:
ws.example.jp     nginx* redis-server*
ws.example.jp   アップグレード: 0 個、新規インストール: 0 個、削除: 2 個、保留: 1 個。
ws.example.jp   この操作後に 610 kB のディスク容量が解放されます。
(データベースを読み込んでいます ... 現在 100734 個のファイルとディレクトリがインストールされています。)
ws.example.jp   nginx を削除しています ...
ws.example.jp   redis-server を削除しています ...
ws.example.jp   Stopping redis-server: redis-server.
ws.example.jp   redis-server の設定ファイルを削除しています ...
ws.example.jp   dpkg: 警告: redis-server の削除中、ディレクトリ '/var/log/redis' が空でないため削除できませんでした。
ws.example.jp   man-db のトリガを処理しています ...
ws.example.jp   ureadahead のトリガを処理しています ...
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています                
状態情報を読み取っています... 完了
node.example.jp 以下のパッケージが自動でインストールされましたが、もう必要とされていません:
node.example.jp   libjpeg-turbo8 libjpeg8 libxslt1.1 nginx-full nginx-common libgd2-noxpm
node.example.jp これらを削除するには 'apt-get autoremove' を利用してください。
node.example.jp 以下のパッケージは「削除」されます:
node.example.jp   nginx* redis-server*
node.example.jp アップグレード: 0 個、新規インストール: 0 個、削除: 2 個、保留: 1 個。
node.example.jp この操作後に 610 kB のディスク容量が解放されます。
(データベースを読み込んでいます ... 現在 61316 個のファイルとディレクトリがインストールされています。)
node.example.jp nginx を削除しています ...
node.example.jp redis-server を削除しています ...
node.example.jp Stopping redis-server: redis-server.
node.example.jp redis-server の設定ファイルを削除しています ...
node.example.jp dpkg: 警告: redis-server の削除中、ディレクトリ '/var/log/redis' が空でないため削除できませんでした。
node.example.jp man-db のトリガを処理しています ...
node.example.jp ureadahead のトリガを処理しています ...
ubuntu@ws:~/chef-repo$

ループを用いてレシピを書き直してみます。

ubuntu@ws:~/chef-repo$ vi cookbooks/multi_pkgs/recipes/default.rb
#
# Cookbook Name:: multi_pkgs
# Recipe:: default
#
# Copyright (c) 2014 The Authors, All Rights Reserved.

%w{ nginx redis-server }.each do |pkg|
  package pkg
end
ubuntu@ws:~/chef-repo$ knife zero chef_client 'name:*' -x ubuntu --sudo
node.example.jp [2014-12-22T16:28:00+09:00] WARN:
    :
node.example.jp Starting Chef Client, version 11.16.4
node.example.jp resolving cookbooks for run list: ["ntp", "multi_pkgs"]
node.example.jp Synchronizing Cookbooks:
node.example.jp   - ntp
node.example.jp   - multi_pkgs
node.example.jp Compiling Cookbooks...
node.example.jp Converging 3 resources
node.example.jp Recipe: ntp::default
node.example.jp   * package[ntp] action install (up to date)
node.example.jp Recipe: multi_pkgs::default
node.example.jp   * package[nginx] action install
node.example.jp     - install version 1.1.19-1ubuntu0.6 of package nginx
node.example.jp   * package[redis-server] action install
node.example.jp     - install version 2:2.2.12-1build1 of package redis-server
node.example.jp
node.example.jp Running handlers:
node.example.jp Running handlers complete
node.example.jp Chef Client finished, 2/3 resources updated in 6.903009063 seconds
ws.example.jp   [2014-12-22T16:28:08+09:00] WARN:
    :
ws.example.jp   Starting Chef Client, version 11.18.0.rc.1
ws.example.jp   resolving cookbooks for run list: ["ntp", "multi_pkgs"]
ws.example.jp   Synchronizing Cookbooks:
ws.example.jp     - ntp
ws.example.jp     - multi_pkgs
ws.example.jp   Compiling Cookbooks...
ws.example.jp   Converging 3 resources
ws.example.jp   Recipe: ntp::default
ws.example.jp     * package[ntp] action install (up to date)
ws.example.jp   Recipe: multi_pkgs::default
ws.example.jp     * package[nginx] action install
ws.example.jp       - install version 1.1.19-1ubuntu0.6 of package nginx
ws.example.jp     * package[redis-server] action install
ws.example.jp       - install version 2:2.2.12-1build1 of package redis-server
ws.example.jp  
ws.example.jp   Running handlers:
ws.example.jp   Running handlers complete
ws.example.jp   Chef Client finished, 2/3 resources updated in 7.603140636 seconds
ubuntu@ws:~/chef-repo$

packageを数並べた場合と同じようにインストールできました。resourcesの数が変わっていないことに注目してください。つまり、同じ結果を得るための書き方はさまざまにできるということです。

テンプレートを用いる

ChefはエンジンにERBを用いたテンプレートが利用できます。

変数から得た値(Hello, World!)をテンプレート(tmpl.hello.erb)中に埋め込み、ファイル(/tmp/hello.txt)として出力するクックブックを作成します。

雛形を作成します。

まずはデータの集合体であるクックブックです。tmplクックブックと命名しています。

ubuntu@ws:~/chef-repo$ chef generate cookbook cookbooks/tmpl
Compiling Cookbooks...
Recipe: code_generator::cookbook
  * directory[/home/ubuntu/chef-repo/cookbooks/tmpl] action create
    - create new directory /home/ubuntu/chef-repo/cookbooks/tmpl
    :
    (diff output suppressed by config)
ubuntu@ws:~/chef-repo$

変数であるアトリビュートファイルです。デフォルト値を入れるので、defaultアトリビュートと命名しています。

ubuntu@ws:~/chef-repo$ chef generate attribute cookbooks/tmpl default
Compiling Cookbooks...
Recipe: code_generator::attribute
  * directory[cookbooks/tmpl/attributes] action create
    - create new directory cookbooks/tmpl/attributes
  * template[cookbooks/tmpl/attributes/default.rb] action create
    - create new file cookbooks/tmpl/attributes/default.rb
    - update content in file cookbooks/tmpl/attributes/default.rb from none to e3b0c4
    (diff output suppressed by config)
ubuntu@ws:~/chef-repo$

テンプレートファイルです。前述の通りtmpl.hello.erbファイルと命名しています。

ubuntu@ws:~/chef-repo$ chef generate template cookbooks/tmpl tmpl.hello.erb
Compiling Cookbooks...
Recipe: code_generator::template
  * directory[cookbooks/tmpl/templates/default] action create
    - create new directory cookbooks/tmpl/templates/default
  * template[cookbooks/tmpl/templates/default/tmpl.hello.erb] action create
    - create new file cookbooks/tmpl/templates/default/tmpl.hello.erb
    - update content in file cookbooks/tmpl/templates/default/tmpl.hello.erb from none to e3b0c4
    (diff output suppressed by config)
ubuntu@ws:~/chef-repo$

tmplクックブックの雛形が準備できました。

ubuntu@ws:~/chef-repo$ tree cookbooks/tmpl/
cookbooks/tmpl/
├── Berksfile
├── README.md
├── attributes
│   └── default.rb
├── chefignore
├── metadata.rb
├── recipes
│   └── default.rb
└── templates
    └── default
        └── tmpl.hello.erb

4 directories, 7 files
ubuntu@ws:~/chef-repo$

各雛形からクックブックを作っていきましょう。

ubuntu@ws:~/chef-repo$ vi cookbooks/tmpl/attributes/default.rb
default['tmpl']['message'] = 'Hello, World!'
ubuntu@ws:~/chef-repo$
ubuntu@ws:~/chef-repo$ vi cookbooks/tmpl/templates/default/tmpl.hello.erb
"<%= node['tmpl']['message'] %>"
ubuntu@ws:~/chef-repo$
ubuntu@ws:~/chef-repo$ vi cookbooks/tmpl/recipes/default.rb
#
# Cookbook Name:: tmpl
# Recipe:: default
#
# Copyright (c) 2014 The Authors, All Rights Reserved.

template '/tmp/hello.txt' do
  source 'tmpl.hello.erb'
end
ubuntu@ws:~/chef-repo$

ノードに適用します。

ubuntu@ws:~/chef-repo$ knife node run_list add node.example.jp 'recipe[tmpl]'
node.example.jp:
  run_list:
    recipe[ntp]
    recipe[multi_pkgs]
    recipe[tmpl]
ubuntu@ws:~/chef-repo$ knife node run_list add ws.example.jp 'recipe[tmpl]'
ws.example.jp:
  run_list:
    recipe[ntp]
    recipe[multi_pkgs]
    recipe[tmpl]
ubuntu@ws:~/chef-repo$
ubuntu@ws:~/chef-repo$ knife zero chef_client 'name:*' -x ubuntu --sudo
node.example.jp [2014-12-22T17:25:29+09:00] WARN:
    :
node.example.jp Starting Chef Client, version 11.16.4
node.example.jp resolving cookbooks for run list: ["ntp", "multi_pkgs", "tmpl"]
node.example.jp Synchronizing Cookbooks:
node.example.jp   - multi_pkgs
node.example.jp   - ntp
node.example.jp   - tmpl
node.example.jp Compiling Cookbooks...
node.example.jp Converging 4 resources
node.example.jp Recipe: ntp::default
node.example.jp   * package[ntp] action install (up to date)
node.example.jp Recipe: multi_pkgs::default
node.example.jp   * package[nginx] action install (up to date)
node.example.jp   * package[redis-server] action install (up to date)
node.example.jp Recipe: tmpl::default
node.example.jp   * template[/tmp/hello.txt] action create
node.example.jp     - create new file /tmp/hello.txt
node.example.jp     - update content in file /tmp/hello.txt from none to 3d06fb
node.example.jp     --- /tmp/hello.txt  2014-12-22 17:25:30.600670333 +0900
node.example.jp     +++ /tmp/chef-rendered-template20141222-9137-1tt4lvn    2014-12-22 17:25:30.600670333 +0900
node.example.jp     @@ -1 +1,2 @@
node.example.jp     +"Hello, World!"
node.example.jp
node.example.jp Running handlers:
node.example.jp Running handlers complete
node.example.jp Chef Client finished, 1/4 resources updated in 1.531860219 seconds
ws.example.jp   [2014-12-22T17:25:31+09:00] WARN:
    :
ws.example.jp   Starting Chef Client, version 11.18.0.rc.1
ws.example.jp   resolving cookbooks for run list: ["ntp", "multi_pkgs", "tmpl"]
ws.example.jp   Synchronizing Cookbooks:
ws.example.jp     - ntp
ws.example.jp     - multi_pkgs
ws.example.jp     - tmpl
ws.example.jp   Compiling Cookbooks...
ws.example.jp   Converging 4 resources
ws.example.jp   Recipe: ntp::default
ws.example.jp     * package[ntp] action install (up to date)
ws.example.jp   Recipe: multi_pkgs::default
ws.example.jp     * package[nginx] action install (up to date)
ws.example.jp     * package[redis-server] action install (up to date)
ws.example.jp   Recipe: tmpl::default
ws.example.jp     * template[/tmp/hello.txt] action create
ws.example.jp       - create new file /tmp/hello.txt
ws.example.jp       - update content in file /tmp/hello.txt from none to 3d06fb
ws.example.jp       --- /tmp/hello.txt  2014-12-22 17:25:33.028000026 +0900
ws.example.jp       +++ /tmp/chef-rendered-template20141222-9405-1e5lqms    2014-12-22 17:25:33.028000026 +0900
ws.example.jp       @@ -1 +1,2 @@
ws.example.jp       +"Hello, World!"
ws.example.jp  
ws.example.jp   Running handlers:
ws.example.jp   Running handlers complete
ws.example.jp   Chef Client finished, 1/4 resources updated in 1.301652536 seconds
ubuntu@ws:~/chef-repo$

ファイルが作成できたことを確認します。

ubuntu@ws:~/chef-repo$ knife ssh 'name:*' 'cat /tmp/hello.txt' -x ubuntu
ws.example.jp   "Hello, World!"
node.example.jp "Hello, World!"
ubuntu@ws:~/chef-repo$

テンプレートにはOhaiで収集した値を埋め込むこともできます。ここではfqdnを埋め込みます。

ubuntu@ws:~/chef-repo$ git diff cookbooks
diff --git a/cookbooks/tmpl/templates/default/tmpl.hello.erb b/cookbooks/tmpl/templates/default/tmpl.hello.erb
index 1c1ac75..b8087c4 100644
--- a/cookbooks/tmpl/templates/default/tmpl.hello.erb
+++ b/cookbooks/tmpl/templates/default/tmpl.hello.erb
@@ -1 +1 @@
-"<%= node['tmpl']['message'] %>"
+<%= node['fqdn'] %> says "<%= node['tmpl']['message'] %>"
ubuntu@ws:~/chef-repo$

ノードに適用します。

ubuntu@ws:~/chef-repo$ knife zero chef_client 'name:*' -x ubuntu --sudo
node.example.jp [2014-12-22T17:29:34+09:00] WARN:
    :
node.example.jp Starting Chef Client, version 11.16.4
node.example.jp resolving cookbooks for run list: ["ntp", "multi_pkgs", "tmpl"]
node.example.jp Synchronizing Cookbooks:
node.example.jp   - multi_pkgs
node.example.jp   - tmpl
node.example.jp   - ntp
node.example.jp Compiling Cookbooks...
node.example.jp Converging 4 resources
node.example.jp Recipe: ntp::default
node.example.jp   * package[ntp] action install (up to date)
node.example.jp Recipe: multi_pkgs::default
node.example.jp   * package[nginx] action install (up to date)
node.example.jp   * package[redis-server] action install (up to date)
node.example.jp Recipe: tmpl::default
node.example.jp   * template[/tmp/hello.txt] action create
node.example.jp     - update content in file /tmp/hello.txt from 3d06fb to f3a09e
node.example.jp     --- /tmp/hello.txt  2014-12-22 17:25:30.600670333 +0900
node.example.jp     +++ /tmp/chef-rendered-template20141222-9637-g2wd8o 2014-12-22 17:29:35.516354291 +0900
node.example.jp     @@ -1,2 +1,2 @@
node.example.jp     -"Hello, World!"
node.example.jp     +node.example.jp says "Hello, World!"
node.example.jp
node.example.jp Running handlers:
node.example.jp Running handlers complete
node.example.jp Chef Client finished, 1/4 resources updated in 1.291272443 seconds
ws.example.jp   [2014-12-22T17:29:36+09:00] WARN:
    :
ws.example.jp   Starting Chef Client, version 11.18.0.rc.1
ws.example.jp   resolving cookbooks for run list: ["ntp", "multi_pkgs", "tmpl"]
ws.example.jp   Synchronizing Cookbooks:
ws.example.jp     - ntp
ws.example.jp     - multi_pkgs
ws.example.jp     - tmpl
ws.example.jp   Compiling Cookbooks...
ws.example.jp   Converging 4 resources
ws.example.jp   Recipe: ntp::default
ws.example.jp     * package[ntp] action install (up to date)
ws.example.jp   Recipe: multi_pkgs::default
ws.example.jp     * package[nginx] action install (up to date)
ws.example.jp     * package[redis-server] action install (up to date)
ws.example.jp   Recipe: tmpl::default
ws.example.jp     * template[/tmp/hello.txt] action create
ws.example.jp       - update content in file /tmp/hello.txt from 3d06fb to 053da1
ws.example.jp       --- /tmp/hello.txt  2014-12-22 17:25:33.028000026 +0900
ws.example.jp       +++ /tmp/chef-rendered-template20141222-10088-1kk0bd5   2014-12-22 17:29:37.982553342 +0900
ws.example.jp       @@ -1,2 +1,2 @@
ws.example.jp       -"Hello, World!"
ws.example.jp       +ws.example.jp says "Hello, World!"
ws.example.jp  
ws.example.jp   Running handlers:
ws.example.jp   Running handlers complete
ws.example.jp   Chef Client finished, 1/4 resources updated in 1.348914275 seconds
ubuntu@ws:~/chef-repo$

ファイルを変更できたことを確認します。

ubuntu@ws:~/chef-repo$ knife ssh 'name:*' 'cat /tmp/hello.txt' -x ubuntu
ws.example.jp   ws.example.jp says "Hello, World!"
node.example.jp node.example.jp says "Hello, World!"
ubuntu@ws:~/chef-repo$

リソース間の通信

notifiesまたはsubscribesを用いることで、設定ファイルに変更があったらサービスを再起動するといった動作を実現できます。

nginxパッケージをインストールし、待ち受けポート番号(listen_port)のみが変更できる設定ファイル(/etc/nginx/sites-available/default)を設置するクックブックを作成します。

まずnginxクックブックの雛形を作成します。

ubuntu@ws:~/chef-repo$ chef generate cookbook cookbooks/nginx
Compiling Cookbooks...
Recipe: code_generator::cookbook
  * directory[/home/ubuntu/chef-repo/cookbooks/nginx] action create
    - create new directory /home/ubuntu/chef-repo/cookbooks/nginx
    :
ubuntu@ws:~/chef-repo$ chef generate attribute cookbooks/nginx default
Compiling Cookbooks...
Recipe: code_generator::attribute
  * directory[cookbooks/nginx/attributes] action create
    - create new directory cookbooks/nginx/attributes
  * template[cookbooks/nginx/attributes/default.rb] action create
    - create new file cookbooks/nginx/attributes/default.rb
    - update content in file cookbooks/nginx/attributes/default.rb from none to e3b0c4
    (diff output suppressed by config)
ubuntu@ws:~/chef-repo$ chef generate template cookbooks/nginx nginx.default.erb
Compiling Cookbooks...
Recipe: code_generator::template
  * directory[cookbooks/nginx/templates/default] action create
    - create new directory cookbooks/nginx/templates/default
  * template[cookbooks/nginx/templates/default/nginx.default.erb] action create
    - create new file cookbooks/nginx/templates/default/nginx.default.erb
    - update content in file cookbooks/nginx/templates/default/nginx.default.erb from none to e3b0c4
    (diff output suppressed by config)
ubuntu@ws:~/chef-repo$

実際にクックブックを作成していきます。ここではnotifiesを使用します。subscribesでも同じことが実現できますが、ここでは説明およびコード例は省略します。各自の宿題としてください。

ubuntu@ws:~/chef-repo$ vi cookbooks/nginx/attributes/default.rb
default['nginx']['listen_port'] = 80
ubuntu@ws:~/chef-repo$
ubuntu@ws:~/chef-repo$ vi cookbooks/nginx/templates/default/nginx.default.erb
#
# Generated by Chef for <%= node['fqdn'] %>
#

server {
    listen <%= node['nginx']['listen_port'] %> default_server;
    listen [::]:<%= node['nginx']['listen_port'] %> default_server ipv6only=on;

    root /usr/share/nginx/html;
    index index.html index.htm;

    server_name localhost;

    location / {
        try_files $uri $uri/ =404;
    }
}
ubuntu@ws:~/chef-repo$ vi cookbooks/nginx/recipes/default.rb
#
# Cookbook Name:: nginx
# Recipe:: default
#
# Copyright (c) 2014 The Authors, All Rights Reserved.

package 'nginx'

template '/etc/nginx/sites-available/default' do
  source 'nginx.default.erb'
  notifies :restart, 'service[nginx]'
end

service 'nginx' do
  supports :restart => true
  action :enable
end
ubuntu@ws:~/chef-repo$

ノードに適用します。

ubuntu@ws:~/chef-repo$ knife node run_list add node.example.jp 'recipe[nginx]'
node.example.jp:
  run_list:
    recipe[ntp]
    recipe[multi_pkgs]
    recipe[tmpl]
    recipe[nginx]
ubuntu@ws:~/chef-repo$ knife node run_list add ws.example.jp 'recipe[nginx]'
ws.example.jp:
  run_list:
    recipe[ntp]
    recipe[multi_pkgs]
    recipe[tmpl]
    recipe[nginx]
ubuntu@ws:~/chef-repo$
ubuntu@ws:~/chef-repo$ knife zero chef_client 'name:*' -x ubuntu --sudo
node.example.jp [2014-12-22T18:17:40+09:00] WARN:
    :
node.example.jp Starting Chef Client, version 11.16.4
node.example.jp resolving cookbooks for run list: ["ntp", "multi_pkgs", "tmpl", "nginx"]
node.example.jp Synchronizing Cookbooks:
node.example.jp   - tmpl
node.example.jp   - ntp
node.example.jp   - multi_pkgs
node.example.jp   - nginx
node.example.jp Compiling Cookbooks...
node.example.jp [2014-12-22T18:17:41+09:00] WARN: Cloning resource attributes for package[nginx] from prior resource (CHEF-3694)
node.example.jp [2014-12-22T18:17:41+09:00] WARN: Previous package[nginx]: /var/chef/cache/cookbooks/multi_pkgs/recipes/default.rb:8:in `block in from_file'
node.example.jp [2014-12-22T18:17:41+09:00] WARN: Current  package[nginx]: /var/chef/cache/cookbooks/nginx/recipes/default.rb:7:in `from_file'

node.example.jp Converging 7 resources
node.example.jp Recipe: ntp::default
node.example.jp   * package[ntp] action install (up to date)
node.example.jp Recipe: multi_pkgs::default
node.example.jp   * package[nginx] action install (up to date)
node.example.jp   * package[redis-server] action install (up to date)
node.example.jp Recipe: tmpl::default
node.example.jp   * template[/tmp/hello.txt] action create (up to date)
node.example.jp Recipe: nginx::default
node.example.jp   * package[nginx] action install (up to date)
node.example.jp   * template[/etc/nginx/sites-available/default] action create
node.example.jp     - update content in file /etc/nginx/sites-available/default from 7fe53b to 97aa27
node.example.jp     --- /etc/nginx/sites-available/default  2012-03-29 11:50:24.000000000 +0900
node.example.jp     +++ /tmp/chef-rendered-template20141222-10157-1n5zvby   2014-12-22 18:17:42.116304044 +0900
node.example.jp     @@ -1,121 +1,18 @@
    :
node.example.jp   * service[nginx] action enable (up to date)
node.example.jp   * service[nginx] action restart
node.example.jp     - restart service service[nginx]
node.example.jp
node.example.jp Running handlers:
node.example.jp Running handlers complete
node.example.jp Chef Client finished, 2/8 resources updated in 2.850105057 seconds
ws.example.jp   [2014-12-22T18:17:44+09:00] WARN:
    :
ws.example.jp   Starting Chef Client, version 11.18.0.rc.1
ws.example.jp   resolving cookbooks for run list: ["ntp", "multi_pkgs", "tmpl", "nginx"]
ws.example.jp   Synchronizing Cookbooks:
ws.example.jp     - ntp
ws.example.jp     - tmpl
ws.example.jp     - multi_pkgs
ws.example.jp     - nginx
ws.example.jp   Compiling Cookbooks...
ws.example.jp   [2014-12-22T18:17:45+09:00] WARN: Cloning resource attributes for package[nginx] from prior resource (CHEF-3694)
ws.example.jp   [2014-12-22T18:17:45+09:00] WARN: Previous package[nginx]: /var/chef/cache/cookbooks/multi_pkgs/recipes/default.rb:8:in `block in from_file'
ws.example.jp   [2014-12-22T18:17:45+09:00] WARN: Current  package[nginx]: /var/chef/cache/cookbooks/nginx/recipes/default.rb:7:in `from_file'

ws.example.jp   Converging 7 resources
ws.example.jp   Recipe: ntp::default
ws.example.jp     * package[ntp] action install (up to date)
ws.example.jp   Recipe: multi_pkgs::default
ws.example.jp     * package[nginx] action install (up to date)
ws.example.jp     * package[redis-server] action install (up to date)
ws.example.jp   Recipe: tmpl::default
ws.example.jp     * template[/tmp/hello.txt] action create (up to date)
ws.example.jp   Recipe: nginx::default
ws.example.jp     * package[nginx] action install (up to date)
ws.example.jp     * template[/etc/nginx/sites-available/default] action create
ws.example.jp       - update content in file /etc/nginx/sites-available/default from 7fe53b to 223390
ws.example.jp       --- /etc/nginx/sites-available/default  2012-03-29 11:50:24.000000000 +0900
ws.example.jp       +++ /tmp/chef-rendered-template20141222-10819-d944p2    2014-12-22 18:17:45.884371929 +0900
ws.example.jp       @@ -1,121 +1,18 @@
    :
ws.example.jp     * service[nginx] action enable (up to date)
ws.example.jp     * service[nginx] action restart
ws.example.jp       - restart service service[nginx]
ws.example.jp  
ws.example.jp   Running handlers:
ws.example.jp   Running handlers complete
ws.example.jp   Chef Client finished, 2/8 resources updated in 2.687193341 seconds
ubuntu@ws:~/chef-repo$

設定通りの80番ポートにアクセスできることを確認します。

ubuntu@ws:~/chef-repo$ curl http://node.example.jp
<html>
<head><title>404 Not Found</title></head>
<body bgcolor="white">
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.1.19</center>
</body>
</html>
ubuntu@ws:~/chef-repo$
ubuntu@ws:~/chef-repo$ curl http://ws.example.jp
<html>
<head><title>404 Not Found</title></head>
<body bgcolor="white">
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.1.19</center>
</body>
</html>
ubuntu@ws:~/chef-repo$

ポート番号を8080に変更します。クックブックのアトリビュートを変更してもかまいませんが、Chef-Repoに格納してあるノード情報を編集します。

ubuntu@ws:~/chef-repo$ knife node edit node.example.jp
{
  "name": "node.example.jp",
  "chef_environment": "_default",
  "normal": {
    "nginx": {
      "listen_port": 8080
    },
    "tags": [

    ]
  },
  "run_list": [
  "recipe[ntp]",
  "recipe[multi_pkgs]",
  "recipe[tmpl]",
  "recipe[nginx]"
]

}
ubuntu@ws:~/chef-repo$
ubuntu@ws:~/chef-repo$ knife node edit ws.example.jp
{
  "name": "ws.example.jp",
  "chef_environment": "_default",
  "normal": {
    "nginx": {
      "listen_port": 8080
    },
    "tags": [

    ]
  },
  "run_list": [
  "recipe[ntp]",
  "recipe[multi_pkgs]",
  "recipe[tmpl]",
  "recipe[nginx]"
]

}
ubuntu@ws:~/chef-repo$
ubuntu@ws:~/chef-repo$ git diff
diff --git a/nodes/node.example.jp.json b/nodes/node.example.jp.json
index c4c6fb5..09e752e 100644
--- a/nodes/node.example.jp.json
+++ b/nodes/node.example.jp.json
@@ -1,6 +1,9 @@
 {
   "name": "node.example.jp",
   "normal": {
+    "nginx": {
+      "listen_port": 8080
+    },
     "tags": [
 
     ]
diff --git a/nodes/ws.example.jp.json b/nodes/ws.example.jp.json
index 9043d68..76bf9b4 100644
--- a/nodes/ws.example.jp.json
+++ b/nodes/ws.example.jp.json
@@ -1,6 +1,9 @@
 {
   "name": "ws.example.jp",
   "normal": {
+    "nginx": {
+      "listen_port": 8080
+    },
     "tags": [
 
     ]
ubuntu@ws:~/chef-repo$

変更したノード情報をノードに適用します。

ubuntu@ws:~/chef-repo$ knife zero chef_client 'name:*' -x ubuntu --sudo -C 1
node.example.jp [2014-12-22T18:26:40+09:00] WARN:
    :
node.example.jp Starting Chef Client, version 11.16.4
node.example.jp resolving cookbooks for run list: ["ntp", "multi_pkgs", "tmpl", "nginx"]
node.example.jp Synchronizing Cookbooks:
node.example.jp   - multi_pkgs
node.example.jp   - tmpl
node.example.jp   - ntp
node.example.jp   - nginx
node.example.jp Compiling Cookbooks...
node.example.jp [2014-12-22T18:26:41+09:00] WARN: Cloning resource attributes for package[nginx] from prior resource (CHEF-3694)
node.example.jp [2014-12-22T18:26:41+09:00] WARN: Previous package[nginx]: /var/chef/cache/cookbooks/multi_pkgs/recipes/default.rb:8:in `block in from_file'
node.example.jp [2014-12-22T18:26:41+09:00] WARN: Current  package[nginx]: /var/chef/cache/cookbooks/nginx/recipes/default.rb:7:in `from_file'

node.example.jp Converging 7 resources
node.example.jp Recipe: ntp::default
node.example.jp   * package[ntp] action install (up to date)
node.example.jp Recipe: multi_pkgs::default
node.example.jp   * package[nginx] action install (up to date)
node.example.jp   * package[redis-server] action install (up to date)
node.example.jp Recipe: tmpl::default
node.example.jp   * template[/tmp/hello.txt] action create (up to date)
node.example.jp Recipe: nginx::default
node.example.jp   * package[nginx] action install (up to date)
node.example.jp   * template[/etc/nginx/sites-available/default] action create
node.example.jp     - update content in file /etc/nginx/sites-available/default from 97aa27 to 3960f9
node.example.jp     --- /etc/nginx/sites-available/default  2014-12-22 18:17:42.116304044 +0900
node.example.jp     +++ /tmp/chef-rendered-template20141222-10563-2yu0aq    2014-12-22 18:26:41.266762936 +0900
node.example.jp     @@ -3,8 +3,8 @@
node.example.jp      #
node.example.jp      
node.example.jp      server {
node.example.jp     -    listen 80 default_server;
node.example.jp     -    listen [::]:80 default_server ipv6only=on;
node.example.jp     +    listen 8080 default_server;
node.example.jp     +    listen [::]:8080 default_server ipv6only=on;
node.example.jp      
node.example.jp          root /usr/share/nginx/html;
node.example.jp          index index.html index.htm;
node.example.jp   * service[nginx] action enable (up to date)
node.example.jp   * service[nginx] action restart
node.example.jp     - restart service service[nginx]
node.example.jp
node.example.jp Running handlers:
node.example.jp Running handlers complete
node.example.jp Chef Client finished, 2/8 resources updated in 2.28401369 seconds
ws.example.jp   [2014-12-22T18:26:43+09:00] WARN:
    :
ws.example.jp   Starting Chef Client, version 11.18.0.rc.1
ws.example.jp   resolving cookbooks for run list: ["ntp", "multi_pkgs", "tmpl", "nginx"]
ws.example.jp   Synchronizing Cookbooks:
ws.example.jp     - ntp
ws.example.jp     - nginx
ws.example.jp     - multi_pkgs
ws.example.jp     - tmpl
ws.example.jp   Compiling Cookbooks...
ws.example.jp   [2014-12-22T18:26:44+09:00] WARN: Cloning resource attributes for package[nginx] from prior resource (CHEF-3694)
ws.example.jp   [2014-12-22T18:26:44+09:00] WARN: Previous package[nginx]: /var/chef/cache/cookbooks/multi_pkgs/recipes/default.rb:8:in `block in from_file'
ws.example.jp   [2014-12-22T18:26:44+09:00] WARN: Current  package[nginx]: /var/chef/cache/cookbooks/nginx/recipes/default.rb:7:in `from_file'

ws.example.jp   Converging 7 resources
ws.example.jp   Recipe: ntp::default
ws.example.jp     * package[ntp] action install (up to date)
ws.example.jp   Recipe: multi_pkgs::default
ws.example.jp     * package[nginx] action install (up to date)
ws.example.jp     * package[redis-server] action install (up to date)
ws.example.jp   Recipe: tmpl::default
ws.example.jp     * template[/tmp/hello.txt] action create (up to date)
ws.example.jp   Recipe: nginx::default
ws.example.jp     * package[nginx] action install (up to date)
ws.example.jp     * template[/etc/nginx/sites-available/default] action create
ws.example.jp       - update content in file /etc/nginx/sites-available/default from 223390 to f0e3c4
ws.example.jp       --- /etc/nginx/sites-available/default  2014-12-22 18:17:45.884371929 +0900
ws.example.jp       +++ /tmp/chef-rendered-template20141222-11323-n203be    2014-12-22 18:26:44.702071618 +0900
ws.example.jp       @@ -3,8 +3,8 @@
ws.example.jp        #
ws.example.jp        
ws.example.jp        server {
ws.example.jp       -    listen 80 default_server;
ws.example.jp       -    listen [::]:80 default_server ipv6only=on;
ws.example.jp       +    listen 8080 default_server;
ws.example.jp       +    listen [::]:8080 default_server ipv6only=on;
ws.example.jp        
ws.example.jp            root /usr/share/nginx/html;
ws.example.jp            index index.html index.htm;
ws.example.jp     * service[nginx] action enable (up to date)
ws.example.jp     * service[nginx] action restart
ws.example.jp       - restart service service[nginx]
ws.example.jp  
ws.example.jp   Running handlers:
ws.example.jp   Running handlers complete
ws.example.jp   Chef Client finished, 2/8 resources updated in 2.440957849 seconds
ubuntu@ws:~/chef-repo$

設定ファイルの変更とnginxの再起動が行われました。設定通りの8080番ポートにアクセスできることを確認します。

ubuntu@ws:~/chef-repo$ curl http://node.example.jp
curl: (7) couldn't connect to host
ubuntu@ws:~/chef-repo$ curl http://node.example.jp:8080
<html>
<head><title>404 Not Found</title></head>
<body bgcolor="white">
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.1.19</center>
</body>
</html>
ubuntu@ws:~/chef-repo$
ubuntu@ws:~/chef-repo$ curl http://ws.example.jp
curl: (7) couldn't connect to host
ubuntu@ws:~/chef-repo$ curl http://ws.example.jp:8080
<html>
<head><title>404 Not Found</title></head>
<body bgcolor="white">
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.1.19</center>
</body>
</html>
ubuntu@ws:~/chef-repo$

まとめ

Knife-ZeroはChef Serverを用いずに、手軽にChefによるInfrastructure as Codeを実践できる大変有用なツールです。Chef Serverによる使い勝手とまったく変わらない上に、管理規模が大きくなったらChef Serverへの移行も簡単に行えるという利点もあります。
Infrastructure as CodeをやってみたいけれどChef Serverはちょっと手に余るな、という方は、Knife-Zeroから始めてみてはいかがでしょう。本稿はなるべく平易となるように心掛けて記述したつもりです。是非その便利さを実感してみてください。


Related post

mautic is open source marketing automation