ようへいの日々精進XP

よかろうもん

altria と LXC で cookbook のテスト環境(Odokumi)を作ってみた

概要

目的

  • サーバーで直接 cookbook のテストをしたい
  • 下記の通り、vagrant でも test-kitchen でもイケることをあえて別な方法でやってみる
  • 以前に考えていたことを実際に具現化

しかしながら

  • vagrantprovisioning コマンドで同じことをやれます
  • test-kitchen でも良いかと思います
  • ただ、メリットも無いわけではなく CI ツールを経由することで結果の履歴が追える(かもしれない)

本件について

  • このテスト環境を一つのフレームワークとしてそれらしい名前も考えてみた
  • テスト→テイスティング→お毒見→odokumi
  • ということで以後は Odokumi と呼ぶことになる。

Odokumi の構成とワークフロー

構成図

f:id:inokara:20130818030742p:plain

構成

コンポーネント 名前
OS Ubuntu 13.04(Ubuntu Server)
CI ツール altria
仮想環境 LXC

ワークフロー

  1. cookbook を修正
  2. git でローカルコミット
  3. hook スクリプトで Odokumi サーバーに cookbook を転送、転送が完了したら LXC コンテナを作成、作成された LXC コンテナに cookbook を転送
  4. 転送された cookbook を chef-solo を使って実行
  5. 結果を altria で捕捉する

構築や設定

すべてを書くとながーくなるのでポイントを幾つか。

altria 関連

altria を動作させるユーザーを作成

  • altria を動作させる altria ユーザーを作成
  • altria ユーザーで altria 自身をセットアップして起動させる
  • 念の為、chef 環境をインストールしておく。(Opscode のインストーラーを使う)

altria から lxc-* コマンドを叩かせる

sudoers の設定を弄る。

ssh の設定も少し追加

コンテナへの接続の際に known_hosts への追加登録を確認されるステップをスキップする設定を /home/altria/.ssh/config に設定する。

LXC 関連

LXC コンテナへの仕込み

今回は Debian 6(squeeze) を環境として用意する。

そして、必要な apt パッケージを導入しておく。

その他、以下のような設定を行なっておく。

  • altria ユーザーの鍵をテスト用の LXC コンテナ root ユーザーの authorized_keys に登録しておく
  • IP アドレスを決め打ちで設定しておく( /etc/hosts とかに設定でも良いかも)
  • 当然、chef 環境をインストールしておく。(Opscode のインストーラーを使う)

仕込みをしておいたコンテナは lxc-clone でコンテナを複製する際のオリジナルコンテナとしておく。


使ってみる

cookbook 作成環境の準備

ワークステーション側で以下のようなディレクトリ構成の cookbook 作成環境を用意する。

$ tree git/chef/ -L 3
git/chef/
└── cookbooks
    ├── .git         #=> 後述
    ├── chef.json #=> run_list を記載
    └── test1       #=> Cookbook
        ├── CHANGELOG.md
        ├── README.md
        ├── attributes
        ├── definitions
        ├── files
        ├── libraries
        ├── metadata.rb
        ├── providers
        ├── recipes
        ├── resources
        └── templates

10 directories, 4 files

ちょっと特殊な対応として run_list が記述された chef.json をテストを行う cookbook ごとに書き換える必要がある。また、それぞれの cookbook と同じディレクトリに設置しておく。(この辺りは要改善)

cookbook 作成環境 での git 関連の準備

git init

  • cookbooks ディレクトリ以下で git init を実行する。

hook script

git init を実行後、cookbooks/.git/hooks 以下のようなに post-commit を設置する。

altria にジョブを登録する

ジョブを新規登録して、以下のような内容のスクリプトを作成して適当なディレクトリ(/home/altria/bin/)に

テストしてみる

何もしない recipe を書いてテストを行なってみる。

レシピ

本当に何もしていない。

odokumi の開始

odokumi の実行は git commit コマンドで始める。

git commit -m "modified."

以下のようにローカルのリポジトリに対してコミットが走った後で cookbooks ディレクトリ全体が odokumi サーバーに転送され、altria にジョブのビルドが要求される。

