神戸学院大学 経営学部 林坂ゼミ

Docker 入門トップページ

« 戻る 次へ »

Docker 入門

Python Django サーバを Docker で構築する

SQLite を使った Django プロジェクトを公開する

このセクション,Python Django のプロジェクトを Docker を使って Web に公開する方法をいくつか紹介します.まずこのページではデータベースに SQLite を用いた最も単純な環境を構築します.具体的には,Nginx コンテナを Web サーバとして http://localhost/ という URL でリクエストを受け付け,Python Django で開発したコメントアプリケーションサーバのコンテナに転送します.SQLite データベースはアプリケーションサーバ内で管理することにします.

web-401

なお,このページの Docker サンプルコードは GitHub の Django01sqlite フォルダで公開しています.また,Python Django のコードも GitHub で公開しています.

任意の新規ディレクトリを作成し,その中に次の構成でディレクトリとファイルを設置します.なお,Django のプロジェクトは django ディレクトリに設置しますが,Docker の Git リポジトリに Django のコードが含まれないように,django/.gitignore を設置しておきます.また,ログファイルも Git に含まれないように logs/nginx/.gitignore を設置しておきます.

% tree -a -F ⏎
./
├── .gitignore
├── django/
│   └── .gitignore
├── docker-compose.yml
├── logs/
│   └── nginx/
│       └── .gitignore
├── nginx/
│   ├── conf.d/
│   │   └── default.conf
│   └── Dockerfile
├── python/
│   ├── Dockerfile
│   └── entrypoint.sh*
└── Readme.md

7 directories, 9 files

全体の設計図である docker-compose.yml は次のような内容にします.Python のアプリケーションサーバである python_app と Web サーバである web という2つのサービスで全体を構成します.

docker-compose.yml
services:
  python_app:
    build:
      context: .
      dockerfile: ./python/Dockerfile
    container_name: python_app_01
    volumes:
      - ./python/entrypoint.sh:/entrypoint.sh
      - ./django/code:/code
    expose:
      - "8000"
    command: /bin/sh -c "pip install -r /code/requirements.txt  && chmod +x /entrypoint.sh && /entrypoint.sh"
  web:
    image: nginx:1.25.5
    container_name: python_nginx_01
    volumes:
      - ./nginx/conf.d:/etc/nginx/conf.d
      # - ./web/html:/usr/share/nginx/html
      - ./django/code/comments/static:/static
      - ./logs/nginx:/var/log/nginx
    ports:
      - "80:80"
    depends_on:
      - python_app

docker-compose.yml の5行目で,Dockerfile のパスを指定しています.Dockerfile には次の内容を入力します.

python/Dockerfile
FROM python:3.11

# pycファイル(および__pycache__)の生成を行わないようにする
ENV PYTHONDONTWRITEBYTECODE=1
# 標準出力・標準エラーのストリームのバッファリングを行わない
ENV PYTHONUNBUFFERED=1
# コンテナのワークディレクトリを/codeに指定
WORKDIR /code
RUN pip install --upgrade pip

python/entrypoint.sh は次の内容を入力します.このスクリプトはコンテナ起動時に実行されます.2行目の pip list は余分なコードですが,これを入れておくことでコンテナ出力にインストール済みのパッケージが表示されることから,慣れないうちはスクリプトの実行状況が確認しやすくなります.また,3行目は gunicorn という HTTP サーバを起動する命令です.このコードは docker-compose.yml の5行目にある command によって実行されます.つまり,コンテナが起動した直後に Django プロジェクトの requirements.txt に記載されたパッケージがインストールされたあと,entrypoint.sh が実行されるという順番です.

python/entrypoint.sh
#!/bin/bash
pip list
gunicorn django_comment.wsgi:application --bind 0.0.0.0:8000

