오늘은 서버 세 대로 PSA 구조의 mongoDB replica set 구성을 하려고 한다.
회사에서 맡고 있는 로그 수집 시스템을 ELK Stack에서 Kafka + MongoDB 로 변경하려고 하여, MongoDB 서버 클러스터 구성이 필요했다.
MongoDB의 Replica set이란 같은 데이터셋을 가지고 있는 여러 mongod 프로세스 그룹이다. 여러 DB 서버에 복수 개의 데이터 copy를 둠으로써, 단일 DB 서버로 구축하는 것보다 fault-tolerant하다.
Replica set은 크게 세 가지 역할로 나눌 수 있다.
위 세 가지 역할을 조합하여, PSS 구조나 PSA 구조로 서버를 구성할 수 있다.
그러나 Arbiter는 데이터를 쌓지 않으며 적은 리소스로 사용 가능하기 때문에, 3개의 서버로 PSA 구조로 가되 두번째 Secondary 서버에 Arbiter도 같이 띄울 것이다.
kt cloud 에서 서버 3대를 생성하였다.
MongoDB는 메모리 용량에 따라 성능이 크게 좌우되기 때문에, 메모리는 최소 32GB에 SSD를 마운팅해야한다.
삽질 1 - CentOS 7.0에서 MongoDB 5.0 버전이 잘 호환되지 않는다.
설치까지는 되는데, mongod 명령어가 먹지 않고, systemctl start mongod도 안된다.
OS를 바꾸기엔 기존 회사 서버가 CentOS7.0 이어서, 그냥 MongoDB 4.4 버전을 사용하기로 했다.
먼저 공식 문서에 나와있는 .repo 파일을 만들기 전, 아래와 같은 작업을 해줘야 한다. 그렇지 않으면 mongodb 설치 시, "[Errorno 14] curl#35 - "Peer reports incompatible or unsupported protocol version"
이라는 오류가 발생한다.
yum update -y nss curl libcurl
이후로는 모든 서버에서 아래를 참고하여 해당 서버에 맞는 명령어를 입력해주면 된다.
Arbiter가 있는 3번 서버는 추가 설정이 있으니 조심해야 한다.
vi /etc/yum.repos.d/mongodb-org-4.4.repo
하고 아래 내용을 입력한다.[mongodb-org-4.4]
name=MongoDB Repository
baseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/4.4/x86_64/
gpgcheck=1
enabled=1
gpgkey=https://www.mongodb.org/static/pgp/server-4.4.asc
yum install
sudo yum install -y mongodb-org
로 최신 stable 버전의 mongodb 4.4를 설치한다.
여기서, yum이 패키지 새 버전이 나왔을 때 자동 업그레이드하는 것을 막으려면 /etc/yum.conf
파일에 exclude=mongodb-org,mongodb-org-server,mongodb-org-shell,mongodb-org-mongos,mongodb-org-tools
를 입력해줘야 한다.
ulimit 설정 변경
ulimit -a
를 통해 시스템 리소스 사용 제한을 확인할 수 있다.
MongoDB에서 recommend 하는 설정대로 변경하자. ulimit -n 64000
와 같이 입력하여 변경하면 된다. 특히 MongoDB 4.4에서는 open files 개수 (-n) 이 64000 보다 적으면 실행 시 오류가 발생하니 꼭 바꿔줘야 한다.
-f (file size): unlimited
-t (cpu time): unlimited
-v (virtual memory): unlimited
-l (locked-in-memory size): unlimited
-n (open files): 64000 // 중요한 설정이므로 꼭 변경
-m (memory size): unlimited
-u (processes/threads): 64000
sudo mkdir -p /data/mongodata
sudo mkdir -p /opt/db/mongodb/logs
sudo chown -R mongod:mongod /data/mongodata
sudo chown -R mongod:mongod /opt/db/mongodb/logs
sudo mkdir -p /opt/db/mongoArbiter/data
sudo mkdir -p /opt/db/mongoArbiter/logs
sudo chown -R mongod:mongod /opt/db/mongoArbiter/data
sudo chown -R mongod:mongod /opt/db/mongoArbiter/logs
mongodb를 실행하기 전, mongod.conf 파일을 수정해야 한다.
vi /etc/mongod.conf
하여 아래와 같이 conf 파일을 수정하자.
1, 2번 서버는 conf 파일이 한개이므로 바로 수정해도 된다.
그러나 3번 서버는 secondary와 arbiter가 각각 다른 conf로 각각 떠있어야 하기 때문에, secondary용 conf는 /opt/db/mongodb/mongodb.conf
에, arbiter용 conf는 /opt/db/mongoArbiter/mongod_arbiter.conf
에 적는 등 conf를 분리하여 적어야 한다.
systemLog:
destination: file #
path: "/opt/db/mongodb/logs/mongod.log"
logAppend: true #
logRotate: rename #
timeStampFormat: iso8601-utc
quiet: true #
storage:
engine: wiredTiger #
dbPath: "/data/mongodata"
directoryPerDB: true
wiredTiger:
engineConfig:
journalCompressor: snappy
cacheSizeGB: 0.4 #
collectionConfig:
blockCompressor: snappy
indexConfig:
prefixCompression: true
journal:
enabled: true #
commitIntervalMs: 300 #
processManagement:
fork: true
pidFilePath: "/opt/db/mongodb/mongod.pid" #
timeZoneInfo: /usr/share/zoneinfo
net:
bindIpAll: true
port: 27017
wireObjectCheck : false #
unixDomainSocket: #
enabled : true
replication:
replSetName: mongo_repl_set #
setParameter:
enableLocalhostAuthBypass: false #
#security:
# authorization: enabled
file
또는 syslog
. syslog
는 timestamp가 mongoDB가 메시지를 발행할 때가 아니라 실제 로그를 남길 때 생성되므로 추천하지 않는다.logrotate
설정이 필요하다. logRotate는 logrotate
의 behavior를 정의한다.rename
은 같은 파일을 이름만 바꿔서 로깅을 지속하고 reopen
은 다른 Linux log rotation과 같이 로그 파일을 닫고 다시 여는 것이다.wiredTiger
또는 inMemory
. wiredTiger가 기본 설정이다.true
이면 각 DB 마다 디렉토리를 분리한다.0.5
of (RAM - 1GB), 256
MB) 이므로, 2개 이상의 mongod가 떠있다면 줄여야 한다. 또한 wiredTiger는 내부 cache 뿐만 아니라 filesystem cache도 사용하기 때문에 기본값 이상으로 설정하는 것은 지양해야 한다.false
로 하면 저널 로그를 디스크에 기록하지 않는다.processManagement.fork
설정과 같이 사용해야 유용하다. 단 리눅스에서 systemctl
로 서비스를 시작하는 경우 PID file 관리는 /etc/init.d
에서 이루어지기 때문에 사용하지 말 것.true
면 클라이언트 요청이 잘못된 BSON인지 유효성 검사를 하게 된다. 요청하는 object가 많이 nesting 되어있는 경우 성능에 영향을 줄 수 있다.true
이며 false
면..? storage.journal.enabled: true
로 설정 해야 한다.<parameter 이름> : <값>
형태로 전달할 수 있다.systemLog:
verbosity: 0
destination: file
path: "/opt/db/mongoArbiter/logs/mongod.log"
logAppend: true
logRotate: rename
timeStampFormat: iso8601-utc
quiet: true
component: #
replication:
verbosity: 3
storage:
dbPath: "/opt/db/mongoArbiter/data"
directoryPerDB: true
wiredTiger:
engineConfig:
journalCompressor: snappy
cacheSizeGB: 0.4
collectionConfig:
blockCompressor: snappy
indexConfig:
prefixCompression: true
journal:
enabled: true
processManagement:
fork: true
pidFilePath: "/opt/db/mongoArbiter/mongod.pid"
timeZoneInfo: /usr/share/zoneinfo
net:
bindIpAll: true
port: 27027
unixDomainSocket:
enabled : false
replication:
replSetName: mongo_repl_set
setParameter:
enableLocalhostAuthBypass: false
NETWORK
는 네트워크 연결과 관련된 로그, COMMAND
는 DB 커맨드와 관련된 로그) component.<name>.verbosity
를 통해 각 component의 시스템 로그 verbosity를 설정할 수 있다.Primary와 secondary는 mongod --port 27017 --config [conf 경로]
로,
arbiter는 mongod --port 27027 --config [arbiter conf 경로]
로 띄우면 된다.
특히 3번 서버는 실행 후 ps -ef | grep mongo
에서 mongod 두 개가 떠있는 것을 꼭 확인해야 한다.
Primary 서버에서 mongo
명령어를 입력한 후, 아래 설정을 통해 replica set을 초기화시킨다.
rs.initiate(
{
_id: “mongo_repl_set”,
version: 1,
members: [
{ _id: 0, host : "[1번 서버 ip주소:27017]" },
{ _id: 1, host : "[2번 서버 ip주소:27017]" },
{ _id: 2, host : "[3번 서버 ip주소:27017]" },
]
}
)
rs.addArb("[3번 서버 ip주소:27027]")
삽질 2. 클라우드 콘솔에서 방화벽 해제 설정을 했는데도 포트가 열리지 않아 오류가 발생했다.
firewall-cmd --zone=public --permanent --add-port=27017/tcp
로 포트를 열고.
firewall-cmd --reload
로 새로고침 한 후
firewall-cmd --zone=public --list-all
로 public zone 의 열린 포트를 확인할 수 있다.
삽질 3. mongod.conf의 replication 설정은 절대 주석처리하고 실행하면 안된다.
Stackoverflow에서
rs.initiate()
시에는mongod.conf
의replication
설정을 주석처리하고 실행하라는 글을 보고 해당 부분을 주석처리 하고 실행했더니, 아래와 같은 에러가 발생하였다.Sep 29 16:16:52 aicc-mongodb-cluster-02.localdomain systemd[1]: Unit mongod.service entered failed state. Sep 29 16:16:52 aicc-mongodb-cluster-02.localdomain systemd[1]: mongod.service failed. Sep 29 16:16:52 aicc-mongodb-cluster-02.localdomain polkitd[1410]: Unregistered Authentication Agent for unix-process:13018:1496936 (system bus name :1.37, object path /org/freedesktop/Pol { “ok” : 0, “errmsg” : “No host described in new configuration with {version: 1, term: 0} for replica set mongo_repl_set maps to this node”, “code” : 93, “codeName” : “InvalidReplicaSetConfig” }
"
mongo_repl_set
이라는 replica set의 설정에 있는 어떤 호스트도 현재 노드에 매핑이 되지 않는다." 라는 건데, rs.initiate() 설정의_id
값과mongod.conf
의replication.replSetName
이 서로 연동이 되지 않아서 발생하였다.
모든 mongod 설정의replication.replSetName
를 주석 해제하고 실행하였더니 정상적으로 연동되었다.
rs.status()
명령어를 입력한 후에 replica set 설정한 정보가 쭉 뜨면 성공이다!
먼저 primary 서버에 접속해서 테스트 DB, collection을 생성한 후 아무 document나 insert 해보겠다.
테스트 DB를 만들기 위해 use testdb
명령어를 입력한다.
이후 show dbs
를 해봐도 방금 생성한 testdb 는 보이지 않는다. collection을 하나 이상 추가해야 보이므로, db.createCollection("testcollection")
명령어 수행 후 show dbs
를 다시 해보면 아래와 같이 보인다.
db.testcollection.insert({"name":"수"})
로 insert 를 해보고, db.testcollection.find()
로 조회하면 primary 서버에서는 잘 보인다.
이제 secondary 서버에서 잘 조회되는지 확인해봐야 한다. secondary 서버에 접속하여 show dbs
명령어를 수행하면.. 아래와 같은 에러가 발생한다..
mongo_repl_set:SECONDARY> show dbs
uncaught exception: Error: listDatabases failed:{
"topologyVersion" : {
"processId" : ObjectId("6155714cd819dce17a0cda49"),
"counter" : NumberLong(5)
},
"operationTime" : Timestamp(1633094786, 1),
"ok" : 0,
"errmsg" : "not master and slaveOk=false",
"code" : 13435,
"codeName" : "NotPrimaryNoSecondaryOk",
"$clusterTime" : {
"clusterTime" : Timestamp(1633094786, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
} :
_getErrorWithCode@src/mongo/shell/utils.js:25:13
Mongo.prototype.getDBs/<@src/mongo/shell/mongo.js:147:19
Mongo.prototype.getDBs@src/mongo/shell/mongo.js:99:12
shellHelper.show@src/mongo/shell/utils.js:937:13
shellHelper@src/mongo/shell/utils.js:819:15
@(shellhelp2):1:1
삽질 4. Secondary에서는 read operation이 막혀있다.
mongo shell에서 DB 조회를 하기 위해서는
rs.secondaryOk()
를 실행해야 한다.
Secondary 서버에서 rs.secondaryOk()
수행 후 show dbs
를 하면 아래와 같이 잘 보인다.
Fin!
다음 포스트에선 replica set에 security 설정을 추가할 계획이다.