nginxとluaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

53
NginxLuaを用いた動的 なリバースプロキシでデ プロイを 100 倍速くした サイボウズ株式会社 深谷敏邦 #devsumi [19-G-6] 1

Upload: toshipp

Post on 14-Jul-2015

15.782 views

Category:

Software


3 download

TRANSCRIPT

Page 1: NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

サイボウズ株式会社

深谷敏邦

#devsumi [19-G-6]

1

Page 2: NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

自己紹介

•深谷敏邦

• 2012年サイボウズ株式会社入社

•インフラチーム Hazama所属 (2012/09~)

•お仕事• デプロイツールの作成

• MySQL HA環境の構築

• Apacheのデバッグ・パッチの作成

• Nginxを使ったリバースプロキシの構築

2

Page 3: NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

1. イントロダクション

2. Apache から Nginx へ

3. 実装について

4. 結果とまとめ

3

Page 4: NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

cybozu.comとは

•サービスとして• サイボウズが運営するクラウドサービス

• 2011 年 11 月サービスイン

• 開始から 4 年で導入社数 9000 社以上

4

Page 5: NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

クラウド基盤としての cybozu.com

• 1000 台規模の物理マシン、数倍の VM• 物理機材から全て自社構築

•1億リクエスト/日

•契約ユーザーライセンス数29万人以上

5

Page 6: NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

サービス概要

•お客様ごとにサブドメインを発行

•契約内容に従って複数のサービスが同ドメインで利用可能

6

fukaya-coop.cybozu.com

Page 7: NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

サブドメイン毎の処理

•アプリケーション振り分け

• IP制限

• Basic認証

•クライアント証明書

7

LB

APAP

fukaya-coop.cybozu.com

/o//k/12.34.5.6

Page 8: NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

サブドメイン毎の処理の裏側

• 1 サブドメインにつき 1 設定ファイルを用意

•読み込みのたびに全ファイルのロードが必要

•サブドメインが増えるとロード処理にも時間がかかり….

8

Page 9: NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

サブドメイン数×設定変更にかかる時間

9

150

170

190

210

230

250

270

290

2/17/2014 3/17/2014 4/17/2014 5/17/2014 6/17/2014

Apache再起動時間

設定変更に時間かかり過ぎ

日付~サブドメイン数

2.5分

5分

Page 10: NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

ミッション

•サブドメインの設定変更を1秒で完了させる

10

Page 11: NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

1. イントロダクション

2. Apache から Nginx へ

3. 実装について

4. 結果とまとめ

11

Page 12: NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

これまで

• LB として Apache 2.2を利用

• Apache を普通に使うと• サブドメイン毎に設定ファイルが必要

• 設定ファイルを安全にリロードするには再起動が必要

12

Page 13: NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

仕組みの転換

•1サブドメインの変更のためにすべての設定を読み込む

13

•アクセス毎に対応するサブドメインの設定を動的に読み込む

Page 14: NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

実際どうやるか?

•さすがに自力で全部作るのは厳しい

•候補に挙がったのは以下の 2 つ• Apache 2.4

• nginx

•今の機能を実現できるのは最低ライン

•パッチを当てすぎると本家の変更に追随できなくなる

•そうだLuaがあるじゃないか!

14

Page 15: NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

ということで比較してみる

機能 ◎(当然)

○(何とかいけそう)

Lua△

(experimental)◎

(実績十分)

性能○

(event mpm)◎

(完全イベント駆動)

15決定

Page 16: NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

nginxと lua-nginx-module

• nginxはイベント駆動型の HTTPサーバ• 2015年1月現在シェア 14%

• 大量のリクエストの処理が得意

• lua-nginx-module は HTTPリクエスト処理を luaで書ける拡張モジュール• cybozu.com で必要な設定項目がおおよそ実現できる

• 出来ないものは Cで書く☺

16

Page 17: NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

1. イントロダクション

2. Apache から Nginx へ

3. 実装について

4. 結果とまとめ

17

Page 18: NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

nginxの設定の基本

• location• リクエスト URIに応じて設定を変えるためのディレクティブ

18

location / {root /www/default;

}

location /hoge/ {root /www/hoge;}

• / にアクセスした場合は、/www/default からコンテンツを探す

• /hoge/ にアクセスした場合は、/www/hogeから探す

Page 19: NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

internal redirect

• location 間を移動する仕組み• 処理が異なる複数の location を組み合わせる事ができる

19

location /

location /login/

location /proxy/

nginx

location /maintenance/

メンテナンス中 初回アクセス

アプリケーションサーバへ

