$30 off During Our Annual Pro Sale. View Details »

uwsgi-docker-pycon2015

bungoume
October 11, 2015

 uwsgi-docker-pycon2015

bungoume

October 11, 2015
Tweet

More Decks by bungoume

Other Decks in Technology

Transcript

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

    View Slide

  2. #PyConJP_M
    2

    View Slide

  3. 自己紹介
    梅崎 裕利
    ● 会社
    ○ 日本経済新聞社デジタル編成局
    ● 主な業務
    ○ Ansibleでサーバ管理
    ○ Django+Elasticsearch(ES)で検索API作成
    ○ Fluentd+ES+Kibanaでログ分析
    3

    View Slide

  4. 社内でのPython活用シーン
    ● Google App Engine (2010〜)
    ● New API (Search, Authorization, Logging, etc…)
    ○ Django, django-rest-frameworkなど
    ○ 全部Python3系で動いています(すでにほぼv3.5)
    ● ちょっとしたスクリプト
    ○ Ansible module
    ○ Slack通知
    ○ データ集計・分析
    ○ テストツール
    4

    View Slide

  5. 目次
    ● uWSGI・Gunicornとは
    ○ 特徴紹介
    ○ Gunicornとの比較
    ● サービスを停止させないGracefulな更新
    ○ 更新の課題
    ○ uWSGIで用意されている機能
    ● Dockerとの併用事例
    ○ 複数PaaS対応(Docker, Heroku)方法
    ○ uWSGI設定例
    ● まとめ
    5

    View Slide

  6. 対象
    ● 話すこと
    ○ Djangoなど従来型のPython WebAppの話
    ● 話さないこと
    ○ aiohttpなど非同期なWeb
    6

    View Slide

  7. uWSGI・Gunicorn
    7

    View Slide

  8. Python Webアプリケーションを動かす方法
    WSGI対応サーバを利用する
    ● gunicorn
    ● uwsgi
    ● Apache+mod_wsgi
    gunicornは利用事例が多くシンプル、
    uwsgiは最近のトレンドで多機能
    8

    View Slide

  9. 質問
    ● Gunicorn使っている人?
    ○ 会場で20% ぐらい
    ● uWSGI使っている人?
    ○ 会場で40% ぐらい
    9

    View Slide

  10. uWSGIは多機能
    ● http以外にもuwsgiプロトコルが話せる
    ● 実はhttpsやspdy、WebSocketも話せる
    ● 複数のアプリも動かせる(Emperorモード)
    ● virtualenvなどが使える
    ● PythonだけでなくRuby, Perl, Lua, PHPなどにも使える
    ● スプーラ機能
    ● プロファイラ
    ● キャッシュや静的ファイル配信
    ● 設定パラメータを数えたら1200以上あった(!!)
    10

    View Slide

  11. Gunicornはシンプル
    ● 簡単に起動できる
    ○ gunicorn --workers=4 app.wsgi
    ○ uwsgi --http 127.0.0.1:8000 --wsgi-file app/wsgi.py --master --processes 4
    ● 利用事例が多い
    ● uWSGIほど設定項目は多くないが、必要十分
    11

    View Slide

  12. uWSGIとGunicornのベンチマーク
    12

    View Slide

  13. ベンチマーク環境
    ● Webサーバ
    ○ AWS EC2 m4.large
    ○ Amazon Linux AMI 2015.9
    ● app
    ○ Nginx: 1.8.0 (amazon-repo)
    ○ debug-server (python3.5)
    ● 負荷かけツール
    ○ wrk
    ○ 別サーバより実行
    13

    View Slide

  14. ベンチマークに利用したアプリケーション
    ● debug-server
    ○ https://www.github.com/bungoume/debug-server
    ○ リクエスト内容をJSONで返すサーバ
    ○ Django1.8.5
    ○ Python 3.5 (branch)
    14

    View Slide

  15. ベンチマーク内容
    以下の秒間レスポンス数を比較
    ● nginx + uwsgi(uwsgi-protocol)
    ● nginx + uwsgi(http)
    ● uwsgi直接(http)
    ● nginx + gunicorn(http)
    ● gunicorn直接(http)
    (ワーカー数・スレッド数)は(3, 3)と(4, 1)でテスト
    15

    View Slide

  16. nginxの設定 (共通)
    ● ほぼamazon linuxデフォルトのまま
    16

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  29. レスポンス内容: Gunicorn 直接
    ヘッダ, body, 終端が別パケットで送られている
    29

    View Slide

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

    View Slide

  31. uWSGI・Gunicorn まとめ
    ● gunicornは事例多い・シンプル
    ● uwsgiはトレンド・多機能
    ● 今回のベンチマークでは uwsgi > gunicorn
    ● uwsgiのhttpやgunicornを直接外部に公開するのは注意
    ○ nginxなどのリバースプロキシを通すのが良い
    31

    View Slide

  32. uWSGIでGracefulな更新
    32

    View Slide

  33. Gracefulな更新とは
    ● サービスを止めずにアプリケーションを更新する方法
    ● ゼロダウンタイム・ゼロウェイトを実現したい
    ● いろいろな方法がある
    ○ DNSで切り替える
    ○ LBでWebサーバを切り替える
    ■ 複数のサーバを利用する
    ■ サーバが切り替わるBrue-Greenデプロイ
    ○ nginxで振り先を切り替える
    ○ WSGIサーバでリロードする
    33

    View Slide

  34. イメージ図
    切り替え箇所
    新アプリ
    (サーバIP、
    Port、
    worker)
    旧アプリ
    (サーバIP、
    Port、
    worker)
    クライアント
    34
    ● 切り替える場所の違い
    ○ DNS, LBはサーバの切り替え
    ○ nginxはサーバやSocket、Portの切り替え
    ○ uWSGIは主にWorkerの切り替え

    View Slide

  35. 更新の注意点
    ● サービスを止めない
    ○ 新しいアプリケーションが起動する間スローダウン
    ○ 古いアプリケーションを止めるときにエラーが発生
    ● なるべく即座に反映できる
    ○ DNSで切り替えるとクライアントが旧IPにアクセスし続ける恐れ
    ● 簡単に更新できるようしておく
    ○ 上段も設定変更が必要になることがある
    ○ nginxで切り替える場合はデプロイのたびにnginxの設定変更が必要
    ○ 結合度が高まってしまう
    ● ロールバックもできるように
    35

    View Slide

  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

    View Slide

  37. Standard Graceful Reload (Prefork, Lazy-app)
    動いているWorkerが止まるのを待って再起動する
    ● 使い方
    ○ FIFOにrを書き込む(--maste-fifo オプションでsocketを要しておきechoなどで書き込む)
    ○ touch-reloadオプションを使う
    ○ SIGHUPを送る
    ○ uwsgi.reload() APIを呼ぶ
    ● メリット
    ○ 管理が簡単
    ○ 軽量なPreforkでも使える
    ○ 不整合が起きない
    ● デメリット
    ○ 長い待ち時間が発生する
    37

    View Slide

  38. Worker Reload(秒間レスポンス数)
    ● 約1分間の停止期間が発生(preforkでテスト)
    38

    View Slide

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

    View Slide

  40. Worker Reload(Lazy-app)
    Workerだけをリロードする
    ● 使い方
    ○ FIFOにwを書き込む
    ○ touch-worker-reloadオプションを使う
    ● メリット
    ○ 全体の再起動が不要になる
    ● デメリット
    ○ コードの更新にしか有効でない
    40

    View Slide

  41. Worker Reload(レスポンス数)
    ● 2秒ほど止まるが500エラーなどは出ていない
    41

    View Slide

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

    View Slide

  43. Chain Reload (Lazy-app)
    Workerを一つひとつ順番にリロードしていく
    ● 使い方
    ○ FIFOにcを書き込む
    ○ touch-chain-reloadオプションを使う
    ● メリット
    ○ ある程度ワーカー数があれば、クライアントの待ち時間を大幅に削減できる
    ○ リロード時の負荷が少ない
    ● デメリット
    ○ コードの更新にしか有効でない
    43

    View Slide

  44. Chain Reload (レスポンス数)
    ● 4workers, 2threadsにて。段階的に切り替わっており総数への影響は少ない
    44

    View Slide

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

    View Slide

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

    View Slide

  47. Unix socketをプールとして活用する(Zerg mode)
    マスターのuwsgiを用意してportをUnix socketに変換し、
    socketに複数のインスタンスを紐付けて振り分ける
    ● 使い方
    ○ masterデーモンを用意しておき、し、アプリはzergでsocketに割り当てる
    ○ 更新時は同じsocketで起動し、古いものを落とす
    ● メリット
    ○ uWSGIの設定変更も可能になる
    ○ 待ち時間が少ない
    ○ ロールバックしやすい
    ● デメリット
    ○ 追加で常駐のmasterデーモンが必要で、リロード時は更にプロセスが必要になる
    ○ 通常と異なる設定が必要。ちょっとわかりにくい
    47

    View Slide

  48. Zerg mode (レスポンス数)
    ● 起動時に減るが、大きな影響はなさそう。終了は問題ない(graceful shutdown)
    48

    View Slide

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

    View Slide

  50. 同一ポートを利用 (Reuse-Port)
    Linux≧3.9かBSD系で使えるSO_REUSEPORTを活用する
    ● 使い方
    ○ --reuse-portオプションを使って起動する
    ○ 新しいアプリを同じポートで起動し、古いものを落とす
    ● メリット
    ○ Zerg modeと同じように、uWSGI自体の設定変更も可能
    ○ masterデーモンが不要なので管理が楽
    ● デメリット
    ○ カーネルサポートが必要
    複数のプロセスで同じTCPポートをバインドできる夢の機能
    LinuxだとKernelがバランシングしてくれる
    50

    View Slide

  51. Reuse Port (レスポンス数)
    ● 古いものをSIGINT(強制終了)で止める
    51

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  56. Master forking (黒魔術!)
    masterを再フォークする
    ● 使い方
    ○ FIFOにfを書き込む
    ● メリット
    ○ カーネルサポートも追加プロセスも不要
    ○ かなり速い
    ● デメリット
    ○ ログやpidなどの一貫性が壊れる
    56

    View Slide

  57. 結果
    ● 試した限りではうまく切り替わりませんでした...
    57

    View Slide

  58. Subscription System
    サブスクリプションサーバとよばれるロードバランサを用意して紐付ける
    ● 使い方
    ○ uwsgi --fastrouter :1717 --fastrouter-subscription-server 192.168.0.100:2626
    ● メリット
    ○ 簡単かつゼロダウンタイム
    ○ 構成がシンプル
    ● デメリット
    ○ fastserverのようなサブスクリプションサーバが必要
    58

    View Slide

  59. Gracefulまとめ
    ● 各機能に特徴があるので要件次第
    ● アプリケーションのみを安全に切り替えるならChain Reload
    ● ライブラリ更新やロールバックを考慮するならZerg Dance
    ● ごく僅かなエラーより管理のしやすさを優先させるならReuse Port
    利点・欠点を見ながら、良いものを選択
    59

    View Slide

  60. uWSGI+Docker利用事例
    60

    View Slide

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

    View Slide

  62. 優先したこと
    62
    ● 管理のしやすさ
    ○ ライブラリを含め、アプリの更新がしやすい
    ○ 別の環境に移行しやすい
    ≒ 昨今のコンテナ技術周りに追従しやすい
    ● 運用のしやすさ
    ○ サーバにログインしなくても状況がわかるように
    ○ 壊れたら消せる = ログは別の場所に送っておく

    View Slide

  63. 最初は優先しない
    ● 性能
    ● 完全にGracefulな更新
    63

    View Slide

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

    View Slide

  65. アプリとインフラの分割単位
    ● Herokuを参考
    ○ アプリ自体はHerokuでも動かせる形にしておく
    ● http(またはuwsgiプロトコル)を話すところまでアプリ
    ○ Pythonバージョンや異存ライブラリはアプリで指定
    ● LBやリバースプロキシ、デプロイ自体はインフラ
    ○ Ansibleやクラウドの機能を利用
    65

    View Slide

  66. アプリ側のディレクトリ構成
    66
    ● debug-serverの例
    ● heroku,dockerどちらでも動く
    ○ heroku deploy
    ○ docker run

    View Slide

  67. Dockerfileの中身
    ● Dockerfile
    ● python:3-onbuild
    67

    View Slide

  68. uWSGIの設定例
    68

    View Slide

  69. 構成図
    69

    View Slide

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

    View Slide

  71. 運用事例まとめ
    ● Dockerを1ホスト1コンテナとして利用
    ○ 汎用的なVirtualenvとして
    ● Dockerを利用することでHerokuでもdocker runでも動かせる
    ● ECSなどのコンテナホスティングにそのまま載せられる
    ● ログなどの状態は内部に持たないようにしておく
    71

    View Slide

  72. 今後の方針
    72
    ● nginxやfluentd側もコンテナにする
    ● デプロイしやすいサービスが出たら乗り換える
    ○ AWSのECSに移行中

    View Slide

  73. まとめ
    73

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  77. 予備スライド
    77

    View Slide

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

    View Slide