TRY

저번에 Spring Data Mongo에서 @Transactional을 사용하려면 MongoDB 가 replica set이 구성되어 있어야 한다고 했다.

local에서 test하기 위해 이를 구성하여 하는데 docker-compose를 이용하기로 생각했다.
docker-compose를 사용하는 이유는 관리하기도 쉽고 공유하기도 쉬워서 선택했다.

CATCH

먼저 완성한 yml과 sh 파일이다.

version: '3.7'

services:
  mongo1:
    hostname: mongo1
    container_name: mongo1
    image: arm64v8/mongo:6.0
    ports:
      - "27017:27017"
    volumes:
      - ./data1:/data/db
      - /yourpath/security.keyFile:/etc/security.keyFile
    environment:
      - MONGO_INITDB_ROOT_USERNAME=root
      - MONGO_INITDB_ROOT_PASSWORD=1234
      - MONGO_INITDB_DATABASE=yourdb
      - MONGO_REPLICA_SET_NAME=rs0
      - MONGO_PRIMARY=mongo1
    privileged: true
    command: ["mongod", "--replSet", "rs0", "--bind_ip_all", "--keyFile", "/etc/security.keyFile"]

  mongo2:
    hostname: mongo2
    container_name: mongo2
    image: arm64v8/mongo:6.0
    ports:
      - "27018:27017"
    volumes:
      - ./data2:/data/db
      - /yourpath/security.keyFile:/etc/security.keyFile
    environment:
      - MONGO_INITDB_ROOT_USERNAME=root
      - MONGO_INITDB_ROOT_PASSWORD=1234
      - MONGO_INITDB_DATABASE=yourdb
      - MONGO_REPLICA_SET_NAME=rs0
      - MONGO_PRIMARY=mongo1
    privileged: true
    command: ["mongod", "--replSet", "rs0", "--bind_ip_all", "--keyFile", "/etc/security.keyFile"]

  mongo3:
    hostname: mongo3
    container_name: mongo3
    image: arm64v8/mongo:6.0
    ports:
      - "27019:27017"
    volumes:
      - ./data3:/data/db
      - /yourpath/security.keyFile:/etc/security.keyFile
    environment:
      - MONGO_INITDB_ROOT_USERNAME=root
      - MONGO_INITDB_ROOT_PASSWORD=1234
      - MONGO_INITDB_DATABASE=yourdb
      - MONGO_REPLICA_SET_NAME=rs0
      - MONGO_PRIMARY=mongo1
    privileged: true
    command: ["mongod", "--replSet", "rs0", "--bind_ip_all", "--keyFile", "/etc/security.keyFile"]
#!/bin/bash

rm -rf ./data1 ./data2 ./data3

openssl rand -base64 741 > /yourpath/security.keyFile
chmod 600  /yourpath/security.keyFile

docker-compose up -d

echo "Waiting for MongoDB instances to start..."
sleep 10

mongosh --username=root --password=1234 --eval <<EOF
  rs.initiate({
    _id: "rs0",
    members: [
      { _id: 0, host: "host.docker.internal:27017" },
      { _id: 1, host: "host.docker.internal:27018" },
      { _id: 2, host: "host.docker.internal:27019" }
    ]
  })
EOF

echo "Replica Set configured"
  • 실행
      $ sh script.sh
    

