tkak's tech blog

This is my technological memo.

Chefを学ぶのが面倒な人のInfrastructure as Code with JSON

最近、いかにChefの学習コストを小さくしつつ、組織にインフラのコード化(Infrastructure as Code)を導入するか、について悶々と考えていたので、まとめてみた。

Chefの辛み

インフラのコード化をするにあたって、Chefは最初に覚えることが多くてなかなか辛い、という話をよく耳にする。Chefの動作概念、Chefの各種リソースの使い方、Chefの周辺ツール群の使い方、Cookbookのコーディング規約など、Chefを使い始めるために覚えるべき内容は結構多い。組織が大きくなればなるほど学習コストはバカにならないし、インフラエンジニアに日常的にコードを書いてもらうことはなかなか難しい。そんな環境でInfrastructure as Codeを広めようとしても、現実的になかなかうまくいかない。インフラをコード化して業務改善がしたいのに、いつの間にかみんなにChefを広めることが目的になり、疲弊したりする。

Infrastructure as Code with JSON

そんな中、DevOpsの流れとは逆行するが、みんなみんながみんなChefのことわかってCookbookが書ける必要はないんじゃないかとふと思った。大規模な組織では業務内容が細分化されているし、Cookbookの作成は得意な人に任せて、最低限必要な変数(attribute)をJSON ファイルで定義してchefを実行する方法だけ学べばいいのではないかと。まさにInfrastructure as Code with ChefならぬInfrastructure as Code with JSONJSON を書いてインフラを管理する。Cookbookに比べてある程度柔軟性は無くなるが、最低限の学習コストでChefを導入することができ、業務の自動化ができそう。そうすれば、少しでも手作業による運用、筋肉運用を減らすことができるんじゃないだろうか。

インフラのコード化を進めていく段階を分類するとだいたいこんな感じになる。

  1. Chefなんて使わずにマンパワーにまかせた筋肉運用をする
  2. Chefはそんなに知らないけどJSONファイルが書けてcookbookが使える
  3. Chefのことをよく知ってて自由にcookbookが書ける

理想は3の人が当たり前にいることだが、いきなり1から3にいける人はそこまで多くないと思うし、まずは1から2に持っていくのが改善効果が期待できるし大事だと思う。

Cookbookを書かずにCookbookを使う

Cookbookを使うだけであれば、Berkshelfの使い方と、attributeとrun_listを学ぶだけだ。作業としてはBerkshelf を使ってcookbookをRubyのBundlerのようにgemを手元に取ってきて、Cookbookの実行に必要なattributeとrun_listをNodeのJSON ファイルに定義してChefを実行する。Chefを実行するだけならChefのCookbookを書く必要はない。以下、使い方を紹介したい。なお、紹介のなかで使うNodeのJSONファイルなどはGithubに置いておいた。

1. BerkshelfでCookbooksを取得する

まず、使いたいCookbookを選ぶ。ChefコミュニティのCookbookを使ってもいいし、社内で使ってるCookbookでもいい。Cookbookの取得元は以下の通り。

  • コミュニティCookbook置き場 - Supermarket
  • 社内のCookbook置き場 - Private supermarketかBerkshelf APIのエンドポイント

timezone-ii Cookbookを使う場合のBerksfileはこんな感じ。

source "https://supermarket.chef.io"

cookbook "timezone-ii"

社内のCookbookを使う場合は、sourceに社内のエンドポイントを指定する。

source "https://shanai.example.com/"

cookbook "hogehoge"
cookbook "fugafuga"
cookbook "piyopiyo"

Berksfileを作ったらberksコマンドでcookbookをローカルに取ってくる。まるでRubyのBundlerのように。以下の例では取得先をcookbooksディレクトリにしている。

$ berks vendor cookbooks
Resolving cookbook dependencies...
Fetching cookbook index from https://supermarket.chef.io...
Using timezone-ii (0.2.0)
Vendoring timezone-ii (0.2.0) to cookbooks/timezone-ii

$ tree -L 2
.
├── Berksfile
├── Berksfile.lock
└── cookbooks
    └── timezone-ii

2. NodeのJSONファイルを定義する

次にChefを実行するために必要なNodeのJSONファイルを作成する。Nodeの設定に必要な可変値(attribute)や、どのrecipeを実行するか(run_list)などを記載する。その際、きちんと使いたいCookbookのドキュメントを読んで、使えるAttributeやrecipeを確認する。

supermarket.chef.io

timezone-iiのcookbookを実行する例だと以下のようになる。

{
    "name": "my_node",
    "tz": "Asia/Tokyo",
    "run_list": [
        "recipe[timezone-ii]"
    ]
}

nodesディレクトリを掘ってそこにJSONファイルをおく。

$ tree -L 2
.
├── Berksfile
├── Berksfile.lock
├── cookbooks
│   └── timezone-ii
└── nodes
    └── my_node.json

3. Chefを実行する

あとはChefを実行するだけ。Chefの実行はchef-solo、chef-client、knife-soloなど、いろんな方法があるが、ここではVagrantで作ったVM上で、chef-clientのlocalモードを実行する例を示す。

まず、以下のようなchefを実行するのに必要な設定ファイルclient.rbを用意する。

cookbook_path '/vagrant/cookbooks'

ちなみに、社内環境とかProxy配下の環境で試す場合は以下のようなファイルになる。chef-zeroでproxyを使う場合はno_proxylocalhostを入れることがポイント。

http_proxy 'http://proxy.example.coml:1234'
no_proxy 'localhost'
cookbook_path '/vagrant/cookbooks'

最後にvagrantVMを立ててchefを実行する。

$ vagrant box add tkak/centos-6.6-x86_64-chef-dk
$ vagrant init tkak/centos-6.6-x86_64-chef-dk
$ vagrant up
$ vagrant ssh

[vagrant@localhost ~]$ date
Sun Apr  5 04:19:24 UTC 2015
[vagrant@localhost ~]$ sudo chef-client -z -j /vagrant/nodes/my_node.json -c /vagrant/conf/client.rb
Starting Chef Client, version 12.0.3
resolving cookbooks for run list: ["timezone-ii"]
Synchronizing Cookbooks:
  - timezone-ii
Compiling Cookbooks...
Converging 4 resources
Recipe: timezone-ii::default
  * yum_package[tzdata] action install (up to date)
  * log[Linux platform 'centos' is unknown to this recipe; using generic Linux method] action write (skipped due to not_if)
Recipe: timezone-ii::linux-generic
  * ruby_block[confirm timezone] action run
    - execute the ruby block confirm timezone
  * file[/etc/localtime] action create
    - update content in file /etc/localtime from ab1ddb to 0bc4b3
    (current file is binary, diff output suppressed)
    - restore selinux security context

Running handlers:
Running handlers complete
Chef Client finished, 2/3 resources updated in 10.584156048 seconds
[vagrant@localhost ~]$ date
Sun Apr  5 13:21:12 JST 2015
[vagrant@localhost ~]$ 

まとめ

ChefのCookbookを書かずにJSONとBerkshelf だけでChefを実行する方法について書いた。もちろんChefをきちんと理解してCookbookが書ける方が望ましいが、Infrastructure as Codeに馴染みがない人やこれから学びたいという人は、とりあえずChefのcookbook使ってみるところからはじめるのがいいと思う。大事なのはChefを使うことではなく業務を改善することなので、まずはCookbookを使ってみて少しでも手作業をなくすことが大切だと思う。