Python
Django
python3
docker-compose

DB の準備が出来るまで Django の起動を待つコマンド作ってみた

Docker-compose 使うとき不便だったので作ってみました。

https://github.com/pistatium/dj_database_waiter

なにこれ

Database の接続が確立するまで待ち続けるだけの Python コマンドです。

 dj_database_waiter myproject.settings

Django の設定モジュールを引数に渡してあげるだけで動きます。(厳密には止まります)

docker-compose で DB と Django 立ち上げた時、Dockerは最低限のネットワーク疎通ができるまで(?)は Django 側のコンテナの立ち上げを待ってくれますが、初期 SQL の投入だったりといった初期化まではしっかり待ってはくれません。
勝手に起動して勝手にコンテナが終了します。つらいめう
StackOverflow とかで対処法調べてもシェルスクリプトで準備するまで待つしかないみたいな事が書かれていて結構つらみがあります。
疎通確認のためだけに Python の Docker に DB Client をインストールしたくないですし…。

となると Python で疎通を確認するスクリプトを書くのがベターです。
ただプロジェクトごとにそのスクリプトをコピペしたり、Django とは別に都度接続情報を渡すのも面倒だなーと思ったのでコマンド化して pip で入るようにしてみました。

最初は Django コマンドとして admin.py から呼べるように実装したのですが、DB が立ち上がる前だとサブコマンドの起動自体失敗する事に気づいて泣く泣く普通のコマンドとして書き直しました。

使い道

docker-compose.yml
version: '3'

services:
  web:
    build: .
    command: python3 myproject/manage.py runserver 0.0.0.0:8000
    volumes:
      - .:/src
    ports:
      - 8000:8000
    depends_on:
      - db
  db:
    image: mysql
    volumes:
      - ./docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d
      - db-volume:/var/lib/mysql
volumes:
  db-volume:

こういう docker-compose を up したときに一回で上手く立ち上がらない時は、ビルド時に dj_database_waiter をインストールするようにして

docker-compose.yml
    command: /bin/sh dj_database_waiter myproject.settings && python3 myproject/manage.py runserver 0.0.0.0:8000

みたいに command を置き換えてあげればよしなに1回で起動できるようになります。

おまけ: NamedTuple 楽しい

dj_database_waiterfrom typing.NamedTuple を使いたかったためだけに Python3.6.1 以上を必要とします。あしからずご了承ください。

dj_database_waiter/cmd.py
class DbStatus(NamedTuple):
    ok: bool
    reason: str = None


def check_status(db_group_name: str) -> DbStatus:
    try:
        ...
    except Exception as e:  # NOQA
        return DbStatus(ok=False, reason=str(e))
    return DbStatus(ok=True)

引数2個なので NamedTuple 使う必要性ほぼ無いのですが、こんな風にわかりやすく書けます。
Scala でいう CaseClass に近いものですね。
型の情報も書いてるので IntelliJ みたいな IDE 使って書くとバリバリ補間・エラーチェックしてくれるので精神衛生上とてもよろしいです。
(ちなみに実行時型チェックはされないので、間違った型を入れても動いてしまいます)