【Docker使えるようになりたい】【#18 DBコンテナ】DBコンテナを使う(自動化・セキュリティ編)

はじめに

この記事は以下の記事で作成したDBコンテナの問題を解決することを目的とした記事になります。

前回の記事の問題点としては以下の2点がありました。

  • コンテナを削除するとデータが消えてしまう
  • コンテナの初回起動時のテーブルの作成とデータの追加が面倒くさい

それに追加で、「セキュリティ対策」も行う予定です

この記事で扱う範囲について

この記事では、太字になっている「 2 ~ 4 」の内容をまとめていきます。

  1. データの永続化をせずに、一通りの動作をチェックしてみる
  2. コンテナ実行時のテーブルの自動作成、テスト用データの自動挿入
  3. データの永続化
  4. ユーザー名、パスワード、データベース名は外部(.env)に括りだす
  5. Goサーバーと通信する

はじめに

今回からは、手動でコマンドを極力打たないでいいようにDockerfileを使っていきます。

より自動化の最適な方法としてはdocker-compose.ymlを使うことですが、まずは基本的な手法を経験しておいたほうがよりdocker-compose.ymlの効果がわかりやすいと思うので、この記事ではDockerfileを使用します。

2. コンテナ実行時のテーブルの自動作成、テスト用データの自動挿入

ディレクトリとファイルの準備

Dockerfileを使うために、docker-db-practiceフォルダを作成して、以下のファイル構造を作成します。
ファイルの中身は空で大丈夫です。

docker-db-practice/ <- カレントディレクトリ
├── Dockerfile
└── init.sql
  • Dockerfile: カスタムPostgreSQLイメージをビルドするための設定ファイル
  • init.sql: テーブルの作成やテストデータの挿入を自動化するSQLスクリプト

初期化用SQLスクリプトの作成

前回手動でコマンドを実行した、CREATE TABLEINSERT INTOの処理をファイルに記述して、コンテナ実行時に読み込んで自動実行してもらうために、init.sqlファイルに以下の内容を記述します。

init.sql
-- テーブルの作成
CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    name VARCHAR(100),
    email VARCHAR(100) UNIQUE
);

-- テストデータの挿入
INSERT INTO users (name, email) VALUES
('山田 太郎', 'taro.yamada@example.com'),
('杉山 光一', 'sugiyama.kouiti@example.com');

Dockerfileの作成

PostgreSQLの公式イメージを元に、前の節で作成した、init.sqlをイメージ内に配置して、コンテナ実行時に自動でSQLを実行してもらえるようにします。

PostgreSQLの場合、CMD命令などは必要なくPostgreSQL起動時に自動で決まったディレクトリ(/docker-entrypoint-initdb.d/)にあるSQLファイルを見に行くようになっているのでファイルをコピーするだけであとはPostgreSQLサーバーが起動したときに自動で実行してくれます。

Dockerfile
# ベースイメージとしてPostgreSQLの公式イメージを使用
FROM postgres:14

# 環境変数の設定
ENV POSTGRES_USER=user
ENV POSTGRES_PASSWORD=pass
ENV POSTGRES_DB=my-db

# 初期化スクリプトをコンテナ内にコピー
COPY init.sql /docker-entrypoint-initdb.d/

/docker-entrypoint-initdb.d/にあるSQLファイルデータディレクトリが空の状態でコンテナを起動した場合にのみ実行されます。

イメージの作成

通常のイメージの作成と同じです。

# コマンド
docker image build -t my-pg-db .

これで、コンテナを実行するとテーブルとデータが自動で挿入されるイメージが作成されました。

動作チェック

コンテナの実行をフォアグラウンドで実行すると内部でどのような処理がされているかわかるので、今回はこのログからinit.sqlの読み込みと、実行が行われているかを確認します。

# コマンド
docker container run --rm my-pg-db                          

# 出力
The files belonging to this database system will be owned by user "postgres".
This user must also own the server process.

The database cluster will be initialized with locale "en_US.utf8".
The default database encoding has accordingly been set to "UTF8".
The default text search configuration will be set to "english".

Data page checksums are disabled.

fixing permissions on existing directory /var/lib/postgresql/data ... ok
creating subdirectories ... ok
selecting dynamic shared memory implementation ... posix
selecting default max_connections ... 100
selecting default shared_buffers ... 128MB
selecting default time zone ... Etc/UTC
creating configuration files ... ok
running bootstrap script ... ok
performing post-bootstrap initialization ... ok
syncing data to disk ... ok


