【Docker入門】PostgreSQLの環境構築手順(データ永続化対応)

Container

こんにちは。 前回はWindows環境のDocker Desktopのインストール方法について解説しました。 まだDocker Desktopを入れていない方は、こちらの記事を参考にインストールを済ませてください。

👉 Docker Desktopのインストール方法

今回は、Windows環境のDockerを使ってPostgreSQLのデータベース環境を構築します。 「PCに直接PostgreSQLをインストールして、バージョン管理で苦労した」という経験がある方も、Dockerであれば環境の作成や破棄が容易に行えるため、非常に効率的です。

この記事の内容
  • Docker DesktopでPostgreSQLを起動する方法
  • データを永続化(PC再起動後もデータが残る設定)

docker runによるDBの環境構築

簡単な起動方法

まずは、簡単な方法でPostgreSQLを立ち上げます。 ターミナル(WindowsならPowerShell)を開いて、以下のコマンドを実行してください。

docker run --name my-postgres -e POSTGRES_DB=mydb -e POSTGRES_USER=myuser -e POSTGRES_PASSWORD=password987 -d -p 5432:5432 postgres:18.1

実行後のログは次のようになります。これで、PostgreSQLが起動しました。各オプションの意味は以下の通りです。 通信環境によりますが、ダウンロード完了まで数分かかることがあります。ダウンロードが終わると自動的にコンテナが起動します。

Unable to find image 'postgres:18.1' locally
18.1: Pulling from library/postgres
・・・
Status: Downloaded newer image for postgres:18.1

<パラメータの説明>

  • --name xxx: コンテナに名前を付けます(今回は my-postgres)。
  • -e POSTGRES_USER=xxx: 環境変数でスーパーユーザー名を指定します。指定しないとpostgresになります。
  • -e POSTGRES_PASSWORD=xxx: 環境変数でパスワードを設定します。
  • -d: バックグラウンドで実行します。
  • -p 5432:5432: パソコンのポート5432番と、コンテナのポート5432番を繋ぎます。
  • postgres: 使用するイメージ名です。
    • イメージのダウンロード: PC内にこのイメージがない場合(初回実行時など)、Dockerの公式レジストリ「Docker Hub」から自動的にダウンロードが始まります。
    • 公式ページURL: https://hub.docker.com/_/postgres このページの「Tags」タブにある latest というタグが付いたファイルが取得されます。
    • バージョン: 上記公式ページに存在するバージョンを選択します。バージョンを指定しなければ、l自動的に最新版(latest)が使用されます。
Docker Hub Container Image Library | App Containerization
Welcome to the world's largest container registry built for developers and open source contributors to find, use, and sh...

Dockerfileについて: 上のDockerHubに用意されている公式イメージをそのまま使用するため、この段階ではDockerfileを自分で作成する必要はありません。

起動確認と現状の把握

docker run により、自分のPCに「どのようなイメージ」と「どのようなコンテナ」が入ったかを確認してみましょう。

下記コマンドは、Windowsのdocker desktopの画面からも確認ができますが、Linux環境でdockerを使用することも考え、ここでもコマンドに慣れておきましょう。
なお、これらdockerのコマンドは、WindowsのコマンドプロンプトかPowerShellで実行します。わざわざWSL2のLinuxOSに入ってからターミナルで実行するものではありません。

1. 起動しているコンテナの確認

>docker ps
CONTAINER ID  IMAGE         COMMAND   CREATED         STATUS  PORTS  NAMES
88d2daf66392  postgres:18.1 "docker…" a minute ago   Up a minute  5432->5432   my-postgres

STATUSUp になっていれば成功です。ここに表示される行数が、現在動いているコンテナの数です。

2. 全コンテナの確認(未起動コンテナも含む)

>docker ps -a
CONTAINER ID   IMAGE      COMMAND   CREATED  STATUS  PORTS  NAMES
88d2daf66392  postgres:18.1"docker…" 12 hours ago   Up 12 hours  5432->5432 my-postgres
878714d8f55a   hello-world "/hello"   19 hours ago   Exited   exciting_fermat

