Djangoの開発・本番環境構築
0. 初めに
Dockerを利用しPython、Django、PostgreSQL、Gunicorn、Nginxの開発・本番環境の構築します。
Dockerとはコンテナ型の仮想環境を作成・配布・実行できるプラットフォームとのことです。Dockerを使えばWebアプリケーションだけでなくサーバーの設定などのインフラもまとめて管理できて、かつバージョンやOSの差を気にすること無く環境の構築ができるというメリットがあります。
Pythonの仮想環境としばしば比較されることがありますが、Pythonの仮想環境はあくまでPythonの依存関係のみをカプセル化し、DockerはOS全体をカプセル化するという違いがあります。
※参考サイト1
0-1. 開発環境
- OS : Ubuntu 22.04.1 LTS on WSL2
- Docker : 20.10.22
- Docker Compose : v2.14.1
0-2. VPS環境
- WebARENA Indigo : メモリ 4G, ストレージ:SSD 80GB, CPU:2コア
- OS:Ubuntu 22.04
- Docker : 20.10.22
- Docker Compose : v2.14.1
- VPS-IP : 160.248.11.107
- Container : Debian GNU/Linux 11 (bullseye)
0-3. 共通環境
- Python : 3.11.1
- Django:4.1.4
- Nginx:1.23.3
- Gunicorn:20.1.0
- PostgrSQL:14.0
- psycopg2-binary:2.9.3
0-4. Python3.11
- Python 3.11はPython 3.10と比べて平均で 25%速くなりました。ワークロードにも寄りますが、10%から60%の速度向上が見込めます。これは「起動時の高速化」と「実行時の高速化」の二つによって達成されています。
- 起動時の高速化:起動時間が10-15%高速化
- 実行時の高速化:最大 2~20%の速度向上
- SSLモジュール
- Python 3.11の新機能Data Class Transforms
- Python3.11の新機能
0-5. Dockerの準備
Ubuntu22.04に最新版のDocker-CE(無償版)をインストールします。 Docker公式ドキュメント
リポジトリへの追加処理に必要なツールのインストール
sudo apt-get update sudo apt-get install apt-transport-https ca-certificates curl gnupg lsb-release
Dockerの公式GPGキーを追加し、リポジトリへの追加を行いファイルを確認します。
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null cat /etc/apt/sources.list.d/docker.list
22.04 LTSのコードネームである「jammy」、「stable」で安定版ということも確認しておきます。 ここまで確認できたら、パッケージリストを更新し、「docker-ce」の情報を確認します。
sudo apt-get update sudo apt info docker-ce -a
Ubuntu 22.04への最新版Dockerのインストール後、バージョンを確認します。
$ sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin (WSL Ubuntuの場合のみ) $ sudo service docker start $ sudo docker version Client: Docker Engine - Community Version: 20.10.22 API version: 1.41 Go version: go1.18.9 Git commit: 3a2c30b Built: Thu Dec 15 22:28:04 2022 OS/Arch: linux/amd64 Context: default Experimental: true Server: Docker Engine - Community Engine: Version: 20.10.22 API version: 1.41 (minimum version 1.12) Go version: go1.18.9 Git commit: 42c8b31 Built: Thu Dec 15 22:25:49 2022 OS/Arch: linux/amd64 Experimental: false containerd: Version: 1.6.14 GitCommit: 9ba4b250366a5ddde94bb7c9d1def331423aa323 runc: Version: 1.1.4 GitCommit: v1.1.4-0-g5fd4c4d docker-init: Version: 0.19.0 GitCommit: de40ad0 ※DockerがSeverへ接続できない場合の対処 sudo update-alternatives --set iptables /usr/sbin/iptables-legacy sudo update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy sudo cat /var/log/docker.logを確認
Docker のリポジトリからCompose をインストール
sudo apt install docker-compose-plugin docker compose version Docker Compose version v2.14.1
1. プロジェクトファイルの設定
1-1. Docker-Djangoプロジェクトの作成
- 以下のように「django_project」というプロジェクト全体を管理するディレクトリを作成します。
~/workspace/Docker-Django$ tree -L 2 . └── django_project
1-2. 仮想環境の作成
virtualenvのインストール
[Pip3のインストール] sudo -H pip3 install --upgrade pip [virtualenvのインストール] python3.11で実行 ※* 2 /usr/bin/python3.11 100 manual mode ~/workspace/Docker-Django$ sudo -H pip3 install virtualenv [Output] Collecting virtualenv Using cached virtualenv-20.17.1-py3-none-any.whl (8.8 MB) Collecting filelock<4,>=3.4.1 Using cached filelock-3.9.0-py3-none-any.whl (9.7 kB) Collecting platformdirs<3,>=2.4 Using cached platformdirs-2.6.2-py3-none-any.whl (14 kB) Collecting distlib<1,>=0.3.6 Using cached distlib-0.3.6-py2.py3-none-any.whl (468 kB) Installing collected packages: distlib, platformdirs, filelock, virtualenv Successfully installed distlib-0.3.6 filelock-3.9.0 platformdirs-2.6.2 virtualenv-20.17.1 WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
virtualenvのファイル「venv」作成
cd ~/workspace/Docker-Django/django_project $ virtualenv venv [Output] created virtual environment CPython3.11.1.final.0-64 in 483ms creator CPython3Posix(dest=/home/ubuntu/workspace/Docker-Django/django_project/venv, clear=False, no_vcs_ignore=False, global=False) seeder FromAppData(download=False, pip=bundle, setuptools=bundle, wheel=bundle, via=copy, app_data_dir=/home/ubuntu/.local/share/virtualenv) added seed packages: pip==22.3.1, setuptools==65.6.3, wheel==0.38.4 activators BashActivator,CShellActivator,FishActivator,NushellActivator,PowerShellActivator,PythonActivator
仮想環境の有効化
$ source venv/bin/activate (venv) xxx@xxx:~/workspace/Docker-Django/django_project$ ll
仮想化の無効化コマンド
(venv)$ deactivate
1-3. Djangoプロジェクトの作成
仮想環境を有効化しpipコマンドでDjangoをインストールします
仮想環境を有効化
(venv)$ pip3 install django==4.1.4 [Output] Collecting django==4.1.4 Using cached Django-4.1.4-py3-none-any.whl (8.1 MB) Collecting asgiref<4,>=3.5.2 Using cached asgiref-3.6.0-py3-none-any.whl (23 kB) Collecting sqlparse>=0.2.2 Using cached sqlparse-0.4.3-py3-none-any.whl (42 kB) Installing collected packages: sqlparse, asgiref, django Successfully installed asgiref-3.6.0 django-4.1.4 sqlparse-0.4.3
Djangoのプロジェクトを作成
(venv)$ django-admin startproject project . ~/workspace/Docker-Django/django_project$ tree -L 2 . ├── manage.py ├── project │ ├── __init__.py │ ├── asgi.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py └── venv
Djangoの起動
(venv)$ python manage.py runserver [Output] Watching for file changes with StatReloader Performing system checks... System check identified no issues (0 silenced). You have 18 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions. Run 'python manage.py migrate' to apply them. January 03, 2023 - 04:54:41 Django version 4.1.4, using settings 'project.settings' Starting development server at http://127.0.0.1:8000/ Quit the server with CONTROL-C. [03/Jan/2023 04:55:00] "GET / HTTP/1.1" 200 10681 [03/Jan/2023 04:55:01] "GET /static/admin/css/fonts.css HTTP/1.1" 200 423 [03/Jan/2023 04:55:01] "GET /static/admin/fonts/Roboto-Bold-webfont.woff HTTP/1.1" 200 86184 [03/Jan/2023 04:55:01] "GET /static/admin/fonts/Roboto-Regular-webfont.woff HTTP/1.1" 200 85876 [03/Jan/2023 04:55:01] "GET /static/admin/fonts/Roboto-Light-webfont.woff HTTP/1.1" 200 85692 Not Found: /favicon.ico [03/Jan/2023 04:55:01] "GET /favicon.ico HTTP/1.1" 404 2111
2. ローカル環境DockerとDjangoの設定
2-1. Dockerファイルの作成
- Dockerfile
FROM python:3.11 WORKDIR /usr/src/app ENV PYTHONDONTWRITEBYTECODE 1 ENV PYTHONUNBUFFERED 1 RUN pip install --upgrade pip COPY ./requirements.txt . RUN pip install -r requirements.txt COPY . .
2-2. requirements.txtファイルの作成
- requirements.txt
Django==4.1.4
2-3. ymlファイルの作成
- docker-compose.yml
version: '3.7' services: web: build: ./django_project command: python manage.py runserver 0.0.0.0:8000 volumes: - ./django_project/:/usr/src/app/ ports: - 8000:8000 env_file: - ./.env.dev
2-4. 環境変数ファイルの作成
.env.dev
[Docker-Django/.env.dev] DEBUG=True SECRET_KEY='django-insecure-aqic3z!5eokrdoh8=j1@3tky7bem85qb0q=e9l#fa3s()ryv$h' ALLOWED_HOSTS=localhost 127.0.0.1 [::1] 160.248.11.107 [::1]
※SECRET_KEYの値は、LocalとVPSの各「settings.py」から転記します。
ymlファイルのテスト
[Docker-Django/docker-compose.yml] docker compose config (Output) name: django_project services: web: build: context: ./django_project dockerfile: Dockerfile command: - python - manage.py - runserver - 0.0.0.0:8000 environment: ALLOWED_HOSTS: localhost 127.0.0.1 [::1] DEBUG: "True" SECRET_KEY: xxxxxxxxxxxxxxxxxxxx networks: default: null ports: - mode: ingress target: 8000 published: "8000" protocol: tcp volumes: - type: bind source: /home/ubuntu/workspace/Docker-Django/django_project/django_project target: /usr/src/app bind: create_host_path: true networks: default: name: django_project_default
2-5. settings.pyファイルの作成
- ~/workspace/Docker-Django/django_project/project/settings.pyの編集
import os ... SECRET_KEY = os.environ.get("SECRET_KEY") DEBUG = os.environ.get("DEBUG") ALLOWED_HOSTS = os.environ.get("ALLOWED_HOSTS").split(" ")
2-6. ローカル用コンテナのイメージ作成、コンテナ作成、起動
イメージの作成
$ sudo docker compose build [確認] $ sudo docker images REPOSITORY TAG IMAGE ID CREATED SIZE docker-django-web latest 5c1b8984f6d8 24 seconds ago 1.01GB
サービスを指定してビルドする場合は以下のコマンド(下記の場合はwebを指定)
sudo docker compose build web
ディレクトリ構造
Docker-Django$ tree -aL 3 . ├── .env.dev ├── README ├── django_project │ ├── Dockerfile │ ├── db.sqlite3 │ ├── manage.py │ ├── project │ │ ├── __init__.py │ │ ├── __pycache__ │ │ ├── asgi.py │ │ ├── settings.py │ │ ├── urls.py │ │ └── wsgi.py │ ├── requirements.txt │ └── venv │ ├── .gitignore │ ├── bin │ ├── lib │ └── pyvenv.cfg └── docker-compose.yml
-dのオプションをつけて、コンテナのの作成後、バックグラウンドで起動
$ sudo docker compose up -d (Opuput) [+] Running 2/2 ⠿ Network docker-django_default Created 0.0s ⠿ Container docker-django-web-1 Started (コンテナの確認) $ sudo docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 48abb0ddee8d docker-django-web "python manage.py ru…" 43 seconds ago Up 42 seconds 0.0.0.0:8000->8000/tcp, :::8000->8000/tcp docker-django-web-1
表示確認
LOCAL:localhost:8000
VPS: 160.248.11.107:8000
DisallowedHost at / Invalid HTTP_HOST header: '160.248.11.107:8000'. You may need to add '160.248.11.107' to ALLOWED_HOSTS. Request Method: GET Request URL: http://160.248.11.107:8000/ Django Version: 4.1.4 Exception Type: DisallowedHost Exception Value: Invalid HTTP_HOST header: '160.248.11.107:8000'. You may need to add '160.248.11.107' to ALLOWED_HOSTS. Exception Location: /usr/local/lib/python3.11/site-packages/django/http/request.py, line 148, in get_host Python Executable: /usr/local/bin/python Python Version: 3.11.1 Python Path: ['/usr/src/app', '/usr/local/lib/python311.zip', '/usr/local/lib/python3.11', '/usr/local/lib/python3.11/lib-dynload', '/usr/local/lib/python3.11/site-packages'] Server time: Mon, 02 Jan 2023 08:57:28 +0000
- 確認後コンテナの起動を停止、削除
$ sudo docker compose down
3. Postgresqlの設定
3-1. docker-compose.ymlの編集
- DockerHub:postgres15.1-alpine
version: '3.7' services: web: build: ./django_project command: python manage.py runserver 0.0.0.0:8000 volumes: - ./django_project/:/usr/src/app/ ports: - 8000:8000 env_file: - ./.env.dev depends_on: - db db: image: postgres:15.1-alpine volumes: - postgres_data:/var/lib/postgresql/data/ env_file: - ./.env.dev volumes: postgres_data:
3-2. settings.pyファイルの編集
- settings.pyのDATABASEの設定を変更
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql_psycopg2', 'NAME': os.environ.get("SQL_DATABASE"), 'USER': os.environ.get("SQL_USER"), 'PASSWORD': os.environ.get("SQL_PASSWORD"), 'HOST': os.environ.get("SQL_HOST"), 'PORT': os.environ.get("SQL_PORT"), } }
3-3. 環境変数ファイルの編集
- 環境変数のファイル(.env.dev)にデータベースの接続に必要な情報を追加※HOSTはdocker-compose.ymlで指定したコンテナ名を設定する。
DEBUG=True SECRET_KEY='django-insecure-aqic3z!5eokrdoh8=j1@3tky7bem85qb0q=e9l#fa3s()ryv$h' ALLOWED_HOSTS=localhost 127.0.0.1 [::1] 160.248.11.107 [::1] SQL_DATABASE=db_name SQL_USER=user_name SQL_PASSWORD=password SQL_HOST=db SQL_PORT=5432 POSTGRES_USER=user_name POSTGRES_PASSWORD=password POSTGRES_DB=db_name
3-4. Dockerfileの編集
- psycopg2の使用に合わせ、postgresqlの周辺ライブラリをインストールするよう編集します。※以下の内容は、エラーとなりますので上記のように変更
FROM python:3.11 WORKDIR /usr/src/app ENV PYTHONDONTWRITEBYTECODE 1 ENV PYTHONUNBUFFERED 1 # install psycopg2 dependencies RUN apt-get update #&& apk add postgresql-dev gcc python3-dev musl-dev RUN pip install --upgrade pip COPY ./requirements.txt . RUN pip install -r requirements.txt COPY . .
# install psycopg2 dependencies RUN apk update \ && apk add postgresql-dev gcc python3-dev musl-dev
3-5. requirements.txtファイルの作成
- requirements.txt
- Package: python-psycopg2
Django==4.1.4 psycopg2-binary==2.9.3
3-6. Postgresqlの動作確認
コンテナを起動して動作を確認
sudo docker compose up -d --build [Output] [+] Building 14.3s (12/12) FINISHED => [internal] load build definition from Dockerfile 0.0s => => transferring dockerfile: 345B 0.0s => [internal] load .dockerignore 0.0s => => transferring context: 2B 0.0s => [internal] load metadata for docker.io/library/python:3.11 0.7s => [internal] load build context 0.1s => => transferring context: 835.38kB 0.1s => [1/7] FROM docker.io/library/python:3.11@sha256:250990a809a15bb6a3e307fec72dead200c882c940523fb1694baa78eb0e47c6 0.0s => CACHED [2/7] WORKDIR /usr/src/app 0.0s => [3/7] RUN apt-get update 2.3s => [4/7] RUN pip install --upgrade pip 1.6s => [5/7] COPY ./requirements.txt . 0.0s => [6/7] RUN pip install -r requirements.txt 9.0s => [7/7] COPY . . 0.4s => exporting to image 0.3s => => exporting layers 0.3s => => writing image sha256:65a5170f390fb7d2558bf21341fcf1eecfa8e08293890f7266c8f1bfb0e42468 0.0s => => naming to docker.io/library/docker-django-web 0.0s [+] Running 4/4 ⠿ Network docker-django_default Created 0.0s ⠿ Volume "docker-django_postgres_data" Created 0.0s ⠿ Container docker-django-db-1 Started 0.4s ⠿ Container docker-django-web-1 Started
※–build のオプションをつける事で、コンテナ作成前にイメージを再ビルドします。
コンテナの確認
sudo docker ps [sudo] matsu のパスワード: CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 3807ceadb077 docker-django-web "python manage.py ru…" 26 minutes ago Up 26 minutes 0.0.0.0:8000->8000/tcp, :::8000->8000/tcp docker-django-web-1 2fe5656abd95 postgres:15.1-alpine "docker-entrypoint.s…" 26 minutes ago Up 26 minutes 5432/tcp docker-django-db-1
データベースのマイグレーション
sudo docker compose exec web python manage.py migrate --noinput [Output] Applying contenttypes.0001_initial... OK Applying auth.0001_initial... OK Applying admin.0001_initial... OK Applying admin.0002_logentry_remove_auto_add... OK Applying admin.0003_logentry_add_action_flag_choices... OK Applying contenttypes.0002_remove_content_type_name... OK Applying auth.0002_alter_permission_name_max_length... OK Applying auth.0003_alter_user_email_max_length... OK Applying auth.0004_alter_user_username_opts... OK Applying auth.0005_alter_user_last_login_null... OK Applying auth.0006_require_contenttypes_0002... OK Applying auth.0007_alter_validators_add_error_messages... OK Applying auth.0008_alter_user_username_max_length... OK Applying auth.0009_alter_user_last_name_max_length... OK Applying auth.0010_alter_group_name_max_length... OK Applying auth.0011_update_proxy_permissions... OK Applying auth.0012_alter_user_first_name_max_length... OK Applying sessions.0001_initial... OK
以下のようなエラーが発生した場合、以下のコマンドを実行して、作成済みのコンテナとボリュームを削除しもう一度buildとmigrateを行います。
django.db.utils.OperationalError: FATAL: database “db_name” does not exist
docker compose down -v
データベースの確認
sudo docker compose exec db psql --username=user_name --dbname=db_name [Output] psql (15.1) Type "help" for help. db_name=#
コマンド「# \l」を実行します。
db_name=# \l List of databases Name | Owner | Encoding | Collate | Ctype | ICU Locale | Locale Provider | Access privileges -----------+-----------+----------+------------+------------+------------+-----------------+------------------------- db_name | user_name | UTF8 | en_US.utf8 | en_US.utf8 | | libc | postgres | user_name | UTF8 | en_US.utf8 | en_US.utf8 | | libc | template0 | user_name | UTF8 | en_US.utf8 | en_US.utf8 | | libc | =c/user_name + | | | | | | | user_name=CTc/user_name template1 | user_name | UTF8 | en_US.utf8 | en_US.utf8 | | libc | =c/user_name + | | | | | | | user_name=CTc/user_name (4 rows) db_name=#
コンテナ作成時に作成されたボリュームデータを確認
sudo docker volume inspect docker-django_postgres_data [Output] [ { "CreatedAt": "2023-01-03T06:39:52+09:00", "Driver": "local", "Labels": { "com.docker.compose.project": "docker-django", "com.docker.compose.version": "2.14.1", "com.docker.compose.volume": "postgres_data" }, "Mountpoint": "/var/lib/docker/volumes/docker-django_postgres_data/_data", "Name": "docker-django_postgres_data", "Options": null, "Scope": "local" } ]
3-7. シェルファイルの作成
コンテナを起動するためのコードを記入したシェルファイルを追加し、Dockerfileの内容も編集します。
entrypoint.shをdjango_projectディレクトリへ追加
[entrypoint.sh]
#!/bin/sh if [ "$DATABASE" = "postgres" ] then echo "Waiting for postgres..." while ! nc -z $SQL_HOST $SQL_PORT; do sleep 0.1 done echo "PostgreSQL started" fi exec "$@"
entrypoint.shの権限を変更
$ chmod +x django_project/entrypoint.sh
作成したシェルファイルの実行をDockerfileへ記入
コンテナのDebian GNU/Linux 11 (bullseye)には、nc2が入っていないのでインストールします。
[Dockerfile]
FROM python:3.11 WORKDIR /usr/src/app ENV PYTHONDONTWRITEBYTECODE 1 ENV PYTHONUNBUFFERED 1 # install psycopg2 dependencies RUN apt-get update RUN pip install --upgrade pip COPY ./requirements.txt . RUN pip install -r requirements.txt # ncのインストール(コンテナのDebian GNU/Linux 11 (bullseye)に入っていない) RUN apt install -y ncat COPY ./entrypoint.sh . COPY . . ENTRYPOINT ["/usr/src/app/entrypoint.sh"]
シェルファイルで使用する環境変数を、.env.devに追加
[.env.dev]
DEBUG=True SECRET_KEY='django-insecure-aqic3z!5eokrdoh8=j1@3tky7bem85qb0q=e9l#fa3s()ryv$h' ALLOWED_HOSTS=localhost 127.0.0.1 [::1] 160.248.11.107 [::1] SQL_DATABASE=db_name SQL_USER=user_name SQL_PASSWORD=password SQL_HOST=db SQL_PORT=5432 POSTGRES_USER=user_name POSTGRES_PASSWORD=password POSTGRES_DB=db_name DATABASE=postgres
Postgresqlをデータベースとして利用するため、デフォルトで作成されるSQLiteファイルを削除
Docker-Django$ . ├── .env.dev ├── django_project │ ├── Dockerfile │ ├── entrypoint.sh │ ├── manage.py │ ├── project │ │ ├── __init__.py │ │ ├── __pycache__ │ │ ├── asgi.py │ │ ├── settings.py │ │ ├── urls.py │ │ └── wsgi.py │ ├── requirements.txt │ └── venv └── docker-compose.yml
イメージの再ビルド
sudo docker compose build
コンテナの作成、起動
sudo docker compose up -d
ブラウザで表示確認
- Local : http://localhost:8000/
- VPS : http://160.248.11.107:8000/
4. Gunicornの設定
本番環境ではGunicornをアプリケーションサーバーとして利用します。
4-1. requirements.txtの編集
- requirements.txtを編集
Django==4.1.4 psycopg2-binary==2.9.3 gunicorn==20.1.0
4-2. ymlファイルの編集
Gunicornを利用する設定にするため、ymlファイルを編集
[docker-compose.yml]
version: '3.7' services: web: build: ./django_project # command: python manage.py runserver 0.0.0.0:8000 command: gunicorn project.wsgi:application --bind 0.0.0.0:8000 volumes: - ./django_project/:/usr/src/app/ ports: - 8000:8000 env_file: - ./.env.dev depends_on: - db db: image: postgres:15.1-alpine volumes: - postgres_data:/var/lib/postgresql/data/ env_file: - ./.env.dev volumes: postgres_data:
Gunicornを起動させる設定で動作確認
まずは、現在起動している開発環境用のコンテナを停止、削除します。
(sudo docker compose down -v) sudo docker compose down --rmi all --volumes (Output) [+] Running 6/6 ⠿ Container docker-django-web-1 Removed 0.5s ⠿ Container docker-django-db-1 Removed 0.4s ⠿ Volume docker-django_postgres_data Removed 0.0s ⠿ Image docker-django-web:latest Removed 0.1s ⠿ Image postgres:15.1-alpine Removed 0.1s ⠿ Network docker-django_default Removed
※-v : –volumes
ディレクトリ構造の確認
Docker-Django$ . ├── .env.dev ├── README ├── django_project │ ├── Dockerfile │ ├── entrypoint.sh │ ├── manage.py │ ├── project │ │ ├── __init__.py │ │ ├── __pycache__ │ │ ├── asgi.py │ │ ├── settings.py │ │ ├── urls.py │ │ └── wsgi.py │ ├── requirements.txt │ └── venv └── docker-compose.yml
本番環境用のymlファイルを指定してイメージの作成、コンテナの作成、起動を行います。
sudo docker compose up -d --build [Output] ... [+] Running 4/4 ⠿ Network docker-django_default Created 0.1s ⠿ Volume "docker-django_postgres_data" Created 0.0s ⠿ Container docker-django-db-1 Started 0.8s ⠿ Container docker-django-web-1 Started
(参考)ymlファイルを指定してイメージの作成、コンテナの作成、起動した際のログを確認する際は、ログ確認コマンド実行時にもymlファイルの指定を行います。
sudo docker compose logs
本番用のymlファイルを利用して起動すると、Djangoのrunserverの機能は使用しないため、Django内のstaticファイルは反映されなくなりますが、対策は後述します。
- Local : http://localhost:8000/admin/
- VPS : http://160.248.11.107:8000/admin/
4-3. Dockerファイルの編集
[Dockerfile]
## BUILDER ## FROM python:3.11 as builder WORKDIR /usr/src/app ENV PYTHONDONTWRITEBYTECODE 1 ENV PYTHONUNBUFFERED 1 # install psycopg2 dependencies RUN apt update \ && apt install gcc musl-dev \ && apt install python3-dev -y \ && apt install musl-dev -y # postgresql-dev gcc python3-dev musl-dev RUN pip install --upgrade pip COPY ./requirements.txt . RUN pip install -r requirements.txt RUN pip wheel --no-cache-dir --no-deps --wheel-dir /usr/src/app/wheels -r requirements.txt # ncのインストール(コンテナのDebian GNU/Linux 11 (bullseye)に入っていない) RUN apt install -y ncat COPY ./entrypoint.sh . COPY . . ENTRYPOINT ["/usr/src/app/entrypoint.sh"] ## FINAL ## FROM python:3.11 # 親ディレクトリも作成 -p #RUN mkdir -p /home/app # useraddはコマンド一発で作るタイプ : デフォルトではホームが作成されない # adduserは対話形式で作るタイプ : デフォルトでホームディレクトリを作成 RUN addgroup -system app && adduser --system app && addgroup app app ENV HOME=/home/app ENV APP_HOME=/home/app/web RUN mkdir $APP_HOME WORKDIR $APP_HOME # RUN apt-get update && apt-get install libpq-dev # libpqがインストールできないのでlibpq-devを使っている。 RUN apt update \ && apt install libpq-dev COPY --from=builder /usr/src/app/wheels /wheels COPY --from=builder /usr/src/app/requirements.txt . RUN pip install --no-cache /wheels/* COPY ./entrypoint.sh $APP_HOME COPY . $APP_HOME RUN chown -R app:app $APP_HOME USER app ENTRYPOINT ["/home/app/web/entrypoint.sh"]
コンテナ内のディレクトリ構造
[/home/app/web/] ~/web$ -rw-rw-r-- 1 app app 1523 Jan 4 18:11 Dockerfile -rwxrwxr-x 1 app app 200 Jan 3 10:45 entrypoint.sh -rwxrwxr-x 1 app app 663 Jan 2 02:06 manage.py drwxrwxr-x 1 app app 4096 Jan 3 23:35 project -rw-rw-r-- 1 app app 53 Jan 3 15:13 requirements.txt drwxrwxr-x 1 app app 4096 Jan 3 23:35 venv [/var/lib/postgresql] /var/lib/postgresql# drwx------ 19 postgres postgres 4096 Jan 5 03:23 data
コンテナ内のプロセス
[app] ~/web$ ps -aux USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND app 1 0.1 0.0 2480 508 ? Ss 03:23 0:01 /bin/sh /home/app/web/entrypoint.sh gunicorn project.wsgi:application --bind 0.0.0.0:8000 [postgres] ~# ps -a PID USER TIME COMMAND 1 postgres 0:00 postgres 50 postgres 0:00 postgres: checkpointer 51 postgres 0:00 postgres: background writer 53 postgres 0:00 postgres: walwriter 54 postgres 0:00 postgres: autovacuum launcher 55 postgres 0:00 postgres: logical replication launcher
4-4. 本番環境用のymlファイルを利用したイメージ、コンテナの作成、起動
使用していたコンテナの停止、削除、新コンテナの起動、新たな設定でデータベースのマイグレート
sudo docker compose down --volumes
新コンテナの起動
sudo docker compose up -d --build
新たな設定でデータベースのマイグレート
sudo docker compose exec web python manage.py migrate --noinpu [Output] Operations to perform: Apply all migrations: admin, auth, contenttypes, sessions Running migrations: Applying contenttypes.0001_initial... OK Applying auth.0001_initial... OK Applying admin.0001_initial... OK Applying admin.0002_logentry_remove_auto_add... OK Applying admin.0003_logentry_add_action_flag_choices... OK Applying contenttypes.0002_remove_content_type_name... OK Applying auth.0002_alter_permission_name_max_length... OK Applying auth.0003_alter_user_email_max_length... OK Applying auth.0004_alter_user_username_opts... OK Applying auth.0005_alter_user_last_login_null... OK Applying auth.0006_require_contenttypes_0002... OK Applying auth.0007_alter_validators_add_error_messages... OK Applying auth.0008_alter_user_username_max_length... OK Applying auth.0009_alter_user_last_name_max_length... OK Applying auth.0010_alter_group_name_max_length... OK Applying auth.0011_update_proxy_permissions... OK Applying auth.0012_alter_user_first_name_max_length... OK Applying sessions.0001_initial... OK
ブラウザで表示確認:どうしてか
- ymlの確認:gunicornで起動している。
web: build: ./django_project # command: python manage.py runserver 0.0.0.0:8000 command: gunicorn project.wsgi:application --bind 0.0.0.0:8000
- コンテナ内で起動
sudo docker exec -it container_ID bash >>>コンテナ内 app@c809940bc729:~/web$ source venv/bin/activate (venv) app@c809940bc729:~/web$ python manage.py runserver 0.0.0.0:8000 Watching for file changes with StatReloader Performing system checks... System check identified no issues (0 silenced). January 04, 2023 - 15:16:45 Django version 4.1.4, using settings 'project.settings' Starting development server at http://0.0.0.0:8000/ Quit the server with CONTROL-C. [プロセスの確認] - プロセスの確認 ~~~bash app@c809940bc729:~/web$ ps -aux USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND app 1 0.1 0.0 2480 568 ? Ss 14:46 0:02 /bin/sh /home/app/web/entrypoint.sh gunicorn project.wsgi:application app 18135 0.0 0.1 58116 46768 pts/0 S+ 15:16 0:00 python manage.py runserver 0.0.0.0:8000 app 18139 0.3 0.1 430168 50932 pts/0 Sl+ 15:16 0:01 /usr/local/bin/python manage.py runserver 0.0.0.0:8000
- gunicornの起動
sudo docker exec -it container_ID bash >>>コンテナ内 app@08a393ac25a1:~/web$ gunicorn project.wsgi:application --bind 0.0.0.0:8000 [2023-01-04 15:39:33 +0000] [2246] [INFO] Starting gunicorn 20.1.0 [2023-01-04 15:39:33 +0000] [2246] [INFO] Listening at: http://0.0.0.0:8000 (2246) [2023-01-04 15:39:33 +0000] [2246] [INFO] Using worker: sync [2023-01-04 15:39:33 +0000] [2248] [INFO] Booting worker with pid: 2248 [プロセスの確認] app@08a393ac25a1:~/web$ ps -aux USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND app 1 0.1 0.0 2480 572 ? Ss 15:35 0:00 /bin/sh /home/app/web/entrypoint.sh gunicorn project.wsgi:application app 2246 0.1 0.0 37404 31148 pts/0 S+ 15:39 0:00 /usr/local/bin/python /usr/local/bin/gunicorn project.wsgi:application app 2248 0.0 0.1 65648 51604 pts/0 S+ 15:39 0:00 /usr/local/bin/python /usr/local/bin/gunicorn project.wsgi:application
- コンテナ内でgunicornが起動ができないので起動する
app@08f41eff4b3a:~/web$ gunicorn project.wsgi:application --bind 0.0.0.0:8000 [2023-01-05 03:37:49 +0000] [8594] [INFO] Starting gunicorn 20.1.0 [2023-01-05 03:37:49 +0000] [8594] [INFO] Listening at: http://0.0.0.0:8000 (8594) [2023-01-05 03:37:49 +0000] [8594] [INFO] Using worker: sync [2023-01-05 03:37:49 +0000] [8597] [INFO] Booting worker with pid: 8597 (結果) ~/web$ ps -aux USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND app 1 0.1 0.0 2480 508 ? Ss 03:23 0:01 /bin/sh /home/app/web/entrypoint.sh gunicorn project.wsgi:application app 8594 0.1 0.0 37404 30396 pts/0 S+ 03:37 0:00 /usr/local/bin/python /usr/local/bin/gunicorn project.wsgi:application app 8597 0.1 0.1 65640 50556 pts/0 S+ 03:37 0:00 /usr/local/bin/python /usr/local/bin/gunicorn project.wsgi:application
- Local : http://localhost:8000/
- VPS : http://160.248.11.107:8000/
5. Nginxの設定
Webサーバとして利用するNginxの設定
5-1. ymlファイルの編集
Nginx用のコンテナを設定するために、ymlファイルを編集
- [docker-compose.yml]
version: '3.7' services: web: build: ./django_project # command: python manage.py runserver 0.0.0.0:8000 command: gunicorn project.wsgi:application --bind 0.0.0.0:8000 #volumes: # - ./django_project/:/usr/src/app/ ports: - 8000:8000 env_file: - ./.env.dev depends_on: - db db: image: postgres:15.1-alpine volumes: - postgres_data:/var/lib/postgresql/data/ env_file: - ./.env.dev nginx: build: ./nginx ports: - 1317:80 depends_on: - web volumes: postgres_data:
5-2. Nginx用のフォルダの作成
Docker-Djangoディレクトリ内に、Nginx用のフォルダを作成し、その中にNginx用の「Dockerfile」、「nginx.conf」を作成します。
$ mkdir nginx
Dockerfileの作成
FROM nginx:1.23.3-alpine RUN rm /etc/nginx/conf.d/default.conf COPY nginx.conf /etc/nginx/conf.d
nginx.confの作成
[nginx.conf]
upstream project { server web:8000; } server { listen 80; location / { proxy_pass http://project; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $host; proxy_redirect off; } }
5-3. ymlファイルの編集(Nginx仕様)
- Nginxを使いますのでDjangoのrunserverコマンドは使用する8000番ポートの設定は不要です。
- [webサービスの変更]
version: '3.7' services: web: build: context: ./django_project dockerfile: Dockerfile.prod command: gunicorn project.wsgi:application --bind 0.0.0.0:8000 expose: - 8000 env_file: - ./.env.dev depends_on: - db db: image: postgres:15.1-alpine volumes: - postgres_data:/var/lib/postgresql/data/ env_file: - ./.env.dev nginx: build: ./nginx ports: - 1317:80 depends_on: - web volumes: postgres_data:
5-4. 表示の確認
ディレクトリ構造
~/workspace/Docker-Django$ tree -aL 3 . ├── .env.dev ├── django_project │ ├── Dockerfile │ ├── entrypoint.sh │ ├── manage.py │ ├── project │ │ ├── __init__.py │ │ ├── __pycache__ │ │ ├── asgi.py │ │ ├── settings.py │ │ ├── urls.py │ │ └── wsgi.py │ ├── requirements.txt │ └── venv │ ├── .gitignore │ ├── bin │ ├── lib │ └── pyvenv.cfg ├── docker-compose.yml └── nginx ├── Dockerfile └── nginx.conf
コンテナの停止・削除
sudo docker compose down --volumes
コンテナの起動
sudo docker compose up -d --build
新たな設定でデータベースのマイグレート
sudo docker compose exec web python manage.py migrate --noinpu
ブラウザで表示を確認
- Local : http://localhost:1317/
- VPS : http://160.248.11.107:1317/
6. Nginxを利用したStatic,Mediaファイルの表示設定
これまでは、Gunicornををwebサーバとして利用していたため、Djangoプロジェクト内のstaticファイルが利用できていませんでした(adminページで表示が崩れます。)
6-1. Staticファイルの設定
webサーバにNginxを利用しますので、Staticファイルを扱う設定を加えます。
6-2. Settings.pyファイルの編集
- STATIC_ROOT = os.path.join(BASE_DIR, “static”) を追加します。
STATIC_URL = '/static/' STATIC_ROOT = os.path.join(BASE_DIR, "static")
6-3. ymlファイルの編集
staticファイル用のボリュームを作成し、ディレクトリを指定します。
- wen、nginxへvolumesを2箇所追加します。
version: '3.7' services: web: build: ./django_project #context: ./django_project #dockerfile: Dockerfile.prod #dockerfile: Dockerfile command: gunicorn project.wsgi:application --bind 0.0.0.0:8000 volumes: - static_volume:/home/app/web/static expose: - 8000 env_file: - ./.env.dev depends_on: - db db: image: postgres:15.1-alpine volumes: - postgres_data:/var/lib/postgresql/data/ env_file: - ./.env.dev nginx: build: ./nginx volumes: - static_volume:/home/app/web/static ports: - 1317:80 depends_on: - web volumes: postgres_data: static_volume:
6-4. Dockerfileファイルの編集
ymlファイルのボリュームの設定に対応するために、Dockerfile内にディレクトリを作成するコードを追加します。
- [Dockerfile.prod]
ENV HOME=/home/app ENV APP_HOME=/home/app/web RUN mkdir $APP_HOME #追加 RUN mkdir $APP_HOME/static WORKDIR $APP_HOME
6-5. nginx.confの編集
他のファイルで設定したstaticファイルの表示に対応するために、nginx.confを編集します。
- [nginx.conf]
upstream project { server web:8000; } server { listen 80; location / { proxy_pass http://project; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $host; proxy_redirect off; } location /static/ { alias /home/app/web/static/; } }
6-6. 表示の確認
設定を確認するために、表示を確認します。
使用していたコンテナの停止、削除
sudo docker compose down --volumes
新コンテナの起動
sudo docker compose up -d --build
新たな設定でデータベースのマイグレート
sudo docker compose exec web python manage.py migrate --noinpu
Djangoプロジェクト内のstatic関係のファイルを、nginxのstaticディレクトリへ移動するために、python manage.py collectstatic を行います。
sudo docker compose exec web python manage.py collectstatic --no-input --clear [Output] 130 static files copied to '/home/app/web/static'.
表示 [Nginx]
- Local : http://localhost:1317/
- VPS:ポートを解放します。
- VPS : http://160.248.11.107:1317/
管理者画面の表示
- Local : http://localhost:1317/admin/
- VPS : http://160.248.11.107:1317/admin/
コンテナの/web/static/の構造
app@ae0ce0954f55:~/web/static/admin$ ls -al drwxr-xr-x 3 app nogroup 4096 Jan 5 10:24 css drwxr-xr-x 2 app nogroup 4096 Jan 5 10:24 fonts drwxr-xr-x 3 app nogroup 4096 Jan 5 10:24 img drwxr-xr-x 4 app nogroup 4096 Jan 5 10:24 js
6-7. Mediaファイルの設定
Djangoでのstaticファイルとmediaファイルの使い分け
staticファイル:cssファイル、JSファイル、開発者がプロジェクトに追加した画像など
mediaファイル:webアプリの利用者が、対象のwebアプリ内で画像やファイルをアップロードして、表示する画像など
6-8. settings.pyの編集
- staticファイル同様にmediaファイル用のurlとrootを指定します。
... STATIC_URL = '/static/' STATIC_ROOT = os.path.join(BASE_DIR, "static") MEDIA_URL = "/media/" MEDIA_ROOT = os.path.join(BASE_DIR, "media")
6-9. ymlファイルの編集
- [docker-compose.yml]
version: '3.7' services: web: build: ./django_project #dockerfile: Dockerfile.prod command: gunicorn project.wsgi:application --bind 0.0.0.0:8000 volumes: - static_volume:/home/app/web/static - media_volume:/home/app/web/media expose: - 8000 env_file: - ./.env.dev depends_on: - db db: image: postgres:15.1-alpine volumes: - postgres_data:/var/lib/postgresql/data/ env_file: - ./.env.dev nginx: build: ./nginx volumes: - static_volume:/home/app/web/static - media_volume:/home/app/web/media ports: - 1317:80 depends_on: - web volumes: postgres_data: static_volume: media_volume:
6-10. Dockerファイルの編集
- [Dockerfile]
ENV HOME=/home/app ENV APP_HOME=/home/app/web RUN mkdir $APP_HOME #追加 RUN mkdir $APP_HOME/static RUN mkdir $APP_HOME/media WORKDIR $APP_HOME
6-11. Nginxのconfigファイルの編集
- [nginx.conf]
upstream project { server web:8000; } server { listen 80; location / { proxy_pass http://project; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $host; proxy_redirect off; } location /static/ { alias /home/app/web/static/; } location /media/ { alias /home/app/web/media/; } }
6-12. コンテナを起動し表示確認
コンテナの停止、削除
sudo docker compose down --volumes
コンテナの起動
sudo docker compose up -d --build
新たな設定でデータベースのマイグレート
sudo docker compose exec web python manage.py migrate --noinpu
Djangoプロジェクト内のstatic関係のファイルを、nginxのstaticディレクトリへ移動。
sudo docker compose exec web python manage.py collectstatic --no-input --clear
guncornの起動
sudo docker compose exec web gunicorn project.wsgi:application --bind 0.0.0.0:8000
ブラウザで表示
- Local : http://localhost:1317/
- VPS : http://160.248.11.107:1317/
7. 独自ドメインの利用とHTTPS化
- Let’s Encrypt によるコンテナ化された Django アプリケーションの保護
- HTTPS Nginxプロキシの背後で実行されているコンテナ化されたDjangoアプリを、SSL証明書を暗号化して保護する方法。
- Django アプリを Postgres、Nginx、Gunicorn と共にコンテナー化する方法を理解していることを前提とします。
7-1 settings.pyの編集
- https化に備えCSRFのセキュリティー強化と、HTTPへリクエストがあった際に、HTTPSへリダイレクトするように以下のコードを追加します。
- [settings.py]
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
8. Dockerfileを本番環境用、開発環境用
Dockerfileやdocker-compose.yamlを本番環境と開発環境で別のものを使用したいという状況があります。 同じ名称のファイルを作成することはできませんし、そもそもdocker compose buildのコマンドは何も指定しないが、自動的にDockerfileを元に buildの対象方法
- build するとき(Dockerfile からイメージを構築する)
Dockerfileを本番環境用、開発環境用にそれぞれ作成します。
- 本番環境用 ・・・ Dockerfile.prod
- 開発環境用 ・・・ Dockerfile.dev
ファイル名を指定してbuildします。
// Dockerfile.prod を build するとき $ docker build -f Dockerfile.prod . // Dockerfile.dev を build するとき $ docker build -f Dockerfile.dev .
- コンテナの作成・起動・削除時
コンテナの作成・起動・削除時には、docker-compose up -dやdocker-compose downのコマンドの入力が必要です。このとき参照されるのは、docker-compose.yamlファイルになりますが、こちらでbuildの項目を指定している場合には注意が必要です。
docker-compose.yamlを本番環境用、開発環境用にそれぞれ作成します。
- 本番環境用 ・・・ docker-compose-prod.yaml
- 開発環境用 ・・・ docker-compose-dev.yaml
build の項目を記述します。
context ・・・ Dockerfileの所在
dockerfile ・・・ Dockerfileの名称 ※ ただのDockerfileという名称であれば省略可能
アプリのディレクトリ直下に存在するDockerfile.prodを指定する場合
[docker-compose-prod.yaml]
version: '3' services: app: build: context: . dockerfile: "Dockerfile.prod" :
- アプリのディレクトリ直下に存在するDockerfile.devを指定する場合
- [docker-compose-dev.yaml]
version: '3' services: app: build: context: . dockerfile: "Dockerfile.dev" :
- コンテナの作成・起動を行います。
# docker-compose-prod.yaml を指定する場合 $ docker compose -f docker-compose-prod.yaml up -d # docker-compose-dev.yaml を指定する場合 $ docker compose -f docker-compose-dev.yaml up -d
nc (または netcat) ユーティリティーは、TCP または UDP に関連付けられたさまざまなタスクに使用できます。nc は、TCP 接続を開き、UDP パケットを送信し、任意の TCP および UDP ポートで待機し、ポートスキャンを実行し、IPv4 と IPv6 の両方に対応します。telnet(1) とは異なり、nc は簡単にスクリプト化でき、エラーメッセージを標準出力に送信するのではなく標準エラーに分割します。 ↩︎