Web サーバに関するサービスは docker-compose.yml の13行目以降に記載されています.今回は13行目のとおりサービス名に web を指定しましたが,以前のように nginx というようなサービス名であっても構いません.Nginx に関する Dockerfile は次のとおりです.

nginx/Dockerfile
FROM nginx:1.25.5

COPY conf.d/default.conf /etc/nginx/conf.d/default.conf

Nginx の設定ファイルは nginx/conf.d/defaul.conf として次の内容とします.ここで,1行目の upstream python と10行目の http://python では同じ名称 python を利用していることに注意してください.また,2行目の python_app:8000python_appdocker-compose.yml の2行目にあるサービス名と,10-11行目のポート番号に一致させていることにも注意してください.つまり,http://localhost/static/ 以外のリクエストは8000番ポートを利用して python_app コンテナに転送されることを意味しています.

nginx/conf.d/default.conf
upstream python {
    server python_app:8000;
}

server {
    listen 80;
    server_name 0.0.0.0;

    location / {
        proxy_pass http://python;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_redirect off;
    }

    location /static/ {
        alias /static/;
    }
}

Web サービスへのアクセスログ (access.log と error.log) を保存する volume となる logs/ngins/ というディレクトリも作成します.しかしながら,ログファイル自体が Git のリポジトリに含まれないように .gitignore ファイルを作成しておきます.

logs/nginx/.gitignore
*

また,Django のプロジェクトは django ディレクトリに格納しますが,ここでもやはり .gitignore を作成しておきます.

django/.gitignore
*

それでは GitHub にある Django プロジェクトについて,リポジトリのクローンを作成します.まず,(1) では django ディレクトリに移動しています.(2) でクローンを作成します.これによりプロジェクトのすべてのファイルがダウンロードされます.(3) でクローンが作成されたことを確認します.クローンはリポジトリ名と同じ名前のディレクトリに作成されます.今回は docker-compose.yml の9行目のとおり,django/code/ ディレクトリにプロジェクトのファイルを設置することになっていることから,(4) でディレクトリ名を変更しています.(5) ではディレクトリ名が正しく変更されたことを確認しています.なお,lsmvBash のコマンドであることから,Windows コマンドプロンプトの場合は dirrn または rename を使用してください.

% cd django ⏎  # (1) ディレクトリを移動
% git clone https://github.com/rinsaka/django_comment2022.git ⏎  # (2) クローンを作成
Cloning into 'django_comment2022'...
remote: Enumerating objects: 250, done.
remote: Counting objects: 100% (250/250), done.
remote: Compressing objects: 100% (105/105), done.
remote: Total 250 (delta 139), reused 246 (delta 135), pack-reused 0 (from 0)
Receiving objects: 100% (250/250), 34.30 KiB | 2.14 MiB/s, done.
Resolving deltas: 100% (139/139), done.
% ls ⏎  # (3) クローンが作成されたことを確認
django_comment2022
% mv django_comment2022 code ⏎  # (4) ディレクトリ名を変更
% ls ⏎ # (5) 変更されたことを確認
code
%

Django プロジェクトのファイル構造を確認しておきます.

tree -F ⏎
./
└── code/
    ├── comments/
    │   ├── __init__.py
    │   ├── admin.py
    │   ├── apps.py
    │   ├── fixtures/
    │   │   └── comments-data.json
    │   ├── forms.py
    │   ├── migrations/
    │   │   ├── __init__.py
    │   │   └── 0001_initial.py
    │   ├── models.py
    │   ├── static/
    │   │   └── css/
    │   │       └── style.css
    │   ├── templates/
    │   │   ├── base.html
    │   │   ├── comments/
    │   │   │   ├── delete_confirm.html
    │   │   │   ├── form.html
    │   │   │   ├── index.html
    │   │   │   └── show.html
    │   │   ├── index.html
    │   │   ├── prev_next.html
    │   │   └── simple_pagination.html
    │   ├── tests.py
    │   ├── urls.py
    │   └── views.py
    ├── db.sqlite3.sample
    ├── django_comment/
    │   ├── __init__.py
    │   ├── asgi.py
    │   ├── settings.py
    │   ├── urls.py
    │   └── wsgi.py
    ├── manage.py*
    ├── readme.md
    └── requirements.txt

