bon now

ありのままの現実を書き殴る吐き溜め。底辺ITエンジニアの備忘録。
Written by bon who just a foolish IT Engineer.

docker-composeでMongoDBをレプリカセット起動する【完全版・6.0対応】

Created Date: 2023/06/28 22:00
Updated Date: 2026/03/04 23:14

MongoDBのバージョン差異・レプリカセット起動の複雑さ・entrypoint-initdb.d が動かない問題など、正確な情報を探すのにとても苦労したのでまとめておく。

なぜレプリカセットで起動するのか?Change Streams を使いたいから。Change Streamsはレプリカセット構成でないと動作しない。

TL;DR(結論から)

完成形のファイル構成はこちら:

1
2
3
4
5
6
.
├── docker
│   └── db
│       └── init
│           └── db_init.js
└── docker-compose.yml

docker-compose.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
version: '3'
services:
  mongo:
    image: mongo:6.0.6
    environment:
      - AUTH=no
    command: [--replSet, my-replica-set, --noauth, --bind_ip_all]
    ports:
      - 27017:27017
    healthcheck:
      test: mongosh mongo-init.js
      interval: 10s
      start_period: 30s
    volumes:
      - mongodb_data:/data/db
      - ./docker/db/init/db_init.js:/mongo-init.js
    restart: always

  mongo-express:
    image: mongo-express
    container_name: mongo_express
    restart: always
    ports:
      - 8081:8081
    environment:
      ME_CONFIG_MONGODB_ADMINUSERNAME: root
      ME_CONFIG_MONGODB_ADMINPASSWORD: password
      ME_CONFIG_MONGODB_SERVER: mongo
      ME_CONFIG_MONGODB_PORT: 27017
    depends_on:
      - mongo

volumes:
  mongodb_data:

レプリカセット初期化スクリプト(db_init.js)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
init = false;
print("Init script ...")

try {
  if (!db.isMaster().ismaster) {
    print("Error: primary not ready, initialize ...")
    rs.initiate(
      {
        _id:'my-replica-set',
        members: [
          { _id:0,
            host: "mongo:27017"
          }
        ]
      }
    )
    quit(1);
  } else {
    if (!init) {
      admin = db.getSiblingDB("admin");
      admin.createUser(
        {
          user: "root",
          pwd: "password",
          roles: ["readWriteAnyDatabase"]
        }
      );
      init = true;
    }
  }
} catch(e) {
  rs.status().ok
}

起動方法

1
docker-compose up -d

起動後、 http://localhost:8081 でmongo-expressのGUI管理画面にアクセスできる。 ログインのユーザー名・パスワードはデフォルトで admin / pass

接続文字列(アプリから接続する場合)

1
mongodb://root:password@localhost:27017/?authSource=admin&replicaSet=my-replica-set

レプリカセットを使う場合、replicaSet=my-replica-set の指定が必要な点に注意。

データの削除

1
2
# コンテナとボリュームをまとめて削除
docker-compose down -v

mongodb_data はdocker volumeで管理されているため、docker volume rm mongodb_data でも削除できる。


なぜこの構成にしたか(詳細解説)

MongoDBコンテナのポイント

--replSet my-replica-set オプションをつけて起動することでレプリカセットが有効になる。今回はローカル開発用なので最小構成のプライマリーノード1台のみ。本番環境では3台構成が推奨。

--bind_ip_all はローカル開発用に外部からの接続を許可するためのオプション。本番では使わないこと。

entrypoint-initdb.d が動かない問題と healthcheck ハック

MongoDBのコンテナイメージは レプリカセット付きで起動すると docker-entrypoint-initdb.d/ に配置したスクリプトを実行してくれない という仕様がある。

これはMongoDBのissueでも長く議論されている問題で、回避策として healthcheck でスクリプトを実行するハックが考案されている。仕組みはこうだ:

  1. コンテナ起動直後、レプリカセット初期化前は healthcheck が必ずコケる
  2. restart: always により自動再起動がかかる
  3. 再起動のたびに db_init.js が実行され、レプリカセット初期化を試みる
  4. 初期化が成功すると healthcheck が通るようになり、安定動作に入る

これが restart: always をつけている理由。

volumesをdocker volumeにした理由

バインドマウント(ホストのディレクトリを直接マウント)にするとLinux環境でパーミッションエラーが出やすいため、named volume(mongodb_data)を使っている。

mongo-expressとは

MongoDBのGUIクライアント。ブラウザからコレクションの閲覧・編集・クエリ実行ができる。environment で接続先を設定できるので、ローカル開発時のデバッグに便利。


よくあるトラブルと対処

「レプリカセットの初期化が終わらない / ずっとrestartしている」start_period: 30s を伸ばしてみる。マシンスペックによっては初回起動に時間がかかる。

「アプリから接続するとエラーになる」 → 接続文字列に replicaSet=my-replica-set が入っているか確認。これがないとChange Streamsが使えない。

「mongo-expressにアクセスできない」 → mongoDBコンテナのhealthcheckが通過するまでmongo-expressが起動しない(depends_on)。少し待ってからアクセスする。


参考にしたサイト:

local_offer
folder work

chat_bubble_outline コメントを残す