個人開発しているwebアプリがやっと形になったので、Cloud Runを使って「よしデプロイしよう!」と意気込んだものの、わからないことだらけでハマりました。
ModuleNotFoundError
にハマる
まずハマったのが、デプロイした直後のModuleNotFoundError: No module named 'config'
というエラー。
gunicornを用いてwsgi.py
からDjangoプロジェクトを起動していくため、Dockerfileに以下を記述しています。
FROM python:3.11
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
WORKDIR /code
COPY requirements.txt ./
RUN python3 -m pip install --upgrade pip setuptools
RUN pip install -r requirements.txt
COPY . ./
CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 --timeout 0 config.wsgi:application
gunicornで起動する先を指定するのにconfig.wsgi:application
としていますが、ここのconfig
が見つけられないらしい。
ディレクトリ構成としてconfig
からの指定で合っているはずなのになぜ…と悩んでいたら閃きました。
シークレットを記述したファイルのマウントの仕方を間違えていた
Clooud Runでは、機密情報を記載したシークレットを作成して、それを各プロジェクトで参照します。
シークレットを使用する際にファイルベースで読み込むには、マウントパスを指定するのですが、ここで間違いが。
マウントパスを指定した先は、ファイル等が存在する場合上書きされてしまうようです。
私はそのパスをプロジェクトルートに設定していたため、すべて上書きされ存在しないものになっていました…。
マウントパスを/conf/.env
として新規ディレクトリを指定し、無事Dockerfileがconfig
を参照できるようになりました!
今度はpython側で.env
が認識できない
無事起動できたと思いきや、今度は.env
が認識されず、
KeyError: 'SECRET_KEY'
とログに記載が…。
.env
を読み込むためのパスを忘れず設定すべし
結論、.env
を読み込むパスの設定が間違っていました。
環境変数周りは、dotenvを使っているので、load_dotenv
で以下のようにパスの設定が必要でした。
import os
from django.core.wsgi import get_wsgi_application
from dotenv import load_dotenv
# wsgi.pyから見て、親の親ディレクトリがルートになるのでdirnameを2回適用している
dotenv_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), "environment/.env")
load_dotenv(dotenv_path=dotenv_path)
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.production")
application = get_wsgi_application()
project-root
├── Dockerfile
├── Dockerfile.dev
├── environment
│ └── .env
├── config
│ ├── __init__.py
│ ├── asgi.py
│ ├── settings
│ │ ├── __init__.py
│ │ ├── base.py
│ │ ├── devlopment.py
│ │ └── production.py
│ ├── urls.py
│ └── wsgi.py
├── manage.py
├── media
├── app
├── requirements.txt
├── static
├── staticfiles
└── templates
無事読み込めて、デプロイできました!!