ログイン後

Page 20: NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

lua-nginx-module

• HTTP 処理の様々なタイミングで luaで記述した任意のコードを実行出来る• https://github.com/openresty/lua-nginx-module

•可能なこと• HTTPヘッダの読み書き

• location に対するリクエストの発行

• internal redirect の実行

20

Page 21: NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

データベースファイルの読み込み

•リクエスト毎にサブドメインに対応する設定をデータベースファイルから読み込む• key: value の簡単なテキスト形式

• luaを使って Hostヘッダを元にローカルファイルを読む

21

nginx

hagi

hagi.cybozu.com

fukaya-coop.cybozu.com

sato-shoji.cybozu.com

fukaya

-coop

sato-

shoji

Page 22: NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

データベースファイルの読み込み実装例

22

location / {rewrite_by_lua ‘path = (“/settings/” .. ngx.var.host)ret = ngx.location.capture(path)ret.body...

‘;}

location /settings/ {internal;root /var/settings;

}

luaによる設定読み込み用location

DBファイル配信用location

Page 23: NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

データベースファイルの読み込み実装例

23

location / {rewrite_by_lua ‘path = (“/settings/” .. ngx.var.host)ret = ngx.location.capture(path)ret.body...

‘;}

location /settings/ {internal;root /var/settings;

}

• Host ヘッダを元にDBファイルパスを作成

Page 24: NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

データベースファイルの読み込み実装例

24

location / {rewrite_by_lua ‘path = (“/settings/” .. ngx.var.host)ret = ngx.location.capture(path)ret.body...

‘;}

location /settings/ {internal;root /var/settings;

}

• Host ヘッダを元にDBファイルパスを作成

• DBファイルを取得するリクエストを投げる

Page 25: NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

データベースファイルの読み込み実装例

25

location / {rewrite_by_lua ‘path = (“/settings/” .. ngx.var.host)ret = ngx.location.capture(path)ret.body...

‘;}

location /settings/ {internal;root /var/settings;

}

• Host ヘッダを元にDBファイルパスを作成

• DBファイルを取得するリクエストを投げる

• レスポンスボディから設定値を取得

Page 26: NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

アクセス毎に変更すべき設定

1. 利用アプリケーション

2. IP アドレス制限

3. Basic認証

4. クライアント証明書による認証

26

Page 27: NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

1. 利用アプリケーション

•各サブドメイン毎に利用可能なアプリケーションが異なる• 申込時に選択

• 後に追加・削除が可能

• luaから以下の制御を行う• 利用不能なアプリケーションへのアクセスは拒否する

• 利用可能なアプリケーションへのアクセスは適切なアプリケーションサーバにリバースプロキシする

27

nginx

hagi.cybozu.com

fukaya-coop.cybozu.com

garoon

AP

kinton

eAP

g

k

Page 28: NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

利用アプリケーション動的化実装例

28

location / {rewrite_by_lua ‘if use_garoon thenngx.exec(“/proxy/garoon”)

endif use_kintone thengx.exec(“/proxy/kintone”)

endngx.exit(ngx.HTTP_NOT_FOUND)

‘;}

location /proxy/garoon {proxy_pass http://garoon-ap/;

}

location /proxy/kintone {proxy_pass http://kintone-ap/;

}

luaによるアプリケーション利用可否と振り分けロジック

リバースプロキシ用の

location

Page 29: NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

利用アプリケーション動的化実装例

29

location / {rewrite_by_lua ‘if use_garoon thenngx.exec(“/proxy/garoon”)

endif use_kintone thengx.exec(“/proxy/kintone”)

endngx.exit(ngx.HTTP_NOT_FOUND)

‘;}

location /proxy/garoon {proxy_pass http://garoon-ap/;

}

location /proxy/kintone {proxy_pass http://kintone-ap/;

}

• 予め DBファイルの内容を lua

の変数に読み込んでおく• アプリケーションが利用可能ならリバースプロキシ用のlocation に internal redirectする

Page 30: NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

利用アプリケーション動的化実装例

30

location / {rewrite_by_lua ‘if use_garoon thenngx.exec(“/proxy/garoon”)

endif use_kintone thengx.exec(“/proxy/kintone”)

endngx.exit(ngx.HTTP_NOT_FOUND)

‘;}

location /proxy/garoon {proxy_pass http://garoon-ap/;

}

location /proxy/kintone {proxy_pass http://kintone-ap/;

}

• 予め DBファイルの内容を lua

の変数に格納しておく• アプリケーションが利用可能ならリバースプロキシ用のlocation に internal redirectする

