やってみたこと
- 前回、djangoチュートリアルで作成したpollsアプリをdockerコンテナから立ち上げてみました。今回は、
docker-compose.yml
にDB情報を追記して実際にテーブル作成まで行う手順を記します。
前提
- 今回も敢えて、
Dockerfile
とdocker-compose.yml
の両方を最低限レベルで使ってdjangoのアプリケーションを構築します。これらのファイルを使った方が、今後汎用的に覚えられるだろうという自分への期待も込めています(笑) - 前回までの記事はこちらから:
- チュートリアル作成における記事はこちら:【初めてのDjango】インストール手順:MVTモデル、ルーティング、DBマイグレーション、views.py、テンプレート、名前空間について
- DjangoチュートリアルをDockerから立ち上げてみた記事はこちら:Djangoの開発環境をdocker-composeを利用してコンテナで起動し、runserverコマンドでホスト側からアクセスする方法
- djangoチュートリアルでpollsアプリケーションを制作したmysiteプロジェクトを、以下ディレクトリ構造で配置(前回と同様)。
.
├── docker
│ ├── docker-compose.yml
│ └── Dockerfile
└── web
├── mysite
│ ├── mysite
│ ├── polls
│ ├── db.sqlite3
│ └── manage.py
└── requirements.txt
- djangoチュートリアルで作成したものをそのまま使っていますので、
settings.py
のDAEABASES
もそのままになっています。
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
手順
- docker関連ファイルの準備
- ビルド・コンテナ起動
- コンテナの中に入って
manage.py
を確認 - Dockerコンテナから開発サーバを起動
docker関連ファイルの準備
-
Dockerfile
は、前回記事と同様です。
# python:3.8の公式 image をベースの image として設定
FROM python:3.8
# 作業ディレクトリの作成
RUN mkdir /mysite
# 作業ディレクトリの設定(以後の RUN は WORKDIR で実行)
WORKDIR /mysite
# カレントディレクトリにある資産をコンテナ上の指定のディレクトリにコピーする
ADD web /mysite
# pipでrequirements.txtに指定されているパッケージを追加する
RUN pip install -r requirements.txt
# 起動(今回はなし)
# CMD python3 manage.py runserver 0.0.0.0:8000
-
requirements.txt
も前回記事同様、必要最低限のままにしています。
Django==4.1
requests
-
docker-compose.yml
に以下のように記載します。- 今回も必要最低限のものしか記載していません。前回から
db-sample1
以下を加えています。
- 今回も必要最低限のものしか記載していません。前回から
- それぞれが意味する詳細は以下記事が非常にわかりやすかったのでご参考までにぜひ。
version: '3'
services:
mysite:
build:
context: ../
dockerfile: ./docker/Dockerfile # Dockerfileのディレクトリを指定します
volumes:
- '../web:/mysite'
ports:
- "8000:8000" # 左側が外部からのアクセス時のポート番号:右側がdockerコンテナからアクセスする時のポート番号。
container_name: docker_mysite
tty: true # ポート待受とかしていないコンテナの場合、trueに設定しておくとコンテナが起動し続けます
working_dir: '/mysite'
db-sample1: # 任意の名前を指定します
build:
context: ../
dockerfile: ./docker/sample1/Dockerfile # Dockerfileのディレクトリを指定します
ports:
- "5438:5432" # 左側が外部からのアクセス時のポート番号:右側がdockerコンテナからアクセスする時のポート番号
container_name: mysite_sample1_db
environment:
POSTGRES_USER: user
POSTGRES_DB: sample
POSTGRES_PASSWORD: pass
volumes:
- postgres-data-volume:/var/lib/postgresql/data # <名前付きvolume名>:マウントしたいコンテナ内のPATH
volumes:
postgres-data-volume:
- portsフィールドは、ホスト・コンテナ間のポートのマッピングを指定します。
- 上記のservice
mysite
の例だと、コンテナポートの8000番(右側)をホストポートの8000番(左側)にマッピングすることで、ブラウザでhttps://localhost:8000/
へアクセスできるようになります。これを「ホストの8000番からコンテナの8000番へポートフォワードする」とも言います。db-sample1
も同様に、ホストの5438からコンテナの5432にポートフォワードしています。 - デフォルトでは、5432がPostgreSQLの利用するポートになります。
- 上記のservice
environmentフィールド
-
environment:
フィールドで一点注意があります。上記のようにUSER名やDB名を指定するか、以下のように記載するか、どちらかを記載しないとError: Database is uninitialized and superuser password is not specified.
エラーになります。以下のように記載した場合、デフォルトで与えられている、postgresというUSER名やDB名でログインできます。(パスワードなしで接続できるのであまり推奨されていません)
environment:
POSTGRES_HOST_AUTH_METHOD: trust
- ちなみに、以下のようにどちらも記載した場合は、
POSTGRES_USER
など指定したものでログインができませんでしたので、ご注意。「あれ、ちゃんと記述したのにpsql: FATAL: role "user" does not exist
って出てくるぞ?」という場合は、誤って重複した書き方をしていました。最初はデフォルトの方でやろうと思って記述していたので、後からPOSTGRES_USER
など指定した時に消すのを忘れてしまっていたんでしょうね。
environment:
POSTGRES_USER: user
POSTGRES_DB: sample
POSTGRES_PASSWORD: pass
volumes:
- postgres-data-volume:/var/lib/postgresql/data
environment:
POSTGRES_HOST_AUTH_METHOD: trust # こちらが優先されるのでご注意
volumesフィールド(名前付きボリュームの作成)
-
volumes:
フィールドでは、トップレベルで定義したvolume名(今回でいえば一番下段に記載しているvolumesのこと)を指定してあげます。- このマウントの手法を「名前付きボリュームの作成」や「ボリュームマウント」と言います。(「ディレクトリのマウント(同期)」については後述)
db-sample1:
# 省略
volumes:
- postgres-data-volume:/var/lib/postgresql/data # データボリューム名:コンテナディレクトリ
volumes:
postgres-data-volume: # ここで名前付きボリューム名を指定します
- 上記により、
postgres-data-volume
をマウントしてデータベースを永続化しています。- 「データの永続化」とは、Dockerコンテナ上で作成されたデータがコンテナ削除後も残り続けている状態のこと。コンテナが削除されてもコンテナ上で作成されたデータを保持し続ける方法として知られています。
- 上記で定義した「データボリューム」(単にボリュームともいいます)とは、1つまたは複数のDockerコンテナ内でデータの共有・再利用をするために設計された特別なディレクトリです。コンテナのライフサイクルとは別なので、コンテナを削除しても残ります。(データボリュームを削除すると永続化したデータは削除されます)
- 何も指定しなければ『匿名(無名)ボリューム』、指定すれば『名前付きボリューム』と呼ばれます。ただし、匿名ボリュームはコンテナが新しく作成されるたびに新たに作成されるので注意が必要な時があります。
- ちなみに、マウントで指定したのに、トップレベルのvolumesで該当のvolume定義がない場合、以下のエラーが返ってきます。
service "db-sample1" refers to undefined volume postgres-data-volume: invalid compose project
- 今回の場合、アプリケーション名_postgres-data-volumeのボリュームの下記の名前で作成されます。
docker_postgres-data-volume
-
/var/lib/postgresql/data
はpostgresコンテナでのデフォルトのDBパスです。- コンテナに入って
ls -a
すれば、どんなデータが保管されているのか確認できます。 - なお、mysqlコンテナならば、
/var/lib/mysql
となりますので、コンテナごとに異なります。 -
https://hub.docker.com/_/postgres
- "The default is /var/lib/postgresql/data"と記載があります
-
https://hub.docker.com/_/mysql
- "...(略).../var/lib/mysql inside the container, where MySQL by default will write its data files."と記載があります
- コンテナに入って
- 補足【ディレクトリのマウント(同期)】:
- 以下のように記載しておくと、Dockerコンテナ上の
/var/lib/postgresql/data
をローカルの/postgres/data
にマウントできます。コンテナ上のDBを誤って消してしまった場合やイメージを削除してしまった場合などのために、ローカルに保存しておくと後で復旧ができます。上記の例は「名前付きボリュームの作成」でしたが、下記の場合を「ディレクトリのマウント(同期)」と言います。
- 以下のように記載しておくと、Dockerコンテナ上の
db-sample1:
# 省略
volumes:
- ./postgres/data:/var/lib/postgresql/data
ビルド・コンテナ起動
-
docker-compose.yml
があるディレクトリで以下のコマンドを実行すると、ビルド(イメージ作成)&コンテナ起動します。
$ docker compose up -d
- 起動しているコンテナを以下のコマンドで確認できます。
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a88071064847 docker-mysite "python3" 10 seconds ago Up 9 seconds 0.0.0.0:8000->8000/tcp docker_mysite
5192dce52ff6 docker-db-sample1 "docker-entrypoint.s…" 10 seconds ago Up 9 seconds 0.0.0.0:5438->5432/tcp mysite_sample1_db
-
-d
オプションはデーモンモードで起動し、プロセスをバックグラウンドで動作させることができます。- アクセスログの内容やエラーなどを確認したい場合は、
-d
を外して実行します。
- アクセスログの内容やエラーなどを確認したい場合は、
-
$ docker compose ps
でもコンテナがRUNNINGかどうか分かります。 -
mysite
が8000ポートに、dbのsample1
が5432ポートに、それぞれdocker-compose.yml
で指定したポートにつながっていることがわかります。 -
また、以下のコマンドにより、
requirement.txt
に記述したモジュールがインストールされているかどうか確認できます。
$ docker exec -it docker_mysite pip list
Package Version
------------------ ---------
asgiref 3.6.0
backports.zoneinfo 0.2.1
certifi 2022.12.7
charset-normalizer 3.1.0
Django 4.1
idna 3.4
pip 22.0.4
requests 2.28.2
setuptools 57.5.0
sqlparse 0.4.3
urllib3 1.26.14
wheel 0.38.4
コンテナの中に入ってpostgresql
を確認
- 以下のコマンドで
mysite_sample1_db
コンテナに入ります。
$ docker exec -it mysite_sample1_db bash
- CONTAINER IDでも入れるので、以下のコマンドでもOKです。
$ docker exec -it 5192dce52ff6 bash
- コンテナの中に入って、
/var/lib/postgresql/data
を確認してみましょう。先ほどマウントしたのは、ここにあるpostgre関連ファイルを、指定したボリュームにマウントしたという意味になります。
5192dce52ff6:/# cd /var/lib/postgresql/data
5192dce52ff6:/var/lib/postgresql/data# ls -a
. base pg_dynshmem pg_logical pg_replslot pg_stat pg_tblspc pg_xact postmaster.opts
.. global pg_hba.conf pg_multixact pg_serial pg_stat_tmp pg_twophase postgresql.auto.conf postmaster.pid
PG_VERSION pg_commit_ts pg_ident.conf pg_notify pg_snapshots pg_subtrans pg_wal postgresql.conf
5192dce52ff6:/var/lib/postgresql/data#
ボリューム確認
- では、作成したボリュームが動いているか確認してみます。
- コンテナから出る場合は
exit
と入力してください。
- コンテナから出る場合は
- ボリュームの確認は、以下のコマンドで可能です。
$ docker volume ls
DRIVER VOLUME NAME
local docker_postgres-data-volume
- 指定したボリュームが作成されていることがわかりました。
- ボリュームは一度作成すれば、
docker compose down
された後でも残ります。上述したように、永続化しているからですね。 -
docker inspect [データボリューム名]
でデータボリュームの詳細情報が確認できます。 - また、
docker inspect [コンテナ名]
でコンテナの詳細情報が確認できます。- Mountsフィールドを見ると、Dockerコンテナのマウント対象なども確認できます。
データベースに接続
- それではデータベースを確認していきましょう。先ほどと同じようにまずは起動中のコンテナの中に入ります。
$ docker exec -it mysite_sample1_db bash
- 以下psqlコマンドで
user
ユーザーによるsample
データベースへのログインができます。
5192dce52ff6:/# psql -U user sample
sample=#
- 以下、いずれでもログイン可能です。
- いずれもホストやポートを指定する場合のログイン方法です。
psql -h localhost sample -U user
psql sample -U user -p 5432
psql -U user -d sample
- ちなみに、
psql -U user
だけだと、データベースの指定がないのでエラーになります。
psql -U user
psql: FATAL: database "user" does not exist
-
上記のエラーは、postgresではデフォルトではユーザー名と同じデータベースが作成されるためです。つまり、
docker-compose.yml
でdbにsample
と指定していない場合は、psql -U user
でもログインができます。 -
また、postgresではデフォルトで
postgres
というデータベース名であればログインができます。
psql -U user postgres
postgres=#
- プロンプトが、
sample
ではなくpostgres
になっていることがわかります。これは接続しているデータベースがpostgres
のためです。 -
sample
に切り替えたい場合は\c
コマンドで可能です。
postgres=# \c sample
You are now connected to database "sample" as user "user".
sample=#
-
\d
コマンドでテーブルを確認できますが、現状まだ何もないことがわかります。
sample=# \d
Did not find any relations.
- ちなみに上記で
user
というユーザー名を作成しこれで接続しました。これはロール(データーベースを管理するもの)といい、以下コマンドで確認できます。
sample=# \du
List of roles
Role name | Attributes | Member of
-----------+------------------------------------------------------------+-----------
user | Superuser, Create role, Create DB, Replication, Bypass RLS | {}
テーブル作成
- テーブルを作成してみましょう。
- 以下のコマンドで
public
スキーマの中にhoge
テーブルを作成できます。
create table public.hoge( id integer, name varchar(10), address varchar(10) );
CREATE TABLE
- ちなみに
public
スキーマを選択せずにhogeaaa
テーブルを作成するとどうなるでしょうか。
sample=# create table hogeaaa( id integer, name varchar(10), address varchar(10) );
CREATE TABLE
sample=# \d
List of relations
Schema | Name | Type | Owner
--------+---------+-------+-------
public | hoge | table | user
public | hogeaaa | table | user
(2 rows)
- 上記のように作成できていることがわかります。指定しない場合、自動的に「public」という名前のスキーマに入れられます。詳しく知りたい方は以下リンクをご参照ください。
- 以下記事では、スキーマという概念について図解があります。PostgreSQLは三層、MySQLは二層なのでちょっとたまに混乱します汗
データベースを別途作成
-
sample
やpostgres
以外にもデータベースを作成することができます。その場合は以下コマンドを実行します。
sample=# create database hogedb;
CREATE DATABASE
- 以下の通り、DBが新しく加わったことが確認できますね。
sample=# \l
List of databases
Name | Owner | Encoding | Collate | Ctype | Access privileges
-----------+-------+----------+------------+------------+-------------------
hogedb | user | UTF8 | ja_JP.utf8 | ja_JP.utf8 |
postgres | user | UTF8 | ja_JP.utf8 | ja_JP.utf8 |
sample | user | UTF8 | ja_JP.utf8 | ja_JP.utf8 |
template0 | user | UTF8 | ja_JP.utf8 | ja_JP.utf8 | =c/user +
| | | | | user=CTc/user
template1 | user | UTF8 | ja_JP.utf8 | ja_JP.utf8 | =c/user +
| | | | | user=CTc/user
(5 rows)
- データの取得したり保存したり確認したりするのも、同様にSQLコマンドで可能ですので、適当にINSERTなどしてみるのも良いと思います。
参考記事
- Dockerのデータを永続化!Data Volume(データボリューム)の理解から始める環境構築入門
- Docker Postgres DB コンテナが起動しないにハマる
- docker-compose.yml個人的Tips集
次回記事
- 初期データ登録用のスクリプトを作成し、SQL実行する手順の記事を作成しました。