ps コマンドに -a (all) をつけると、起動中だけでなく停止しているものも含めた全てのコンテナが表示されます。 過去に作ったコンテナが残っていないかを確認する際に便利です。

3. ダウンロード済みイメージの確認(イメージ数の確認)

>docker images
IMAGE                ID             DISK USAGE   CONTENT SIZE   EXTRA
hello-world:latest   d4aaab6242e0       25.9kB         9.52kB    U
postgres:18.1        bfe50b2b0ddd        649MB          168MB    U

postgres を含む、ダウンロード済みのイメージ一覧が表示されます。 リストの行数を見ることで、PC内にいくつのイメージファイル(コンテナの元ネタ)が保存されているかを確認できます。

データベースへの接続確認

コンテナ内部に入り、PostgreSQLが動作しているか確認します。
docker exec コマンドは、すでに実行中のコンテナ内でコマンドを実行するためのものです。コンテナを再起動せずに設定の変更やデバッグを行うことができます。

docker exec -it my-postgres psql -U myuser -d mydb

<パラメータ説明>

  • -it :インタラクティブモード(標準入力を開く + TTYを有効化)。これを実行すると、プロンプトが以下のように変わります。

プロンプトに「mydb=#」と現れたら、SQLコマンドが実行可能な状態になったことを意味します。試しにPostgreSQLバージョンを確認し、create table, insert, select, drop tableをしてみましょう。

# SELECT version();
PostgreSQL 18.1 (Debian 18.1-1.pgdg13+2) on x86_64-pc-linux-gnu, compiled by gcc (Debian 14.2.0-19) 14.2.0, 64-bit
# CREATE TABLE test_users (id SERIAL PRIMARY KEY, name VARCHAR(50));
CREATE TABLE

# INSERT INTO test_users (name) VALUES ('Alice'), ('Bob');
INSERT 0 2

# SELECT * FROM test_users;
 id | name
----+-------
  1 | Alice
  2 | Bob

# DROP TABLE test_users;
DROP TABLE

# \q

確認が終わったら、\q と入力してEnterを押すとコンテナから抜けられます。
コンテナの外側である PCに「DBeaver」や「pgAdmin」などのDB管理ツールがインストールされていれば、ホスト: localhost、ポート: 5432、データベース:mydb、ユーザー: myuser、パスワード: password987 でコンテナ内のDBに接続が可能です。

DBeaverによる接続例
DBeaverによる接続後

データの永続化設定

上記の方法では、コンテナを削除するとデータベースの中身(テーブルやデータ)も全て消去されます。 開発データを保持するためには、「ボリュームのマウント」という設定が必要です。

※もちろん「ボリュームのマウント」の設定を行ったとしても、コンテナの削除に加えてボリュームの削除まで行ってしまえば、データベースの中身も削除されてしまう点はご注意ください。

まず環境の削除

データの永続化の為に、まずは先ほど作成したコンテナを、docker stopコマンドとdocker rmコマンドで停止・削除します。
最後にdocker ps -aコマンドでmy-postgresが消えているか確認してみます。

>docker stop my-postgres
my-postgres
>docker rm my-postgres
my-postgres
>docker ps -a
CONTAINER ID   IMAGE         COMMAND    CREATED       STATUS  NAMES                 
878714d8f55a   hello-world   "/hello"   19 hours ago  Exited  exciting_fermat

ちゃんとmy-postgresのコンテナが消えていますね。

ボリューム名と対象ディレクトリを指定して起動

-vオプションを追加し、データを保存したいボリューム名(dbdata)とコンテナ内のパスを指定して起動します。

docker run --name my-postgres -e POSTGRES_DB=mydb -e POSTGRES_USER=myuser -e POSTGRES_PASSWORD=password987 -d -p 5432:5432 -v dbdata:/var/lib/postgresql postgres:18.1