test build
rsync done...
{"id":16,"status":null,"started_at":null,"finished_at":null,"job_id":1,"output":null,"created_at":"2013-08-18T00:35:15.398+09:00","updated_at":"2013-08-18T00:35:15.398+09:00","properties":{},"incremental_id":16}
cookbook request done...
[master 0018f64] modified
 1 file changed, 1 insertion(+), 6 deletions(-)

odokumi の実行

お毒見中

altriaodokumi のビルドが走る。

f:id:inokara:20130818010236p:plain

殿、毒は盛られておりませぬ

cookbook が LXC コンテナに正常に適用される。

f:id:inokara:20130818010639p:plain

残念ながら毒が盛られていた場合

recipe 等にシンタックスエラーがあった場合には見事にコケる。

f:id:inokara:20130818012530p:plain

コケた場合には LXC コンテナの削除は行われず、ログインして詳細な調査が出来るようにしている。


ということで

odokumi 環境を構築していて気づいた点、改善点等。

altria と LXC について

altria について

  • ジョブ script へのスクリプトの貼り付け方を確認する(直接シェルスクリプトを貼り付けていいのか?)
  • redis-server が固まって、それに引きずられて altria 自身が固まってしまうことがあった

LXC について

  • lxc-info で存在しないコンテナ名を指定しても STOPPED 等と表示されてしまうので、lxc-info でコンテナの動作確認する場合には注意する

改善点

  • cookbook 以外にも ansible の playbooks や puppet の manifest もテスト出来るように
  • 対応する OS を増やして、test-kitchen のように複数の OS に対して一度にテスト出来るように

cookbook のテスト環境が作れないか考えている

概要

  • chef の cookbook を git で管理している前提でローカル cookbook テスト環境が作れないか考えている
  • 既に test-kitchen という Opscode が提供するフレームワークがあるが、より汎用的な環境が作れないか考えてみる

考察

使うツール

使うツールとしては...

  • CI 環境として altria
  • 仮想ホストとして LXC
  • 基本的には Chef でも Ansible でもイケるように

処理の流れ

前提

あらかじめ altria のジョブに LXC コンテナを起動して knife solo prepare を叩いて knife colo cook までを実行するジョブを登録しておく。

実際の流れ

  1. git commit
  2. git の hook command で altria に登録された job を起動
  3. 結果を待つ
  4. テストが通ったら git push でリモートのリポジトリに登録する

みたいな感じ。

要調査と確認

git

  • ローカル commit で hook スクリプトを実行出来るか?

altria

  • API でコントロール出来るか再確認

LXC

  • IP を取得出来るか?

うまくいくんかいな

  • うまくいくんかいな

サーバー構築後にやっていることの自動化について考えてみる(1)

概要

  • サーバー構築後に行なっている作業を整理して、出来るだけ自動化とオペミスを無くす環境を構築出来ないか考えてみた

まずは整理

サーバー構築直後と、運用開始後についての作業を整理してみる。尚、サーバーの構築に関しては chefansible 等のツールを利用し、監視ツールとしては cacti を利用している環境を想定する。

サーバー構築直後

  1. serverspec による構築直後のチェック(標準的な設定の確認)
  2. 監視ツールへのホスト登録
  3. 内部 DNS を利用していれば内部 DNS への登録
  4. 開発チームへの提供、もしくはアプリケーションのデプロイ
  5. アプリケーション側のテスト
  6. 外部 DNS または Proxy への登録
  7. リリース

運用開始後

  • 様々な監視(稼働サービス監視、リソース監視、サーバー構成監視)を行い、障害発生の事前検知とチューニング
  • 障害発生時の対応

等、ざっくりと現在の自分が行なっている作業も含め書いてみたが、今回はサーバー構築直後の 01 から 03 あたりまでをひと通りやってみて可能性を探ってみることにする。

実装

構成はこんな感じ

f:id:inokara:20130804064732p:plain

