55
PaaSの作り方 Sqaleの場合 @hiboma paperboy&co. 1

PaaSの作り方 Sqaleの場合

  • Upload
    hiboma

  • View
    10.193

  • Download
    0

Embed Size (px)

DESCRIPTION

 

Citation preview

Page 1: PaaSの作り方 Sqaleの場合

PaaSの作り方Sqaleの場合

@hiboma paperboy&co.

1

Page 2: PaaSの作り方 Sqaleの場合

• 名前

• 伊藤洋也 (いとう ひろや)

• 所属

• 株式会社 paperboy&co. 6年目

• 技術基盤チーム

• いろいろやる人

• 新卒研修の面倒見 etc

@hiboma

2

Page 3: PaaSの作り方 Sqaleの場合

3

Page 4: PaaSの作り方 Sqaleの場合

• Perl, Ruby, PHP

• Apache, Nginx, MySQL, ...

• C ( ミドルウェア ... Apache 成分多い)

• トラブル調査などで触れてます

• カーネル周りは拾い読み

• 開発はさっぱりできません !!!

業務

4

Page 5: PaaSの作り方 Sqaleの場合

• ゲーム厨の兄がいます

@hiboma

5

Page 6: PaaSの作り方 Sqaleの場合

ブログhttp://d.hatena.ne.jp/hiboma

6

Page 7: PaaSの作り方 Sqaleの場合

ひゃっはー !!!

7

Page 8: PaaSの作り方 Sqaleの場合

アジェンダ• LXCを使ったPaaSの作り方 : Sqale の場合• サービスの概要• コンテナの構築• HTTP, SSHのルーティング • Sqaleで遭遇したカーネルのバグ話 • Xenのバグ• cgroupのバグ

8

Page 9: PaaSの作り方 Sqaleの場合

LXCを使ったPaaSの作り方Sqale の場合

9

Page 10: PaaSの作り方 Sqaleの場合

PaaS?• Platform as a Service

何を持って 「PaaS」を定義するか各論あるかと思いますが ...

• 本トークでは「git pushしたらRuby on Railsがいい感じに動いてくれる」「でもミドルウェアの管理はわからんのでお任せしたい」「 heroku っぽいの」といった意味でお話します ...

10

Page 11: PaaSの作り方 Sqaleの場合

Sqale

11

Page 12: PaaSの作り方 Sqaleの場合

12

Page 13: PaaSの作り方 Sqaleの場合

13

Page 14: PaaSの作り方 Sqaleの場合

14

Page 15: PaaSの作り方 Sqaleの場合

15

Page 16: PaaSの作り方 Sqaleの場合

environment• Sqale は AWSを全面利用しています

• EC2

• EBS (ユーザ領域、Git等のストレージ)

• Route53 (ドメイン管理)

• ELB ( HTTP,SSHDのリバースプロキシの前段, SSL)

• RDS (共有MySQL)

16

Page 17: PaaSの作り方 Sqaleの場合

ホストOS• EC2 Amazon Linux

• CentOS6, SL6 相当 *Q. Ubuntuとか使わないの?A. RedHat系の運用に長けた人が多いので

• いろいろ大変 !!!

• Patched Kernel

• grsecurity patch

• restrcit bind(2) patch ( mizzy )

• fork bomb patch ( mizzy )

* 厳密な意味では全然違うと思いますが ...

17

Page 18: PaaSの作り方 Sqaleの場合

EC2を利用• EC2インスタンス内に LXC をいっぱい生やす

• Ruby環境のコンテナ

• PHP環境のコンテナ

* 厳密な意味では全然違うと思いますが ...

18

Page 19: PaaSの作り方 Sqaleの場合

Rubyコンテナ• Nginx + Ruby(Rackアプリ)を動かせるLXC環境

• Ruby 1.9.3 ~ 2.0.0 の最新パッチレベルを提供

• supervisord で Nginx と Rackアプリを監視

• SSH, Cron 使用可