10 directories, 29 files
%

別のコマンドでもファイルの一覧を確認しておきます.Django プロジェクトのディレクトリに manage.py などのファイルが存在することに注意してください.

% ls -la ⏎
total 400
drwxr-xr-x  11 rinsaka  staff     352 10月 14 15:05 .
drwxr-xr-x   4 rinsaka  staff     128 10月 14 15:05 ..
-rw-r--r--   1 rinsaka  staff   53248 10月 14 15:05 .coverage
drwxr-xr-x  12 rinsaka  staff     384 10月 14 15:05 .git
-rw-r--r--   1 rinsaka  staff      71 10月 14 15:05 .gitignore
drwxr-xr-x  14 rinsaka  staff     448 10月 14 15:05 comments
-rw-r--r--   1 rinsaka  staff  135168 10月 14 15:05 db.sqlite3.sample
drwxr-xr-x   7 rinsaka  staff     224 10月 14 15:05 django_comment
-rwxr-xr-x   1 rinsaka  staff     670 10月 14 15:05 manage.py
-rw-r--r--   1 rinsaka  staff     439 10月 14 15:05 readme.md
-rw-r--r--   1 rinsaka  staff     174 10月 14 15:05 requirements.txt
%

2つ上の階層(Docker のディレクトリ)に戻っておきます.

% cd .. ⏎
% cd .. ⏎
%

すべてのファイルの準備ができたらいよいよコンテナを起動します.web サービス(nginxイメージ)がクラウドサービスからプルされ,django01sqlite-python_app イメージがビルドされたことが出力から分かります.その後,2つのコンテナが起動したことも読み取れます.

% docker compose up -d ⏎
[+] Running 8/8
 ✔ web Pulled                                                                         7.9s
   ✔ 24c63b8dcb66 Pull complete                                                       2.6s
   ✔ ac894f1d1dfb Pull complete                                                       3.1s
   ✔ 2572d4eb2260 Pull complete                                                       3.1s
   ✔ 0ac3805c647c Pull complete                                                       3.1s
   ✔ da20f09652a8 Pull complete                                                       3.1s
   ✔ 2de21a3abd85 Pull complete                                                       3.1s
   ✔ 77cea143f3c3 Pull complete                                                       3.2s