データ永続化では、フォルダのマウントを用いる方法もありますが、今回は「名前付きボリューム(Named Volume)」を使用してデータの永続化を行っています。
今回名前付きボリュームを採用した理由は次の通りです。

  • Docker内部のボリュームの方がWindowsファイルシステムから読み込むよりオーバーヘッドが少ない(データベースコンテナでは読み書きが頻繁に発生するので考慮するべき)
  • OSごとのファイル権限の違いによる意図したい「起動エラー」を防げる

postgresのimageはダウンロード済みであったため、コンテナは一瞬で起動するはずです。

>docker ps -a
CONTAINER ID   IMAGE         COMMAND    CREATED       STATUS  NAMES
e8444b72af05   postgres:18.1 "docker.." Up 9 seconds  5432->5432  my-postgres            
878714d8f55a   hello-world   "/hello"   19 hours ago  Exited  exciting_fermat


Dockerfileとcompose.yamlを定義

先ほど説明した docker run コマンドでは、毎回入力するのが手間になりますね。
そこで開発現場では、設定をファイル(compose.yaml または docker-compose.yml)に記述して管理するのが一般的です。
また、公式イメージをそのまま使うだけでなく、初期設定を最初からイメージに組み込みたい場合は、Dockerfileを作成します。

Dockerfileの記載方法

「PostgreSQLのバージョン18を使いたい」「日本語ロケールを設定したい」という場合の Dockerfile を作成してみます。

ファイル名:Dockerfile

# ベースとなるイメージとバージョンを指定
FROM postgres:18.1

# タイムゾーンを日本時間に設定
ENV TZ Asia/Tokyo

# ロケール設定に必要なパッケージは標準イメージに含まれているため
# localedefコマンドを使って日本語ロケール(ja_JP.UTF-8)を生成します
RUN localedef -i ja_JP -c -f UTF-8 -A /usr/share/locale/locale.alias ja_JP.UTF-8

# 環境変数を設定
# これにより、DBのメッセージやソート順(Collation)が日本語対応になります
ENV LANG ja_JP.UTF-8
ENV LC_ALL ja_JP.UTF-8

compose.yamlファイルの記載方法

次にcompose.yamlファイルを作成します。

yaml(ヤムル)とは「YAML Ain’t Markup Language(YAMLはマークアップ言語ではない)」の略で、人間が読み書きしやすいように設計された、構造化データを記述するためのデータフォーマットのことです。名前が再帰的に定義されていて面白いですね。

dockerのcompose.yamlファイルは、次のような内容を定義するファイルです。

  • イメージファイル (image:)
  • ポートのマッピング (ports:)
  • ボリュームのマウント (volumes:)
  • コンテナ間の依存関係 (depends_on:)
  • 環境変数 (environment:) どのような変数を利用するかはイメージ作成者が決めます。DockerHubのimage over viewページのEnvironment Variablesの段落をご覧ください。

早速、dockerfileと同じディレクトリにcompose.yaml を作成します。

ファイル名:compose.yaml

services:
  db:
    build: .  # Dockerfileがある場所(カレントディレクトリ)を指定
    container_name: my-postgres
    environment:
      POSTGRES_USER: myuser
      POSTGRES_PASSWORD: password987
      POSTGRES_DB: mydb
  restart: unless-stopped
    ports:
      - "5432:5432"
    volumes:
      - dbdata:/var/lib/postgresql   
volumes:
  dbdata:
  • YAML(ヤムル)形式において、「- (ハイフン)」はリスト(配列)の項目を意味します。例えば、もしポートを2つ開けたい場合は以下のようにリストとして追記することができます。
      ports:
      - “5432:5432” # 1つ目のポート設定
      - “8080:80” # 2つ目のポート設定
  •  container_name を追記すると、コンテナが起動した際、db-1 といった自動生成名ではなく、指定した名前で表示されます。
  • restart: unless-stoppedは、コンテナが起動している状態でDockerが再起動した場合、コンテナも再起動します。しかしコンテナが停止している状態でDockerが再起動された場合にはコンテナは再起動されなません。なおデフォルトは「no」で: コンテナを自動的に再起動しません。
  • volumes: -(ボリューム名):(パス)、という「名前付きボリューム(Named Volume)」を使用してデータの永続化を行っています。ファイルの最後にもvolumesキーの記載がありますが、これはボリュームの変数宣言のようなものです。