• デプロイすると bundle install, assets:precompile などを自動で行い Rackアプリを再起動する

19

Page 20: PaaSの作り方 Sqaleの場合

Rubyコンテナ• Nginx は port 8000 + n で listen(2)

• sshd は port 2000 + n で listen(2)

• n は コンテナごとに一意の数値

• network namespace は使用していない

• コンテナ内で TCP port の bind(2) を制限するパッチ当てた

20

Page 21: PaaSの作り方 Sqaleの場合

PHPコンテナ• Apache2.4 + php-fpm を動かせるLXC環境

• 5.3, 5.4 系を提供

• supervisord で Apache と php-fpm を監視

• SSH, Cron 使用可

21

Page 22: PaaSの作り方 Sqaleの場合

rootfs• コンテナ用のファイルツリーを rootfs と呼んでいます

• 全てのコンテナで mount するディレクトリツリー

22

Page 23: PaaSの作り方 Sqaleの場合

$ 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

Page 24: PaaSの作り方 Sqaleの場合

rootfs

• rootfs を lxc-start 起動時に '/' として mount --bind (ro)更に ユーザ領域 (EBS) を mount --bind

• ユーザ領域以外は read only ( errno is EROFS)

24

Page 25: PaaSの作り方 Sqaleの場合

rootfsの共有• pros:

管理の一元化 ディスクスペースの節約

• cons: 個別カスタマイズの自由度は低い

25

Page 26: PaaSの作り方 Sqaleの場合

コンテナ作成

26

Page 27: PaaSの作り方 Sqaleの場合

コンテナ作成• 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

Page 28: PaaSの作り方 Sqaleの場合

UIDの統一• アプリ実行ユーザは全てのLXCで sqale (uid:1000) で統一

28

Page 29: PaaSの作り方 Sqaleの場合

UIDの統一: プロセス数制限• Q. uid が統一されている。 プロセス数上限 どうする?

setrlimit(2) の RLIMIT_NPROC はシステムワイドで各コンテナごとに sqaleユーザ(uid:1000) がいる

• A. id:mizzy の cgroup form bomb パッチで対応uid:1000 の RLIMIT_NPROC は上限まであげとく

29

Page 30: PaaSの作り方 Sqaleの場合

UIDの統一 : クォータ• Q. uid が統一されている。

uidベースのディスククォータ使えないけどどうする?

• A. XFS のプロジェクトクォータで解決ディレクトリ単位でクォータを設定可能

/var/sqale/<ユーザ名>/<アプリ名>/<id>

30

Page 31: PaaSの作り方 Sqaleの場合

リソース制限• Q. コンテナの CPU, メモリのリソース制限は?

• A. cgroup.cpuset, cgroup.memory で基本通り

• lxc-start の設定ファイルに記述

• 動的に変更する運用は(まだ)していない

31

Page 32: PaaSの作り方 Sqaleの場合

lxc-start の監視• lxc-start は monit で監視

lxc-start がバグで止まったことは今ん所観測して無い :)

• ごく初期に supervisord で監視してたが ...supervisord を停止すると lxc-start が orphaned になって厄介なので monit にした

32

Page 33: PaaSの作り方 Sqaleの場合

ルーティング• コンテナを作って次に考えないといけないこと

• ドメイン管理どうする?

• HTTP, SSH, Git ( over SSH) でどうアクセスさせる?

• ロードバランサやリバースプロキシを介してアクセスさせたい

• HTTP Reverse Proxy

• SSH Reverse Proxy

33

Page 34: PaaSの作り方 Sqaleの場合

ドメイン管理• 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

Page 35: PaaSの作り方 Sqaleの場合

HTTPのルーティング

• HTTPのリバースプロキシどうしよう

• うーん Nginx ?

ELB Nginx

35

Page 36: PaaSの作り方 Sqaleの場合

HTTPのルーティング• Q. リバースプロキシ(upstream)設定をどう管理する ?

• 都度設定ファイル足す?

• コンテナが増える/削除する度に?