[+] Building 18.9s (9/9) FINISHED
 => [internal] load local bake definitions                                            0.0s
 => => reading from stdin 598B                                                        0.0s
 => [internal] load build definition from Dockerfile                                  0.0s
 => => transferring dockerfile: 385B                                                  0.0s
 => [internal] load metadata for docker.io/library/python:3.11                        2.6s
 => [internal] load .dockerignore                                                     0.0s
 => => transferring context: 2B                                                       0.0s
 => [1/3] FROM docker.io/library/python:3.11@sha256:a2bd92ce584000ca1a93aea40b7afa1  14.1s
 => => resolve docker.io/library/python:3.11@sha256:a2bd92ce584000ca1a93aea40b7afa13  0.0s
 => => sha256:a2bd92ce584000ca1a93aea40b7afa138171e8e2bfba59d2ac2a 10.32kB / 10.32kB  0.0s
 => => sha256:c29fe597c17ce398a04cf884bdfa0b42312fb5e8af6c0fdca67004 2.32kB / 2.32kB  0.0s
 => => sha256:c0bb6bc63b1ec99f8691645f22a462e7735b41a88808f8216c46b8 6.23kB / 6.23kB  0.0s
 => => sha256:28aec8b14b3eeacbf47ef38af72fab694b109fdcdf3158172275 49.65MB / 49.65MB  3.4s
 => => sha256:003e6ed58c0c6ddbc757cdcbd876d66b9b5eed39128088a0055c 25.02MB / 25.02MB  2.2s
 => => sha256:390c9631087ef516f060328537d417f223e1f264c968e3918689 67.58MB / 67.58MB  3.4s
 => => sha256:fa24803fc7a76e7898f7a43b6b4f9851f1150fe72077582125 226.07MB / 226.07MB  8.2s
 => => extracting sha256:28aec8b14b3eeacbf47ef38af72fab694b109fdcdf31581722750599baf  1.3s
 => => sha256:cef01e0402edb7321b12b5be495a7e1da3cecc4ec67d0b5acaedd1 6.20MB / 6.20MB  4.0s
 => => sha256:fbe585a92f31bac8761c765519ed760128e2e548d160dd1f5dac 23.50MB / 23.50MB  5.0s
 => => sha256:a46927c07bdb1349e9a75d0189f3d9391e8d75ca95ef931cc65c96a3ba 249B / 249B  4.5s
 => => extracting sha256:003e6ed58c0c6ddbc757cdcbd876d66b9b5eed39128088a0055c819ebe1  0.5s
 => => extracting sha256:390c9631087ef516f060328537d417f223e1f264c968e39186895696e78  1.8s
 => => extracting sha256:fa24803fc7a76e7898f7a43b6b4f9851f1150fe7207758212555ada9266  4.5s
 => => extracting sha256:cef01e0402edb7321b12b5be495a7e1da3cecc4ec67d0b5acaedd16bcdf  0.2s
 => => extracting sha256:fbe585a92f31bac8761c765519ed760128e2e548d160dd1f5dac1fc787f  0.6s
 => => extracting sha256:a46927c07bdb1349e9a75d0189f3d9391e8d75ca95ef931cc65c96a3bad  0.0s
 => [2/3] WORKDIR /code                                                               0.2s
 => [3/3] RUN pip install --upgrade pip                                               1.8s
 => exporting to image                                                                0.1s
 => => exporting layers                                                               0.1s
 => => writing image sha256:aefe61bf7d8dabdf4baaa698f773040ed6561701c1e6bdbd3d1c582a  0.0s
 => => naming to docker.io/library/django01sqlite-python_app                          0.0s
 => resolving provenance for metadata file                                            0.0s
[+] Running 4/4
 ✔ django01sqlite-python_app       Built                                              0.0s
 ✔ Network django01sqlite_default  Created                                            0.0s
 ✔ Container python_app_01         Started                                            0.2s
 ✔ Container python_nginx_01       Started                                            0.2s
%

Docker イメージの一覧を確認します.上の作業でダウンロード(プル)やビルドされたイメージが表示されるはずです.なお,ビルドされたイメージの名称は ディレクトリ名-サービス名 になっていることに注意してください.

% docker image ls ⏎
REPOSITORY                  TAG       IMAGE ID       CREATED              SIZE
django01sqlite-python_app   latest    aefe61bf7d8d   About a minute ago   1.13GB
nginx                       1.25.5    8dd77ef2d82e   17 months ago        193MB
%

2つのコンテナが起動状態であることを確認します.

% docker container ls ⏎
CONTAINER ID   IMAGE                       COMMAND                   CREATED              STATUS              PORTS                                 NAMES
a24f5386aac4   nginx:1.25.5                "/docker-entrypoint.…"   About a minute ago   Up About a minute   0.0.0.0:80->80/tcp, [::]:80->80/tcp   python_nginx_01
dc103f45b919   django01sqlite-python_app   "/bin/sh -c 'pip ins…"   About a minute ago   Up About a minute   8000/tcp                              python_app_01
%

起動中の Python アプリケーションサーバ(サービス名:python_app,コンテナ名:python_app_01,コンテナID:dc103f45b919)にログインためのコマンドは複数あります.次のいずれかのコマンドを利用してください.