Success. You can now start the database server using:

    pg_ctl -D /var/lib/postgresql/data -l logfile start

initdb: warning: enabling "trust" authentication for local connections
You can change this by editing pg_hba.conf or using the option -A, or
--auth-local and --auth-host, the next time you run initdb.
waiting for server to start....2024-10-03 00:17:58.179 UTC [48] LOG:  starting PostgreSQL 14.13 (Debian 14.13-1.pgdg120+1) on aarch64-unknown-linux-gnu, compiled by gcc (Debian 12.2.0-14) 12.2.0, 64-bit
2024-10-03 00:17:58.179 UTC [48] LOG:  listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
2024-10-03 00:17:58.182 UTC [49] LOG:  database system was shut down at 2024-10-03 00:17:58 UTC
2024-10-03 00:17:58.186 UTC [48] LOG:  database system is ready to accept connections
 done
server started
CREATE DATABASE


/usr/local/bin/docker-entrypoint.sh: running /docker-entrypoint-initdb.d/init.sql
CREATE TABLE
INSERT 0 2


2024-10-03 00:17:58.387 UTC [48] LOG:  received fast shutdown request
waiting for server to shut down....2024-10-03 00:17:58.389 UTC [48] LOG:  aborting any active transactions
2024-10-03 00:17:58.393 UTC [48] LOG:  background worker "logical replication launcher" (PID 55) exited with exit code 1
2024-10-03 00:17:58.393 UTC [50] LOG:  shutting down
2024-10-03 00:17:58.406 UTC [48] LOG:  database system is shut down
 done
server stopped

PostgreSQL init process complete; ready for start up.

2024-10-03 00:17:58.510 UTC [1] LOG:  starting PostgreSQL 14.13 (Debian 14.13-1.pgdg120+1) on aarch64-unknown-linux-gnu, compiled by gcc (Debian 12.2.0-14) 12.2.0, 64-bit
2024-10-03 00:17:58.510 UTC [1] LOG:  listening on IPv4 address "0.0.0.0", port 5432
2024-10-03 00:17:58.510 UTC [1] LOG:  listening on IPv6 address "::", port 5432
2024-10-03 00:17:58.512 UTC [1] LOG:  listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
2024-10-03 00:17:58.515 UTC [65] LOG:  database system was shut down at 2024-10-03 00:17:58 UTC
2024-10-03 00:17:58.520 UTC [1] LOG:  database system is ready to accept connections

出力結果のserver startedあたりからinit.sqlが読み込まれて実行されているのが確認できます。
これで自動でテーブルとデータの挿入ができていることが確認できました。

うまく行ったか不安な方は、コンテナをデタッチドモードで実行して、前の「基本編の記事」で行った、コンテナ内のデータベースへ直接アクセスする方法を使ってデータを確認してみてください。

次の節ではも一つの問題点として、「コンテナを削除するとデータが無くなる」という問題の解決方法を紹介します。

3. データの永続化

Dockerの機能であるボリュームを使用することで、データをコンテナのライフサイクルから分離します。

DBコンテナでDB情報の永続にボリュームが使われるのは、データの整合性とパフォーマンスバックアップの取りやすさなど、様々な利点がボリュームにあるからです。
バインドマウントを使用すると、ホストOS側でファイルを開かれる可能性があるのでセキュリティやデータの整合性が取れなくなる場合が考えられます。

ボリュームの生成

まずは、ボリュームを生成します。
このボリュームを消さない限りデータベースのデータは生き続ける事ができます。

# コマンド
docker volume create db-volume   

# 出力
db-volume

ボリュームを紐づけて起動

PostgreSQLは/var/lib/postgresql/dataディレクトリにデータを配置します。
そのディレクトリをボリュームと一致させることでデータを永続化させる事ができます。

-vオプションを使って先ほど作成したボリュームと紐づけて起動します。

docker container run -v db-volume:/var/lib/postgresql/data --rm my-pg-db 

コンテナを起動するたびに毎回データベースを作り直してしまうのではないか

PostgreSQLはデータベースのでデータがなにもない状態に限り、/docker-entrypoint-initdb.d/の下にあるsqlスクリプトを実行します。

ボリュームにデータを退避させているので、コンテナ実行時にはデータが有る状態になるのでこの問題は発生しません。

実際に新しいボリュームにつなげたコンテナを連続で2回生成すると最初しかsqlスクリプトが実行されていないことに気がつくと思います。

# ボリュームに紐づけた初回起動
docker container run -v db-volume:/var/lib/postgresql/data --rm my-pg-db 