docker compose upコマンド

上記2つのファイルの準備が整いましたら、これらファイルのディレクトリ内で、docker compose up -d –build を実行してください。DockerfileをもとにビルドされたPostgreSQL環境が立ち上がります。
docker compouseコマンドの基本的な使い方を下に整理します。

・イメージをビルドしてコンテナを起動
docker compose up -d --build 

・コンテナ起動
docker compose up -d

・コンテナ停止
docker compose stop

・コンテナ削除(コンテナ・ネットワークを削除)
docker compose down

<オプションの説明>

  • -dはデタッチモードで、バックグラウンドでコンテナを起動します。コンテナ起動後も同じターミナルで作業を続行することができます。
  • –buildオプションは、Dockerfileの書き換えなどでビルド処理の変更をイメージに反映させます。

補足:Dockerfileは必要性?

Dockerfileは、Dockerイメージを「どのように作成するか」を定義するスクリプトです。これにより、以下のことが可能になります。 

  • イメージ構築の自動化と再現性:
    • 手動でのコマンド実行と違い、Dockerfileを使えば誰がどこで実行しても常に全く同じイメージが作成されます。これはCI/CDパイプラインにおいて不可欠です。
  • 環境の標準化と分離:
    • アプリケーションとその実行に必要なOS、ライブラリ、依存関係などをすべてイメージ内にパッケージングできます。ホストマシンに関わらず一貫した環境で動作します。
  • きめ細やかな設定:
    • ベースイメージの指定(FROM)、ファイルのコピー(COPY)、コマンドの実行(RUN)、環境変数の設定(ENV)など、イメージの内部構造を詳細に制御できます。
  • レイヤーキャッシュによる効率化:
    • Dockerfileの各命令はイメージの「レイヤー」としてキャッシュされます。変更があったレイヤー以降だけを再構築するため、効率的にイメージを更新できます。 

つまり、次のようにイメージの中身を変える必要がない場合は、Dockerfileは不要です。

  • 公式イメージ(postgres, redis, nginx, python, node など)をそのまま使う
  • OSやライブラリを追加インストールしない
  • アプリを volumes でマウントと環境変数の設定だけ(compose.yamlで対応)
  • ビルド工程(compile, pip install, npm install など)が不要

逆に、次の事をやりたいならDockerfile が必要です。

  • apt install や apk add をしたい
  • pip install -r requirements.txt をしたい
  • アプリをイメージに組み込みたい
  • ビルド成果物を含めたい

補足:Dockerfileは使わずにcompose.yamlだけで運用できる?

Dockerfileでもcompose.yamlでも、同じように環境変数を指定できるので、機能が重複しているように見えますが、機能も役割も大きく違います。
Docker Composeで使用されるcompose.yamlファイルは、「作成済みのイメージ」を「どのように実行するか」を定義するためのものですので、Dockerfileが担うイメージを定義する機能はありません

イメージを作成したり公開イメージを修正するなら、Dockerfileが必要です。

たしかに、yamlファイルだけでも、無理をすればcommandキーに次のようにライブラリインストール関係の記載も可能です。

command: >
      sh -c "pip install -r requirements.txt &&
             python app.py"


しかしこの方法でライブラリインストールすると、コンテナ終了とともにインストール結果は消えてしまいますし、また毎回のコンテナ起動が遅くなります。この方法を実施するとしても検証用で試す場合だけにしましょう。
あくまで、環境(イメージ)を確定させるのがDockerfileであり、環境(イメージ)を起動するのがYAML / docker runと考えてください。

補足:compose.yamlは必要性ですか?