% docker compose exec python_app /bin/bash ⏎  # (1) docker-compose.yml のサービス名
% docker exec -it python_app_01 /bin/bash ⏎   # (2) container name
% docker exec -it dc103f45b919 /bin/bash ⏎    # (3)container ID

例えば (1) docker-compose.yuml のサービス名を指定してコンテナにログインします.ログイン後のワークディレクトリ(カレントディレクトリ)が /code になっています.これは Dockerfile の8行目にある WORKDIR /code の指定による結果です.また,ls コマンドを利用して,manage.py などのファイルが /code/ ディレクトリに存在していることを確認します.

% docker compose exec python_app /bin/bash ⏎
root@dc103f45b919:/code# ls ⏎
comments  db.sqlite3.sample  django_comment  manage.py  readme.md  requirements.txt
root@dc103f45b919:/code#

上の表示から SQLite データベースのファイル /code/db.sqlite が存在しないことが分かったので,データベースのマイグレーションサンプルデータの投入を行います.次のコマンドではマイグレーションの実行状況を確認しています.まだマイグレーションファイルがまだ一切実行されていない(つまり,データベースにテーブルが存在しない)ことが分かります.

root@dc103f45b919:/code# python manage.py showmigrations ⏎
System check identified some issues:

WARNINGS:
?: (staticfiles.W004) The directory '/code/static' in the STATICFILES_DIRS setting does not exist.
admin
 [ ] 0001_initial
 [ ] 0002_logentry_remove_auto_add
 [ ] 0003_logentry_add_action_flag_choices
auth
 [ ] 0001_initial
 [ ] 0002_alter_permission_name_max_length
 [ ] 0003_alter_user_email_max_length
 [ ] 0004_alter_user_username_opts
 [ ] 0005_alter_user_last_login_null
 [ ] 0006_require_contenttypes_0002
 [ ] 0007_alter_validators_add_error_messages
 [ ] 0008_alter_user_username_max_length
 [ ] 0009_alter_user_last_name_max_length
 [ ] 0010_alter_group_name_max_length
 [ ] 0011_update_proxy_permissions
 [ ] 0012_alter_user_first_name_max_length
comments
 [ ] 0001_initial
contenttypes
 [ ] 0001_initial
 [ ] 0002_remove_content_type_name
sessions
 [ ] 0001_initial
root@dc103f45b919:/code#

マイグレーションを実行して各種テーブルを生成します.

root@dc103f45b919:/code# python manage.py migrate ⏎
System check identified some issues:

WARNINGS:
?: (staticfiles.W004) The directory '/code/static' in the STATICFILES_DIRS setting does not exist.
Operations to perform:
  Apply all migrations: admin, auth, comments, 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 comments.0001_initial... OK
  Applying sessions.0001_initial... OK
root@dc103f45b919:/code#

テーブルの生成ができたのでサンプルデータを投入します.

root@dc103f45b919:/code# python manage.py loaddata comments/fixtures/comments-data.json ⏎
System check identified some issues:

WARNINGS:
?: (staticfiles.W004) The directory '/code/static' in the STATICFILES_DIRS setting does not exist.
Installed 10 object(s) from 1 fixture(s)
root@dc103f45b919:/code#

データベースのファイル db.sqlite3 が生成されたことが分かります.(なお,このリポジトリにはサンプルのデータベースファイルとして db.sqlite3.sample というファイルも存在します.このサンプルデータにはすでにサンプルデータが投入されているので,マイグレーションとサンプルデータの投入を行う代わりに,このファイル名を変更する方法でも動作します.)

root@dc103f45b919:/code# ls ⏎
comments    db.sqlite3.sample  manage.py  requirements.txt
db.sqlite3  django_comment     readme.md
root@dc103f45b919:/code#

データベースの準備ができたのでコンテナからログアウトします.

root@dc103f45b919:/code# exit ⏎
%

