Last Updated on 9월 2, 2023 by Jade(정현호)
안녕하세요
이번 포스팅에서는 MongoDB의 Shard Cluster(샤드 클러스터) 구성에 대해서 확인해보려고 합니다.
Contents
샤딩 및 샤드 클러스터
샤딩은 대용량 데이터베이스 또는 데이터에 대해서 여러 시스템에 데이터를 저장, 처리할 수 있도록 처리 또는 배포하는 방법입니다.
가령 데이터 세트가 크거나 처리량이 많은 애플리케이션을 사용하는 데이터베이스 시스템은 단일 서버의 용량에 문제가 될 수 있습니다. 또는 쿼리의 처리가 늦거나 쿼리 요청이 많다면 서버의 CPU 사용량이 많이 증가할 수 있으며, 시스템의 메모리(RAM) 또는 설정된 캐시(버퍼풀) 크기보다 더 큰 작업 세트 크기는 많은 디스크 I/O를 유발할 수도 있습니다.
그래서 대용량 데이터를 다루는 여러 방안 중에 하나가 샤딩 이라는 기술이며, MongoDB에서는 샤딩을 구현(적용) 하려면 Shard Cluster(샤드 클러스터)를 통해서 사용할 수 있으며, 샤드 클러스터는 MongoDB의 주요한 핵심 기능 또는 메인 기능이라고 할 수 있습니다. MongoDB는 샤딩을 통해 수평 스케일링을 지원합니다.
[mongodb.com/docs/manual/sharding]
MongoDB의 샤드 클러스터는 다음과 같은 구성 요소로 구성됩니다
- shard : shard 또는 shard server 로 부르며, 샤드 서버는 실제 데이터를 저장하고 있는 데이터 노드(or 데이터 세트) 역할을 하며, 1개 이상의 ReplicaSet이 필요 합니다.
- mongos: 쿼리 라우터 역할을 통해서 클라이언트 응용 프로그램과 샤드 클러스터 간의 인터페이스를 제공합니다. MongoDB 4.4부터 mongos는 지연 시간을 최소화하기 위해 Hedged Reads를 지원하며, 1개 이상의 mongos 가 필요 합니다.
- config server: 샤드 클러스터에 대한 메타데이터 및 구성 설정 정보가 저장하며, 1개의 ReplicaSet 으로 구성됩니다.
일반적인 Production 환경에서는 다음과 같은 컴포넌트 별로 고가용성을 위한 다수의 구성이 요구됩니다.
- Config Server를 3개 멤버의 ReplicaSet 으로 배포
- 각 샤드 서버를 3 멤버의 ReplicaSet 으로 배포
- 하나 이상의 mongos 라우터 배포
그래서 그림으로 표현하면 아래와 다음과 같은 구성의 형태가 되게 됩니다.
[mongodb.com/docs/manual/core/sharded-cluster-components]
포스팅에서는 테스트/학습의 목적으로 구성하기 위해서 1개의 단일 물리 시스템에서 포트를 다르게 설정하여 위와 같은 구성을 진행하였습니다.
Development 환경에서는 다음과 같이 최소의 컴포넌트 수를 통해서 샤드 클러스터를 구성할 수도 있습니다.
- mongos 인스턴스 1개
- 1개의 ReplicaSet으로 구성된 Shard 서버
- ReplicaSet 으로 구성된 config server
이러한 Non-Production(Development) 에서의 샤드 클러스터 구성에 대한 이미지는 다음과 같습니다.
[mongodb.com/docs/manual/core/sharded-cluster-components]
[참고] 포스팅에서 사용한 버전 정보
OS : RockyLinux 8.7
MongoDB : 5.0.18
OS 사전 환경 구성
Transparent Huge Page(THP) 옵션 비활성화
Transparent Huge Page(THP) 기능 비활성화가 필요하며 서비스를 생성하여 진행하겠습니다.
[참고] 포스팅에서는 OS root 유저를 사용하며, SELinux 는 비활성화 상태로 진행합니다.
아래와 같은 경로에 파일을 vi 및 기타 편집기로 생성합니다.
vi /etc/systemd/system/disable-transparent-huge-pages.service
##### 아래 내용으로 작성 [Unit] Description=Disable Transparent Huge Pages (THP) DefaultDependencies=no After=sysinit.target local-fs.target Before=mongod.service [Service] Type=oneshot ExecStart=/bin/sh -c "echo 'never' >/sys/kernel/mm/transparent_hugepage/enabled && echo 'never' >/sys/kernel/mm/transparent_hugepage/defrag" [Install] WantedBy=basic.target
파일 작성이 완완료되었다면아래와 같이 서비스를 시작합니다.
systemctl daemon-reload systemctl enable disable-transparent-huge-pages systemctl start disable-transparent-huge-pages
THP 기능이 비활성화 되었는지는 아래와 같이 확인합니다.
cat /sys/kernel/mm/transparent_hugepage/enabled always madvise [never] cat /sys/kernel/mm/transparent_hugepage/defrag always defer defer+madvise madvise [never]
never 에 [] 으로 선택되어 있다면 비활성화 된 상태를 의미합니다.
Prerequisites OS Package
MongoDB 실행에 필요한 필요 OS 패키지를 설치합니다.
yum install libcurl openssl xz-libs
Ulimit 수정
ulimit 의 open files 값이 64000 미만이면 MongoDB 시작 오류가 발생될 수 있으므로 설정이 필요하며, 또한 권장되는 nproc 값에 대해서도 수정하도록 하겠습니다.
vi /etc/security/limits.conf ## 아래 내용 입력 mongod soft nofile 64000 mongod hard nofile 64000 mongod hard nproc 64000 mongod hard nproc 64000
포스팅에서는 OS root 유저로 실행을 진행하기 때문에 별도의 OS mongod 그룹 및 유저를 생성하지 않았으나, systemd 를 통해서 mongodb의 실행 유저를 일반 유저인 mongod(또는 mongo) 로 사용할 경우 OS 사전 작업 단계에서 아래와 같이 유저를 생성하시면 됩니다.
groupadd mongodb useradd -M -s /usr/sbin/nologin -g mongodb mongodb
-M 옵션을 사용해서 홈디렉토리를 생성하지 않고,
-s /bin/false 혹은 /sbin/nologin 옵션을 사용해서 유저의 로그인쉘을 사용할 수 없게 하는 것입니다.
즉 mongodb 이라는 유저는 MongoDB 데몬을 실행하기 위한 유저이고
서버의 보안강화 측면에서 외부에서 mongodb 유저로 쉘 로그인은 필요가 없습니다.
샤드 클러스터 구성
포트 및 디렉토리 경로 정보
포스팅에서 사용한 포트 정보 및 디렉토리 경로 정보입니다.
- config server
Port : 27031 / 27032 / 27033
디렉토리 :
/usr/local/mongodb/config_server/data_27031
/usr/local/mongodb/config_server/data_27032
/usr/local/mongodb/config_server/data_27033 - shard server
Port :
replset1 - 27021 / 27022 / 27023(Arbiter)
replset2 - 27024 / 27025 / 27026(Arbiter)
replset3 - 27027 / 27028 / 27029(Arbiter)
디렉토리 :
replset1
/usr/local/mongodb/shard_server/data_27021
/usr/local/mongodb/shard_server/data_27022
/usr/local/mongodb/shard_server/data_27023
replset2
/usr/local/mongodb/shard_server/data_27024
/usr/local/mongodb/shard_server/data_27025
/usr/local/mongodb/shard_server/data_27026
replset3
/usr/local/mongodb/shard_server/data_27027
/usr/local/mongodb/shard_server/data_27028
/usr/local/mongodb/shard_server/data_27029 - mongos
Port : 27041
디렉토리 :
/usr/local/mongodb/mongos/mongos_27041
MongoDB 다운로드 및 구성
MongoDB는 5.0.18 버전을 사용하였으며 다음과 같이 다운로드 및 압축을 해제합니다.
포스팅에서는 /usr/local 경로 아래에 파일을 위치하였습니다.
wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel80-5.0.18.tgz tar zxvf mongodb-linux-x86_64-rhel80-5.0.18.tgz -C /usr/local
사용 편의성 및 관리용의성을 위해서 심볼릭 링크를 생성하도록 하겠습니다.
cd /usr/local/ ln -s mongodb-linux-x86_64-rhel80-5.0.18 mongodb
OS PATH 변수를 설정하도록 하겠습니다.
vi ~/.bash_profile ## 아래 변수 추가 export PATH=$PATH:/usr/local/mongodb/bin
파일에 내용을 추가하였다면, 다음과 같이 파일을 다시 읽어 적용합니다.
source ~/.bash_profile
config server 구성
config server Replica Set으로 구성하도록 하겠습니다.
MongoDB 샤드클러스터 구성에서 config 서버로 사용되는 레플리카 셋 구성에는 다음과 같은 제한 사항이 적용됩니다.
- arbiter가 없어야 합니다.
- 지연된 멤버가 없어야 합니다.
- 인덱스를 작성해야 합니다. 즉, 어떤 멤버도 members[n].buildIndexes 설정을 false로 설정해서는 안 됩니다.
그래서 Config 서버의 Replica Set의 멤버 중 하나는 arbiter 로 설정하지 않습니다.
config server 구성을 위한 데이터 저장 경로를 생성합니다.
mkdir -p /usr/local/mongodb/config_server/data_27031 mkdir -p /usr/local/mongodb/config_server/data_27032 mkdir -p /usr/local/mongodb/config_server/data_27033
config server 구동 합니다.
mongod --configsvr --replSet configrs \ --dbpath /usr/local/mongodb/config_server/data_27031 \ --logpath /usr/local/mongodb/config_server/data_27031/mongo_config.log --logappend \ --fork --port 27031 --bind_ip_all --directoryperdb mongod --configsvr --replSet configrs \ --dbpath /usr/local/mongodb/config_server/data_27032 \ --logpath /usr/local/mongodb/config_server/data_27032/mongo_config.log --logappend \ --fork --port 27032 --bind_ip_all --directoryperdb mongod --configsvr --replSet configrs \ --dbpath /usr/local/mongodb/config_server/data_27033 \ --logpath /usr/local/mongodb/config_server/data_27033/mongo_config.log --logappend \ --fork --port 27033 --bind_ip_all --directoryperdb
실행된 Config Server 중 하나의 서버에 접속하여 ReplicaSet 설정을 합니다.
## OS 에서 config server 접속 mongo --host 127.0.0.1 --port 27031 ## config server 접속후 아래 명령어 수행 use admin rs.initiate( { _id : "configrs", configsvr : true, members : [ { _id:0, host: "127.0.0.1:27031"}, { _id:1, host: "127.0.0.1:27032"}, { _id:2, host: "127.0.0.1:27033"} ] } )
구성된 config server ReplicaSet에 대한 정보는 config server에 접속하여 다음의 명령어를 통해서 확인합니다.
rs.hello()
keyfile을 사용하여 authorization 활성화하여 인증설정을 할 경우 동일한 keyfile을 사용해서 config 서버와 shard server를 시작해야 합니다.
keyfile 생성과 keyfile에 대한 내용은 아래 포스팅을 참조하시면 됩니다.
keyfile을 생성하였다면 config server 시작 시 keyfile 옵션은 다음과 같이 사용합니다.
mongod --configsvr --keyFile /경로/mongod.key --auth --clusterAuthMode keyFile \ .... ....
Shard Server 구성
샤드 서버를 위해서 다음과 같이 ReplicaSet 3개 세트를 구성하도록 하겠습니다.
- replset1 – 27021, 27022, 27023
- replset2 – 27024, 27025, 27026
- replset3 – 27027, 27028, 27029
ReplicaSet은 1개 Primary, 1개 Secondary, 1개 Arbiter 로 구성하도록 하겠습니다.
MongoDB ReplicaSet에 대한 추가적인 내용은 아래 포스팅을 확인하시면 됩니다.
data dir 를 생성합니다.
mkdir -p /usr/local/mongodb/shard_server/data_27021 mkdir -p /usr/local/mongodb/shard_server/data_27022 mkdir -p /usr/local/mongodb/shard_server/data_27023 mkdir -p /usr/local/mongodb/shard_server/data_27024 mkdir -p /usr/local/mongodb/shard_server/data_27025 mkdir -p /usr/local/mongodb/shard_server/data_27026 mkdir -p /usr/local/mongodb/shard_server/data_27027 mkdir -p /usr/local/mongodb/shard_server/data_27028 mkdir -p /usr/local/mongodb/shard_server/data_27029
Shard Server 시작합니다.
## Shard-1 mongod --shardsvr --replSet replset1 \ --dbpath /usr/local/mongodb/shard_server/data_27021 \ --logpath /usr/local/mongodb/shard_server/data_27021/mongo_shard.log --logappend \ --fork --port 27021 --bind_ip_all --directoryperdb mongod --shardsvr --replSet replset1 \ --dbpath /usr/local/mongodb/shard_server/data_27022 \ --logpath /usr/local/mongodb/shard_server/data_27022/mongo_shard.log --logappend \ --fork --port 27022 --bind_ip_all --directoryperdb mongod --shardsvr --replSet replset1 \ --dbpath /usr/local/mongodb/shard_server/data_27023 \ --logpath /usr/local/mongodb/shard_server/data_27023/mongo_shard.log --logappend \ --fork --port 27023 --bind_ip_all --directoryperdb ## Shard-2 mongod --shardsvr --replSet replset2 \ --dbpath /usr/local/mongodb/shard_server/data_27024 \ --logpath /usr/local/mongodb/shard_server/data_27024/mongo_shard.log --logappend \ --fork --port 27024 --bind_ip_all --directoryperdb mongod --shardsvr --replSet replset2 \ --dbpath /usr/local/mongodb/shard_server/data_27025 \ --logpath /usr/local/mongodb/shard_server/data_27025/mongo_shard.log --logappend \ --fork --port 27025 --bind_ip_all --directoryperdb mongod --shardsvr --replSet replset2 \ --dbpath /usr/local/mongodb/shard_server/data_27026 \ --logpath /usr/local/mongodb/shard_server/data_27026/mongo_shard.log --logappend \ --fork --port 27026 --bind_ip_all --directoryperdb ## Shard-3 mongod --shardsvr --replSet replset3 \ --dbpath /usr/local/mongodb/shard_server/data_27027 \ --logpath /usr/local/mongodb/shard_server/data_27027/mongo_shard.log --logappend \ --fork --port 27027 --bind_ip_all --directoryperdb mongod --shardsvr --replSet replset3 \ --dbpath /usr/local/mongodb/shard_server/data_27028 \ --logpath /usr/local/mongodb/shard_server/data_27028/mongo_shard.log --logappend \ --fork --port 27028 --bind_ip_all --directoryperdb mongod --shardsvr --replSet replset3 \ --dbpath /usr/local/mongodb/shard_server/data_27029 \ --logpath /usr/local/mongodb/shard_server/data_27029/mongo_shard.log --logappend \ --fork --port 27029 --bind_ip_all --directoryperdb
keyfile을 사용하여 authorization 활성화하여 인증설정을 할 경우 동일한 keyfile을 사용해서 config 서버와 shard server를 시작해야 합니다.
keyfile 생성과 keyfile에 대한 내용은 아래 포스팅을 참조하시면 됩니다.
keyfile을 생성하였고 keyfile옵션을 사용할 경우 다음과 mongod 명령어를 다음과 같이 사용합니다.
mongod --shardsvr --keyFile /경로/mongod.key --auth --clusterAuthMode keyFile \ .... ....
ReplicaSet을 설정하기 위해서 ReplicaSet 에서 1개 서버에 접속합니다.
mongo --host 127.0.0.1 --port 27021
1개 서버를 arbiter 로 변경(지정) 합니다. arbiter 노드가 아닌 Secondary 로 지정하여 사용하여도 됩니다.
use admin rs.initiate( { _id : "replset1", members : [ {_id : 0, host: "127.0.0.1:27021"}, {_id : 1, host: "127.0.0.1:27022"}, {_id : 2, host: "127.0.0.1:27023",arbiterOnly:true} ] } ) { "ok" : 1 }
위의 방법으로 나머지 2개 ReplicaSet도 변경합니다.
$ mongo --host 127.0.0.1 --port 27024 use admin rs.initiate( { _id : "replset2", members : [ {_id : 0, host: "127.0.0.1:27024"}, {_id : 1, host: "127.0.0.1:27025"}, {_id : 2, host: "127.0.0.1:27026",arbiterOnly:true} ] } ) { "ok" : 1 } ========================================================================= $ mongo --host 127.0.0.1 --port 27027 use admin rs.initiate( { _id : "replset3", members : [ {_id : 0, host: "127.0.0.1:27027"}, {_id : 1, host: "127.0.0.1:27028"}, {_id : 2, host: "127.0.0.1:27029",arbiterOnly:true} ] } ) { "ok" : 1 }
위의 설정 변경을 완료 후 다음 명령어를 통해서 ReplicaSet 정보를 확인합니다.
rs.hello()
여기까지 진행이 되었다면 Config 서버 1Set(3개 서버), ReplicaSet 3개세트(총 9개 서버)가 구성된 상태가 되게 됩니다.
MongoS 실행
mongod 로 구동하였던 config server 와 shard server 와 달리 mongos 는 mongos 라는 바이너리 실행 파일을 통해서 실행합니다.
logpath 를 위해서 디렉토리를 생성합니다.
mkdir -p /usr/local/mongodb/mongos/mongos_27041
mongos 를 구동 합니다.
mongos --configdb configrs/127.0.0.1:27031,127.0.0.1:27032,127.0.0.1:27033 \ --logpath /usr/local/mongodb/mongos/mongos_27041/mongos.log --logappend \ --fork --port 27041 --bind_ip_all
샤드 클러스터에 추가
ReplicaSet 으로 구성한 Shard 서버를 클러스터에 추가하도록 하겠습니다.
mongos(router) 서버로 접속합니다. 포스팅에서는 mongos 포트를 27041 로 사용중입니다.
mongo --host 127.0.0.1 --port 27041
샤드 클러스터에 서버를 추가 시 다음과 같이 arbiter 노드를 제외하고 Primary 와 Secondary만 입력하면 됩니다.
sh.addShard("replset1/127.0.0.1:27021,127.0.0.1:27022") sh.addShard("replset2/127.0.0.1:27024,127.0.0.1:27025") sh.addShard("replset3/127.0.0.1:27027,127.0.0.1:27028")
샤드 클러스터에 추가가 완료되었다면 다음 명령어를 통해서 샤드 클러스터의 상태를 확인합니다.
sh.status()
여기까지 완료되었다면 MongoDB 샤드 클러스터 구성이 완료된 상태입니다.
샤드 활성화 및 샤딩 테스트
mongos로 접속합니다.
mongo --host 127.0.0.1 --port 27041
shard cluster 활성화는 샤드로 사용할 데이터베이스에 대해서 한번은 실행이 반드시 필요 합니다.
샤드로 사용할 데이터베이스명을 입력해서 enable 을 해야 합니다.
포스팅에서는 tdb 라는 데이터베이스에 대해서 지정하였습니다.
sh.enableSharding("tdb")
샤딩할 Collection 을 입력하고 샤드키를 선정합니다.
sh.shardCollection("tdb.employees",{empno: "hashed"})
여기까지 완료되었다면 샤드 클러스터 상태 정보를 확인해 봅니다.
sh.status()
실제 데이터를 입력하여 샤딩이 되어 데이터가 입력되는지를 확인해보도록 하겠습니다.(100건 입력)
use tdb for(var a=1; a< 100; a++) db.employees.insert({ empno :a })
다시 샤드 클러스터의 상태 정보를 확인합니다.
sh.status()
db.collection.getShardDistribution() 메서드를 사용하여 특정 콜렉션의 분할 상태를 확인할 수 있습니다. 이 메서드는 콜렉션의 데이터가 어떤 샤드에 어떻게 분산되었는지 확인할 수 있습니다.
use tdb db.employees.getShardDistribution()
위의 명령을 실행하면 각 샤드에 대한 정보와 해당 샤드에 저장된 청크(chunk)의 범위, 데이터의 분포 등을 확인할 수 있습니다. 샤드 클러스터에서 샤딩된 컬렉션의 분할 상태와 관련된 자세한 정보를 제공합니다.
이번 포스팅은 여기서 마무리하도록 하겠으며, MongoDB Shard Cluster에 대한 더 자세한 내용은 별도의 포스팅에서 이어 가도록 하겠습니다.
Reference
Reference URL
• mongodb.com/docs/sharding
• mongodb.com/docs/sharded-cluster-components
• mongodb.com/docs/sharded-cluster-config-servers
연관된 다른 글
Principal DBA(MySQL, AWS Aurora, Oracle)
핀테크 서비스인 핀다에서 데이터베이스를 운영하고 있어요(at finda.co.kr)
Previous - 당근마켓, 위메프, Oracle Korea ACS / Fedora Kor UserGroup 운영중
Database 외에도 NoSQL , Linux , Python, Cloud, Http/PHP CGI 등에도 관심이 있습니다
purityboy83@gmail.com / admin@hoing.io