• 利用可能なアプリケーションがなければ 404 エラーをクライアントに返す

Page 31: NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

2. IP アドレス制限

•標準モジュールではアクセス毎動的に設定を変更することが出来ない

• luaで IPアドレス制限を実装した

31

• 192.168.0.1

• 192.168.0.2

• 172.16.0.1

fukaya-coop

192.168.0.1

192.168.0.3

Page 32: NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

IP アドレス制限の実装

32

function authenticate_remote_addr(allows)local bit = require("bit")

local remote_addr = ngx.var.binary_remote_addrlocal x0, x1, x2, x3 = string.byte(remote_addr, 1, 4)local ipip = x0 * 16777216ip = x1 * 65536 + ipip = x2 * 256 + ipip = x3 + ip

for i, allow in ipairs(allows) doif bit.band(ip, allow[2]) == bit.tobit(allow[1]) then

return trueend

endreturn false

end

Page 33: NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

IP アドレス制限の実装

33

function authenticate_remote_addr(allows)local bit = require("bit")

local remote_addr = ngx.var.binary_remote_addrlocal x0, x1, x2, x3 = string.byte(remote_addr, 1, 4)local ipip = x0 * 16777216ip = x1 * 65536 + ipip = x2 * 256 + ipip = x3 + ip

for i, allow in ipairs(allows) doif bit.band(ip, allow[2]) == bit.tobit(allow[1]) then

return trueend

endreturn false

end

nginxの内部変数からクライアント IPアドレスを取得する

Page 34: NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

IP アドレス制限の実装

34

function authenticate_remote_addr(allows)local bit = require("bit")

local remote_addr = ngx.var.binary_remote_addrlocal x0, x1, x2, x3 = string.byte(remote_addr, 1, 4)local ipip = x0 * 16777216ip = x1 * 65536 + ipip = x2 * 256 + ipip = x3 + ip

for i, allow in ipairs(allows) doif bit.band(ip, allow[2]) == bit.tobit(allow[1]) then

return trueend

endreturn false

end

データベースファイルから読み込んだ許可リストと比較してアクセス可否を

判定

Page 35: NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

3. Basic認証

•標準モジュールでリクエストごとにパスワードファイルを変更することが出来る

•ただし認証自体は全ドメインにかかってしまう

•一方認証自体を利用するか否かはサブドメイン毎に設定できる

•認証を利用するかどうかを luaで制御するようにした

35

Page 36: NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

nginxのリクエスト処理

• nginxではリクエストはいくつかのフェイズを通って処理される

• Basic 認証は Access フェイズで実施される

36

rewrite フェイズ

access フェイズ

content フェイズ

Basic 認証

Page 37: NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

Basic 認証のスキップ

• Basic 認証を利用しない場合 luaを使って Access フェイズより前に internal redirect する

•後のフェイズが実行されないので Basic認証が行われなくなる

37

rewrite フェイズ

access フェイズ

content フェイズ

luaでスキップ

Page 38: NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

Basic認証の実装例

38

location / {rewrite_by_lua ‘if not use_basic_auth thenngx.exec(“/contents/”)

end‘;

auth_basic “closed site”;auth_basic_user_file /var/passwd/$host;

content_by_lua ‘ngx.exec(“/contents/”)

‘;}

location /contents/ {internal;root /var/www;

}

basic 認証用 location

実コンテンツ用

location

Page 39: NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

Basic認証の実装例

39

location / {rewrite_by_lua ‘if not use_basic_auth thenngx.exec(“/contents/”)

end‘;

auth_basic “closed site”;auth_basic_user_file /var/passwd/$host;

content_by_lua ‘ngx.exec(“/contents/”)

‘;}

location /contents/ {internal;root /var/www;

}

rewrite フェイズ

access フェイズ

content フェイズ

Page 40: NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

Basic認証の実装例

40

location / {rewrite_by_lua ‘if not use_basic_auth thenngx.exec(“/contents/”)

end‘;

auth_basic “closed site”;auth_basic_user_file /var/passwd/$host;

content_by_lua ‘ngx.exec(“/contents/”)

‘;}

location /contents/ {internal;root /var/www;

}

• basic 認証を利用しない場合は /content/に直接飛ばす

Page 41: NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

Basic認証の実装例

41

location / {rewrite_by_lua ‘if not use_basic_auth thenngx.exec(“/contents/”)

end‘;

auth_basic “closed site”;auth_basic_user_file /var/passwd/$host;

content_by_lua ‘ngx.exec(“/contents/”)

‘;}

location /contents/ {internal;root /var/www;

}

• basic 認証を利用する場合は rewrite フェイズでは何もしない

• access フェイズではサブドメイン毎にパスワードファイルを切り替える

Page 42: NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

Basic認証の実装例

42

location / {rewrite_by_lua ‘if not use_basic_auth thenngx.exec(“/contents/”)

end‘;

auth_basic “closed site”;auth_basic_user_file /var/passwd/$host;

content_by_lua ‘ngx.exec(“/contents/”)

‘;}

location /contents/ {internal;root /var/www;

}

• basic 認証を利用する場合は rewrite フェイズでは何もしない

• access フェイズではサブドメイン毎にパスワードファイルを切り替える

• content フェイズで実コンテンツ用 location に移動する

Page 43: NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

4. クライアント証明書による認証

•クライアント証明書は SSL/TLS接続で利用できる認証方法

•サーバ証明書とは逆に HTTPサーバがクライアントに証明書を要求する

•クライアントはサーバが持つ CA が発行した証明書を送信する必要がある

• cybozu.com ではサブドメイン毎に CAを作成

43

LB

クライアント証明書を要求

証明書を送信

証明書を確認

Page 44: NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

クライアント証明書認証の実装上の課題

•標準モジュールではアクセス毎に CAを切り替えるという器用なことは出来ない

• SSL/TLSプロトコルでの認証なので luaでは実装できない• lua-nginx-module は HTTPレベルの処理しか書けない

• nginxに直接パッチを行った

44

Page 45: NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

クライアント証明書認証の実装の概要

•サーバはクライアント証明書の発行者を動的に呼び出して証明書を検証

•ただし他のサブドメインの証明書でも認証が通るためその証明書がアクセス先のサブドメインのものかどうかチェックが必要

45

LB

クライアント証明書を要求

証明書を送信

証明書を確認

CA

クライアント証明書に対応したCAを取得

Page 46: NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

SNIについて

• SNIは TLS拡張の一つで TLSハンドシェイク時にアクセスしたい FQDNをサーバに渡すこと• 名前ベースバーチャルホストでも FQDN毎に証明書を変えることが出来る

•クライアント証明書認証ではサーバは受け付けるクライアント証明書の CAの DNを送信することが出来る• 複数のクライアント証明書を持っている場合ユーザーの証明書選択が楽になる

46

LBこの CAが発行した証明書を送ってね

証明書を送信

fukaya-coop.s.cybozu.com にアクセスしたいよ

Page 47: NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

SNI対応

•パッチでは SNIで送信された FQDNに従って CAをルックアップするようにした• CA は FQDNをファイル名とした通常の PEMファイルとして保存

47

LBこの CAが発行した証明書を送ってね

fukaya-coop.s.cybozu.com にアクセスしたいよ

fukaya-coop.s.cybozu.com

Page 48: NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

その他おこなったこと

• LBを複数サーバにした場合の問題• 各 LB毎に SSLハンドシェイクを行うので、iOS だと証明書選択ダイアログ複数回でてしまう

• LB全体で状態を共有できない

1. 複数サーバー間での SSL セッションキャッシュ共有パッチ

2. DoS対策用同時リクエスト制限モジュールの開発• yrmcdsのセマフォを利用

•いずれも OSSとして公開予定

48

Page 49: NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

1. イントロダクション

2. Apache から Nginx へ

3. 実装について

4. 結果とまとめ

49

Page 50: NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

適用結果

•5分かかっていた設定の反映が1~2秒で終わるようになった

50

0

50

100

150

200

250

300

設定リロード時間

適用前 適用後

100倍以上高速化

Page 51: NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

パフォーマンスについて

•リクエストごとに luaを実行するためパフォーマンスの劣化が懸念されたが問題なかった

51

count count

log10(response time[μs]) log10(response time[μs])

適用前のレスポンスタイムの分布 適用後のレスポンスタイムの分布

Page 52: NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

nginx化のメリット

•大量のコネクションを扱えるので keepalive秒数を伸ばした• 15秒から75秒に

•送信トラフィックが顕著に落ちた• 処理したリクエスト数は減っていない

•サーバ証明書の送信量が減ったことが理由• 中間証明書を合わせると3~4KB

52適用前トラフィック 適用後トラフィック

Page 53: NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした

まとめ

•従来は LBへの設定の反映に5分かかっていた• nginx + luaを使いサブドメイン毎に設定を動的に読み込んだ

• アプリケーションの利用可否

• IP制限

• Basic認証

• クライアント証明書認証

•新しい LBの実装は設定の反映が1秒になった

53