Knife-Zero を使った時につまづいた点とその解決策

こんにちは、sunflatです。 今期のアニメは、とんかつDJアゲ太郎と宇宙パトロールルル子が好きです。

今回は、リモートのサーバーを設定するためのツールである Knife-Zero を使った時に、つまづいた点とその解決策をまとめてみました。

Knife-Zero とは

Misocaでは、サーバーの設定の自動化にChefを使っています。

f:id:sunflat:20160525130948p:plain

Chefのレシピ(設定手順をコードで書いたもの)をリモートのノードに適用するためのツールとして、Misocaでは以前から knife-solo を使っていました。しかし、knife-soloが使っているChefの機能であるChef Soloが非推奨となったことなどから、代わりにKnife-Zeroを使うことを計画しています。 *1

Knife-Zero を使った時につまづいた点とその解決策

その1. ノードファイルに色々書き込まれる

knife-solo では、ノードオブジェクトの json ファイル(nodesディレクトリにあるもの。以下ノードファイルと呼ぶ)は、以下のように、ノード固有の属性(例: set_fqdn)とrun_listのみを書いておく感じでした。レシピを適用(knife-solo cook)しても、ノードファイルは変化しません。

{
  "set_fqdn": "hoge.example.com",
  "run_list": [ 〜 ]
}

一方 Knife-Zero では、レシピを適応(knife-zero converge)した時に、ノードの全ての属性がノードファイルに保存されます。*2

しかし、自動取得された動的なシステム情報などの属性も(かなり大量に)書き込まれてしまうため、ノードファイルをGit等でバージョン管理したい場合には不都合です。

解決策

.chef/knife.rbホワイトリストを設定しておけば、指定した属性のみが保存されるようになります(参考)。knife[:automatic_attribute_whitelist] の場合、自動収集された属性(automatic属性)がホワイトリストの対象となります。

以下は、automatic属性のうち fqdn, hostname, chef_package.chef.version のみをノードファイルの保存する場合の、.chef/knife.rb の設定例です。

local_mode true
cookbook_path ['berks-cookbooks', 'site-cookbooks']

# ホワイトリストの設定
knife[:automatic_attribute_whitelist] = %w(
  fqdn/
  hostname/
  chef_packages/chef/version/
)

なお、ノードファイルに保存された defaultautomatic 以下の属性は、次回のレシピ適用時には破棄されて使われません(ただし、automaticfqdn属性のみ、Knife-Zero がレシピ適用時のsshの接続先として使う)。

ノード固有の属性を設定したい場合は、以下のようにノードファイルの normal 以下に書けば、レシピ適用時に使用されます。(About Attributes の Attributes Type を参考)

{
  "normal": {
    "set_fqdn": "hoge.example.com"
  },
  "run_list": [ 〜 ],
}

また、ノードファイルを編集する時には、直接jsonファイルを編集するのではなく、以下のようにknifeコマンドで編集すれば、永続的な属性のみが表示されますし、保存時にバリデーションもしてくれます。 *3

knife node edit -z hoge.example.com

その2. ホワイトリストが有効にならない

その1の解決策でホワイトリストを設定する方法を紹介しましたが、最初はホワイトリストを定義してもそれが反映されず、原因究明に時間がかかりました。

結局、設定先のノードに古いバージョンのChefが入っていて、それがそのまま使われていたのが原因でした。動作確認にVagrantを使ったのですが、Ubuntu14.04のVagrant用イメージには古いバージョンのChefがプレインストールされています。

設定先のノードにChefをインストールするために knife zero bootstrap コマンドを利用できますが、設定先のノードにChefが既にインストールされている場合は、(古いバージョンだったとしても)Chefのインストールは行われません。

解決策

knife zero bootstrap を実行する前に古いバージョンのChefを予めアンインストールしておきましょう。VagrantのイメージにはChefが入っていることが多いので、自分で入れた覚えがなくても要注意です。

その3. FQDNがノード名として使われる

デフォルトでは、設定先ノードのFQDNドメイン名込みのホスト名)が、ノード名(ノードファイルのファイル名)やレシピ適用時のsshの接続先として使われます。このため、ノードのFQDNやその名前解決を設定する前の状態だと、ノードファイルのファイル名が意図しないものとなったり、レシピ適用時にsshで接続できなかったりして困ります。

解決策

knife zero bootstrap を使って設定先にChefをインストールする時に、以下のように -N オプションをつければ、設定先ノードのFQDNとは別の名前をノード名として使うことができます。*4

knife zero bootstrap [sshの接続先(IPアドレスなど)] -N [ノード名]
※必要に応じて、-x(sshのユーザ名)や、 --sudo(sudo権限で実行)などのオプションも付加

また、knife zero converge を使ってレシピを適用する時には、-a オプションをつけると、sshの接続先として使う属性(ノードファイルに保存された属性の名前)を変更できます。デフォルトでは fqdn (ノードのFQDN)が使われますが、これを knife_zero.hostknife zero bootstrap 実行時に使った接続先。ノードファイルにnormal属性として保存されている)に変更すれば、ノードのFQDNやその名前解決を設定する前の状態でも、レシピ適用時にsshで接続できるようになります。

knife zero converge -a knife_zero.host -x [sshのユーザ名] "name:[ノード名]"
※必要に応じて、-x(sshのユーザ名)などのオプションも付加

その4. デバッグ実行したい

Rubyでレシピを書いていると、pry-byebug などを使ってデバック実行をしたくなることがあります。レシピの中で

require 'pry'
binding.pry

などと書けば停止はするのですが、 Knife-Zeroだとキー入力が伝わらないようなので、デバック実行中の操作ができなくて困りました。

解決策

(もはや Knife-Zeroを使っていないですが)設定先のノードにレシピを転送して、Chef Client Local Mode を使ってローカル環境にレシピを適用すればデバッグ実行できました。

.chef/knife.rb では、cookbook_path をフルパスで指定する必要があるようです(参考)。*5

chef_repo = File.join(File.dirname(__FILE__), "..")
cookbook_path ["#{chef_repo}/berks-cookbooks", "#{chef_repo}/site-cookbooks"]

rsync などで chef-repo(レシピが含まれているリポジトリ)を設定先のノードへ転送し、設定先ノードのchef-repoのディレクトリ上で、以下のように Chef Client を Local Mode で起動します。

sudo chef-client -z

これで、普通にpry-byebugを使ったデバック実行が出来ました。

まとめ

今回は、Knife-Zero を使った時に、つまづいた点とその解決策を紹介しました。 Knife-Zero はまだ色々と試している段階ですが、今後のMisocaのサーバ運用に活用していきたいと思います。

*1:公式blog記事 https://www.chef.io/blog/2014/06/24/from-solo-to-zero-migrating-to-chef-client-local-mode/ などで、Chef Solo の代わりに Chef Client Local Mode(Chef Zero)を使うようにアナウンスされています。 そこで、Chef Client Local Mode を使ってレシピをリモートのノードに適用するためのツールであるKnife-Zeroを、knife-soloの代わりに使うことにしました。

*2:Chefは、ノードの設定だけでなく、ノードの状態を管理するツールでもあり、特定の状態を持ったノードを検索(サーチ機能)したりする時にこれらの保存した属性を使うことができます。

*3: .chef/knife.rb に local_mode true を書いておけば、-z オプションは省略できます

*4:ノード名には、将来割りあてる予定のFQDNを指定しておくと良いです。

*5:追記: chef_client用のホワイトリストを設定する場合は knife[:automatic_attribute_whitelist] ではなく automatic_attribute_whitelist というDSLで書きます。