Kubernetes 클러스터에 MongoDB 구성하기
kubernetes 에 MongoDB 를 replica set 으로 구성해서 사용해본다. MongoExpress 도 함께 설치할 예정인데 MongoExpress는 MongoDB 관리를 위한 GUI 웹어드민 툴이다.
우선 replica set 구성을 위해서는 각 노드가 통신할 수 있는 인증 key 가 필요하고 kubernetes 환경에서는 특정 key 파일을 secret 로 만들어서 각 노드에서 활용한다. 즉, 아래와 같다.
openssl rand -base64 741 > ./replica-sets-key.txt
kubectl create secret generic shared-bootstrap-data --from-file=internal-auth-mongodb-keyfile=./replica-sets-key.txt
이렇게 생성한 secret 는 mongo pod 구성시 볼륨으로 참조한다.
MongoDB 구성에 사용한 yaml 파일은 아래와 같다.
apiVersion: v1
kind: ConfigMap
metadata:
name: tcp-services
namespace: ingress-nginx
data:
27017: "default/mongo-svc:27017"
---
kind: Service
apiVersion: v1
metadata:
name: mongo-svc-vip # 서비스 명에 맞게 변경
namespace: ingress-nginx
spec:
externalTrafficPolicy: Local
type: LoadBalancer
ports: # 서비스에 맞게 변경
- name: mongo-svc
port: 27017
targetPort: 27017
protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
name: mongo
labels:
name: mongo
spec:
ports:
- port: 27017
targetPort: 27017
clusterIP: None
selector:
role: mongo
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mongo
spec:
serviceName: mongo-svc
replicas: 3
selector:
matchLabels:
role: mongo
template:
metadata:
labels:
role: mongo
environment: test
spec:
terminationGracePeriodSeconds: 10
containers:
- name: mongo
image: ...mongo-5.0.11
command:
- "numactl"
- "--interleave=all"
- "mongod"
- "--bind_ip"
- "0.0.0.0"
- "--replSet"
- "MainRepSet"
- "--auth"
- "--clusterAuthMode"
- "keyFile"
- "--keyFile"
- "/etc/secrets-volume/internal-auth-mongodb-keyfile"
- "--setParameter"
- "authenticationMechanisms=SCRAM-SHA-1"
resources:
requests:
cpu: 0.2
memory: 200Mi
ports:
- containerPort: 27017
volumeMounts:
- name: secrets-volume
readOnly: true
mountPath: /etc/secrets-volume
- name: mongo-persistent-storage
mountPath: /data/db
- name: mongo-sidecar
image: ...cvallance-mongo-k8s-sidecar-20220824
volumes:
- name: secrets-volume
secret:
secretName: shared-bootstrap-data
defaultMode: 256
volumeClaimTemplates:
- metadata:
name: mongo-persistent-storage
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 10Gi
여기에서는 클러스터 외부에서 접근하기 위해 LoadBalancer 서비스를 사용했고 TCP 통신을 위해 ingress 컨트롤러가 참조할 tcp-services 라는 ConfigMap 을 정의했다. 위 yaml 이 정상적으로 동작하기 위해서는 클러스터에 LoadBalancer 와 ingress 컨트롤러가 구성되어 있어야 한다.
추가로 mongo-svc 에 해당하는 Service 를 Headless 서비스로 만들었는데 replica set 을 구성했기 때문에 굳이 이렇게 만들지 않아도 된다.
MongoDB 를 실행시키는 커맨드 명령어인 mongod 의 옵션들은 추후 조사할 예정이다. 사이드카 pod는 k8s 환경에 MongoDB를 구성할 때 필요한 유틸성 pod 인거 같다.
volumeClaimTemplates 에 StorageClass 지정이 필요할 수도 있다. 일부 포스팅을 보면 아래와 같이 annotations 에 StorageClass 를 지정하는 예가 많은데 해당 annotations은 아직까지는 사용할 수 있지만 향후 쿠버네티스 릴리스에서 완전히 사용 중단(deprecated)이 될 예정이다.
...
volumeClaimTemplates:
- metadata:
name: mongo-persistent-storage
annotations:
volume.beta.kubernetes.io/storage-class: "cinder-csi"
...
이에 StroageClass 지정이 필요하다면 아래와 같이 하도록 한다.
...
volumeClaimTemplates:
- metadata:
name: mongo-persistent-storage
spec:
storageClassName: "cinder-csi"
...
물론 굳이 지정하지 않아도 아래 명령으로 확인되는 StorageClass 중 default 로 지정된 것이 있다면 그게 사용되긴 한다.
kubectl get storageclass
내가 사용하는 환경에서는 아래와 같이 cinder-csi 가 Default StroageClass 로 지정되어 있었다.
정상적으로 구성되었다면 이제부터 replica set 을 구성해줘야 한다. 아래 명령어로 특정 mongo-0 pod 에 접속한다.
kubectl exec -ti mongo-0 -- /bin/bash
아래 명령어로 MongoDB 터미널에 접속한다.
mongo
아래 명령어로 replica set 을 구성한다. 각 pod 의 내부 도메인들은 CoreDns 를 사용하는 기준으로 작성한 것임에 유의하자.
rs.initiate({_id: "MainRepSet", version: 1, members: [
{ _id: 0, host : "mongo-0.mongo-svc.default.svc.cluster.local:27017" },
{ _id: 1, host : "mongo-1.mongo-svc.default.svc.cluster.local:27017" },
{ _id: 2, host : "mongo-2.mongo-svc.default.svc.cluster.local:27017" }
]
});
만약 지정한 내부 도메인이 유효하지 않다면 아래와 같은 에러 메시지가 출력될 수 있는데 이 때는 busybox 를 통해 nslookup 으로 도메인을 체크해보자.
{
"ok" : 0,
"errmsg" : "No host described in new configuration with {version: 1, term: 0} for replica set MainRepSet maps to this node",
"code" : 93,
"codeName" : "InvalidReplicaSetConfig"
}
아래 명령어로 구성된 replica set 을 확인할 수 있을텐데 mongo-0 이 Primary 노드가 되었을 것이고 그 외 두 개의 pod 이 Secondary노드가 되어 있을 것이다.
rs.status()
이제 database 와 user 를 만들어야 한다. 그 전에 RDBMS에 해당하는 MySql 과 같은 와 NoSql 에 해당하는 MongoDB의 용어를 비교하면 아래와 같다.
Mongo | RDBMS |
database | database |
collection | table |
document | row 혹은 record |
field | column |
database 와 user 는 아래와 같이 생성한다.
use admin #생성과 동시에 해당 database로 스위칭 된다.
db.createUser({user:"admin",pwd:passwordPrompt(),roles:[{role:"root",db:"admin"}]})
기본적으로 user 가 없이도 database 에 접속은 가능하다. 인증없이 접근이 가능하다는 말인데 아무런 인증을 받지 않았기 때문에 권한도 없다. 즉, 때문에 뭔가 유의미한 작업을 하려면 적절한 role 의 user 를 생성하고 이를 통해 database 에 접근해야 한다.
database 이 인증을 포함해서 접근하는 방법은 세 가지 방법이 있다.
1. mongo 터미널 접속과 동시에 인증 획득
mongo --port 27017 -u "admin" -p "1234" --authenticationDatabase "admin"
2. 인증 없이 mongo 터미널 접속 후 database 스위칭->인증획득
mongo
use admin
db.auth('admin', passwordPrompt())
3. database 스위칭 없이 특정 database 접근
db.getSiblingDB('admin').auth("admin", passwordPrompt())
마지막으로 DataGrip 로 위에서 구성한 MongoDB에 접속해 보자. 외부 노출 아이피는 아래 명령어의 결과 중 EXTERNAL-IP 를 사용하면 된다.
kubectl get svc mongo-svc-vip -n ingress-nginx
DataGrip 커넥션은 아래와 같이 작성하면 된다.
https://linux.systemv.pe.kr/statefulset-에서-로컬-디스크-사용/
https://kubernetes.io/ko/docs/concepts/storage/persistent-volumes/
https://www.cloudskillsboost.google/focuses/640?locale=ko&parent=catalog
https://toma0912.tistory.com/85
https://support.skdt.co.kr/ko/support/solutions/articles/42000055916-pvc-persistentvolumeclaim-를-생성하고-pod-에-마운트하기
https://hyeo-noo.tistory.com/m/364
http://daplus.net/mongodb-mongodb-복제-및-오류-err-not-master-and-slaveok-false-code-13435/
https://dba.stackexchange.com/questions/211605/node-is-not-in-primary-or-recovering-state
https://maruftuhin.com/blog/mongodb-replica-set-on-kubernetes/
https://blog.1day1.org/599
https://knight76.tistory.com/entry/mongodb-replica-set-만들기
https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=humongousdb&logNo=220090678711
https://emong.tistory.com/237
https://www.shellhacks.com/mongodb-create-user-database-admin-root/
https://kubernetes.github.io/ingress-nginx/user-guide/exposing-tcp-udp-services/