Jenkins 先生に色々と頑張って頂くことを前提に以下のようなフローとなる。

  1. サーバー構築直後にホスト側から Jenkinsserverspec 実行のビルドを API を使ってオーダー
  2. serverspec の実行結果を解析して正常であれば cacti にホストを登録
  3. cacti からホストの監視が開始される
  4. serverspec の実行のトリガーはサーバー側の rc.local 等に設定されている為、サーバーが再起動すると毎回実行される

サーバーから Jenkins API を叩いて serverspec を実行する

構築直後、再起動毎で serverspec でテストする手順については割愛し、さらに jenkinsAPI を叩いて serverspec を実行する方法についてはこちらにて検証を行なっており、細かい設定はあるものの実装は可能。

serverspec の結果を以ってホストを Cacti に登録する

Cacti がコマンドラインから扱えることと Jenkinsビルド結果を解析して次のビルドをキックするか、一つのジョブ内でシェルを複数書けるのでそれを利用する手もある。

f:id:inokara:20130804072546p:plain

ジョブのパラメータを使いまわせるということで、後者の一つのジョブ内でシェルを複数書くように設定してみた。登録したシェルスクリプトは以下の通り。

serverspec のホストを登録するシェルスクリプト

テストする為の spec ファイルは以下の通り。

とりあえずは Cacti でのグラフ生成に必要な snmpd が稼働していることを確認する。他にも各環境に応じて適宜テストを書く。

登録したホストでテストを実行するシェルスクリプト

Cacti にホストを登録するシェルスクリプト

テスト

jenkins のコンソールから実行

パラメータ付きビルドにてビルドを行う

f:id:inokara:20130804123023p:plain

ビルドが成功

ビルドが成功を以って、以下が成功している。

  • serverspec 用の spec ファイルの生成
  • 生成された spec ファイルでの対象サーバーのテスト
  • Cacti への対象サーバーの登録

f:id:inokara:20130804123135p:plain

しばらくすると...

Cacti にて登録したホストの監視が開始されている。

f:id:inokara:20130805072751p:plain

ということで

引き続きの目標として...

  • serverspec でのテスト項目を煮詰めていく
  • ChefAnsible 等でサーバーを一から構築して試してみる
  • DNS への登録の自動化
  • 自動化だけではなく管理や冪等性をどのように担保するか検討

等を試していくつもり。

LXC コンテナの IP アドレスを取得する(出来てないけど)

きっかけ

  • knife-lxc を使っていてコンテナの IP アドレスが取得出来ない
  • そもそもどうやって IP を取得しているんだろう
  • ついでに私的な knife-lxc を作ってしまおうという算段

knife-lxc

  • ざっくり言うと knife-lxc は Chef の補助ツールの一つ
  • 動きとしては任意のコンテナを起動してから任意の cookbook を起動する等...

機能

とてもシンプル。

コマンド 機能 メモ
knife lxc server list コンテナの一覧を取得 一応、動く
knife lxc server create コンテナを作成して run list に記載された cookbook を実行する cucumber-chef の古いバージョンに依存していて動かない
knife lxc server delete コンテナの削除 一応、動く

インストールは gem で一発。

sudo gem install knife-lxc --no-ri --no-rdoc -V

いきなり、動かなかった

上記のメモにある通り cucumber-chef1.0.4 までに含まれている handy というモジュールに knife lxc server create が依存している関係でインストール当初は動作しなかった為、knife lxc server create の該当部分をコメントアウトして動作させた。

とりあえずは knife lxc server list を試してみた。

IP を取得する為の試行錯誤

ようやく本題。 knife lxc server list を試している過程でコンテナの IP が表示出来るのに表示されていなかったので調査をしてみた。

$ knife lxc server list
Lxc containers list
Name          Ip
centos-test1
centos1

knife lxc ではどうやっているのか?

knife lxc においてコンテナの IP アドレス取得はコンテナのコンフィグファイルから取得してきている。以下は lxc_server_list.rb" の一部抜粋。

コンフィグファイルは環境によって異なる。尚、Debian wheezy の場合には /var/lib/lxc/コンテナ名/config となるが、そもそも、IP アドレスを決め打ちで記載してはいないので IP の取得はこの方法では難しい。

dnsmasq.leases で取得する

