Dockerコンテナ上のPostgreSQLにデータベースを作成

# はじめに

DB からのデータ取得などの動作を含む簡単な Web アプリを試したいので、DB 環境の構築を行った。本記事では、環境を容易に作成・破棄できるよう Docker 上に PostgreSQL 環境を構築し、データベース・テーブルの作成、データの追加・確認を行う。実行環境は以下。

- Windows 10 Pro (WSL2: Ubuntu-20.04)
- Docker: 20.10.7, build f0df350
- psql: 12.8

# psql のインストール

まず PostgreSQL をコマンドで扱えるよう WSL2 環境に psql をインストールする。

```
$ sudo apt update
$ sudo apt install postgresql
$ psql --version
# psql (PostgreSQL) 12.8 (Ubuntu 12.8-0ubuntu0.20.04.1)
```

# PostgreSQL 環境の構築

[PostgreSQL を Docker 環境で日本語ロケールで動作させる](https://qiita.com/suketa/items/f63ff05284593d40135d)を参考に PostgreSQL 環境を Docker コンテナ上に作成する。

まず適当なディレクトリを作成し、移動する。

```bash
$ mkdir postgresql
$ cd postgresql
```

このディレクトリ内に、Dockerfile、docker-compose.yml、.env の3つのファイルを作成していく。

## Dockerfile の作成

[Postgres 公式イメージ](https://hub.docker.com/_/postgres)が日本語ロケールで動作するよう、以下のような Dockerfile を作成する。

```dockerfile
FROM postgres
ARG DB_LANG=en_US
RUN localedef -i $DB_LANG -c -f UTF-8 -A /usr/share/locale/locale.alias $DB_LANG.UTF-8
ENV LANG $DB_LANG.utf8
```

## docker-compose.yml の作成

次に、以下のように docker-compose.yml を作成する。`build` 内で指定されている `args` によって、日本語ロケールでビルドされる。

```yaml:docker-compose.yml
version: "3"

services:
  db:
    build:
      context: .
      dockerfile: ./Dockerfile
      args:
        - DB_LANG=ja_JP
    container_name: "db"
    environment:
      - POSTGRES_USER
      - POSTGRES_PASSWORD
    ports:
      - "5432:5432"
    restart: always
    volumes:
      - /home/containers/postgresql:/var/lib/postgresql
```

## .env の作成

docker-compose.yml 内で環境変数に指定されている `POSTGRES_USER` と `POSTGRES_PASSWORD` を .env 内で指定する。

```.env
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
```

# Docker コンテナの起動

上記までで作成したファイルを元に、以下コマンドで Docker イメージをビルドし、コンテナを起動する。コンテナにアクセスし、.env で指定したユーザ名とパスワードでログインできる。

```bash
$ docker-compose up -d               # イメージをビルドし、コンテナ起動
$ docker-compose exec db /bin/bash   # コンテナにアクセス
root:/# psql -U postgres
Password for user postgres:
postgres=#
```

以下コマンドでも同様の状態になる。

```bash
$ docker-compose up -d 
$ psql -h 127.0.0.1 -p 5432 -U postgres
Password for user postgres:
postgres=#
```

# データベースの作成

データベースを作成していく。ログイン状態で以下コマンドを実行。`\l` でデータベース一覧を確認すると、`test_db` が作成されていることが確認できる。また各テーブルのロケールが **ja_JP.utf8** になっていることも確認できる。

```sql
postgres=# create database test_db;
CREATE DATABASE

postgres=# \l
 postgres  | postgres | UTF8     | ja_JP.utf8 | ja_JP.utf8 |
 template0 | postgres | UTF8     | ja_JP.utf8 | ja_JP.utf8 | =c/postgres          +
           |          |          |            |            | postgres=CTc/postgres
 template1 | postgres | UTF8     | ja_JP.utf8 | ja_JP.utf8 | =c/postgres          +
           |          |          |            |            | postgres=CTc/postgres
 test_db   | postgres | UTF8     | ja_JP.utf8 | ja_JP.utf8 |
```

# テーブルの作成

上記で作成した `test_db` にテーブルを追加する。ここでは `user_id`, `name`, `age` をカラムにもつ `users` テーブルを作成する。

```sql
postgres=# \c test_db;   /* test_db データベースに接続 */
You are now connected to database "test_db" as user "postgres".

test_db=# create table users
test_db-# (user_id integer not null,
test_db(# name character varying(50) not null,
test_db(# age integer not null,
test_db(# PRIMARY KEY(user_id));
CREATE TABLE

test_db=# \d users   /* users テーブルのスキーマを表示 */
                       Table "public.users"
 Column  |         Type          | Collation | Nullable | Default
---------+-----------------------+-----------+----------+---------
 user_id | integer               |           | not null |
 name    | character varying(50) |           | not null |
 age     | integer               |           | not null |
Indexes:
    "users_pkey" PRIMARY KEY, btree (user_id)
```

# データ登録

データを以下コマンドで入力する。日本語ロケールに対応しているかの確認もしたいので、`name` は漢字で登録したが、問題なく登録できていることが確認できる。

```sql
test_db=# insert into users   /* データの登録 */
test_db-# (user_id, name, age) values
test_db-# (1, '佐藤', 20),
test_db-# (2, '鈴木', 30),
test_db-# (3, '田中', 40);
INSERT 0 3

test_db=# select * from users;
 user_id | name | age
---------+------+-----
       1 | 佐藤 |  20
       2 | 鈴木 |  30
       3 | 田中 |  40
(3 rows)
```

# おわりに

Docker コンテナ上で PostgreSQL 環境を構築し、動作の確認をすることができた。この DB に外部コンテナからアクセスすることも試したい。