こんにちは。 エンジニアの nobushi です。

RDBを扱うWebアプリケーションを構築しているとRDBのレプリケーション環境を必要とすることもあると思います。

アプリケーション側で対応が必要なのでできれば開発を行うローカル環境の段階で導入したいところです。

そこで今回は手軽にローカルのdocker-composeでRDBのレプリケーション環境を構築してみたいと思います。

使用するRDBは PostgreSQL です。

ついでに検証用に pgAdmin も導入してみます。

導入

docker-compose が使える環境で、任意のディレクトリに以下のファイルを配置して

/
  db_primary/
    init.sh
    pg_hba.conf
  db_read_replica/
    entrypoint.sh
  docker-compose.yml

配置したディレクトリで起動してください。

docker compose up -d

docker-compose.yml

services:
  db_primary:
    image: postgres:14.8
    command: -c "hba_file=/etc/postgresql/pg_hba.conf"
    environment:
      - POSTGRES_PASSWORD=hoge
      - POSTGRES_DB=main
    volumes:
      - db_primary_data:/var/lib/postgresql/data
      - ./db_primary/pg_hba.conf:/etc/postgresql/pg_hba.conf
      - ./db_primary/init.sh:/docker-entrypoint-initdb.d/init.sh
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "postgres"]

  db_read_replica:
    image: postgres:14.8
    entrypoint: /etc/postgresql/entrypoint.sh
    volumes:
      - db_read_replica_data:/var/lib/postgresql/data
      - ./db_read_replica/entrypoint.sh:/etc/postgresql/entrypoint.sh
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "postgres"]
    depends_on:
      db_primary:
        condition: service_healthy

  pgadmin:
    image: dpage/pgadmin4:7.3
    volumes:
      - pgadmin_data:/var/lib/pgadmin
    environment:
      - PGADMIN_DEFAULT_EMAIL=johndoe@example.com
      - PGADMIN_DEFAULT_PASSWORD=johndoe
    ports:
      - 50080:80

volumes:
  db_primary_data:
    driver: local
  db_read_replica_data:
    driver: local
  pgadmin_data:
    driver: local

構成はPostgreSQLのインスタンスがプライマリ、レプリカでそれぞれ一つ、 pgAdminが一つです。

またそれらのデータ保持用のボリュームを定義しています。

db_primary/init.sh

#!/bin/sh -xeu

psql -v ON_ERROR_STOP=1 <<- _EOT_
	CREATE USER replicator WITH REPLICATION;
	SELECT pg_create_physical_replication_slot('my_replication_slot');
_EOT_

db_primary/pg_hba.conf

local   all             all                                     trust
host    all             all             127.0.0.1/32            trust
host    all             all             ::1/128                 trust
local   replication     all                                     trust
host    replication     all             127.0.0.1/32            trust
host    replication     all             ::1/128                 trust
host    replication     replicator      0.0.0.0/0               trust

host all all all scram-sha-256

db_read_replica/entrypoint.sh

#!/bin/sh -xeu

pg_basebackup \\
	-h db_primary \\
	-D ${PGDATA} \\
	-S my_replication_slot \\
	-X stream \\
	-U replicator \\
	-R || true

/usr/local/bin/docker-entrypoint.sh postgres

プライマリDB

プライマリDBで必要なことは以下の3点です。

  • レプリケーションスロットの作成
  • レプリケーションユーザーの作成
  • レプリケーションユーザーの認証設定

レプリケーションスロットの作成

レプリケーション接続を行うためのレプリケーションスロットを作成します。

init.sh で行っています。

    SELECT pg_create_physical_replication_slot('my_replication_slot');

レプリケーションユーザーの作成

レプリカ側からの接続用にユーザーを作成します。

init.sh で行っています。

    CREATE USER replicator WITH REPLICATION;

このサンプルではセキュリティは気にしないのでパスワードは設定しません。

レプリケーションユーザーの認証設定

pg_hba.conf は認証設定ファイルです。

デフォルトの pg_hba.conf/var/lib/postgresql/data/pg_hba.conf にありますので、それをベースにして以下の設定を追加しています。

host    replication     replicator      0.0.0.0/0               trust

この設定により replicator ユーザーのレプリケーション接続は接続元に関係なくパスワード不要となります。

本サンプルはローカルの環境用ですので簡易的に設定します。

db_primaryの設定

使用する pg_hba.conf を変更するためにコマンドを設定しています。

    command: -c "hba_file=/etc/postgresql/pg_hba.conf"

また、ファイルを差し替えるためにvolumeを設定しています。

      - ./db_primary/pg_hba.conf:/etc/postgresql/pg_hba.conf
      - ./db_primary/init.sh:/docker-entrypoint-initdb.d/init.sh

レプリカDB

レプリカDBでは pg_basebackup を使用してレプリケーションを作成します。

pg_basebackup はPostgreSQLの起動前に実行し、 PostgreSQLの起動時にはレプリケーションDBが既に生成されているように制御する必要があります。

そのためにENTRYPOINTを差し替えて、 従来のENTRYPOINTの前に pg_basebackup を実行するようにしたのが entrypoint.sh です。

pg_basebackup の引数はプライマリDBの設定内容に合わせたものです。 また、2回目以降の起動時にはすでにデータがあるため pg_basebackup がエラー終了します。 その場合はエラーを無視するように || true をつけています。

pg_basebackup \\
	-h db_primary \\
	-D ${PGDATA} \\
	-S my_replication_slot \\
	-X stream \\
	-U replicator \\
	-R || true

また、プライマリDB側が起動した状態でないと起動に失敗するため、 composeでプライマリDBのステータスがHealthyになってから起動するように制御しています

    depends_on:
      db_primary:
        condition: service_healthy

pgAdminで検証

ではpgAdminで動作確認してみます。

pgAdminへのアクセスは docker-compose の通りであれば http://127.0.0.1:50080 でアクセス可能なはずです。

login

ログイン画面が表示されるので

docker-compose の環境変数で設定した以下のメールアドレス、パスワードでログインします。

johndoe@example.com / johndoe

接続

ログインしたら各サーバーへの接続を追加します。

General / NameConnection / Host nameConnection / UsernameConnection / Password
db_primarydb_primarypostgreshoge
db_read_replicadb_read_replicapostgreshoge

検証

この段階でレプリカDB側にも main データベースがある等 プライマリDBと同期していることが分かります。

ではプライマリDBにテーブルを追加してみます。

レプリカDB側にも同期されました。

またレプリカDB側にテーブルを追加しようとするとエラーが発生します。

ERROR: cannot execute CREATE TABLE in a read-only transaction

所感

ローカルでRDBのレプリケーション環境ができればアプリケーションのテスト等やりやすくなると思います。

compose一発で構築可能なのでぜひお試しください。

SHARE

  • facebook
  • twitter

SQRIPTER

AGEST Engineers

AGEST

記事一覧

AGESTのエンジニアが情報発信してます!

株式会社AGEST

Sqriptsはシステム開発における品質(Quality)を中心に、エンジニアが”理解しやすい”Scriptに変換して情報発信するメディアです

  • 新規登録/ログイン
  • 株式会社AGEST