すべての準備が整ったので,ブラウザで http://localhost/ に接続します.

docker-2025-django-01

comments アプリケーションに移動すると正しくシステムが動作していることが確認できるはずです.コメントの新規投稿や編集,削除などの機能を試してください.

docker-2025-django-02

なお,Django の内部ファイルを変更してもリアルタイムにはその変更が反映されません.例えば,django/code/comments/views.py ファイルを変更した後は次のようにコンテナを再起動する必要があります.

% docker compose restart python_app ⏎
[+] Restarting 1/1
 ✔ Container python_app_01  Started                                                  10.2s
%

さらに,サービス(コンテナ)の起動や終了方法を確認しておきます.現時点では2つのコンテナが起動しており,すべてのコンテナを停止するには docker compose stop を実行します.

% docker container ls ⏎
CONTAINER ID   IMAGE                       COMMAND                   CREATED         STATUS         PORTS                                 NAMES
57ff831ee93f   nginx:1.25.5                "/docker-entrypoint.…"   4 minutes ago   Up 4 minutes   0.0.0.0:80->80/tcp, [::]:80->80/tcp   python_nginx_01
5ee89da6a0b9   django01sqlite-python_app   "/bin/sh -c 'pip ins…"   4 minutes ago   Up 4 minutes   8000/tcp                              python_app_01
% docker compose stop ⏎
[+] Stopping 2/2
 ✔ Container python_nginx_01  Stopped                                                 0.2s
 ✔ Container python_app_01    Stopped                                                10.1s
%

起動中のコンテナがなくなったことを確認します.

% docker container ls ⏎
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
%

-a オプションを付けると終了したコンテナも表示されます.2つのコンテナが終了状態 (Exited) であることが分かりました.

% docker container ls -a ⏎
CONTAINER ID   IMAGE                       COMMAND                   CREATED         STATUS                       PORTS     NAMES
57ff831ee93f   nginx:1.25.5                "/docker-entrypoint.…"   5 minutes ago   Exited (0) 17 seconds ago              python_nginx_01
5ee89da6a0b9   django01sqlite-python_app   "/bin/sh -c 'pip ins…"   5 minutes ago   Exited (137) 6 seconds ago             python_app_01
%

もう一度すべてのコンテナを起動します.

% docker compose up -d ⏎
[+] Running 2/2
 ✔ Container python_app_01    Started                                                 0.1s
 ✔ Container python_nginx_01  Started                                                 0.1s
% docker container ls ⏎
CONTAINER ID   IMAGE                       COMMAND                   CREATED         STATUS          PORTS                                 NAMES
57ff831ee93f   nginx:1.25.5                "/docker-entrypoint.…"   5 minutes ago   Up 12 seconds   0.0.0.0:80->80/tcp, [::]:80->80/tcp   python_nginx_01
5ee89da6a0b9   django01sqlite-python_app   "/bin/sh -c 'pip ins…"   5 minutes ago   Up 12 seconds   8000/tcp                              python_app_01
%

今度は docker compose down コマンドを実行します.このコマンドを利用するとすべてのコンテナを終了させるだけでなくコンテナの破棄も行います.

% docker compose down ⏎
[+] Running 3/3
 ✔ Container python_nginx_01       Removed                                            0.2s
 ✔ Container python_app_01         Removed                                           10.2s
 ✔ Network django01sqlite_default  Removed                                            0.2s
% docker container ls ⏎
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
% docker container ls -a ⏎
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
%

最後に Docker イメージを確認しておきます.これらがもう不要であれば削除しても構いません.(nginxのイメージは次のページでも再利用します.)

% docker image ls ⏎
REPOSITORY                  TAG       IMAGE ID       CREATED          SIZE
django01sqlite-python_app   latest    aefe61bf7d8d   13 minutes ago   1.13GB
nginx                       1.25.5    8dd77ef2d82e   17 months ago    193MB
%