こんにちは。株式会社ベーシックのCTOやってる@zaruです。今年はじめてQiitaのアドベントカレンダーに参加しました。25日埋まるようなんとか頑張ります。また、ベーシックのエンジニアについて興味のある人はベーシックエンジニアのQiita記事に目を通してみてください。それなりに面白い記事があると思います。
長い前置き
Chef、めっちゃ流行って今や定番ツールになってますね。僕はChefに挑戦したものの脱落したダメエンジニアです。なんで脱落したかというと、セコセコ作ったレシピを保守できなかったんですね。Chefさわれる人が社内に全然いない&教えようにも自分がよく分かっていないという、ツールに振り回されてダメダメという状況になってしまいました。
そこでもういっそ、VagrantBoxを直接配布したり、AWSならAMI化されたものを使いまわしたりしてたわけです。そこまで大規模なインフラでもないし、これで十分かーとか言い訳しつつ。コマンド一つで実行できるから教える手間もないしね。
でもやっぱり、時は残酷なもので、新しいメンバーが入ったり、テスト環境をサクッと作らなきゃいけない事があったり、Webサーバのミドルウェアを入れ替えたりといった、インフライベントがあるわけで、そんなの人力手動で乗り切るなんてツライわけです。
前置きが長かったですが、救世主とも言えるItamaeさんによって快適なインフラ生活を過ごすことができるようになりました。
そもそもの問題
- ローカルとステージングとプロダクション環境がバラバラ
- インフラ変更に伴うレビュー体制がない
- Chefをチームメンバー全員が覚える余裕はない
ベーシックでは、大きく3つの問題がありました。
環境がバラバラ
手動でインフラを管理したり、ローカルのVagrant環境はエンジニアによってChef + Berkshelf使ったり、誰かからかもらったVagrantBoxを使っていたり、お手製だったりバラバラでした。豚バラですね。
これは、Chefを代表とするプロビジョニングツールで解決できますね。
レビュー体制がない
今まではインフラが得意なエンジニアがサーバの面倒を見て、手動でゴニョゴニョしてたわけなんですが、そうなると変更作業に対して他のエンジニアの目が入りにくいんです。せいぜい隣りにいるエンジニアに目検するくらい。こうなると、サービスを支える大事なインフラがどんな構成になっているのか把握できなくなってしまいますし、ミスってサービスがダウンすることもあるわけです。
これも、Chefの要素の1つである「Infrastructure as Code」で解決できます。インフラをコード化する。素晴らしい。プルリクで改善されるサーバ達。
Chefを覚えられない
上記2つの問題だけであれば、Chefで良いじゃんとなるわけですが、Chefをチームメンバー全員に覚えてもらうコストがバカにならないな、と自分でChefを学習していて思いました。
ベーシックではインフラ専任のエンジニアはいないため、メイン業務はアプリケーションの開発です。片手間でも使いこなせるくらいの簡易なツールでないとダメでした。
そこで軽量版Chefと呼ばれるItamaeをさわってみると、簡単というレベルじゃない。マニュアルをサラッと読むだけで普通に使えて驚きました。おどろ木ももの木さんしょの木。
Chefを覚えられないなら、Itamaeを覚えれば良いじゃない。
Itamaeの使い方
Itamaeは、itamae
コマンド。これ1つだけで完結します。素晴らしい。レシピはRuby DSLで記述します。ここはChefっぽいです。
基本的にItamaeは実行環境にだけインストールされていれば、SSH経由でリモートサーバに適用するため、リモート環境にはインストールされている必要ありません。
(それなりの数のサーバに適用する場合は、SSH経由だと遅いため、対象サーバにItamaeをインストールしてレシピを転送する方法もあります)
$ itamae ssh -h 192.168.1.100 -u user recipe.rb
このコマンド一発で、指定レシピが実行されます。
Itamaeベストプラクティス
$ gem i itamae
でインストールしても良いんですが、ItamaeのプラグインGemなどもインストールする可能性があるため、プロジェクトごとにGemfileを作ってやるのが良いと思います。
source "https://rubygems.org"
gem "itamae"
$ bundle install --path vendor/bundler
Itamaeはディレクトリ構成などしばりはないんですが、みんなで使うならルールはあったほうが良いということで、公式でベストプラクティスが紹介されています。
なるべく再利用可能になるようサービス単位で分割するのが良いです。そしてinclude_recipe
を使って読みこめばOK。板前なのでsabaku.rb
(捌く)っていうファイル名にして、いろんなレシピをぶち込んでます。
また、下の方で紹介しているテンプレートファイルなどで使用可能な変数を指定するノードファイルは、環境ごとに分けると楽です。
.
└── nodes
├── development.yml
├── production.yml
└── staging.yml
Dry-runで事前確認
本番サーバに対していきなりitamae
コマンドをぶち込むのは流石に怖い。コードレビューが通っていたとしても、念のため確認したい。なぜなら、本番サーバに対してテスト環境のノードファイルとかレシピを指定しちゃっているかもしれないから!
そんな時のために、--dry-run
オプションが用意されています。これは、変更はせず差分だけ確認ができるというもの。これで変更内容を確認して問題なければ適用するようにしましょう。
Itamae逆引き
レシピを書くにはいくつかのリソースを覚えれば大抵のことは実現できます。
パッケージのインストール
package
package "nginx" do
action :install
end
%w(readline readline-devel).each do |pkg|
package pkg
end
RPMファイルを直接指定してインストール
package
yumのデフォルトリポジトリに存在しているパッケージなら、パッケージ名を指定すれば良いんですが、ない場合もあります。そんな時でもURL指定でRPMファイルをインストールすることができます。
冪等性を保つためにも、not_if
でインストールされていなければという条件をつけたほうが良いでしょう。まぁ、この場合はnot_if
してもしなくても変化はないと思いますが。
package "http://yum.postgresql.org/9.3/redhat/rhel-6.7-i386/pgdg-redhat93-9.3-2.noarch.rpm" do
not_if 'rpm -q pgdg-redhat93'
end
指定リポジトリを有効にするオプションを付けたい
package
package
に限った話じゃないですが、コマンドにオプションを付けることが出来ます。今回の例だと、yum
コマンドにenablerepo
オプションを付けています。
package "jpegoptim" do
options "--enablerepo=epel"
end
サービスの起動/停止/自動起動
service
いわゆるchkconfig on
とサービスの起動です。
service 'nginx' do
action [:enable, :start]
end
指定ファイルを監視して、更新されたら再起動するsubscribes
やnotify
が便利です。
service "nginx" do
subscribes :restart, "template[/etc/nginx/conf.d/virtual.conf]"
end
template "/etc/nginx/conf.d/virtual.conf" do
notifies :restart, "service[nginx]"
end
ファイルの転送
template
テンプレートファイル(ERB)を転送することが出来ます。オーナーやパーミッションなども指定が可能です。テンプレートファイルというくらいなので変数で中の数値を変更できます。itamae
コマンド実行時にYAML/JSONファイルで変数を指定できます。
template "/etc/nginx/conf.d/virtual.conf" do
owner "root"
group "root"
mode "644"
source "./templates/etc/nginx/conf.d/virtual.conf.erb"
end
下記は、YAMLファイル形式で指定した場合の例です。
$ itamae ssh -h 192.168.1.100 -u user -y nodes/development.yml recipe.rb
hoge:
piyo: "fuga"
普通にERBで書けるので馴染み深いですね。
<%= node[:hoge][:piyo] %>
remote_file / remote_directory
テンプレートファイルではなく、単純にファイルを転送したい場合はremote_file
やremote_directory
を使うと良いです。ディレクトリ単位でも転送できるので楽です。
remote_file "/etc/nginx/conf.d/.htpasswd" do
owner "nginx"
group "nginx"
source "./templates/etc/nginx/conf.d/htpasswd"
end
remote_directory "./files/etc/nginx/ssl" do
action :create
path "/etc/nginx/ssl"
source "./files/etc/nginx/ssl"
mode "600"
owner "root"
group "root"
end
ディレクトリを作成したい
directory
もう単純にdirectory
指定でディレクトリを作成できます。
directory "/home/user/hoge"
条件を指定して、コマンドを実行したい
execute
自由にコマンドを打ちたい時ってあると思います。レシピゴリゴリ書いて最適化させていこうとすると、どうしてもコマンド書かなきゃいけない時が来る気がします。その場合、コマンドを実行すべきかどうかを判定しなければ、Itamaeを実行するたびに重複して変になってしまいます。そこでnot_if
です。便利です。
指定ファイルに指定文字列があったら実行しない、という感じです。
execute "add rbenv path" do
command <<-"EOH"
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> /home/#{node[:server][:user]}/.bashrc
echo 'eval "$(rbenv init -)"' >> /home/#{node[:server][:user]}/.bashrc
EOH
not_if "grep -q rbenv /home/#{node[:server][:user]}/.bashrc"
end
コマンドの実行結果を受け取りたい
run_command
コマンドを実行した後の出力を受け取って、レシピの中で利用したい時ってあると思います。run_command
を使えば標準出力を受け取れます。第2引数でerror: false
を指定してエラーを無視すると良い感じに動きました。
result = run_command('sudo -i -u postgres cat .bash_profile | grep PGDATA=', error: false)
PGDATA = result.stdout.strip.gsub('PGDATA=', '')
対象サーバの情報を知りたい
ItamaeはSpecinfraというGemを利用していて、Specinfraで取得できるサーバの情報を利用することが出来ます。
p node[:platform] # redhat, ubuntu, darwin etc...
p node[:platform_version] # 7.0
p node[:hostname] # localhost
また、Ohaiにも対応していてOhaiで取得する情報も利用できます。--ohai
オプションを付けるだけです。
$ itamae ssh -h 192.168.1.100 -u user --ohai recipe.rb
ノードファイルのバリデーションをしたい
ノードファイルの値にバリデーションを定義できるようですが、僕はまだ使ったことが無いです。
参考: Use node.validate! in recipes that will be included.
Itamaeプラグインを作る
ItamaeはChefと比べるとエコシステムがまだまだ弱くプラグインの数が少ないです。まぁ、プラグインを使わなくともある程度のことは簡単にできるっていうのもありますが、プラグインがあるなら使いたい!というわけで、皆がプラグインを作ってGemで公開すれば幸せになるので是非作りましょう。作り方も簡単。
Gem名は、itamae-plugin-recipe-hoge
のような形式にします。bunldeコマンドでさくっとGemのテンプレを作ります。
$bundle gem itamae-plugin-recipe-hoge -t
僕が作ったzabbixプラグインのディレクトリ構成です。単純なレシピであればitamae/plugin/recipe/hoge.rb
で完結してもいいし、機能別に分けてhoge::piyo
と呼び出させても良いと思います。
参考: itamae-plugin-recipe-zabbix
.
├── Gemfile
├── Gemfile.lock
├── README.md
├── Rakefile
├── itamae-plugin-recipe-zabbix.gemspec
└── lib
└── itamae
└── plugin
└── recipe
├── zabbix
│ ├── agent.rb
│ ├── templates
│ │ └── etc
│ │ └── zabbix
│ │ └── zabbix_agentd.conf.erb
│ └── version.rb
└── zabbix.rb
独自のリソースを定義する
プラグインを作っていると、独自のリソースを定義したくなることがありますよね。define
で簡単に実現できます。
define :piyo, version: [] do
name = params[:name]
package params[:name] do
version params[:version]
action :install
end
end
piyo 'nginx' do
version '1.6.1'
end
プラグインの書き方は、すでに公開されているプラグインのソースを参考にすると良いかもしれません。
お世話になっているプラグインたち
僕が作ったやつ
CentOS / RedHat系しか考慮されてないので、改善せねば…
さいごに
僕がItamaeに出会って試して実戦投入するまでの期間は約1週間弱くらいです。使い方は1日程度で把握でき、あとは運用の仕方を肌感つかむまでトライアンドエラーを繰り返した感じです。
また、他のメンバーでRubyにあまり縁がないエンジニアでもレシピを改善したりと保守面でも問題なく行けている感じがします。
次は、CloudFormationから、Terraformに行ってAWSなどのクラウドサービスも含めたコード化を進めたいところです。
明日の担当は…@zaruです。がんばります。