Python用のnginx-unitをDockerで起動した
webアプリを作ってみたいなーとおもったので。
とりあえずAPサーバを立ち上げるところまでやってみました。
できたもの
各種ファイル解説
ディレクトリ構成
Dockerfile
# NGINX Unit image of Python3.7 FROM nginx/unit:1.21.0-python3.7 # create work directory of container WORKDIR /usr/src/app COPY src . RUN apt-get update \ && apt-get install -y python3-pip \ && pip3 install --no-cache-dir -r ./requirements.txt \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* # port used by the listener in config.json EXPOSE 8080
ここで配信されているdockerイメージを利用します。
Python3.7かつ現在(2021/01/24)時点で最新のイメージを利用したいので、
FROM nginx/unit:1.21.0-python3.7
を指定。
WORKDIR
の設定は好み。
RUN
では、python3を入れています。
requirements.txt
は現段階では何も記述していないので何もおきませんが、後々ここに必要な物を追加していく予定なので、とりあえずそれをもとにpip3 install
してくれるような記述をしておきます。
apt-get clean
は、 Dockerfile のベストプラクティス — Docker-docs-ja 1.9.0b ドキュメント に従って記述した、
イメージのサイズ縮小のための処理です。
EXPOSE 8080
で、コンテナが接続用にリッスンするポートを指定します。
あくまでコンテナ向けの設定で、外部からアクセスするポートではありません。(それはdocker-compose.yml
で記述)
docker-compose.yml
# 利用するdocker-comnposeのバージョン version: '3' # アプリケーションで動かすための各要素 services: # サービス名 timer_app: # image: selenium/standalone-chrome-debug # 起動するコンテナの設定 build: # ディレクトリの指定 context: . # 使用するDockerfileの名前を指定、「Dockerfile」という名前なら省略可能 dockerfile: Dockerfile container_name: timer_app_container # ディレクトリのマウントの設定、:前がホストのディレクトリ、:後がコンテナのディレクトリ volumes: - ./:/usr/src/app - ./nginx:/docker-entrypoint.d working_dir: /usr/src/app # ポート設定、:前がホスト側のポート、:あとがコンテナ側のポート ports: - "8000:8080"
composeファイルはだいたいつけたコメントのとおりです。
特にちゃんと解説が必要なものは、./nginx:/docker-entrypoint.d
です。
nginx-unit
では制御用の設定をconfig.json
をputしたりで登録する必要があります。
公式のドキュメントでもこれをするためにcurl -X PUT
をしてね、というのですが今回は別の方法で制御用の設定を追加します。
それが今回の./nginx:/docker-entrypoint.d
という記述で、/docker-entrypoint.d
に、./nginx
以下のファイルを共有するという処理です。
この記述のポイントしてそもそも、
コンテナの起動時に実行される/usr/local/bin/docker-entrypoint.sh
の処理があります。
この処理がどんなものかというと、
#!/usr/bin/env bash set -e curl_put() { RET=`/usr/bin/curl -s -w '%{http_code}' -X PUT --data-binary @$1 --unix-socket /var/run/control.unit.sock http://localhost/$2` RET_BODY=${RET::-3} RET_STATUS=$(echo $RET | /usr/bin/tail -c 4) if [ "$RET_STATUS" -ne "200" ]; then echo "$0: Error: HTTP response status code is '$RET_STATUS'" echo "$RET_BODY" return 1 else echo "$0: OK: HTTP response status code is '$RET_STATUS'" echo "$RET_BODY" fi return 0 } if [ "$1" = "unitd" -o "$1" = "unitd-debug" ]; then if /usr/bin/find "/var/lib/unit/" -mindepth 1 -print -quit 2>/dev/null | /bin/grep -q .; then echo "$0: /var/lib/unit/ is not empty, skipping initial configuration..." else if /usr/bin/find "/docker-entrypoint.d/" -mindepth 1 -print -quit 2>/dev/null | /bin/grep -q .; then ... <略> ... echo "$0: Looking for configuration snippets in /docker-entrypoint.d/..." for f in $(/usr/bin/find /docker-entrypoint.d/ -type f -name "*.json"); do echo "$0: Applying configuration $f"; curl_put $f "config" done ... <略> ... else echo "$0: /docker-entrypoint.d/ is empty, skipping initial configuration..." fi fi fi exec "$@"
要約すると、docker-entrypoint.d
の*.json
をに対して、putを実行する処理を含んでいます。
そして、もう一つ。
今回のディレクトリ構成では./nginx
以下に、config.json
を格納しています。
なので、./nginx:/docker-entrypoint.d
という記述で、/docker-entrypoint.d
に、./nginx
以下のファイルを共有すると、/usr/local/bin/docker-entrypoint.sh
が設定を自動的に追加してくれます。
config.json
{ "listeners": { "*:8080": { "pass": "applications/python" } }, "applications": { "python": { "type": "python", "path": "/usr/src/app/src/", "module": "wsgi" } } }
App Samples — NGINX Unit で配布されていたサンプルほぼそのままです。
path
を自分のディレクトリ構成に合わせて修正しています。
wsgi.py
# -*- coding: utf-8 -*- def application(environ, start_response): start_response("200 OK", [("Content-Type", "text/plain")]) return (b"Hello, Python on Unit!")
App Samples — NGINX Unit で配布されていたサンプルそのままです。
動かし方
下で起動できます
docker-compose build --no-cache docker-compose up -d
コンテナ内に入るときは
docker-compose exec timer_app bash
curl localhost:8000
で、レスポンスが返ってくれば正常に動いています。
参考
だいたいのソースコードはここ。 ただしconfigの設定あたりで訳わからなくなっていろいろ調べることにしてました。
コンテナで利用する際にconfigが必須ということを理解したセクション。 なんとかしてconfigを追加したいなと考えていたのはこの辺り。
/var/run/control.unit.sock
に対してconfig.json
を反映させることができれば良いことを理解した記事。
ただcurl
をするのは面倒なので何か別の方法がないかなと思い調べてみました。
config.json
は /docker-entrypoint.d/
に入れることで設定ができることを知った記事。
それならdockerの設定でよしなにできそうだなと思ったのでその方向で進めることに。
ただし、なんでそれでできるんだ……ということがわからなかったのでもう少し調べてます。
その答えがあった記事。
どうやら、/usr/local/bin/docker-entrypoint.sh
がdocker-entrypoint.d
のconfigを配置してくれる処理を含んでいるので、
確信を持って進めることができました。
ありがたい。。。(どうやってこういうのに気がつくんだろう)
ホスト側から制御をするメリットについてかいてあった記事。 そういう観点もあるんだなぁとなりました。