• 設定のリロード大変そう

• プロキシが複数いたら atomic な変更できない

• DBとの不整合出たらやだなー ... 心配種は尽きない

ELB Nginx

36

Page 37: PaaSの作り方 Sqaleの場合

HTTPのルーティング

• Q. リバースプロキシ先をどう設定する ?

• いい感じに扱いたい

• Nginx に DB, KVS を参照させて解決したい

• そこで Lua ですよ !

• ( or ngx_mruby ! )

37

Page 38: PaaSの作り方 Sqaleの場合

HTTPのルーティング 実装• OpenResty (aka. ngx_openresty)

• 便利モジュールが詰め込まれた Nginx

• 継続的にリリースされてて安心++

38

Page 39: PaaSの作り方 Sqaleの場合

HTTPのルーティング 実装• Redis, MySQL, memcached と連携できる

• Lua で upstream (プロキシ設定) をいじれる

39

Page 40: PaaSの作り方 Sqaleの場合

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

Page 41: PaaSの作り方 Sqaleの場合

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

Page 42: PaaSの作り方 Sqaleの場合

HTTPのルーティング 実装

42

Page 43: PaaSの作り方 Sqaleの場合

SSH Router の実装• Q. SSHのリバースプロキシ、どうしよう ...

• A. OpenSSH にパッチ

• "AuthorizedKeysScript" を追加

• 公開鍵認証を任意のスクリプトに委譲できる

• 公開鍵をDBで照合 => 接続ユーザと収容コンテナを判別

43

Page 44: PaaSの作り方 Sqaleの場合

SSHのルーティング

ELB

SSHRouter

PatchedOpenSSH

SSH

Git

SFTPGit Server

File server

DB (MySQL)

公開鍵認証

・ユーザが登録した公開鍵と照合する・収容されているコンテナを問い合わせる

SSH

Git

SFTP

Git

SFTP

SSH

① ②

44

Page 45: PaaSの作り方 Sqaleの場合

git push のルーティング

ELB

SSHRouter

PatchedOpenSSH

git push

Build Server

Git ServerDB (MySQL)

...

...

① ②

bundle installassets:precompile

etc ...

git の hook スクリプトでキューにjob追加

git clone

rsync

Rackアプリの再起動

45

Page 46: PaaSの作り方 Sqaleの場合

独自ドメインSSL• 1SSLごとにELBを割り当て

• SSL証明書をアップロードしてもらいELBにアタッチ

Redis

ELB

Nginx+

Lua

ELB

独自ドメインHTTPS

+SSL

HTTP

46

Page 47: PaaSの作り方 Sqaleの場合

今日の発表の技術的な詳細

http://slidesha.re/R4Kjjo47

Page 48: PaaSの作り方 Sqaleの場合

Sqaleで遭遇したカーネルのバグ話

48

Page 49: PaaSの作り方 Sqaleの場合

バグその1• リリース後に悩まされたバグ

• 現象

• 稀にホストダウン

• oops メッセージ取れてない

• 特定のコンテナが OOM Killer を連発していた

• 再現

• コンテナで OOM を連続させることで稀に再現

• EC2インスタンスでのみ再現

• 社内のKVMの開発環境だと再現しない

• カーネルのバージョンをいろいろ変えてやっと oops 取れた

49

Page 50: PaaSの作り方 Sqaleの場合

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

Page 51: PaaSの作り方 Sqaleの場合

バグその1• Xenのttyドライバ?のバグぽい

• workaround

• klogctl(2) で コンソールへの printk を無効にすることで回避

• OOMを連発させる負荷テスト必須

/* 6 -- コンソールへの printk を無効にする */ klogctl(6, NULL, 0);

51

Page 52: PaaSの作り方 Sqaleの場合

バグその2

52

Page 53: PaaSの作り方 Sqaleの場合

バグその2

53

Page 54: PaaSの作り方 Sqaleの場合

バグその2

54

Page 55: PaaSの作り方 Sqaleの場合

終わり

55