ググると...こちらでは dnsmasq のリリースした記録(/var/lib/misc/dnsmasq.leases)を cat するのがシンプルでイイよって書かれていたので試してみた。

cat /var/lib/misc/dnsmasq.leases

を実行すると...

1374364571 de:33:db:24:0f:45 192.168.8.84 * *

おお、ちゃんと IP が...しかし、この場合コンテナとの関連付けをどのようにハンドリングするか悩ましいところ。さらに、こちらには、コンテナに対して任意のコマンドを投げることが出来る lxc-attach というコマンドを使って you should be able to とあったので lxc-attach も試してみた。

lxc-attach は...orz

結論から言うと、lxc-attach はカーネルの 3.8 以降で動作するらしく、それ以下の環境だと動作しないようで lxc 0.9.0~alpha3-2 ですら動作しないことを @ten_forward さんに教えて頂いた。

lxc-attach の動作については @ten_forward さんのブログ記事が詳細に記載されていて解り易かった。記事にも書かれているように、カーネル 3.8 以下な環境(手元の Debian wheezy(3.2.0-4-amd64)) で lxc-attach すると、以下のようなエラーとなってしまう...

lxc-attach: No such file or directory - failed to open '/proc/14854/ns/pid'
lxc-attach: failed to enter the namespace
lxc-attach: No such file or directory - failed to open '/sys/fs/cgroup//lxc/lxc/centos1/tasks'

残念。ちなみに、カーネル 3.8 な Ubuntu 13.04 だと見事に動作することを確認している。

結局

現時点、現環境(カーネル 3.8 以下)でホスト側からコンテナの IP アドレスを取得する手段としては...

方法 課題
コンテナのコンフィグに IP を決め打ち コンテナを DHCP 環境で IP 取得させる場合にどうするか?
dnsmasq.leases を確認する IP とコンテナの関連付けをどうするか?

しか無さそう...前途、多難。

参考

databag を使ってユーザーアカウントを作成する+それを serverspec でテストする

概要

  • databag を使ってでサーバーにログインするユーザーを作成する

チートシート

レシピ

${chef-repo}/site-cookbooks/manage_user/recipes/manage_user.rb

databag

${chef-repo}/data_bags/users/user.json

パスワードは openssl passwd -1 "your_password" で作成すると良いでしょう。

serverspec

個人的には...

  • 気をつけるのは solo.rb 内の databag のディレクトリパス

postfix のログを fluentd を使って redis に突っ込んでみる(2)

宿題

mail.log の日付フォーマットを変更してしまっていた

rsyslog.conf を調整して mail.log の日付フォーマットを Jul 13 17:45:37 となっているのを 2013-07-11T07:01:44.698907+09:00 に変更した上で fluentd でパースさせてた。

Debian に fluentd をパッケージから導入する

Debian に fluentd をパッケージからインストールしてみる。gem からインストールする場合には既存の ruby 環境を弄らずに導入する方法を模索する。

解決

fluentd のドキュメントをちゃんと読めば良かった

日付のフォーマットを従来のフォーマット(Jul 13 17:45:37 のようなフォーマット)でも fluentd でパースさせるには format にて以下のような正規表現で記述する。

format /^(?<time>[^ ]* [^ ]* [^ ]*)

これは fluentd の Input Plugin である in_tail プラグインドキュメントにちゃんと記述されていた...。恥ずかしい限りです。ということで、format を修正して以下のような fluent.conf を作成した。

修正後は redis にも以下のように登録されるようになった。

td-agent の deb パッケージが提供されている

有難い日本語のドキュメントに紹介されていた方法通りに deb パッケージに td-agent を導入する。ドキュメントからの抜粋となるが Debian 6 系の場合には以下のようにインストールする。尚、ドキュメントに記載されているように Ubuntu 12.04 LTS / PreciseUbuntu 10.04 LTS / Lucid のみサポートしているようだ。

curl -L http://toolbelt.treasure-data.com/sh/install-ubuntu-lucid.sh | sh

上記は /etc/apt/sources.list.d/treasure-data.list に新しい apt リポジトリを登録し、td-agent の deb パッケージをインストールする。インストールに際しては curlsudo が必要となるので予めインストールしておく。

