SlideShare a Scribd company logo
@hiboma
Sqale の
Puppet と Chef
2013年5月10日金曜日
• 伊藤洋也 (いとう ひろや)
• 株式会社 paperboy&co. 6年目氏
• 弟 30年目氏
• 技術基盤エンジニア
• ホスティングの開発業務に関わってる時間が多め
@hiboma
2013年5月10日金曜日
どっちも買って下さい
2013年5月10日金曜日
前置きの話
• レンタルサーバー / ホスティング
• 分譲マンションモデル
• リソースの切り売り
2013年5月10日金曜日
ユーザーで共有されるリソース
システム管理者用リソース
PHP Perl Ruby Python Apache postfix sshd
user A user B user C user D
ユーザーごとのリソース (任意のタイミングで追加/削除)
レンサバ モデルレイヤ
nagios munin fluentd ldap
ftpd
resque
2013年5月10日金曜日
Sqale
2013年5月10日金曜日
2013年5月10日金曜日
2013年5月10日金曜日
http://www.slideshare.net/mizzy/inside-sqales-backend-at-sapporo-ruby-kaigi-2012
Sqaleのバックエンド諸々
2013年5月10日金曜日
* PaaS *
2013年5月10日金曜日
今日の話
• アプリがうごく環境
• 「コンテナ」と呼んでいます
2013年5月10日金曜日
Sqaleのコンテナ
• Rubyコンテナ
• Nginx + Rackアプリ
• PHPコンテナ
• Apache2.4 + PHP-FPM
2013年5月10日金曜日
コンテナの実体
• LXC ( Linux Container )
• chroot + namespace + cgroup
• 仮想化 = 隔離、分離、制限
• プロセスレベルでの仮想化
• chroot な環境を作らないといけない !
• 構成管理だ!
2013年5月10日金曜日
構成管理
• ということで構成管理の話
2013年5月10日金曜日
構成管理
• Sqale は 基本的に Puppet 管理
• appサーバー(Rails)とか
• gitリポジトリ管理サーバーとか
• アプリをビルドする build サーバーとか
• queueサーバー(resque) とか ... etc
• ユーザのアプリを動かすサーバーが特殊
• Puppet と Chef
2013年5月10日金曜日
EC2 Instance
PHP Apache phpenv ruby unicorn nginx
chef-solo で chroot環境chef-solo で chroot環境
ldap munin nagios rbenv kernel cgroup
Puppet の土台
/var/rootfs/ruby/var/rootfs/php
lxc-start lxc-start lxc-start lxc-start
2013年5月10日金曜日
Puppetのレイヤ
• LDAP とか munin とか nagios とか 何かいろいろ
• ログインしてオペできる土台
• カーネルも
• LXC を動かせる土台
2013年5月10日金曜日
Puppet + AWS 自動化
http://www.slideshare.net/lamanotrama/on-aws-sqale
2013年5月10日金曜日
Chefのレイヤ
• Puppet の土台の上に
• chroot 環境を chef-solo で作る
• ユーザーごとの環境も chef-solo で作る
• ユーザー追加のタイミングで作る
2013年5月10日金曜日
Chefでchroot環境
• chroot 環境を Chef 管理
• /var/rootfs/php # PHP用
• /var/rootfs/ruby # Ruby用
• chroot してから chef-solo 流す
• レシピは mount --bind してホストOSを参照
• レシピの作り方は慣例通り
• Ruby や PHPに必要なものをガッと入れる
2013年5月10日金曜日
Why Chef ?
• chroot するので ホスト名ベースでレシピを当てられない
• なので chef-solo にした
• puppet apply でもできるかも
2013年5月10日金曜日
#レンサバあるある
2013年5月10日金曜日
#レンサバあるある
• ユーザを追加する度に環境作成
• アカウントの追加
• ストレージ ($HOME) の作成
• mkdir したり
• uid:gid 変えたり
• パーミッション変えたり
• ミドルウェアの設定追加
• 例) httpd.conf
2013年5月10日金曜日
#ホスティングあるある
• たんぽぽ作業
• chef-solo で作ろう
2013年5月10日金曜日
ユーザー作成
user node.username do
action :create
end
directory "/var/www/#{node.username}" do
owner node.username
group node.username
action :create
mode "0755"
end
template "/etc/httpd/conf.d/#{node.username}.conf" do
notifies :restart, "service[httpd]"
source "httpd.conf.erb"
owner "apache"
group "apache"
mode "0644"
end
service "httpd" do
action :restart
end
{
"username": "sushi",
}
attribute/sushi.json
2013年5月10日金曜日
ユーザー作成
• attributeで変数を注入 *1
• ユーザー名を変えて環境複製
• べき等性 = 上書き、やり直しも簡単
(注) 任意の値を入れると死ぬので要バリデート
2013年5月10日金曜日
ユーザー削除
• 消す時にも便利
• べき等性が効いてくる
user node.username do
action :remove
end
directory "/var/www/#{node.username}" do
action :delete
end
file "/etc/httpd/conf.d/#{node.username}.conf" do
notifies :restart, "service[httpd]"
action :delete
end
service "httpd" do
action :restart
end
2013年5月10日金曜日
chef-solo -j <URL>
• でも attribute ファイル作るの面倒じゃないですか ...
• chef-solo -j <URL> 便利 そう
$ sudo chef-solo -j http://chef-api.example.com/user/fuga
chef-api.example.com
ユーザ収容のサーバーs
① kick
② chef-solo
2013年5月10日金曜日
Sqale
• sqale でも ユーザごとに attribute を作って管理
2013年5月10日金曜日
コンテナ作成のchef-solo
{
"run_list": [
"role[create_container_ruby]"
],
"sqale": {
"consecutive_number": "1",
"container_id": "1",
"port_offset": "0",
"project_name": "ruby",
"role": "ruby",
"username": "namahage"
}
}
$ sudo chef-solo -j namahage_sqale_1.json
• Resqueワーカーが chef-solo
attribute/namahage-ruby-1.json
2013年5月10日金曜日
仕様変更
• 仕様変更したい場合も chef-solo
• 1. レシピ更新
• 2. chef-solo 再度実行
• 3. コンテナのプロセスを再起動
• 4. 仕様 update !
• ユーザ名に依存しちゃうような仕様を変える際に便利
2013年5月10日金曜日
DSL
• #{node.key.value} と埋め込むのはイマイチ
• ふつうのRubyのコードっぽくなるし ...
• 内部DSLを逸脱するので若干 BK な匂い
2013年5月10日金曜日
DSL
• ラップするクラスを入れてキレイに
• "define" でラップするのも良いですかね
directory lxc.userfs_root "/home/sqale/current" do
owner "sqale"
group "sqale"
mode "0755"
recursive true
action :create
end
directory "/var/sqale/namahage/ruby/1/home/sqale/current"
after
2013年5月10日金曜日
• puppet apply でもできそう ?
• antipopさん お願いします
2013年5月10日金曜日
混ぜるな危険 !!!!1
• Puppet と Chef が混在 していましたが ...
• 管理しているスコープが違うのでOK
• 特殊事例
2013年5月10日金曜日
#テスト
2013年5月10日金曜日
#テスト
• 「Puppet, Chef」でプロビジョニング しました!!」
• 期待通り動くのかな ?
• 手作業で確認するの ?
• 変更する度に ?
2013年5月10日金曜日
Infrastructure as Code
• コード書いたらテストですよねー
2013年5月10日金曜日
#あるある
• パッケージの追加が怖い
• パッケージのアップデートが怖い
• パッケージの削除が怖い
• 設定追加が怖い
• 設定変更が怖い
• 設定削除が怖い
• 意図しないところで不具合
2013年5月10日金曜日
#あるある
• 振る舞いが把握できてないとこわい
2013年5月10日金曜日
#テスト
• 振る舞いをテスト = 動く仕様
• 「あれ、この設定なんだったかな...」
• テストで思い出す
• require "serverspec"
• Puppet, Chef のリファクタリング
• 振る舞いを保証して マニフェスト/レシピをいじる
2013年5月10日金曜日
EC2 Instance
PHP Apache phpenv ruby unicorn nginx
Chef の chroot環境Chef の chroot環境
ldap munin nagios rbenv kernel cgroup
Puppet の土台
/var/rootfs/ruby/var/rootfs/php
lxc-start lxc-start lxc-start lxc-start
2013年5月10日金曜日
Sqaleのテスト
• 1. chef-solo で LXCの作成/起動
• 2. LXCの外からプロセスが起動しているかテスト
• nginx, unicorn, supervisord, sshd, syslogd ...
• 3. SSHでログイン、コンテナでRSpecを実行
• 4. chef-soloで LXCの停止/削除
2013年5月10日金曜日
RSpecの中味
• ユーザーさんの行動を模擬テスト
• SSHログインできる?
• /etc/init.d/nginx で操作できる?
• Rails動く? Sinatra動く?
• bundle install できる?
• シェルコマンド使える?
• ログ見れる?
• cron 動いてる ?
• セキュリティ制限効いてる?
• cgroupのリソース制限効いてる?
2013年5月10日金曜日
RSpec / Railsの例
describe "rubyコンテナでRailsをUnicornで動かす" do
before(:all) do
system("/etc/init.d/rails stop")
end
it "アプリケーション /home/sqale/current に配置することができること" do
system("tar xfz /home/sqale/rspec/misc/rails_app.tar.gz").should be_true
system("rm -rf /home/sqale/current").should be_true
system("mv rails_app /home/sqale/current").should be_true
end
it "Railsを /etc/init.d/rails で 起動することができる" do
system("/etc/init.d/rails start").should be_true
end
it "HTTPリクエストを出して200が返ってくること" do
response = http_get
response.code.should == "200"
response.body.should == "hello,world"
end
end
2013年5月10日金曜日
RSpec / rbenvの例
%w{ 2.0.0-p0 2.0.0-preview1 1.9.3-p327 1.9.3-p286 1.9.3-p194 }.each do |version|
context "rbenv で #{version} に切り替える場合に" do
it "バージョン切り替えできていること" do
rbenv_global(version)
end
it "rubyのパスが通っていること" do
system("which ruby >/dev/null").should be_true
end
it "irbのパスが通っていること" do
system("which irb >/dev/null").should be_true
end
it "gemsのパスが通っていること" do
system("which gem >/dev/null").should be_true
end
it "bundlerのバージョンが #{@bundler_version} であること" do
`bundle -v`.split[2].should == @bundler_version
end
end
2013年5月10日金曜日
RSpec / PHP Pearの例
describe "phpコンテナのpear" do
it "pearでライブラリをインストールできること" do
system("pear install Numbers_Roman").should be_true
end
it "pearでインストールしたライブラリを読み込めること" do
File.write("numbers_roman.php", <<'....')
<?php
require_once 'Numbers/Roman.php';
$roman = new Numbers_Roman();
echo $roman->toNumeral(1);
....
`php numbers_roman.php`.chomp.should == "I"
end
end
2013年5月10日金曜日
#お問い合わせあるある
• 「○○コマンドは使えますか?」
• 「○○ライブラリは使えますか?」
• 確認作業 => テストで残しておく
• サポートした仕様がデグレードしないようにする
2013年5月10日金曜日
#テスト
• 「○○できない」こともテスト
• su, sudo できないとか
• LXC を reboot できないとか
• forkbomb 対策できてるかとか
• されると困るアクションをテスト
2013年5月10日金曜日
Rspec / ○○できない
it "/var/log/messagesを読み込めないこと" do
expect { File.read("/var/log/messages") }.to raise_error(Errno::EACCES)
end
it "/var/log/secureを読み込めないこと" do
expect { File.read("/var/log/messages") }.to raise_error(Errno::EACCES)
end
it "cgroup の fork.remaing で fork bomb対策が施されている" do
# forkbomb するワンライナー
system "perl -e 'for(0..255){$i = fork;exit 255 unless defined $i;$i == 0 && sleep 1 && exit 0; }'"
$?.success?.should be_false
end
context "カーネルの制限で sqaleユーザーはポートをbindできない" do
it "httpdの予約ポート 8000 ~ 10000 をbindできない" do
(8000..10000).each do |port|
expect { TCPServer.open(port) }.to raise_error(Errno::EACCES)
end
end
2013年5月10日金曜日
#テスト
• 手続きをRSpec化
• 一般ユーザ権限で実行 = ユーザーの取れる行動
• パッケージの有無とかは見てない
• ホスティングだと価値高いテスト
2013年5月10日金曜日
SqaleのJenkins
• Jenkins で毎日 LXC のテスト
• yum も毎回 update
• RSpecが通る = 振る舞い保証
2013年5月10日金曜日
終わり
2013年5月10日金曜日

More Related Content

Sqale の Puppet と Chef (と テスト)