Posted on Sun Aug 04 20:54:45 +0900 2013 by nabeken
knife-soloとVagrantを共存させ、試験環境へはVagrant + chef-solo provisonerでプロビジョニングし、 本番環境へは knife solo cook でプロビジョニングできるようにしました。 その時の工夫を紹介します。
chef-repo をベースとしたリポジトリ構成は以下になります。
$ tree -d (不要なものは削除) . ├── certificates ├── config ├── cookbooks │ ├── tknetworks │ ├── tknetworks_bird │ ├── tknetworks_openbsd │ ├── tknetworks_openvpn │ ├── tknetworks_sudo ├── data_bags │ ├── certs │ ├── ipsec │ ├── openvpn │ ├── ssh_keys │ └── tknetworks-ipsec ├── fabfile ├── nodes │ ├── gw-openbsd1.osaka.tknetworks.org │ └── nino.sakura.tknetworks.org └── roles
ほとんどchef-repoそのままですが、 nodes ディレクトリ以下にはさらに各ノードのディレクトリがあり、 そのディレクトリには
$ ls -alh nodes/nino.sakura.tknetworks.org -rw-r--r-- 1 nabeken staff 958B 7 15 22:52 Vagrantfile -rw-r--r-- 1 nabeken staff 315B 7 19 10:04 node.json
と、各ノード毎に専用のVagrantfileと本番環境に適用するJSONを配置しています。
この状態で試験環境にプロビジョニングする場合はVagrantを使用します。
$ cd nodes/nino.osaka.tknetworks.org $ vagrant up
本番環境にプロビジョニングする場合は knife solo cook を使用します。
$ bundle exec knife solo cook root@nino.osaka.tknetworks.org
ディレクトリ構造を少し変更したのは Vagrantfileを各ノード単位で用意したかったから です。 単一のVagrantfileに各ノードを定義することもできますが、1システム 1 Vagrantfileと考えると 別々に管理したいと思いました。
では具体的に見ていきます。
すでにいろいろな記事がありますが、 knife-solo は0.3系以上を使用してください(2013/08/03 追記: 0.3.0がリリースされました)。 chef-repoにGemfileを用意しておくのが便利です。
$ cat Gemfile source 'https://rubygems.org' gem 'chef' gem 'chefspec' gem 'knife-solo' $ bundle install $ bundle exec knife
knife-soloがインストールされていれば以下のサブコマンドが表示されます。
** SOLO COMMANDS ** knife solo cook [USER@]HOSTNAME [JSON] (options) knife solo init DIRECTORY knife solo prepare [USER@]HOSTNAME [JSON] (options) knife solo bootstrap [USER@]HOSTNAME [JSON] (options) knife solo clean [USER@]HOSTNAME
通常、 knife solo cook foo.example.org とした場合、JSONは自動的に nodes/foo.example.org.json から読み込まれます。 今回は nodes/foo.example.org/node.json から読み込んで欲しいので、 chef-repoのトップディレクトリに .chef ディレクトリを用意し、 ここにmonkey patchのためのknife pluginを用意しました。
$ cat .chef/plugins/knife/solo_patch.rb module KnifeSolo module NodeConfigCommand def node_config Pathname.new(@name_args[1] || "#{nodes_path}/#{node_name}/node.json") end end end
これで knife solo cook root@foo.example.org とすれば 第2引数で明示的にJSONを指定しなくても自動的に nodes/foo.example.org/node.json から設定を読み込んでくれます。
書いたrecipeはできる限り公開することにしています。私自身、community cookbookを参考にすることはよくあります。 最近はGithubに専用のorganizationを作成し、1 cookbook 1 repositoryで管理してます。
community cookbookをフォークするのはアンチパターンの1つのようですが、私がフォークする場合の主な理由は
などです。どうせフォークするならGithubのorganizationを作り、そこへフォークしておくと管理がすこしは楽になります。 さもないと、個人アカウントに紛らわしいリポジトリ名がたくさんできてしまいます。
話がそれましたが、chef-repoのcookbookには非公開のcookbookを入れています。 機密情報は必ずencrypted data bagで格納し、鍵は別リポジトリに保管しています。 encrypted data bagをchef-solo環境向けに作成するには簡単なツールを使うのが便利です。 私は Chef Solo encrypted data bags を参考に 既存のdata bagのJSONを暗号化するスクリプト を作成し、使用しています。
このスクリプトを使うと
$ cat data_bag.json | ruby json2encrypted_data_bag.rb
で暗号化されたJSONが得られます。
これらを踏まえた最低限の knife.rb は以下になります。
$ cat ~/.chef/knife.rb encrypted_data_bag_secret '/path/to/data_bag.key' cookbook_path %w{ /path/to/tknetworks-cookbooks /path/to/chef-repo/cookbooks } role_path '/path/to/chef-repo/roles' data_bag_path '/path/to/chef-repo/data_bags'
などの理由からknife-soloのJSONに影響を与えずにVagrant特有の設定を入れる必要があります。 例えばsudoをrecipeで管理する場合、knife-soloではvagrantユーザーに対するsudoの設定は不要ですが、 Vagrant環境では設定が必要です。
これらの要件を満すためVagrantfileに工夫しています。
Vagrant.configure("2") do |config| # <snip> config.vm.provision :chef_solo do |chef| chef.encrypted_data_bag_secret_key_path = '../../../path/to/data_bag.key' chef.cookbooks_path = %w{../../../tknetworks-cookbooks ../../cookbooks} chef.data_bags_path = "../../data_bags" chef.roles_path = "../../roles" chef.json = JSON.parse(open(File.expand_path('../node.json', __FILE__)).read).tap { |j| j['run_list'] += %w{tknetworks_sudo::vagrant tknetworks_openbsd::vagrant} } end end
chef-solo provisonerでは設定をJSONから読み込めるため、まずknife-solo用の設定を読み込み、 Object#tap を 使いVagrant特有のrun_listを追加しています。
knife-soloとVagrantの組み合わせ方法を工夫し、試験環境と本番環境の切り替える方法をまとめてみました。 それではよいcookingを。