LoginSignup
2
0

More than 1 year has passed since last update.

uwsgiのDjangoアプリをnginxと同一コンテナで起動する場合の非機能要件Tips

Last updated at Posted at 2023-03-10

上記の記事は職場の方が先行して書いてくれたもので、関連してコンテナ周りの実装Tipsを少しまとめました。

Dockerfile概要

  • ECS Fagateを使う
  • ベースはdebian:bookworm
  • アプリはVueフロントエンド + Djangoバックエンド
  • Vueビルドはデプロイパイプライン上で済ませる
    • DockerfileではCOPY対象
  • 機能開発を優先したいので最低限の可用性・応答性能を確保できればコンテナ構成は単純で良い

1コンテナ1プロセスの原則は?

今回は1つのコンテナにnginxもアプリ(uwsgi + django, ビルド済みのdist/**)も詰め合わせです。
DevOpsの落とし所として、1コンテナにフルスタック構成することは今後もあるでは、と考えています。

なので今回はコンテナ1つで一定の非機能要件を担保するためのTips記録です。

非機能要件いろいろ

  • モニタリング
    • コンテナのHEALTHCHECK機能を使う
    • あとはECS AutoScalingにキャパシティ管理(間接的なリソース監視)を任せて、最低限の可用性を確保する
  • デバッグ用途もあるのでログ収集したい
  • webサーバとしてnginx、appサーバとしてuwsgiを使う
    • 同時接続を捌いたりweb/appそれぞれでプロセス管理してもらうため
    • 同一コンテナなのでsoft/hardのlimitは定義できず、柔軟性には欠ける
    • 今回はECS AutoScalingに任せる方針なのでweb/app一緒にスケールする
  • デプロイパイプライン
    • (長くなるため...本記事では割愛)

コンテナHEALTHCHECK

ELB構成のECS Fargateであればポート疎通のヘルスチェック状態は担保されますが、よりアプリ機能に即したヘルスチェックを簡単に実装できるのがコンテナHEALTHCHECK機能です。

ECS AutoScalingに任せておけば可用性は問題なし、
と言えるのは正常系だけなので、アクセスが集中したり直近のリリースにバグが含まれていたり、アプリが応答不能になってしまう場合、コンテナHEALTHCHECK結果に基づいてECS Fargateはタスクを自動交換してくれます。

  • ECS Fargateの場合、コンテナHEALTHCHECKはDockerfileではなくECSタスク定義に記述する
  • タスク定義例(抜粋)
            "healthCheck": {
                "command": [
                    "CMD-SHELL",
                    "curl -fso /dev/null http://localhost/ || exit 1"
                ],
                "interval": 60,
                "timeout": 3,
                "retries": 3,
                "startPeriod": 0
            }
ECSコンソール

ecsTask_healthCheck01.png

ecsTask_healthCheck02.jpg

コンテナログ収集

AWSドキュメントにも記載されていますが、stdout/stderrをキャプチャすることがコンテナのロギング仕様です。1コンテナ1プロセスではない今回の方針ではいくらかtipsが必要でした。

  • コンテナではnginxとuwsgi、最終的に2つのプロセスを起動する必要がある
  • コンテナはフォアグランドプロセスを必要とする
    • フォアであれば標準出力(ロギング)されるが、2つ以上のフォア起動は不可能では...
  • web検索した限り、systemdsupervisordを使って複数プロセスをフォアグランド起動している試みは発見した
    • 信頼できる方法ではあるが...他チームに展開するには気が引ける
  • そもそも複数プロセスを責務とする場合、ENTRYPOINTはどうなる?
    • nginxとuwsgiの起動パラメータを調整して何とかなった
  • uwsgiはフォアグラウンド実行して、そのままstdout/stderrにログ出力する
  • nginxはバックグラウンド実行しつつ、serverステートメントでログ出力先をstdout/stderrに変更

Dockerfile: ENTRYPOINT

ENTRYPOINT /usr/sbin/nginx -g "daemon on; master_process on;" && \
    /usr/local/bin/uwsgi --ini /var/app/django.ini
  • ENTRYPOINTのプロセス(つまりフォアグラウンド)はPID1として扱われる
    • graceful shutdownしたい対象をフォアグランド選択する方が良い
    • 今回はuwsgiアプリでシグナルハンドラを実装する余地を残している

nginxログ設定(抜粋)

server {
    access_log /dev/stdout combined if=$log_enable;
    error_log /dev/stderr;
}

# conf.d/配置
# ELBヘルスチェックとコンテナHEALTHCHECK、両方をログから除外する
map $http_user_agent $log_enable {
    ~ELB-HealthChecker      0;
    ~curl/                  0;
    default                 1;
}

uwsgiパラメータ(抜粋)

[uwsgi]
master=true
single-interpreter=true
chown-socket=www-data
  • socketファイルの書き込み権限がwebサーバにないとリクエストは失敗する
  • 公式の"Things to know"にならって、mastersingle-interpreterを追加

appendix; uwsgiリロード

uwsgiに明示的なreload/restart命令はないため、OSプロセス一般の方法にならう。

#/usr/local/bin/uwsgi --ini /var/app/django.iniを実行中、かつPPID=1のプロセスを見つける
ps -ef
# 見つけたuwsgiプロセスIDにSIGHUPを送る
kill -HUP {PID}
# 以下のようなuwsgiログが記録される
gracefully (RE)spawned uWSGI master process (pid: 10)

参考ページ

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0