추후 추가 예정
MongoDB의 Authentication 설정을 위해서 Primary 노드에 root 계정을 생성합니다.
# MongoDB Shell
> use admin
> db.createUser({
user: "admin", // root 계정명
pwd: "xxxxx", // root 패스워드
roles: [ {role: "root", db: "admin"} ]
}
)
MongoDB 접속 및 Replica Set 설정을 위해 /etc/mongod.conf
파일을 수정합니다.
# mongod.conf
# for documentation of all options, see:
# http://docs.mongodb.org/manual/reference/configuration-options/
# Where and how to store data.
storage:
dbPath: /var/lib/mongodb
journal:
enabled: true
# engine:
# mmapv1:
# wiredTiger:
# where to write logging data.
systemLog:
destination: file
logAppend: true
path: /var/log/mongodb/mongod.log
net:
port: 27017
bindIp: localhost,{해당 인스턴스의 host name}
# bindIp: 0.0.0.0 (모든 외부 IP 허용, 누구든 접근할 수 있게되므로 인증 추가 필요)
# how the process runs
processManagement:
timeZoneInfo: /usr/share/zoneinfo
#security
#operationProfiling:
replication:
# oplogSizeMB: 2000 # oplog 사이즈 2000MB
replSetName: "{원하는 이름}"
#sharding:
## Enterprise-Only Options:
#auditLog:
#snmp:
net.port
: MongoDB 접속 포트 정보 (기본: 27017)net.bindIp
: MongoDB 접속 허용 IP (기본: 127.0.0.1)security
옵션을 활성화할 필요성이 있습니다.replication.oplogSizeMB
: Replication에서 사용되는 oplog의 용량을 설정합니다. (선택 사항)replication.replSetName
: Replica Set의 명칭을 입력합니다.MongoDB에 접근 할 수 있도록 해당 포트에 대한 방화벽을 오픈합니다.
// 데비안 계열 (Ubuntu 등)
# TCP 27017 포트 오픈
> sudo ufw allow 27017/tcp
// 레드햇 계열 (CentOS 등)
# TCP 27017 포트 오픈
> firewall-cmd --permanent --add-port=27017/tcp
# 방화벽 정책 재반영
> firewall-cmd --reload
# 방화벽 정책 확인
> firewall-cmd --list-all
설정이 완료되었다면 MongoDB 각 노드를 재시작 합니다.
sudo systemctl start (or restart) mongod
Replica Set 구성에 앞서 각 MongoDB 노드 간 통신이 되는지 확인합니다.
편의상 각 노드를
mongodb-1
,mongodb-2
,mongodb-3
로 표현,
MongoDB Host는 IP 주소 보다 도메인 주소로 설정하여 접속하는 것을 권장하고 있습니다.
**# MongoDB 접속 확인**
> mongo --host mongodb-1 --port 27017
MongoDB shell version v4.4.13
connecting to: mongodb://mongodb-1:27017/?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session
MongoDB server version: 4.4.13
> mongo --host mongodb-2 --port 27017
MongoDB shell version v4.4.13
connecting to: mongodb://mongodb-2:27017/?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session
MongoDB server version: 4.4.13
> mongo --host mongodb-3 --port 27017
MongoDB shell version v4.4.13
connecting to: mongodb://mongodb-3:27017/?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session
MongoDB server version: 4.4.13
---
※ MongoDB 버전 6.0 버전 이상인 경우 "mongo" 명령어 대신 "mongosh" 명령어로 접속 가능
Replica Set을 구성하기 위해서는 초기화해야 하며, 초기화 방법으로는 크게 2가지가 있습니다.
**# 초기화 수행**
> rs.initiate()
{
"info2" : "no configuration specified. Using a default configuration for the set",
"me" : "mongodb-1:27017",
"ok" : 1
}
**# 멤버 추가**
> rs.add("mongodb-2:27017")
또는
> rs.add({_id: 1, host: "mongodb-2:27017"})
**# 멤버 지정하여 초기화 수행**
> rs.initiate({
_id: "replSetName에서 지정한 이름",
members: [
{_id: 0, host: "mongodb-1:27017"},
{_id: 1, host: "mongodb-2:27017"},
...
]
})
# (Arbiter 필요 시) Arbiter 노드 멤버 추가
> rs.addArb("mongodb-3:27017")
또는
> rs.add({host: "mongodb-3:27017", arbiterOnly: true})
또는
> rs.initiate({
_id: "replSetName에서 지정한 이름",
members: [
{_id: 0, host: "mongodb-1:27017"},
{_id: 1, host: "mongodb-2:27017"},
{_id: 2, host: "mongodb-3:27017", arbiterOnly: true},
...
]
})
# 멤버 삭제
> rs.remove("mongodb-3:27017")
**# Replica Set 구성 확인**
> rs.conf()
{
_id: 'replSetName에서 지정한 이름',
version: 10,
term: 2,
members: [
{
_id: 0,
host: 'mongodb-1:27017',
arbiterOnly: false,
buildIndexes: true,
hidden: false,
priority: 1,
tags: {},
secondaryDelaySecs: Long("0"),
votes: 1
},
{
_id: 1,
host: 'mongodb-2:27017',
arbiterOnly: false,
buildIndexes: true,
hidden: false,
priority: 1,
tags: {},
secondaryDelaySecs: Long("0"),
votes: 1
},
{
_id: 2,
host: 'mongodb-3:27017',
arbiterOnly: false, // Arbiter 노드일 경우 true
buildIndexes: true,
hidden: false,
priority: 1, // Arbiter 노드일 경우 0
tags: {},
secondaryDelaySecs: Long("0"),
votes: 1
}
],
protocolVersion: Long("1"),
writeConcernMajorityJournalDefault: true,
settings: {
chainingAllowed: true,
heartbeatIntervalMillis: 2000,
heartbeatTimeoutSecs: 10,
electionTimeoutMillis: 10000,
catchUpTimeoutMillis: -1,
catchUpTakeoverDelayMillis: 30000,
getLastErrorModes: {},
getLastErrorDefaults: { w: 1, wtimeout: 0 },
replicaSetId: ObjectId("64dc403e529739256a0f9c25")
}
}
**# Replica Set 상태 확인**
> rs.status()
{
set: 'replSetName에서 지정한 이름',
date: ISODate("2023-08-17T00:49:54.342Z"),
myState: 1,
term: Long("6"),
syncSourceHost: '',
syncSourceId: -1,
heartbeatIntervalMillis: Long("2000"),
majorityVoteCount: 2,
writeMajorityCount: 2,
votingMembersCount: 2,
writableVotingMembersCount: 2,
optimes: {
lastCommittedOpTime: { ts: Timestamp({ t: 1692233393, i: 1 }), t: Long("6") },
lastCommittedWallTime: ISODate("2023-08-17T00:49:53.179Z"),
readConcernMajorityOpTime: { ts: Timestamp({ t: 1692233393, i: 1 }), t: Long("6") },
appliedOpTime: { ts: Timestamp({ t: 1692233393, i: 1 }), t: Long("6") },
durableOpTime: { ts: Timestamp({ t: 1692233393, i: 1 }), t: Long("6") },
lastAppliedWallTime: ISODate("2023-08-17T00:49:53.179Z"),
lastDurableWallTime: ISODate("2023-08-17T00:49:53.179Z")
},
lastStableRecoveryTimestamp: Timestamp({ t: 1692233352, i: 1 }),
electionCandidateMetrics: {
lastElectionReason: 'electionTimeout',
lastElectionDate: ISODate("2023-08-17T00:14:12.992Z"),
electionTerm: Long("6"),
lastCommittedOpTimeAtElection: { ts: Timestamp({ t: 1692231251, i: 1 }), t: Long("5") },
lastSeenOpTimeAtElection: { ts: Timestamp({ t: 1692231251, i: 1 }), t: Long("5") },
numVotesNeeded: 2,
priorityAtElection: 1,
electionTimeoutMillis: Long("10000"),
priorPrimaryMemberId: 1,
numCatchUpOps: Long("0"),
newTermStartDate: ISODate("2023-08-17T00:14:13.013Z"),
wMajorityWriteAvailabilityDate: ISODate("2023-08-17T00:14:14.024Z")
},
electionParticipantMetrics: {
votedForCandidate: true,
electionTerm: Long("5"),
lastVoteDate: ISODate("2023-08-16T09:02:17.902Z"),
electionCandidateMemberId: 1,
voteReason: '',
lastAppliedOpTimeAtElection: { ts: Timestamp({ t: 1692176530, i: 1 }), t: Long("4") },
maxAppliedOpTimeInSet: { ts: Timestamp({ t: 1692176530, i: 1 }), t: Long("4") },
priorityAtElection: 1
},
members: [
{
_id: 1,
name: 'mongodb-2:27017',
health: 1,
state: 2,
**stateStr: 'SECONDARY',**
uptime: 61637,
optime: { ts: Timestamp({ t: 1692233383, i: 1 }), t: Long("6") },
optimeDurable: { ts: Timestamp({ t: 1692233383, i: 1 }), t: Long("6") },
optimeDate: ISODate("2023-08-17T00:49:43.000Z"),
optimeDurableDate: ISODate("2023-08-17T00:49:43.000Z"),
lastAppliedWallTime: ISODate("2023-08-17T00:49:53.179Z"),
lastDurableWallTime: ISODate("2023-08-17T00:49:53.179Z"),
lastHeartbeat: ISODate("2023-08-17T00:49:52.740Z"),
lastHeartbeatRecv: ISODate("2023-08-17T00:49:53.716Z"),
pingMs: Long("0"),
lastHeartbeatMessage: '',
syncSourceHost: '192.168.56.102:27017',
syncSourceId: 2,
infoMessage: '',
configVersion: 11,
configTerm: 6
},
{
_id: 2,
name: 'mongodb-1:27017',
health: 1,
state: 1,
**stateStr: 'PRIMARY',**
uptime: 76741,
optime: { ts: Timestamp({ t: 1692233393, i: 1 }), t: Long("6") },
optimeDate: ISODate("2023-08-17T00:49:53.000Z"),
lastAppliedWallTime: ISODate("2023-08-17T00:49:53.179Z"),
lastDurableWallTime: ISODate("2023-08-17T00:49:53.179Z"),
syncSourceHost: '',
syncSourceId: -1,
infoMessage: '',
electionTime: Timestamp({ t: 1692231252, i: 1 }),
electionDate: ISODate("2023-08-17T00:14:12.000Z"),
configVersion: 11,
configTerm: 6,
self: true,
lastHeartbeatMessage: ''
},
...
],
ok: 1,
'$clusterTime': {
clusterTime: Timestamp({ t: 1692233393, i: 1 }),
signature: {
hash: Binary(Buffer.from("0000000000000000000000000000000000000000", "hex"), 0),
keyId: Long("0")
}
},
operationTime: Timestamp({ t: 1692233393, i: 1 })
}
※ Replica Set 상태를 확인했을 때 노드의 상태 (“stateStr”)가 “STARTUP” 또는 "STARTUP2"로 출력되는 경우 참조
- 구성 시간이 약간 소요되므로 대기하면 해결되거나
- 그래도 연결되지 않는다면 Replica Set 재구성이 필요합니다.
# Replica Set 재구성 (Mongo Shell)
> var cfg = {
_id: "replSetName에서 지정한 이름",
members: [
{_id: 0, host: "mongodb-1:27017"},
{_id: 1, host: "mongodb-2:27017"},
...
]
};
> rs.reconfig(cfg);
keyfile 생성
security.authorization: enabled
설정을 하면 오류가 발생합니다.security.keyFile
설정을 추가해야 하며, 생성한 key는 최소한의 권한만 가져야 합니다.# keyfile 저장 경로 생성 (해당 경로는 원하는 경로로 생성)
> mkdir "keyfile 경로"
# keyfile 경로로 이동
> cd "keyfile 경로"
# Keyfile 생성 (1024자 base64) 및 권한 부여
> openssl rand -base64 756 > "keyfile 경로/keyfile.key"
> chmod 400 "keyfie 경로/keyfile.key" // 400: 읽기 권한만
> chown mongod.mongod "keyfile 경로/keyfile.key"
※ 테스트 환경에서는 Keyfile 경로를 "/var/lib/mongodb/key/mongo.key"로 지정했습니다.
Replica Set에 연결된 각 노드는 동일한 keyfile을 공유해야 하므로 생성한 keyfile을 복제하여 동일 경로에 저장합니다.
/etc/mongod.conf
systemLog:
...
security:
authorization: enabled
clusterAuthMode: keyFile
keyFile: "keyfile 경로/keyfile.key"
설정을 완료했다면 각 노드에 해당하는 mongod 서비스를 재실행합니다.
> sudo systemctl restart mongod
[2) MongoDB 관리자 계정 생성](#2-MongoDB-관리자-계정-생성)
에서 생성했던 root 계정을 통해 접속하여 Security가 정상적으로 작동되는지 확인합니다.
# MongoDB 관리자 로그인
> mongo -u admin -p "패스워드"
# Replica Set 상태 확인
> rs.status()
MongoDB 접속 후 Replica Set 상태 확인이 정상적으로 조회된다면 authentication 설정이 완료된 것입니다.
MongoDB에 데이터 CRUD를 통해 정상 동작 여부를 확인합니다.
**# 테스트 전용 DB 생성**
primary> use replTest
**# DB 생성 확인**
primary> show dbs
admin 156.00 KiB
config 296.00 KiB
local 656.00 KiB
replTest 72.00 KiB
secondary> show dbs
admin 156.00 KiB
config 296.00 KiB
local 688.00 KiB
replTest 72.00 KiB
not primary
라는 에러 메시지 출력**# 컬렉션 추가 및 데이터 Insert (Create)**
primary> db.book.insertOne({"name": "book1", "author": "john"})
{
acknowledged: true,
insertedId: ObjectId("64df279ed10006156ad507fe")
}
secondary> use replTest
secondary> db.book.insertOne({"name": "book2", "author": "smith"})
MongoServerError: not primary
not primary and secondaryOk=false
라는 에러 메시지 출력**# 데이터 Find (Read)**
primary> db.book.find({"name": "book1"})
[
{
_id: ObjectId("64df279ed10006156ad507fe"),
name: 'book1',
author: 'john'
}
]
secondary> db.book.find({"name": "book1"})
MongoServerError: not primary and secondaryOk=false - consider using db.getMongo()
.setReadPref() or readPreference in the connection string
not primary
라는 에러 메시지 출력**# 데이터 Update (Update)**
primary> db.book.updateOne({"name": "book1"}, {$set: {"pages": 300}})
{
acknowledged: true,
insertedId: null,
matchedCount: 1,
modifiedCount: 0,
upsertedCount: 0
}
secondary> db.book.updateOne({"name": "book1"}, {$set: {"pages": 720}})
MongoServerError: not primary
not primary
라는 에러 메시지 출력**# 데이터 Delete (Delete)**
primary> db.book.deleteOne({"name": "book1"})
{ acknowledged: true, deletedCount: 1 }
secondary> db.book.deleteOne({"name": "book1"})
MongoServerError: not primary
간혹 Replica Set 설정이 잘못되었거나 에러가 발생할 경우 Replica Set을 재설정 해야 할 필요가 있습니다.
# /etc/mongod.conf에서 Replication 주석 처리 (security 설정이 되어있다면 security도 주석 처리)
> vi /etc/mongod.conf
#replication:
# replSetName: "Replica Set 이름"
...
#security:
# mongod 재시작
> systemctl restart mongod
# local DB 삭제 (MongoDB Shell 접속)
> mongo -u admin (관리자 계정) -p
> use local
> db.dropDatabase()
# /etc/mongod.conf에서 Replication 주석 해제 (security 설정이 되어있다면 security도 주석 해제)
> vi /etc/mongod.conf
replication:
replSetName: "Replica Set 이름"
...
security:
# mongod 재시작
> systemctl restart mongod
# local DB에 "startup_log" Collection이 정상적으로 생성되었는지 확인
> use local
> show collections
https://hoing.io/archives/4282
https://www.mongodb.com/docs/manual/reference/replication/