uwsgi-docker-pycon2015

D405f3b9dc9fa223f6fa507717f41372?s=47 bungoume
October 11, 2015

 uwsgi-docker-pycon2015

D405f3b9dc9fa223f6fa507717f41372?s=128

bungoume

October 11, 2015
Tweet

Transcript

  1. uWSGI/Dockerを利用したWebサービス運用事例 PyConJP 2015, 10/11 Yuri Umezaki

  2. #PyConJP_M 2

  3. 自己紹介 梅崎 裕利 • 会社 ◦ 日本経済新聞社デジタル編成局 • 主な業務 ◦

    Ansibleでサーバ管理 ◦ Django+Elasticsearch(ES)で検索API作成 ◦ Fluentd+ES+Kibanaでログ分析 3
  4. 社内でのPython活用シーン • Google App Engine (2010〜) • New API (Search,

    Authorization, Logging, etc…) ◦ Django, django-rest-frameworkなど ◦ 全部Python3系で動いています(すでにほぼv3.5) • ちょっとしたスクリプト ◦ Ansible module ◦ Slack通知 ◦ データ集計・分析 ◦ テストツール 4
  5. 目次 • uWSGI・Gunicornとは ◦ 特徴紹介 ◦ Gunicornとの比較 • サービスを停止させないGracefulな更新 ◦

    更新の課題 ◦ uWSGIで用意されている機能 • Dockerとの併用事例 ◦ 複数PaaS対応(Docker, Heroku)方法 ◦ uWSGI設定例 • まとめ 5
  6. 対象 • 話すこと ◦ Djangoなど従来型のPython WebAppの話 • 話さないこと ◦ aiohttpなど非同期なWeb

    6
  7. uWSGI・Gunicorn 7

  8. Python Webアプリケーションを動かす方法 WSGI対応サーバを利用する • gunicorn • uwsgi • Apache+mod_wsgi gunicornは利用事例が多くシンプル、

    uwsgiは最近のトレンドで多機能 8
  9. 質問 • Gunicorn使っている人? ◦ 会場で20% ぐらい • uWSGI使っている人? ◦ 会場で40%

    ぐらい 9
  10. uWSGIは多機能 • http以外にもuwsgiプロトコルが話せる • 実はhttpsやspdy、WebSocketも話せる • 複数のアプリも動かせる(Emperorモード) • virtualenvなどが使える •

    PythonだけでなくRuby, Perl, Lua, PHPなどにも使える • スプーラ機能 • プロファイラ • キャッシュや静的ファイル配信 • 設定パラメータを数えたら1200以上あった(!!) 10
  11. Gunicornはシンプル • 簡単に起動できる ◦ gunicorn --workers=4 app.wsgi ◦ uwsgi --http

    127.0.0.1:8000 --wsgi-file app/wsgi.py --master --processes 4 • 利用事例が多い • uWSGIほど設定項目は多くないが、必要十分 11
  12. uWSGIとGunicornのベンチマーク 12

  13. ベンチマーク環境 • Webサーバ ◦ AWS EC2 m4.large ◦ Amazon Linux

    AMI 2015.9 • app ◦ Nginx: 1.8.0 (amazon-repo) ◦ debug-server (python3.5) • 負荷かけツール ◦ wrk ◦ 別サーバより実行 13
  14. ベンチマークに利用したアプリケーション • debug-server ◦ https://www.github.com/bungoume/debug-server ◦ リクエスト内容をJSONで返すサーバ ◦ Django1.8.5 ◦

    Python 3.5 (branch) 14
  15. ベンチマーク内容 以下の秒間レスポンス数を比較 • nginx + uwsgi(uwsgi-protocol) • nginx + uwsgi(http)

    • uwsgi直接(http) • nginx + gunicorn(http) • gunicorn直接(http) (ワーカー数・スレッド数)は(3, 3)と(4, 1)でテスト 15
  16. nginxの設定 (共通) • ほぼamazon linuxデフォルトのまま 16

  17. ベンチマーク: uWSGI + nginx (uwsgi) nginxの設定 uwsgiの設定(改行しています) 17

  18. ベンチマーク: uWSGI + nginx (uwsgi) レスポンス内容(tcpdump) 18

  19. ベンチマーク: uWSGI + nginx (uwsgi) ベンチマーク結果 19

  20. ベンチマーク: uWSGI (http)+ nginx nginxの設定 uwsgiの設定 20

  21. ベンチマーク: uWSGI (http)+ nginx レスポンス内容(tcpdump) 21

  22. httpプロキシの注意 Python側(uwsgi)でRemote_IPやHost, protocolの値が変わってしまう X-Forwarded-ForやX-Forwarded-Host または、RFC7239のForwarded Headerに入れて利用する 22

  23. ベンチマーク: uWSGI (http)+ nginx ベンチマーク結果 23

  24. ベンチマーク: Gunicorn + nginx nginxの設定 gunicornの設定 24

  25. ベンチマーク: Gunicorn + nginx レスポンス内容 25

  26. ベンチマーク: Gunicorn + nginx ベンチマーク結果 26

  27. ベンチマーク結果まとめ • 今回試した限りは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
  28. 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
  29. レスポンス内容: Gunicorn 直接 ヘッダ, body, 終端が別パケットで送られている 29

  30. レスポンス内容: uWSGI 直接 コネクションが毎回切れている。chunkedになってない 30

  31. uWSGI・Gunicorn まとめ • gunicornは事例多い・シンプル • uwsgiはトレンド・多機能 • 今回のベンチマークでは uwsgi >

    gunicorn • uwsgiのhttpやgunicornを直接外部に公開するのは注意 ◦ nginxなどのリバースプロキシを通すのが良い 31
  32. uWSGIでGracefulな更新 32

  33. Gracefulな更新とは • サービスを止めずにアプリケーションを更新する方法 • ゼロダウンタイム・ゼロウェイトを実現したい • いろいろな方法がある ◦ DNSで切り替える ◦

    LBでWebサーバを切り替える ▪ 複数のサーバを利用する ▪ サーバが切り替わるBrue-Greenデプロイ ◦ nginxで振り先を切り替える ◦ WSGIサーバでリロードする 33
  34. イメージ図 切り替え箇所 新アプリ (サーバIP、 Port、 worker) 旧アプリ (サーバIP、 Port、 worker)

    クライアント 34 • 切り替える場所の違い ◦ DNS, LBはサーバの切り替え ◦ nginxはサーバやSocket、Portの切り替え ◦ uWSGIは主にWorkerの切り替え
  35. 更新の注意点 • サービスを止めない ◦ 新しいアプリケーションが起動する間スローダウン ◦ 古いアプリケーションを止めるときにエラーが発生 • なるべく即座に反映できる ◦

    DNSで切り替えるとクライアントが旧IPにアクセスし続ける恐れ • 簡単に更新できるようしておく ◦ 上段も設定変更が必要になることがある ◦ nginxで切り替える場合はデプロイのたびにnginxの設定変更が必要 ◦ 結合度が高まってしまう • ロールバックもできるように 35
  36. 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
  37. Standard Graceful Reload (Prefork, Lazy-app) 動いているWorkerが止まるのを待って再起動する • 使い方 ◦ FIFOにrを書き込む(--maste-fifo

    オプションでsocketを要しておきechoなどで書き込む) ◦ touch-reloadオプションを使う ◦ SIGHUPを送る ◦ uwsgi.reload() APIを呼ぶ • メリット ◦ 管理が簡単 ◦ 軽量なPreforkでも使える ◦ 不整合が起きない • デメリット ◦ 長い待ち時間が発生する 37
  38. Worker Reload(秒間レスポンス数) • 約1分間の停止期間が発生(preforkでテスト) 38

  39. Worker Reload(平均レスポンス時間) • 60秒でタイムアウト(504)が発生している • アプリケーション起動前の502も発生 39

  40. Worker Reload(Lazy-app) Workerだけをリロードする • 使い方 ◦ FIFOにwを書き込む ◦ touch-worker-reloadオプションを使う •

    メリット ◦ 全体の再起動が不要になる • デメリット ◦ コードの更新にしか有効でない 40
  41. Worker Reload(レスポンス数) • 2秒ほど止まるが500エラーなどは出ていない 41

  42. Worker Reload(レスポンス時間) • 一瞬詰まるが問題なさそう 42

  43. Chain Reload (Lazy-app) Workerを一つひとつ順番にリロードしていく • 使い方 ◦ FIFOにcを書き込む ◦ touch-chain-reloadオプションを使う

    • メリット ◦ ある程度ワーカー数があれば、クライアントの待ち時間を大幅に削減できる ◦ リロード時の負荷が少ない • デメリット ◦ コードの更新にしか有効でない 43
  44. Chain Reload (レスポンス数) • 4workers, 2threadsにて。段階的に切り替わっており総数への影響は少ない 44

  45. Chain Reload (平均レスポンス時間) • 待ちは発生していない 45

  46. Worker reload, Chain reloadの問題点 uwsgiの設定更新には使えない • 依存ライブラリ更新ができない • Pythonのバージョンをあげられない 46

  47. Unix socketをプールとして活用する(Zerg mode) マスターのuwsgiを用意してportをUnix socketに変換し、 socketに複数のインスタンスを紐付けて振り分ける • 使い方 ◦ masterデーモンを用意しておき、し、アプリはzergでsocketに割り当てる

    ◦ 更新時は同じsocketで起動し、古いものを落とす • メリット ◦ uWSGIの設定変更も可能になる ◦ 待ち時間が少ない ◦ ロールバックしやすい • デメリット ◦ 追加で常駐のmasterデーモンが必要で、リロード時は更にプロセスが必要になる ◦ 通常と異なる設定が必要。ちょっとわかりにくい 47
  48. Zerg mode (レスポンス数) • 起動時に減るが、大きな影響はなさそう。終了は問題ない(graceful shutdown) 48

  49. Zerg mode (平均レスポンス時間) • 起動時に若干増える。 49

  50. 同一ポートを利用 (Reuse-Port) Linux≧3.9かBSD系で使えるSO_REUSEPORTを活用する • 使い方 ◦ --reuse-portオプションを使って起動する ◦ 新しいアプリを同じポートで起動し、古いものを落とす •

    メリット ◦ Zerg modeと同じように、uWSGI自体の設定変更も可能 ◦ masterデーモンが不要なので管理が楽 • デメリット ◦ カーネルサポートが必要 複数のプロセスで同じTCPポートをバインドできる夢の機能 LinuxだとKernelがバランシングしてくれる 50
  51. Reuse Port (レスポンス数) • 古いものをSIGINT(強制終了)で止める 51

  52. Reuse Port (レスポンス数) • しかし若干エラー(502)が出ている 52

  53. Reuse Port (平均レスポンス時間) • 起動時と終了時に僅かな待ちが発生している。(緑は502) 53

  54. Reuse Port (レスポンス数) • 古いバージョンをgraceful shutdownすると1分間止まってしまう 54

  55. Reuse Portについて • 試したOSはLinux(4.1.7) • 複数バインド時にどちらに飛ばすかはKernel次第なので細かい制御は難しい • 夢の機能は万能ではなかった。 55

  56. Master forking (黒魔術!) masterを再フォークする • 使い方 ◦ FIFOにfを書き込む • メリット

    ◦ カーネルサポートも追加プロセスも不要 ◦ かなり速い • デメリット ◦ ログやpidなどの一貫性が壊れる 56
  57. 結果 • 試した限りではうまく切り替わりませんでした... 57

  58. Subscription System サブスクリプションサーバとよばれるロードバランサを用意して紐付ける • 使い方 ◦ uwsgi --fastrouter :1717 --fastrouter-subscription-server

    192.168.0.100:2626 • メリット ◦ 簡単かつゼロダウンタイム ◦ 構成がシンプル • デメリット ◦ fastserverのようなサブスクリプションサーバが必要 58
  59. Gracefulまとめ • 各機能に特徴があるので要件次第 • アプリケーションのみを安全に切り替えるならChain Reload • ライブラリ更新やロールバックを考慮するならZerg Dance •

    ごく僅かなエラーより管理のしやすさを優先させるならReuse Port 利点・欠点を見ながら、良いものを選択 59
  60. uWSGI+Docker利用事例 60

  61. 事例 61 • 新規モバイルアプリ向けAPI • 規模はまだ小さいサービス • Djangoを利用して作成

  62. 優先したこと 62 • 管理のしやすさ ◦ ライブラリを含め、アプリの更新がしやすい ◦ 別の環境に移行しやすい ≒ 昨今のコンテナ技術周りに追従しやすい

    • 運用のしやすさ ◦ サーバにログインしなくても状況がわかるように ◦ 壊れたら消せる = ログは別の場所に送っておく
  63. 最初は優先しない • 性能 • 完全にGracefulな更新 63

  64. というわけで、 reuse-port を使います 64

  65. アプリとインフラの分割単位 • Herokuを参考 ◦ アプリ自体はHerokuでも動かせる形にしておく • http(またはuwsgiプロトコル)を話すところまでアプリ ◦ Pythonバージョンや異存ライブラリはアプリで指定 •

    LBやリバースプロキシ、デプロイ自体はインフラ ◦ Ansibleやクラウドの機能を利用 65
  66. アプリ側のディレクトリ構成 66 • debug-serverの例 • heroku,dockerどちらでも動く ◦ heroku deploy ◦

    docker run
  67. Dockerfileの中身 • Dockerfile • python:3-onbuild 67

  68. uWSGIの設定例 68

  69. 構成図 69

  70. 現在のデプロイ方法 現在は1ホスト1コンテナの構成 • 新バージョンのコンテナをビルドする • 古いバージョンのコンテナIDを控えておく • reuse-portを使って新バージョンのコンテナを起動する • 数秒待つ

    • 古いコンテナを止める 70
  71. 運用事例まとめ • Dockerを1ホスト1コンテナとして利用 ◦ 汎用的なVirtualenvとして • Dockerを利用することでHerokuでもdocker runでも動かせる • ECSなどのコンテナホスティングにそのまま載せられる

    • ログなどの状態は内部に持たないようにしておく 71
  72. 今後の方針 72 • nginxやfluentd側もコンテナにする • デプロイしやすいサービスが出たら乗り換える ◦ AWSのECSに移行中

  73. まとめ 73

  74. 74 • uWSGIは多機能、Gunicornはシンプル • 今回のベンチではuWSGIのほうがGunicornより速い • uWSGIはいろいろなGraceful Reload方法がある • 事例としてはReuse-Portを利用している

  75. 75 性能・運用のしやすさ・コストなどを 考慮してうまく使うようにしましょう

  76. ご清聴ありがとうございました。 76

  77. 予備スライド 77

  78. アクセスログの取り方 78