uwsgi-docker-pycon2015
by
bungoume
Link
Embed
Share
Beginning
This slide
Copy link URL
Copy link URL
Copy iframe embed code
Copy iframe embed code
Copy javascript embed code
Copy javascript embed code
Share
Tweet
Share
Tweet
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