# 出力
The files belonging to this database system will be owned by user "postgres".
This user must also own the server process.

The database cluster will be initialized with locale "en_US.utf8".
The default database encoding has accordingly been set to "UTF8".
The default text search configuration will be set to "english".

Data page checksums are disabled.

fixing permissions on existing directory /var/lib/postgresql/data ... ok
creating subdirectories ... ok
selecting dynamic shared memory implementation ... posix
selecting default max_connections ... 100
selecting default shared_buffers ... 128MB
selecting default time zone ... Etc/UTC
creating configuration files ... ok
running bootstrap script ... ok
performing post-bootstrap initialization ... ok
syncing data to disk ... initdb: warning: enabling "trust" authentication for local connections
You can change this by editing pg_hba.conf or using the option -A, or
--auth-local and --auth-host, the next time you run initdb.
ok


Success. You can now start the database server using:

    pg_ctl -D /var/lib/postgresql/data -l logfile start

waiting for server to start....2024-10-03 00:30:53.594 UTC [48] LOG:  starting PostgreSQL 14.13 (Debian 14.13-1.pgdg120+1) on aarch64-unknown-linux-gnu, compiled by gcc (Debian 12.2.0-14) 12.2.0, 64-bit
2024-10-03 00:30:53.595 UTC [48] LOG:  listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
2024-10-03 00:30:53.598 UTC [49] LOG:  database system was shut down at 2024-10-03 00:30:53 UTC
2024-10-03 00:30:53.602 UTC [48] LOG:  database system is ready to accept connections
 done
server started
CREATE DATABASE


/usr/local/bin/docker-entrypoint.sh: running /docker-entrypoint-initdb.d/init.sql
CREATE TABLE
INSERT 0 2


2024-10-03 00:30:53.808 UTC [48] LOG:  received fast shutdown request
waiting for server to shut down....2024-10-03 00:30:53.809 UTC [48] LOG:  aborting any active transactions
2024-10-03 00:30:53.811 UTC [48] LOG:  background worker "logical replication launcher" (PID 55) exited with exit code 1
2024-10-03 00:30:53.813 UTC [50] LOG:  shutting down
2024-10-03 00:30:53.828 UTC [48] LOG:  database system is shut down
 done
server stopped

PostgreSQL init process complete; ready for start up.

2024-10-03 00:30:53.934 UTC [1] LOG:  starting PostgreSQL 14.13 (Debian 14.13-1.pgdg120+1) on aarch64-unknown-linux-gnu, compiled by gcc (Debian 12.2.0-14) 12.2.0, 64-bit
2024-10-03 00:30:53.934 UTC [1] LOG:  listening on IPv4 address "0.0.0.0", port 5432
2024-10-03 00:30:53.934 UTC [1] LOG:  listening on IPv6 address "::", port 5432
2024-10-03 00:30:53.937 UTC [1] LOG:  listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
2024-10-03 00:30:53.940 UTC [65] LOG:  database system was shut down at 2024-10-03 00:30:53 UTC
2024-10-03 00:30:53.944 UTC [1] LOG:  database system is ready to accept connections
^C2024-10-03 00:31:04.070 UTC [1] LOG:  received fast shutdown request
2024-10-03 00:31:04.071 UTC [1] LOG:  aborting any active transactions
2024-10-03 00:31:04.076 UTC [1] LOG:  background worker "logical replication launcher" (PID 71) exited with exit code 1
2024-10-03 00:31:04.079 UTC [66] LOG:  shutting down
2024-10-03 00:31:04.099 UTC [1] LOG:  database system is shut down



# ボリュームに紐づけた2回目の起動
docker container run -v db-volume:/var/lib/postgresql/data --rm my-pg-db 

# 出力
PostgreSQL Database directory appears to contain a database; Skipping initialization

2024-10-03 00:31:06.558 UTC [1] LOG:  starting PostgreSQL 14.13 (Debian 14.13-1.pgdg120+1) on aarch64-unknown-linux-gnu, compiled by gcc (Debian 12.2.0-14) 12.2.0, 64-bit
2024-10-03 00:31:06.558 UTC [1] LOG:  listening on IPv4 address "0.0.0.0", port 5432
2024-10-03 00:31:06.558 UTC [1] LOG:  listening on IPv6 address "::", port 5432
2024-10-03 00:31:06.559 UTC [1] LOG:  listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
2024-10-03 00:31:06.562 UTC [27] LOG:  database system was shut down at 2024-10-03 00:31:04 UTC
2024-10-03 00:31:06.566 UTC [1] LOG:  database system is ready to accept connections