尚、td-agentfluentd の安定版という立ち位置のプロダクトで、こちらにその違いが明記されている。

deb パッケージで導入すると init.d 以下に起動スクリプトが設置されると共に /usr/lib/fluent 以下に組み込みの ruby 等がインストールされる。

root@host:/usr/lib/fluent# tree -L 2
.
├── jemalloc
│   ├── bin
│   ├── include
│   ├── lib
│   └── share
└── ruby
    ├── bin
    ├── include
    ├── lib
    └── share

10 directories, 0 files

プラグインのインストールは以下のようにして組み込みの gem を利用してインストールすることになる。

/usr/lib/fluent/ruby/bin/gem install fluent-pluをgin-redis --no-ri --no-rdoc -V

また、インストールしたプラグインは /usr/lib/fluent/ruby/lib/ruby/gems/1.9.1/gems 以下にインストールされる。

そして td-agent をセットアップする chef の cookbook まで用意されている

板前には嬉しいことに td-agent をセットアップする cookbook が提供されているので利用してみる。尚、こちらも deb パッケージと同様に UbuntuCentOSRedHat 環境のみのサポートとなっているようなので Debian 環境での利用はお試しの範囲で...。

利用に際しては aptyum の cookbook が必要となるので予め用意した上で、以下のように run_list を用意する。

当然ながら Debian 6.0.7(squeeze)だとうまくインストールが終わらかなったので Debian 環境(今んとこ squeeze のみ)でパッケージがインストール出来るように修正したものを pull request してみた。

以下の部分を追記してみた。

手元の検証環境では上記を追記した cookbook で td-agent をインストールすることが出来た。やはり、ここでも ohai サマサマ。働きモノの ohai です。

ということで...

導入を試すことからちゃんと運用してくように引き続き検証を進めていくべ。

redis をセットアップする cookbook を Community cookbook に登録してみました

概要

  • redis を導入する為の cookbook をちゃんと作ってみた
  • とは言え、以前に作ったものを fork する形で作ることにした
  • せっかくなので github で marge も初体験してみた
  • さらに Community Cookbook にも登録してみた

まずは...

redis_2_cookbook

名前は変だけどこちらにアップした。特に凝ったこともせず、attributetemplate を使ったもの。次期バージョンでは、以下を検討する。

  • 既存バージョンを自動判定して最新バージョンより低ければバージョンアップする
  • 様々な OS で動くようにする(現在のバージョンでもイケるかも。但し、未検証)

serverspec の spec ファイルを同梱した

今回、cookbook の作成にあたり serverspec のテストを作った後で cookbook を作っていったので、テストドンブリスペシャルということでアーカイブに同梱してみた。

こんな感じ。テストが全部通ればインストール出来たってことで。


github で merge した

master ブランチはイジらない

恥ずかしながら、これまでは master ブランチをジャンジャンバリバリ cookbook を弄っていたが、それはアカンらしいので作業用のブランチを作って作業した。master ブランチから fork した後で...

git clone https://github.com/inokappa/redis_2_cookbook.git
cd redis_2_cookbook
git checkout -b newfeature

以後は newfeature ブランチで作業を続ける。

自作自演の pull request

いい感じで出来たところで newfeature ブランチに push する。

git push origin newfeature

push した後で pull request を以下のように送ってみた。

f:id:inokara:20130713114357p:plain

自作自演なので悪い組織の片棒を担ぐような気分になってしまう。

そして merge

自作自演の pull request に応えるべく master ブランチに merge する。

そして、最後に git push して完了。ただし、上記の手順はベストプラクティスでは無いと思われるので引き続き勉強する。


勢い余って Community Cookbook に登録した

knife コマンドでサクッと

タイムリーにこちらに紹介された方法で簡単に登録出来た。(但し、こちらでアカウントを作成して API キーこちら生成してダウンロードしておく必要がある)

knife cookbook site share redis_2_cookbook ${category} -o ${my-cookbook-path} -u ${usre-name}                                                                        

取得した API キー(xxxxx.pem)は以下のように knife.rb に記載しておいても良い。(上記の例は knife.rb に記載されていることを前提としている。)