이제 만났던 에러들을 나열해 본다.

  • zsh: command not found: mongo

    oh my zsh

    역시 날 만나러 와줬다.

    처음 script는 mongo 를 이용해 replica set을 구성하려고 했었다.

    하지만 내가 사용하는 버전은 mongodb-community@6.0 였으므로 deprecated 되었고 mongosh를 사용하라고 되어있었다.

    참고

    그래서 mongosh로 변경하였다.

    난 brew를 통해 mongodb-community@6.0를 설치하였다.

      $ brew tap mongodb/brew
    
      $ brew update
    
      $ brew install mongodb-community@6.0
    
    
  • NoReplicationEnabled

      {
              "ok" : 0,
              "errmsg" : "This node was not started with replication enabled.",
              "code" : 76,
              "codeName" : "NoReplicationEnabled"
      }
    
    

    이 에러는 기존에 구성해두었던 volume으로 생성된 데이터들이 mongoDB의 파일들을 삭제하지않아서 발생하였다.

    이를 삭제 하였다.

    이것과 함께

      {"error":"UPGRADE PROBLEM: Found an invalid featureCompatibilityVersion document (ERROR: Location4926900: Invalid featureCompatibilityVersion document in admin.system.version: { _id: \"featureCompatibilityVersion\", version: \"7.0\" }. See https://docs.mongodb.com/master/release-notes/5.0-compatibility/#feature-compatibility. :: caused by :: Invalid feature compatibility version value, expected '5.0' or '5.3' or '6.0. See https://docs.mongodb.com/master/release-notes/5.0-compatibility/#feature-compatibility.). If the current featureCompatibilityVersion is below 5.0, see the documentation on upgrading at https://docs.mongodb.com/master/release-notes/5.0/#upgrade-procedures."}
    

    이 에러도 같이 발생하였는데 기존 구성 파일이 mongoDB 다른 버전을 사용하고있었다.

  • Unauthorized

      {
              "ok" : 0,
              "errmsg" : "Command replSetInitiate requires authentication",
              "code" : 13,
              "codeName" : "Unauthorized"
      }
    

    이 에러는 replSetInitiate을 하는데 authentication이 필요하다는 에러다.

    user와 password를 추가해 주었다.

      mongosh --username=root --password=1234 --eval <<EOF
      rs.initiate({
          ...
      })
      EOF
    
    
  • Connection refused

      MongoServerError: replSetInitiate quorum check failed because not all proposed set members responded affirmatively: localhost:27018 failed with Error connecting to localhost:27018 (127.0.0.1:27018) :: caused by :: Connection refused, localhost:27019 failed with Error connecting to localhost:27019 (127.0.0.1:27019) :: caused by :: Connection refused
    

    이 에러는 localhost를 사용해서 그렇다.

    docker에 올라간 호스트의 다른 컨테이너들을 연결하려면 host.docker.internal 를 통해 접근할 수 있다.

  • Authentication failed 이는 keyFile이 필요해서 그렇다.
    위에 security.keyFile을 생성하는 부분으로 해결하였다.

    그리고..

  • mongodb compass ENOTFOUND {host}

    난 간단하게 데이터 확인하고 할때는 mongodb의 compass를 사용하고 있었는데 localhost를 사용해도, 컨테이너 이름을 사용해도 ENOTFOUND 에러가 발생하였다.
    찾아보니 나만 그런게 아니었고.참고

    다음과 같이 hosts 파일에 추가해서 해결하였다.

      s sudo vim /etc/hosts
    
      add
    
      127.0.0.1       mongo1
      127.0.0.1       mongo2
      127.0.0.1       mongo3
    
      ---
    
    
      mongosh --username=root --password=1234 --eval <<EOF
      rs.initiate({
          _id: "rs0",
          members: [
          { _id: 0, host: "mongo1:27017" },
          { _id: 1, host: "mongo2:27018" },
          { _id: 2, host: "mongo3:27019" }
          ]
      })
      EOF
    
      ---
    
      or
    
      add
    
      127.0.0.1       host.docker.internal
    
      ---
    
      mongosh --username=root --password=1234 --eval <<EOF
      rs.initiate({
          _id: "rs0",
          members: [
          { _id: 0, host: "host.docker.internal:27017" },
          { _id: 1, host: "host.docker.internal:27018" },
          { _id: 2, host: "host.docker.internal:27019" }
          ]
      })
      EOF
      ---
    
    

    입맛대로 골라 넣으면 된다.

FINALLY

이것 외에도 정말 다양한 걸 만났고 정말 긴 시간이었다.

kubernetes를 사용했다면 helm을 이용해 간단하게 했을텐데..

그냥 k8s 사용할까 하고 계속 고민 했다.

k8s & helm 쓰세요!