動作チェック

長くなるので、すでにチェックで使ったコマンドはまとめて表示します。

  1. 1回目のコンテナの起動(停止時に自動削除オプション付き)
  2. psqlをコンテナ内で実行
  3. データの追加
  4. psql終了
  5. コンテナ停止(自動削除)
  6. 2回目のコンテナの起動(停止時に自動削除オプション付き)
  7. psqlをコンテナ内で実行
  8. データの確認(データが増えたまま残っていればOK)
#  1. 1回目のコンテナの起動(停止時に自動削除オプション付き)
docker container run -v db-volume:/var/lib/postgresql/data -d --rm my-pg-db
3288c5b1d8074d56682a26b1f5fda4f620e060d85538801825a75bfd5d7805ad

# 2. psqlをコンテナ内で実行
docker container exec -it 3288c5b1d8074d56682a26b1f5fda4f620e060d85538801825a75bfd5d7805ad psql -U user -d my-db
# 出力
psql (14.13 (Debian 14.13-1.pgdg120+1))
Type "help" for help.

#  3. データの追加
my-db=# INSERT INTO users (name,email) VALUES
# 出力
my-db-# ('コジマ 広長', 'test@example.com');
INSERT 0 1

# 4. psql終了
my-db=# \q

# 5. コンテナ停止(自動削除)
docker container stop 3288c5b1d8074d56682a26b1f5fda4f620e060d85538801825a75bfd5d7805ad
# 出力
3288c5b1d8074d56682a26b1f5fda4f620e060d85538801825a75bfd5d7805ad

# 6. 2回目のコンテナの起動(停止時に自動削除オプション付き)
docker container run -v db-volume:/var/lib/postgresql/data -d --rm my-pg-db
# 出力
46aae2c6416454963f21ee801d1fc34b688e9eacafd13e0d5ebf3be28063c6d3

# 7. psqlをコンテナ内で実行
docker container exec -it 46aae2c6416454963f21ee801d1fc34b688e9eacafd13e0d5ebf3be28063c6d3 psql -U user -d my-db 
# 出力
psql (14.13 (Debian 14.13-1.pgdg120+1))
Type "help" for help.

# 8. データの確認(データが増えたまま残っていればOK)
my-db=# SELECT * FROM users;
# 出力
 id |    name     |            email            
----+-------------+-----------------------------
  1 | 山田 太郎   | taro.yamada@example.com
  2 | 杉山 光一   | sugiyama.kouiti@example.com
  3 | コジマ 広長 | test@example.com
(3 rows)

3. ユーザー名、パスワード、データベース名は外部(.env)に括りだす

最後に重要なパスワードユーザー名の部分です。
みなさんもモヤモヤしていたかと思いますが、現状では、Dockerfileにハードコードしてしまっていて、GitHubなどで管理するときに間違ってアップしてしまう可能性があり、セキュリティー的によろしくありません。

対策として、「重要な情報を外部ファイルに書いておいてそれはバージョン管理や共有しない」ようにするという手法があります。

それが、.envファイルになります。
.envファイルに環境変数の定義をすべて記述して、コンテナ生成時に読み込ませる方式を使います。

より簡単にするには、docker-composeを使うことで実現できますが、今回は古典的な方法を紹介しました。

.env

このファイルに、重要なデータを記載しておきます。

.env
POSTGRES_USER=user
POSTGRES_PASSWORD=pass
POSTGRES_DB=my-db

Dockerfile

Dockerfileには重要なデータは記載しないようにします。

Dockerfile
FROM postgres:14

# 初期化スクリプトをコンテナ内にコピー
COPY init.sql /docker-entrypoint-initdb.d/

.envの内容をコンテナに紐づけて実行

コンテナ生成時(実行時)に--env-fileオプションで.envファイルを紐づけることで環境変数を挿入する事ができます。

# コンテナ実行コマンド
docker run -d --env-file .env pg-image
# 出力
48244ef9ed62a35cb1ccdef56272bc40d56f8600de18b7fcf520ba38fcfef499

# psql実行コマンド
docker container exec -it 48244ef9ed62a35cb1ccdef56272bc40d56f8600de18b7fcf520ba38fcfef499 psql -U user -d my-db
# 出力
psql (14.13 (Debian 14.13-1.pgdg120+1))
Type "help" for help.

my-db=# 

さいごに

今回で基本的なDBコンテナの使い方の解説が終わりました。

次回からは、DBコンテナと別のコンテナをつなげてどの様にやり取りを行うかをまとめていきます。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA