uwsgi-docker-pycon2015
by
bungoume
×
Copy
Open
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Slide 1
Slide 1 text
uWSGI/Dockerを利用したWebサービス運用事例 PyConJP 2015, 10/11 Yuri Umezaki
Slide 2
Slide 2 text
#PyConJP_M 2
Slide 3
Slide 3 text
自己紹介 梅崎 裕利 ● 会社 ○ 日本経済新聞社デジタル編成局 ● 主な業務 ○ Ansibleでサーバ管理 ○ Django+Elasticsearch(ES)で検索API作成 ○ Fluentd+ES+Kibanaでログ分析 3
Slide 4
Slide 4 text
社内でのPython活用シーン ● Google App Engine (2010〜) ● New API (Search, Authorization, Logging, etc…) ○ Django, django-rest-frameworkなど ○ 全部Python3系で動いています(すでにほぼv3.5) ● ちょっとしたスクリプト ○ Ansible module ○ Slack通知 ○ データ集計・分析 ○ テストツール 4
Slide 5
Slide 5 text
目次 ● uWSGI・Gunicornとは ○ 特徴紹介 ○ Gunicornとの比較 ● サービスを停止させないGracefulな更新 ○ 更新の課題 ○ uWSGIで用意されている機能 ● Dockerとの併用事例 ○ 複数PaaS対応(Docker, Heroku)方法 ○ uWSGI設定例 ● まとめ 5
Slide 6
Slide 6 text
対象 ● 話すこと ○ Djangoなど従来型のPython WebAppの話 ● 話さないこと ○ aiohttpなど非同期なWeb 6
Slide 7
Slide 7 text
uWSGI・Gunicorn 7
Slide 8
Slide 8 text
Python Webアプリケーションを動かす方法 WSGI対応サーバを利用する ● gunicorn ● uwsgi ● Apache+mod_wsgi gunicornは利用事例が多くシンプル、 uwsgiは最近のトレンドで多機能 8
Slide 9
Slide 9 text
質問 ● Gunicorn使っている人? ○ 会場で20% ぐらい ● uWSGI使っている人? ○ 会場で40% ぐらい 9
Slide 10
Slide 10 text
uWSGIは多機能 ● http以外にもuwsgiプロトコルが話せる ● 実はhttpsやspdy、WebSocketも話せる ● 複数のアプリも動かせる(Emperorモード) ● virtualenvなどが使える ● PythonだけでなくRuby, Perl, Lua, PHPなどにも使える ● スプーラ機能 ● プロファイラ ● キャッシュや静的ファイル配信 ● 設定パラメータを数えたら1200以上あった(!!) 10
Slide 11
Slide 11 text
Gunicornはシンプル ● 簡単に起動できる ○ gunicorn --workers=4 app.wsgi ○ uwsgi --http 127.0.0.1:8000 --wsgi-file app/wsgi.py --master --processes 4 ● 利用事例が多い ● uWSGIほど設定項目は多くないが、必要十分 11
Slide 12
Slide 12 text
uWSGIとGunicornのベンチマーク 12
Slide 13
Slide 13 text
ベンチマーク環境 ● Webサーバ ○ AWS EC2 m4.large ○ Amazon Linux AMI 2015.9 ● app ○ Nginx: 1.8.0 (amazon-repo) ○ debug-server (python3.5) ● 負荷かけツール ○ wrk ○ 別サーバより実行 13
Slide 14
Slide 14 text
ベンチマークに利用したアプリケーション ● debug-server ○ https://www.github.com/bungoume/debug-server ○ リクエスト内容をJSONで返すサーバ ○ Django1.8.5 ○ Python 3.5 (branch) 14
Slide 15
Slide 15 text
ベンチマーク内容 以下の秒間レスポンス数を比較 ● nginx + uwsgi(uwsgi-protocol) ● nginx + uwsgi(http) ● uwsgi直接(http) ● nginx + gunicorn(http) ● gunicorn直接(http) (ワーカー数・スレッド数)は(3, 3)と(4, 1)でテスト 15
Slide 16
Slide 16 text
nginxの設定 (共通) ● ほぼamazon linuxデフォルトのまま 16
Slide 17
Slide 17 text
ベンチマーク: uWSGI + nginx (uwsgi) nginxの設定 uwsgiの設定(改行しています) 17
Slide 18
Slide 18 text
ベンチマーク: uWSGI + nginx (uwsgi) レスポンス内容(tcpdump) 18
Slide 19
Slide 19 text
ベンチマーク: uWSGI + nginx (uwsgi) ベンチマーク結果 19
Slide 20
Slide 20 text
ベンチマーク: uWSGI (http)+ nginx nginxの設定 uwsgiの設定 20
Slide 21
Slide 21 text
ベンチマーク: uWSGI (http)+ nginx レスポンス内容(tcpdump) 21
Slide 22
Slide 22 text
httpプロキシの注意 Python側(uwsgi)でRemote_IPやHost, protocolの値が変わってしまう X-Forwarded-ForやX-Forwarded-Host または、RFC7239のForwarded Headerに入れて利用する 22
Slide 23
Slide 23 text
ベンチマーク: uWSGI (http)+ nginx ベンチマーク結果 23
Slide 24
Slide 24 text
ベンチマーク: Gunicorn + nginx nginxの設定 gunicornの設定 24
Slide 25
Slide 25 text
ベンチマーク: Gunicorn + nginx レスポンス内容 25
Slide 26
Slide 26 text
ベンチマーク: Gunicorn + nginx ベンチマーク結果 26
Slide 27
Slide 27 text
ベンチマーク結果まとめ ● 今回試した限りはuWSGIのほうが早かった ● gunicornはthreadを2以上にしたりeventletを有効にしたりすると遅くなった ● (もちろんアプリや設定、環境などによって異なる可能性はあるので注意) nginx+ uwsgi nginx+ uwsgi(http) uwsgi (http) nginx+ gunicorn gunicorn nginx (参考) worker:3 thread:3 1723.10 1747.53 1656.80 1139.12 1260.17 47868.37 worker:4 thread:1 1500.75 1525.72 1608.57 1357.21 1439.42 (req/s) 27
Slide 28
Slide 28 text
uwsgiやgunicornのhttpを直接使うときの注意点 ● gunicorn直接だとheaderとコンテンツが分かれて送信される ● uwsgi直接だとheaderにchunkedが入らない。keep-aliveにならない ● uwsgi直接はkeep-aliveを使うのは少々無理やりな感じになる ○ add-header=Connection: Keep-Alive ● uwsgiはhttp-soketでなくhttpを使ったほうがよい ○ ただし、httpをつかってもkeep-aliveになるわけではない ● nginxを挟むとheaderが追加され、パケットも一つになる 28
Slide 29
Slide 29 text
レスポンス内容: Gunicorn 直接 ヘッダ, body, 終端が別パケットで送られている 29
Slide 30
Slide 30 text
レスポンス内容: uWSGI 直接 コネクションが毎回切れている。chunkedになってない 30
Slide 31
Slide 31 text
uWSGI・Gunicorn まとめ ● gunicornは事例多い・シンプル ● uwsgiはトレンド・多機能 ● 今回のベンチマークでは uwsgi > gunicorn ● uwsgiのhttpやgunicornを直接外部に公開するのは注意 ○ nginxなどのリバースプロキシを通すのが良い 31
Slide 32
Slide 32 text
uWSGIでGracefulな更新 32
Slide 33
Slide 33 text
Gracefulな更新とは ● サービスを止めずにアプリケーションを更新する方法 ● ゼロダウンタイム・ゼロウェイトを実現したい ● いろいろな方法がある ○ DNSで切り替える ○ LBでWebサーバを切り替える ■ 複数のサーバを利用する ■ サーバが切り替わるBrue-Greenデプロイ ○ nginxで振り先を切り替える ○ WSGIサーバでリロードする 33
Slide 34
Slide 34 text
イメージ図 切り替え箇所 新アプリ (サーバIP、 Port、 worker) 旧アプリ (サーバIP、 Port、 worker) クライアント 34 ● 切り替える場所の違い ○ DNS, LBはサーバの切り替え ○ nginxはサーバやSocket、Portの切り替え ○ uWSGIは主にWorkerの切り替え
Slide 35
Slide 35 text
更新の注意点 ● サービスを止めない ○ 新しいアプリケーションが起動する間スローダウン ○ 古いアプリケーションを止めるときにエラーが発生 ● なるべく即座に反映できる ○ DNSで切り替えるとクライアントが旧IPにアクセスし続ける恐れ ● 簡単に更新できるようしておく ○ 上段も設定変更が必要になることがある ○ nginxで切り替える場合はデプロイのたびにnginxの設定変更が必要 ○ 結合度が高まってしまう ● ロールバックもできるように 35
Slide 36
Slide 36 text
uWSGI上で利用できる仕組み ● Standard graceful reload ● Worker reload ● Chain reload ● Zerg mode ● Reuse port ● Master forking ● Subscription system http://uwsgi-docs.readthedocs.org/en/latest/articles/TheArtOfGracefulReloading.html 36
Slide 37
Slide 37 text
Standard Graceful Reload (Prefork, Lazy-app) 動いているWorkerが止まるのを待って再起動する ● 使い方 ○ FIFOにrを書き込む(--maste-fifo オプションでsocketを要しておきechoなどで書き込む) ○ touch-reloadオプションを使う ○ SIGHUPを送る ○ uwsgi.reload() APIを呼ぶ ● メリット ○ 管理が簡単 ○ 軽量なPreforkでも使える ○ 不整合が起きない ● デメリット ○ 長い待ち時間が発生する 37
Slide 38
Slide 38 text
Worker Reload(秒間レスポンス数) ● 約1分間の停止期間が発生(preforkでテスト) 38
Slide 39
Slide 39 text
Worker Reload(平均レスポンス時間) ● 60秒でタイムアウト(504)が発生している ● アプリケーション起動前の502も発生 39
Slide 40
Slide 40 text
Worker Reload(Lazy-app) Workerだけをリロードする ● 使い方 ○ FIFOにwを書き込む ○ touch-worker-reloadオプションを使う ● メリット ○ 全体の再起動が不要になる ● デメリット ○ コードの更新にしか有効でない 40
Slide 41
Slide 41 text
Worker Reload(レスポンス数) ● 2秒ほど止まるが500エラーなどは出ていない 41
Slide 42
Slide 42 text
Worker Reload(レスポンス時間) ● 一瞬詰まるが問題なさそう 42
Slide 43
Slide 43 text
Chain Reload (Lazy-app) Workerを一つひとつ順番にリロードしていく ● 使い方 ○ FIFOにcを書き込む ○ touch-chain-reloadオプションを使う ● メリット ○ ある程度ワーカー数があれば、クライアントの待ち時間を大幅に削減できる ○ リロード時の負荷が少ない ● デメリット ○ コードの更新にしか有効でない 43
Slide 44
Slide 44 text
Chain Reload (レスポンス数) ● 4workers, 2threadsにて。段階的に切り替わっており総数への影響は少ない 44
Slide 45
Slide 45 text
Chain Reload (平均レスポンス時間) ● 待ちは発生していない 45
Slide 46
Slide 46 text
Worker reload, Chain reloadの問題点 uwsgiの設定更新には使えない ● 依存ライブラリ更新ができない ● Pythonのバージョンをあげられない 46
Slide 47
Slide 47 text
Unix socketをプールとして活用する(Zerg mode) マスターのuwsgiを用意してportをUnix socketに変換し、 socketに複数のインスタンスを紐付けて振り分ける ● 使い方 ○ masterデーモンを用意しておき、し、アプリはzergでsocketに割り当てる ○ 更新時は同じsocketで起動し、古いものを落とす ● メリット ○ uWSGIの設定変更も可能になる ○ 待ち時間が少ない ○ ロールバックしやすい ● デメリット ○ 追加で常駐のmasterデーモンが必要で、リロード時は更にプロセスが必要になる ○ 通常と異なる設定が必要。ちょっとわかりにくい 47
Slide 48
Slide 48 text
Zerg mode (レスポンス数) ● 起動時に減るが、大きな影響はなさそう。終了は問題ない(graceful shutdown) 48
Slide 49
Slide 49 text
Zerg mode (平均レスポンス時間) ● 起動時に若干増える。 49
Slide 50
Slide 50 text
同一ポートを利用 (Reuse-Port) Linux≧3.9かBSD系で使えるSO_REUSEPORTを活用する ● 使い方 ○ --reuse-portオプションを使って起動する ○ 新しいアプリを同じポートで起動し、古いものを落とす ● メリット ○ Zerg modeと同じように、uWSGI自体の設定変更も可能 ○ masterデーモンが不要なので管理が楽 ● デメリット ○ カーネルサポートが必要 複数のプロセスで同じTCPポートをバインドできる夢の機能 LinuxだとKernelがバランシングしてくれる 50
Slide 51
Slide 51 text
Reuse Port (レスポンス数) ● 古いものをSIGINT(強制終了)で止める 51
Slide 52
Slide 52 text
Reuse Port (レスポンス数) ● しかし若干エラー(502)が出ている 52
Slide 53
Slide 53 text
Reuse Port (平均レスポンス時間) ● 起動時と終了時に僅かな待ちが発生している。(緑は502) 53
Slide 54
Slide 54 text
Reuse Port (レスポンス数) ● 古いバージョンをgraceful shutdownすると1分間止まってしまう 54
Slide 55
Slide 55 text
Reuse Portについて ● 試したOSはLinux(4.1.7) ● 複数バインド時にどちらに飛ばすかはKernel次第なので細かい制御は難しい ● 夢の機能は万能ではなかった。 55
Slide 56
Slide 56 text
Master forking (黒魔術!) masterを再フォークする ● 使い方 ○ FIFOにfを書き込む ● メリット ○ カーネルサポートも追加プロセスも不要 ○ かなり速い ● デメリット ○ ログやpidなどの一貫性が壊れる 56
Slide 57
Slide 57 text
結果 ● 試した限りではうまく切り替わりませんでした... 57
Slide 58
Slide 58 text
Subscription System サブスクリプションサーバとよばれるロードバランサを用意して紐付ける ● 使い方 ○ uwsgi --fastrouter :1717 --fastrouter-subscription-server 192.168.0.100:2626 ● メリット ○ 簡単かつゼロダウンタイム ○ 構成がシンプル ● デメリット ○ fastserverのようなサブスクリプションサーバが必要 58
Slide 59
Slide 59 text
Gracefulまとめ ● 各機能に特徴があるので要件次第 ● アプリケーションのみを安全に切り替えるならChain Reload ● ライブラリ更新やロールバックを考慮するならZerg Dance ● ごく僅かなエラーより管理のしやすさを優先させるならReuse Port 利点・欠点を見ながら、良いものを選択 59
Slide 60
Slide 60 text
uWSGI+Docker利用事例 60
Slide 61
Slide 61 text
事例 61 ● 新規モバイルアプリ向けAPI ● 規模はまだ小さいサービス ● Djangoを利用して作成
Slide 62
Slide 62 text
優先したこと 62 ● 管理のしやすさ ○ ライブラリを含め、アプリの更新がしやすい ○ 別の環境に移行しやすい ≒ 昨今のコンテナ技術周りに追従しやすい ● 運用のしやすさ ○ サーバにログインしなくても状況がわかるように ○ 壊れたら消せる = ログは別の場所に送っておく
Slide 63
Slide 63 text
最初は優先しない ● 性能 ● 完全にGracefulな更新 63
Slide 64
Slide 64 text
というわけで、 reuse-port を使います 64
Slide 65
Slide 65 text
アプリとインフラの分割単位 ● Herokuを参考 ○ アプリ自体はHerokuでも動かせる形にしておく ● http(またはuwsgiプロトコル)を話すところまでアプリ ○ Pythonバージョンや異存ライブラリはアプリで指定 ● LBやリバースプロキシ、デプロイ自体はインフラ ○ Ansibleやクラウドの機能を利用 65
Slide 66
Slide 66 text
アプリ側のディレクトリ構成 66 ● debug-serverの例 ● heroku,dockerどちらでも動く ○ heroku deploy ○ docker run
Slide 67
Slide 67 text
Dockerfileの中身 ● Dockerfile ● python:3-onbuild 67
Slide 68
Slide 68 text
uWSGIの設定例 68
Slide 69
Slide 69 text
構成図 69
Slide 70
Slide 70 text
現在のデプロイ方法 現在は1ホスト1コンテナの構成 ● 新バージョンのコンテナをビルドする ● 古いバージョンのコンテナIDを控えておく ● reuse-portを使って新バージョンのコンテナを起動する ● 数秒待つ ● 古いコンテナを止める 70
Slide 71
Slide 71 text
運用事例まとめ ● Dockerを1ホスト1コンテナとして利用 ○ 汎用的なVirtualenvとして ● Dockerを利用することでHerokuでもdocker runでも動かせる ● ECSなどのコンテナホスティングにそのまま載せられる ● ログなどの状態は内部に持たないようにしておく 71
Slide 72
Slide 72 text
今後の方針 72 ● nginxやfluentd側もコンテナにする ● デプロイしやすいサービスが出たら乗り換える ○ AWSのECSに移行中
Slide 73
Slide 73 text
まとめ 73
Slide 74
Slide 74 text
74 ● uWSGIは多機能、Gunicornはシンプル ● 今回のベンチではuWSGIのほうがGunicornより速い ● uWSGIはいろいろなGraceful Reload方法がある ● 事例としてはReuse-Portを利用している
Slide 75
Slide 75 text
75 性能・運用のしやすさ・コストなどを 考慮してうまく使うようにしましょう
Slide 76
Slide 76 text
ご清聴ありがとうございました。 76
Slide 77
Slide 77 text
予備スライド 77
Slide 78
Slide 78 text
アクセスログの取り方 78