本記事の最初にdocker runコマンドだけでデータベースを起動して見せました。
では、イメージファイルは公式イメージそのままで利用するとして、docker runコマンドに多くのパラメータを指定すれば、compose.yamlは作らずに済むのでしょうか。
結論を申し上げますと、「可能」ではあります。
しかし、docker runコマンドだけで対応すると、パラメータが多いので長いコマンドになりがちです。
また、docker runコマンド単体ではcompose.yamlのdepends_on機能には対応できませんので、例えばDBサービスが起動後にアプリサービスを起動する必要がある場合は、シェルスクリプトでdocker run –name db の後にsleep 10を入れてdocker run –name appを実行するなどの工夫が必要でしょう。
compose.yaml は、この長いコマンド群を「設定ファイル」として保存し、docker compose up という短いコマンド一発で実行できるようにするためのものなのです。

コマンドの補足説明

volumeの削除

今回、データベース永続化の為に、volumeを作成しています。docker rm でコンテナを削除しても、volumeは永続的に残っています。環境を綺麗にするには、volumeを削除する必要があります。
まず、現在のvolumeを確認するには次のをコマンドを実施します。

>docker volume ls
DRIVER VOLUME NAME
local 6f3428c3646f53fb2eac5bf6b5a2a62f590348072d76067a211fb305669df7d3
local 8a4bf3d12c33061bb9190593710c1b092fe45930e3d1b3e64208bbf0dc83d051
local dbf8dff456dc0ef0e896ba778835bff48e7093b32b0ae616f1c145a766261b2c

削除するコマンド 特定のボリュームを消す場合:

docker volume rm <ボリューム名>

使われていないボリュームをまとめて全削除する場合:

docker volume prune

prune は現在どのコンテナにもマウントされていないボリュームを全て削除します。必要なデータが消えないよう注意して実行してください。

プロジェクトを片付ける時の便利なコマンド

コンテナを停止・削除する際に、一緒にネットワークやボリュームも消したい場合は docker compose down コマンドを使うと楽です。

コンテナとネットワークを削除(ボリュームは残す)

docker compose down

コンテナ、ネットワーク、そしてボリュームも全て削除
今回のようにデータベースの永続化をしている場合は、ボリュームの削除でデータがなくなりますので、気を付けてください。

docker compose down --volumes

バックアップ・リストア方法

SQL形式のバックアップ・リストア

コンテナ内のpg_dumpを使います

SQL形式のバックアップとリストアを行います。このバックアップファイルはSQL文なので、人が中身を確認することができます。
筆者環境では、デフォルトでバックアップファイルがUTF16になってしまいましたので、一般的なUSF8のファイルになるよう、下のコマンドではエンコードを明示的に指摘しています。
リストア時は、もしデータベースにテーブル等が残っていると復元時にエラーとなりますので、下のコマンドでは、DBを作り直してから復元しています。まあたバックアップファイルの中身はSQL文ですので、リストアにはSQLを実行するためのコマンドであるpsqlを使います。

1.バックアップ
docker exec my-postgres pg_dump -U myuser --encoding=UTF8 mydb > mydb.sql

2.リストア
docker exec    my-postgres dropdb   -U myuser mydb 
docker exec    my-postgres createdb -U myuser mydb 
docker exec -i my-postgres psql     -U mysuer -d mydb < mydb.sql

<オプション説明>
docker execのオプション -i (=–interactive) : コンテナ側の STDIN を開く

カスタム形式のバックアップ・リストア

PostgreSQLのカスタム形式でバックアップ・リストアを行う方法も紹介します。こちらのバックアップ形式のメリットは、圧縮によりバックアップファイルのサイズが小さくなる点、特定テーブルだけ・データだけといった選択的リストアが可能な点です。
バックアップファイルがバイナリデータになりますので、リストア時はpsqlではなくpg_restoreコマンドを使います。

1.バックアップ
docker exec my-postgres pg_dump -Fc -U myuser mydb > mydb.dump

2.リストア
docker exec    my-postgres dropdb     -U myuser mydb 
docker exec    my-postgres createdb   -U myuser mydb 
docker exec -i my-postgres pg_restore -U myuser -d mydb < mydb.dump

<オプション説明>
pg_dump のオプション-Fc: カスタム形式でのバックアップ(圧縮された形式)

ネットワークを確認するコマンド

docker network ls

タイトルとURLをコピーしました