current_dir = File.dirname(__FILE__)
client_key               "#{current_dir}/xxxxx.pem"

正常に登録されると以下のようなページが出来た。ヤッター。

f:id:inokara:20130713120706p:plain

README がグチャグチャなので後で書き直したい...


ということで

  • せっかくアップしたので何かしらのフィードバックがあると嬉しい

参考

以下を参考にさせて頂きました。

knife solo に -VV オプションをつけて実行して判ったこと

概要

  • いきなり knife solo が動かなくなってしまったので --verbose オプションをつけて実行した
  • 脱初心者は程遠い...

判ったこと

solo.rb はちゃんと書くこと

knife solo を実行する環境の solo.rb をそのまま rsync で転送しており、適切に cookbook_path 等が設定されていないと...

Chef::Exceptions::CookbookNotFound: Cookbook setup not found. If you're loading setup from another cookbook, make sure you configure the dependency in your metadata

と怒られる。

はい。すいません。

ということで、上記のように solo.rb をちゃんと書くこと。

knife.rb に指定した cookbook_path 以下の cookbook を丸ごと rsync している

run_list に記載された cookbook 以外に knife.rb に指定した cookbook_path 以下に保存されている cookbook を全部 rsync しているみたい。

以下、knife solo を実行する環境の cookbook_path に保存された cookbook 達。

$ tree -L 1 my-cookbooks
my-cookbooks
├── ansible_install_cookbook
├── db_benchmark
├── hoge_cookbook
├── lamp
├── mariadb_install_cookbook
├── memcached
├── mysql_install_cookbook
├── redis_1_cookbook
├── redis_2_cookbook
└── setup

以下、knife solo によって chef-solo が実行される環境の cookbook_path に保存された cookbook 達。

# pwd
/tmp/chef-solo
# tree -L 1 my-cookbooks/
my-cookbooks/
├── ansible_install_cookbook
├── chef_solo_patches
├── db_benchmark
├── hoge_cookbook
├── lamp
├── mariadb_install_cookbook
├── memcached
├── mysql_install_cookbook
├── redis_1_cookbook
├── redis_2_cookbook
└── setup

ん、run_list に書かれた cookbook だけを転送するんぢゃなかったのね。初めて知りました。

knife solo が動かなくなって焦った件について

現象

knife-solo を二回目に実行しようとした際に以下のようなエラーとなってしまう。

ERROR: RuntimeError: Couldn't find Chef >=0.10.4 on xxx.xxx.xxx.xxx

尚、この現象は Debian 環境のみで確認したが、他の OS 環境でも発生するかもしれない。(調査中)


原因

chef-solo のバージョンが適切に判断されない

原因は knife solo を実行する際にデフォルトで対象 node の chef-solo バージョンを以下のようにチェックするが、

sudo chef-solo --version

が実行される際 sudo コマンドの出力結果として、

sudo: unable to resolve host xxxxxxxxxxxx

と出力されてしまっている為。

chef-solo のバージョン判断を行なっているのが knife-solo-0.2.0/lib/chef/knife/solo_cook.rb の以下の記述。

sudo: unable to resolve host xxxxxxxxxxxx となってしまった場合には v には ["sudo", " unable to resolve host inokappa2\r\nChef", " 11.4.4\r\n"] こんな感じで値が入ってしまい v[0].strip == 'Chef' が成立しない為 chef_version に値が入らない。


対策

--no-chef-check を利用する

対策として一番手っとり早いのが --no-chef-check オプションを使う。

/etc/hosts を適切に修正する

もしくは /etc/hosts にホスト名を追加する。

127.0.1.1 ${hostname}

追記(2013/07/11)

発生する条件

検証の結果、以下の条件を全て満たす場合に sudo コマンドを実行すると sudo: unable to resolve host xxxxxxxxxxxx となり knife solo 側で chef-solo のバージョン判定が出来なくなるようだ。

  • Debian 又は Ubuntu 環境
  • sudo がインストールされている
  • /etc/hosts と /etc/hostname のホスト名が異なる

参考