PaaSの作り方Sqaleの場合
@hiboma paperboy&co.
1
• 名前
• 伊藤洋也 (いとう ひろや)
• 所属
• 株式会社 paperboy&co. 6年目
• 技術基盤チーム
• いろいろやる人
• 新卒研修の面倒見 etc
@hiboma
2
3
• Perl, Ruby, PHP
• Apache, Nginx, MySQL, ...
• C ( ミドルウェア ... Apache 成分多い)
• トラブル調査などで触れてます
• カーネル周りは拾い読み
• 開発はさっぱりできません !!!
業務
4
• ゲーム厨の兄がいます
@hiboma
5
ひゃっはー !!!
7
アジェンダ• LXCを使ったPaaSの作り方 : Sqale の場合• サービスの概要• コンテナの構築• HTTP, SSHのルーティング • Sqaleで遭遇したカーネルのバグ話 • Xenのバグ• cgroupのバグ
8
LXCを使ったPaaSの作り方Sqale の場合
9
PaaS?• Platform as a Service
何を持って 「PaaS」を定義するか各論あるかと思いますが ...
• 本トークでは「git pushしたらRuby on Railsがいい感じに動いてくれる」「でもミドルウェアの管理はわからんのでお任せしたい」「 heroku っぽいの」といった意味でお話します ...
10
Sqale
11
12
13
14
15
environment• Sqale は AWSを全面利用しています
• EC2
• EBS (ユーザ領域、Git等のストレージ)
• Route53 (ドメイン管理)
• ELB ( HTTP,SSHDのリバースプロキシの前段, SSL)
• RDS (共有MySQL)
16
ホストOS• EC2 Amazon Linux
• CentOS6, SL6 相当 *Q. Ubuntuとか使わないの?A. RedHat系の運用に長けた人が多いので
• いろいろ大変 !!!
• Patched Kernel
• grsecurity patch
• restrcit bind(2) patch ( mizzy )
• fork bomb patch ( mizzy )
* 厳密な意味では全然違うと思いますが ...
17
EC2を利用• EC2インスタンス内に LXC をいっぱい生やす
• Ruby環境のコンテナ
• PHP環境のコンテナ
* 厳密な意味では全然違うと思いますが ...
18
Rubyコンテナ• Nginx + Ruby(Rackアプリ)を動かせるLXC環境
• Ruby 1.9.3 ~ 2.0.0 の最新パッチレベルを提供
• supervisord で Nginx と Rackアプリを監視
• SSH, Cron 使用可
• デプロイすると bundle install, assets:precompile などを自動で行い Rackアプリを再起動する
19
Rubyコンテナ• Nginx は port 8000 + n で listen(2)
• sshd は port 2000 + n で listen(2)
• n は コンテナごとに一意の数値
• network namespace は使用していない
• コンテナ内で TCP port の bind(2) を制限するパッチ当てた
20
PHPコンテナ• Apache2.4 + php-fpm を動かせるLXC環境
• 5.3, 5.4 系を提供
• supervisord で Apache と php-fpm を監視
• SSH, Cron 使用可
21
rootfs• コンテナ用のファイルツリーを rootfs と呼んでいます
• 全てのコンテナで mount するディレクトリツリー
22
$ sudo yum --releasever=$ver --installroot=/var/rootfs/$role/ groupinstall Base
rootfs の構築• Ruby, PHP それぞれ用の rootfs ( ≒ chroot 環境) を作る
1. yum --installroot でベースを作る
2. ruby や chef-solo も入れる3. chroot して chef-solo でペチペチして構築 ・Nginx とか Apache とか Ruby, PHP をいれてく
• 構築時に lxc のテンプレートは使ってない
• お手本として何度も参照しました :)
23
rootfs
• rootfs を lxc-start 起動時に '/' として mount --bind (ro)更に ユーザ領域 (EBS) を mount --bind
• ユーザ領域以外は read only ( errno is EROFS)
24
rootfsの共有• pros:
管理の一元化 ディスクスペースの節約
• cons: 個別カスタマイズの自由度は低い
25
コンテナ作成
26
コンテナ作成• lxc-start の設定ファイル作成、起動 • ユーザ領域のディレクトリの作成• 監視設定の更新 など• Resque + chef-solo
app(Rails) Redis
https://sqale.jp/projects/new からアプリ作成Redis に job を enqueue する
Resquedequeue
enqueue
Resque が job を dequeue
chef-solo で LXCの設定をセットアップしてlxc-start を起動する
chef-solo
27
UIDの統一• アプリ実行ユーザは全てのLXCで sqale (uid:1000) で統一
28
UIDの統一: プロセス数制限• Q. uid が統一されている。 プロセス数上限 どうする?
setrlimit(2) の RLIMIT_NPROC はシステムワイドで各コンテナごとに sqaleユーザ(uid:1000) がいる
• A. id:mizzy の cgroup form bomb パッチで対応uid:1000 の RLIMIT_NPROC は上限まであげとく
29
UIDの統一 : クォータ• Q. uid が統一されている。
uidベースのディスククォータ使えないけどどうする?
• A. XFS のプロジェクトクォータで解決ディレクトリ単位でクォータを設定可能
/var/sqale/<ユーザ名>/<アプリ名>/<id>
30
リソース制限• Q. コンテナの CPU, メモリのリソース制限は?
• A. cgroup.cpuset, cgroup.memory で基本通り
• lxc-start の設定ファイルに記述
• 動的に変更する運用は(まだ)していない
31
lxc-start の監視• lxc-start は monit で監視
lxc-start がバグで止まったことは今ん所観測して無い :)
• ごく初期に supervisord で監視してたが ...supervisord を停止すると lxc-start が orphaned になって厄介なので monit にした
32
ルーティング• コンテナを作って次に考えないといけないこと
• ドメイン管理どうする?
• HTTP, SSH, Git ( over SSH) でどうアクセスさせる?
• ロードバランサやリバースプロキシを介してアクセスさせたい
• HTTP Reverse Proxy
• SSH Reverse Proxy
33
ドメイン管理• Route53で sqale.jp ドメインを管理
• CNAME を ELB に向けている
•• コンテナ作成/破棄のタイミングで Route53 の API を叩く
ELB Proxy
CNAME: ruby-hiboma.sqale.jp
ruby-hiboma.sqale.jp. 0 IN CNAME proxy-lb001-******.ap-northeast-1.elb.amazonaws.com.
Route53
HTTP
DNS
34
HTTPのルーティング
• HTTPのリバースプロキシどうしよう
• うーん Nginx ?
ELB Nginx
35
HTTPのルーティング• Q. リバースプロキシ(upstream)設定をどう管理する ?
• 都度設定ファイル足す?
• コンテナが増える/削除する度に?
• 設定のリロード大変そう
• プロキシが複数いたら atomic な変更できない
• DBとの不整合出たらやだなー ... 心配種は尽きない
ELB Nginx
36
HTTPのルーティング
• Q. リバースプロキシ先をどう設定する ?
• いい感じに扱いたい
• Nginx に DB, KVS を参照させて解決したい
• そこで Lua ですよ !
• ( or ngx_mruby ! )
37
HTTPのルーティング 実装• OpenResty (aka. ngx_openresty)
• 便利モジュールが詰め込まれた Nginx
• 継続的にリリースされてて安心++
38
HTTPのルーティング 実装• Redis, MySQL, memcached と連携できる
• Lua で upstream (プロキシ設定) をいじれる
39
HTTPの動的ルーティング
Redis
ELBNginx
+Lua
CNAME: ruby-hiboma.sqale.jp
Hostヘッダ (CNAME) 内部ホスト
ruby-hiboma.sqale.jp users001.sqale.lan:8000
php-sushi.sqale.jp users002.sqale.lan:8100
users001.sqale.lan:8001
Hostヘッダ(CNAME) を Redis に投げて 内部ホストに変換
CNAME を ELB に向けてる(Route53管理)
① ②
③
④
40
HTTPの動的ルーティングhttp { server { listen 8888; server_name localhost; location / { set $upstream ""; rewrite_by_lua ' local res = ngx.location.capture("/redis") if res.status == ngx.HTTP_OK then ngx.var.upstream = res.body else ngx.exit(ngx.HTTP_FORBIDDEN) end '; proxy_pass http://$upstream; } # HostヘッダをキーにしてRedisに問い合わせ location /redis { internal; set $redis_key $host; redis_pass 127.0.0.1:6379; default_type text/html; } }
location / { set $upstream ""; rewrite_by_lua ' local res = ngx.location.capture("/redis") if res.status == ngx.HTTP_OK then ngx.var.upstream = res.body else ngx.exit(ngx.HTTP_FORBIDDEN) end '; proxy_pass http://$upstream; } # HostヘッダをキーにしてRedisに問い合わせ location /redis { internal; set $redis_key $host; redis_pass 127.0.0.1:6379; default_type text/html; }
41
HTTPのルーティング 実装
42
SSH Router の実装• Q. SSHのリバースプロキシ、どうしよう ...
• A. OpenSSH にパッチ
• "AuthorizedKeysScript" を追加
• 公開鍵認証を任意のスクリプトに委譲できる
• 公開鍵をDBで照合 => 接続ユーザと収容コンテナを判別
43
SSHのルーティング
ELB
SSHRouter
PatchedOpenSSH
SSH
Git
SFTPGit Server
File server
DB (MySQL)
公開鍵認証
・ユーザが登録した公開鍵と照合する・収容されているコンテナを問い合わせる
SSH
Git
SFTP
Git
SFTP
SSH
① ②
③
④
44
git push のルーティング
ELB
SSHRouter
PatchedOpenSSH
git push
Build Server
Git ServerDB (MySQL)
...
...
① ②
③
④
bundle installassets:precompile
etc ...
git の hook スクリプトでキューにjob追加
git clone
rsync
Rackアプリの再起動
45
独自ドメインSSL• 1SSLごとにELBを割り当て
• SSL証明書をアップロードしてもらいELBにアタッチ
Redis
ELB
Nginx+
Lua
ELB
独自ドメインHTTPS
+SSL
HTTP
46
Sqaleで遭遇したカーネルのバグ話
48
バグその1• リリース後に悩まされたバグ
• 現象
• 稀にホストダウン
• oops メッセージ取れてない
• 特定のコンテナが OOM Killer を連発していた
• 再現
• コンテナで OOM を連続させることで稀に再現
• EC2インスタンスでのみ再現
• 社内のKVMの開発環境だと再現しない
• カーネルのバージョンをいろいろ変えてやっと oops 取れた
49
Jun 26 20:14:48 users900 kernel: [71494876.304224] ------------[ cut here ]------------Jun 26 20:14:48 users900 kernel: [71494876.304225] kernel BUG at drivers/tty/hvc/hvc_xen.c:101!Jun 26 20:14:48 users900 kernel: [71494876.304227] invalid opcode: 0000 [#1] SMPJun 26 20:14:48 users900 kernel: [71494876.304381] Modules linked in: ipv6 binfmt_misc xfs libcrc32c dm_mirror dm_region_hash dm_log dm_mod crc32c_intel pcspkr microcode ext4 jbd2 mbcache crc16Jun 26 20:14:48 users900 kernel: [71494876.304384] CPU 0Jun 26 20:14:48 users900 kernel: [71494876.304384] CPU 0Jun 26 20:14:48 users900 kernel: [71494876.304385] Pid: 7746, comm: perl Not tainted 3.9.7 #2Jun 26 20:14:48 users900 kernel: [71494876.304389] RIP: e030:[<ffffffff81285bac>] [<ffffffff81285bac>] domU_write_console+0x13c/0x180Jun 26 20:14:48 users900 kernel: [71494876.304390] RSP: e02b:ffff8808502f9898 EFLAGS: 00010002Jun 26 20:14:48 users900 kernel: [71494876.304391] RAX: 00000000004a62c6 RBX: 0000000000000000 RCX: 0000000000000010Jun 26 20:14:48 users900 kernel: [71494876.304392] RDX: 0000000000000823 RSI: 0000000000000000 RDI: 0000000000000000Jun 26 20:14:48 users900 kernel: [71494876.304393] RBP: ffff8808502f98e8 R08: ffff8800076fc000 R09: 00000000004a5aa3Jun 26 20:14:48 users900 kernel: [71494876.304393] R10: 0000000000000000 R11: 00000000000061df R12: 0000000000000010Jun 26 20:14:48 users900 kernel: [71494876.304394] R13: 0000000000000000 R14: ffff8808502f9910 R15: ffff88085fc09000Jun 26 20:14:48 users900 kernel: [71494876.304398] FS: 00007fecd7312720(0000) GS:ffff880887000000(0000) knlGS:0000000000000000Jun 26 20:14:48 users900 kernel: [71494876.304399] CS: e033 DS: 0000 ES: 0000 CR0: 000000008005003bJun 26 20:14:48 users900 kernel: [71494876.304400] CR2: 00007fecb1e15000 CR3: 000000084edf0000 CR4: 0000000000002660Jun 26 20:14:48 users900 kernel: [71494876.304400] CR2: 00007fecb1e15000 CR3: 000000084edf0000 CR4: 0000000000002660Jun 26 20:14:48 users900 kernel: [71494876.304401] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000Jun 26 20:14:48 users900 kernel: [71494876.304402] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400Jun 26 20:14:48 users900 kernel: [71494876.304403] Process perl (pid: 7746, threadinfo ffff8808502f8000, task ffff88084f1016d0)Jun 26 20:14:48 users900 kernel: [71494876.304403] Process perl (pid: 7746, threadinfo ffff8808502f8000, task ffff88084f1016d0)Jun 26 20:14:48 users900 kernel: [71494876.304403] Stack:Jun 26 20:14:48 users900 kernel: [71494876.304405] ffff8808502f98e8 ffff8808502f98a8 0000000000000007 000000020000000aJun 26 20:14:48 users900 kernel: [71494876.304406] 000000000442ecdc 0000000000000010 000000000000003c 0000000000000020Jun 26 20:14:48 users900 kernel: [71494876.304407] 0000000000000000 ffffffff817cd1e0 ffff8808502f9958 ffffffff8128437cJun 26 20:14:48 users900 kernel: [71494876.304408] Call Trace:Jun 26 20:14:48 users900 kernel: [71494876.304410] [<ffffffff8128437c>] hvc_console_print+0xdc/0x130Jun 26 20:14:48 users900 kernel: [71494876.304414] [<ffffffff81044683>] call_console_drivers.constprop.10+0x93/0xb0Jun 26 20:14:48 users900 kernel: [71494876.304416] [<ffffffff8104548d>] console_unlock+0x1fd/0x3e0Jun 26 20:14:48 users900 kernel: [71494876.304416] [<ffffffff8104548d>] console_unlock+0x1fd/0x3e0Jun 26 20:14:48 users900 kernel: [71494876.304418] [<ffffffff81045ad5>] vprintk_emit+0x245/0x480Jun 26 20:14:48 users900 kernel: [71494876.304420] [<ffffffff810da519>] ? find_lock_task_mm+0x29/0x70Jun 26 20:14:48 users900 kernel: [71494876.304422] [<ffffffff813bae75>] printk+0x5c/0x5eJun 26 20:14:48 users900 kernel: [71494876.304423] [<ffffffff813bbd78>] dump_header+0x18d/0x1ceJun 26 20:14:48 users900 kernel: [71494876.304425] [<ffffffff810da906>] oom_kill_process+0x1b6/0x320Jun 26 20:14:48 users900 kernel: [71494876.304427] [<ffffffff8111eeb1>] ? mem_cgroup_iter+0x1a1/0x1f0Jun 26 20:14:48 users900 kernel: [71494876.304429] [<ffffffff8104e630>] ? has_ns_capability_noaudit+0x10/0x20Jun 26 20:14:48 users900 kernel: [71494876.304430] [<ffffffff811216b6>] __mem_cgroup_try_charge+0xb16/0xb40Jun 26 20:14:48 users900 kernel: [71494876.304432] [<ffffffff81121c40>] ? mem_cgroup_charge_common+0x60/0x60Jun 26 20:14:48 users900 kernel: [71494876.304434] [<ffffffff81121c12>] mem_cgroup_charge_common+0x32/0x60Jun 26 20:14:48 users900 kernel: [71494876.304435] [<ffffffff81123372>] mem_cgroup_newpage_charge+0x22/0x30Jun 26 20:14:48 users900 kernel: [71494876.304437] [<ffffffff810f8124>] handle_pte_fault+0x6c4/0xa10Jun 26 20:14:48 users900 kernel: [71494876.304439] [<ffffffff81007373>] ? pte_mfn_to_pfn+0x93/0x110Jun 26 20:14:48 users900 kernel: [71494876.304440] [<ffffffff81004859>] ? __raw_callee_save_xen_pmd_val+0x11/0x1e
50
バグその1• Xenのttyドライバ?のバグぽい
• workaround
• klogctl(2) で コンソールへの printk を無効にすることで回避
• OOMを連発させる負荷テスト必須
/* 6 -- コンソールへの printk を無効にする */ klogctl(6, NULL, 0);
51
バグその2
52
バグその2
53
バグその2
54
終わり
55