~nabeken/diary/

Gentoo Linux(6年くらい)とFreeBSD(1年くらい)とOpenBSD(新参者)を使う日々。


IHANet BGP peering overview

knife-soloとVagrantの組み合せ方を工夫して本番環境と試験環境にプロビジョニングする

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

すでにいろいろな記事がありますが、 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 から設定を読み込んでくれます。

knife-solo向けknife.rb

書いたrecipeはできる限り公開することにしています。私自身、community cookbookを参考にすることはよくあります。 最近はGithubに専用のorganizationを作成し、1 cookbook 1 repositoryで管理してます。

community cookbookをフォークするのはアンチパターンの1つのようですが、私がフォークする場合の主な理由は

  • OpenBSD対応とかFreeBSD対応を考えるとき
  • wrapper cookbookで対処しきれないとき(definitionやprovider内のリソース定義が当てはまる)

などです。どうせフォークするなら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'

Vagrant

  • Vagrant(試験環境)とknife-solo(本番環境)の設定が食い違うとVagrantの意味がない
  • しかし、Vagrant特有の設定はknife-soloに適用したくない

などの理由から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を。

更新履歴

  • Vagrant 1.2.7では chef_add_recipe nil が不要だったので削除 (Sun, 4 Aug 2013 20:54:45 +0900)
  • knife-solo 0.3.0がリリースされたのでGemfile修正 (Sat, 3 Aug 2013 15:57:37 +0900)
  • s/デプロイ/プロビジョニング/g (Wed, 31 Jul 2013 09:51:04 +0900)
  • Add space (Wed, 24 Jul 2013 00:16:51 +0900)
  • 初稿 (Wed, 24 Jul 2013 00:11:08 +0900)