4주차

쿠버네티스 데이터베이스 오퍼레이터 4주차

NoSQL

NoSQL은 전통적인 SQL 데이터베이스 관리 시스템(RDBMS)과는 다른 데이터베이스 모델과 저장 및 검색 방법을 사용하는 데이터베이스 관리 시스템을 의미함.NoSQL 데이터베이스는 대량의 데이터를 저장하고 분산 처리, 빠른 읽기 및 쓰기, 고 가용성 등의 다양한 요구 사항을 충족하는 데 특히 유용하다. Nosql 의 특징은 아래와 같다.

주요 특징:

  1. 스키마 유연성: NoSQL 데이터베이스는 스키마가 정적이거나 엄격하게 정의되지 않으므로 데이터 모델을 변경하거나 확장하기가 쉽습니다. 이는 데이터 모델링 및 스키마 설계를 간소화
  2. 분산 데이터베이스: 대부분의 NoSQL 데이터베이스는 분산 아키텍처를 기반으로 하며, 데이터를 여러 노드 또는 서버에 저장하고 처리합니다. 이를 통해 확장성과 고 가용성을 제공
  3. 고성능: NoSQL 데이터베이스는 대부분의 경우 빠른 읽기 및 쓰기 성능을 제공하며 대규모 데이터 집합을 효율적으로 처리
  4. 다양한 데이터 모델: NoSQL 데이터베이스는 다양한 데이터 모델을 지원합니다. 주요 종류로는 문서, 키-값, 열 지향, 그래프 등이 있으며, 각 데이터 모델은 다른 유형의 데이터 처리에 적합

NoSQL 데이터베이스의 주요 종류:

  1. 문서 지향 데이터베이스: 문서 데이터 모델을 사용하며, JSON 또는 BSON과 같은 형식으로 데이터를 저장. 예시로는 MongoDB
  2. 키-값 데이터베이스: 간단한 키와 해당 값을 저장하는 데이터 모델을 사용. 예시로는 Redis, Amazon DynamoDB
  3. 열 지향 데이터베이스: 데이터를 열 기반으로 저장하며, 대량의 구조화된 데이터를 처리하는 데 적합. 예시로는 Apache Cassandra, HBase
  4. 그래프 데이터베이스: 그래프 데이터 모델을 사용하여 데이터의 관계를 표현하며, 복잡한 관계와 쿼리에 특히 유용. 예시로는 Neo4j

NoSQL 데이터베이스는 다양한 사용 사례와 요구 사항에 따라 선택할 수 있으며, 일괄 처리, 실시간 분석, IoT 데이터 저장, 소셜 미디어 분석, 로그 및 이벤트 처리 등 다양한 분야에서 활용. 그러나 NoSQL 데이터베이스를 선택할 때는 데이터 모델과 요구 사항을 고려하여 적절한 유형의 데이터베이스를 선택해야 한다. 아래는 nosql 중에 많이 사용되는 것을 비교한 표이다.

몽고DB

  • 확장 기능 : 보조 인덱스 secondary index , 범위 쿼리 range query , 정렬 sorting , 집계 aggregation , 공간 정보 인덱스 geospatial index 등
  • 손쉬운 사용 : 도큐먼트 지향 데이터베이스 document-oriented database. 관계형 모델을 사용하지 않은 주된 이유는 분산 확장 scale-out 을 쉽게 하기 위함
    • 행 개념 대신에 보다 유연한 모델인 도큐먼트 document 를 사용.
    • 내장 도규먼트와 배열을 허용함으로써 도큐먼트 지향 모델은 복잡한 계층 관계 hierarchical relationship 를 하나의 레코드로 표현할 수 있다.
    • 도큐먼트의 키와 값을 미리 정의 하지 않음. 따라서 고정된 스키마가 없다. 고정된 스키마가 없으므로 필요할 때마다 쉽게 필드를 추가하거나 제거할 수 있다.
  • 확장 가능한 설계 : 몽고DB는 분산 확장을 염두에 두고 설계됨, 도큐먼트 지향 데이터 모델은 데이터를 여러 서버에 더 쉽게 분산하게 해줌.

  • 다양한 기능 : CRUD 이외도 DBMS 의 대부분의 기능을 제공
    • 인덱싱 : 보조 인덱스를 지원하며 고유 unique, 복합 compound, 공간 정보, 전문 full-text 인덱스 기능도 제공. 중첩된 도큐먼트 nested document 보조 인덱스도 지원
    • 집계 : 집계 파이프라인 aggregation pipeline 은 데이터베이스 최적화를 활용해, 서버 측에서 간단히 데이터를 처리하여 복잡한 분석 엔진 analytics engine 을 구착하게 해줌.
    • 특수한 컬렉션 유형 : 로그와 같은 최신 데이터를 유지하고자 세션이나 고정 크기 컬렉션(제한 컬렉션 capped collection)과 같이 특정 시간에 만료해야 하는 데이터에 대한 유효 시간(TTL) 컬렉션을 지원함. 또한 기준 필터 criteria filter 와 일치하는 도큐먼트에 한정된 부분 인덱스 partial index 를 지원함으로써 효율성을 높이고 필요한 저장 공간을 줄임.
    • 파일 스토리지 : 큰 파일과 파일 메타데이터를 편리하게 저장하는 프로토콜을 지원.
  • 고성능 : 동시성 **concurrency 과 처리량을 극대화하기 위해 와이어드타이거 WiredTiger 스토리지 엔진에 기회적 락** opportunistic locking 을 사용함.
    • 따라서 캐시처럼 제한된 용량의 램으로 퀴리에 알맞은 인덱스를 자동으로 선택할 수 있다 → 요약하면 모든 측면에서 고성능을 유지하기 위해 설계됨.

Percona Operator for MongoDB

  • Percona Server for MongoDB 6.0은 MongoDB 6.0을 기반으로 합니다.
  • Percona Server for MongoDB는 MongoDB Community Edition을 확장하여 MongoDB Enterprise Edition에서만 사용 가능한 기능을 포함하고 있습니다.

복제

복제만 테스트하기 위해 sharding  diasble 하여 테스트, 그 외에 운영이 아닌 실습환경이기에 backup, arbiter diasble

설치

# CRD 설치
kubectl apply --server-side -f https://raw.githubusercontent.com/gasida/DOIK/main/psmdb/crd.yaml

# namespace 생성, 실습 편리를 위해서 네임스페이스 변경
kubectl create ns psmdb
kubectl ns psmdb   

# RBAC 설치
kubectl apply -f https://raw.githubusercontent.com/gasida/DOIK/main/psmdb/rbac.yaml

# 오퍼레이터 설치
curl -s -O https://raw.githubusercontent.com/gasida/DOIK/main/psmdb/operator.yaml
kubectl apply -f operator.yaml

# 닉네임 변수 지정 : 클러스터 이름으로 사용됨
MYNICK=hanship
echo "export MYNICK=hanship" >> /etc/profile

# 계정 정보를 위한 secret 생성
curl -s -O https://raw.githubusercontent.com/gasida/DOIK/main/psmdb/secrets.yaml
cat secrets.yaml | sed -e "s/my-cluster-name/$MYNICK/" | kubectl apply -f -

# 클러스터 생성 : 복제 세트(3개 파드) replsets(rs0, size 3)
curl -s -O https://raw.githubusercontent.com/gasida/DOIK/main/psmdb/cluster1.yaml
cat cluster1.yaml | sed -e "s/my-cluster-name/$MYNICK/" | kubectl apply -f -

# 클러스터 생성 정보 확인 : 약자 psmdb
kubectl get perconaservermongodbs
kubectl get psmdb
NAME      ENDPOINT                              STATUS         AGE
hanship   hanship-rs0.psmdb.svc.cluster.local   initializing   56s

노드에 나눠서 설치 정보확인
affinity:
antiAffinityTopologyKey: "kubernetes.io/hostname"
kubectl get node --label-columns=kubernetes.io/hostname,topology.kubernetes.io/zone

NAME STATUS ROLES AGE VERSION HOSTNAME ZONE
ip-192-168-1-95.ap-northeast-2.compute.internal Ready 84m v1.27.6-eks-a5df82a ip-192-168-1-95.ap-northeast-2.compute.internal ap-northeast-2a
ip-192-168-2-89.ap-northeast-2.compute.internal Ready 84m v1.27.6-eks-a5df82a ip-192-168-2-89.ap-northeast-2.compute.internal ap-northeast-2b
ip-192-168-3-193.ap-northeast-2.compute.internal Ready 84m v1.27.6-eks-a5df82a ip-192-168-3-193.ap-northeast-2.compute.internal ap-northeast-2c

같은 노드에 배포되지 않도록 설정하였기에 서로 다른 노드에 배포

노드에 나눠서 설치 정보확인
affinity:
antiAffinityTopologyKey: "kubernetes.io/hostname"
kubectl get node --label-columns=kubernetes.io/hostname,topology.kubernetes.io/zone

NAME STATUS ROLES AGE VERSION HOSTNAME ZONE
ip-192-168-1-95.ap-northeast-2.compute.internal Ready 84m v1.27.6-eks-a5df82a ip-192-168-1-95.ap-northeast-2.compute.internal ap-northeast-2a
ip-192-168-2-89.ap-northeast-2.compute.internal Ready 84m v1.27.6-eks-a5df82a ip-192-168-2-89.ap-northeast-2.compute.internal ap-northeast-2b
ip-192-168-3-193.ap-northeast-2.compute.internal Ready 84m v1.27.6-eks-a5df82a ip-192-168-3-193.ap-northeast-2.compute.internal ap-northeast-2c

같은 노드에 배포되지 않도록 설정하였기에 서로 다른 노드에 배포

DB접속

# myclient 데몬셋 배포
curl -s -O https://raw.githubusercontent.com/gasida/DOIK/main/psmdb/myclient.yaml
VERSION=4.4.24-23 envsubst < myclient.yaml | kubectl apply -f -

# [터미널1] 클러스터 접속(ADMIN_USER)
kubectl exec ds/myclient -it -- mongo --quiet "mongodb+srv://userAdmin:userAdmin123456@$MYNICK-rs0.psmdb.svc.cluster.local/admin?replicaSet=rs0&ssl=false"
rs0:PRIMARY> show dbs # db list 출력
rs0:PRIMARY> db # 현재 사용 db 출력

## 데이터베이스를 사용할 유저 생성
rs0:PRIMARY> db.createUser({user: "doik" , pwd: "qwe123" , roles: [ "userAdminAnyDatabase", "dbAdminAnyDatabase","readWriteAnyDatabase"]})

## 복제 정보 확인 시도
rs0:PRIMARY> rs.status()

# [터미널2] 클러스터 접속(CLUSTER_USER)
kubectl exec ds/myclient -it -- mongo --quiet "mongodb+srv://clusterAdmin:clusterAdmin123456@$MYNICK-rs0.psmdb.svc.cluster.local/admin?replicaSet=rs0&ssl=false"

CRUD

  • 몽고디비 특징
    • primary key 를 위한 별도 컬럼 만들 필요 없음, mongodb는 collection에서 _id가 각 document마다 자동생성되어 primary key 역햘을 함
    • 컬럼마다 데이터 타입을 정할 필요 없음 ("컬럼명": 컬럼값 이 기본 형태임)
    • collection 구조 변경 : rdb 처럼 alter table 은 기본적으로 collection 에서는 필요 없음
# [터미널3] 클러스터 접속(doik)
kubectl exec ds/myclient -it -- mongo --quiet "mongodb+srv://doik:qwe123@$MYNICK-rs0.psmdb.svc.cluster.local/admin?replicaSet=rs0&ssl=false"
# doik 테이터베이스 선택(없으면 데이터베이스 생성됨) 및 test 콜렉션에 도큐먼트 1개 넣기
rs0:PRIMARY> use doik

# Create 
rs0:PRIMARY> db.createCollection("employees")

# 콜렉션 확인
rs0:PRIMARY> show collections
------------------
employees
system.profile

# Insert
rs0:PRIMARY> db.employees.insertMany(
[
   { user_id: "user01", age: 45, status: "A" },
   { user_id: "user02", age: 35, status: "A" },
   { user_id: "user03", age: 25, status: "B" },
   { user_id: "user04", age: 20, status: "A" },
   { user_id: "abcd01", age: 28, status: "B" }
 ]
)

# Search
rs0:PRIMARY> db.employees.find({ age: { $gt: 25, $lte: 50 } }) *# SELECT * FROM people WHERE age > 25 AND age <= 50*
rs0:PRIMARY> db.employees.find({ status: "A", age: 20 }) *# SELECT * FROM people WHERE status = "A" AND age = 20*

# Update
rs0:PRIMARY> db.employees.updateMany( { age: {$gt: 30} }, { $set: {status: "B"} } )
------------------
{ "acknowledged" : true, "matchedCount" : 2, "modifiedCount" : 2 }

# 콜렉션 삭제하기
rs0:PRIMARY> db.db.employees.drop()

복제테스트

시나리오: primary 에서는 100개의 데이터를 insert 하고 2개의 secondary 에서 count를 수행하여 100개가 일치하는 지 확인

# [터미널1] 프라이머리 파드 접속(doik) : 헤드리스 서비스 주소
kubectl exec ds/myclient -it -- mongo --quiet "mongodb://doik:qwe123@$MYNICK-rs0-0.$MYNICK-rs0.psmdb.svc.cluster.local/admin?replicaSet=rs0&ssl=false"
rs0:PRIMARY> use doik
rs0:PRIMARY> db.createCollection("test")

# [터미널2] 세컨더리 파드1 접속(doik) : 헤드리스 서비스 주소
kubectl exec ds/myclient -it -- bash -il
--------------------------------------
# 변수 지정
MYNICK=hanship
while true; do echo $'rs.secondaryOk()\nuse doik\ndb.test.count()' | mongo --quiet "mongodb://doik:qwe123@$MYNICK-rs0-1.$MYNICK-rs0.psmdb.svc/admin?ssl=false" | grep -v Error; date; sleep 1; done


# [터미널3] 세컨더리 파드2 접속(doik) : 헤드리스 서비스 주소
kubectl exec ds/myclient -it -- bash -il
--------------------------------------
# 변수 지정
MYNICK=hanship
while true; do echo $'rs.secondaryOk()\nuse doik\ndb.test.count()' | mongo --quiet "mongodb://doik:qwe123@$MYNICK-rs0-2.$MYNICK-rs0.psmdb.svc/admin?ssl=false" | grep -v Error; date; sleep 1; done


# [터미널1] 프라이머리 파드 접속(doik) : 대량의 도큐먼트 생성 및 복제 확인
rs0:PRIMARY> for (i=0; i<100; i++) {db.test.insert({count: i, "created_at" : new Date()})}

[터미널2]

[터미널3]

Sun Nov 5 13:50:12 UTC 2023
switched to db doik
100
Sun Nov 5 13:50:17 UTC 2023
switched to db doik
100

🔥장애1 Primary Pod 강제 삭제 시 Read 테스트

현재 primary 는 hanship-rs0-0 이므로 hanship-rs0-0 삭제

# [터미널3] 세컨더리 파드2 접속(doik) : 헤드리스 서비스 주소
kubectl exec ds/myclient -it -- bash -il
--------------------------------------
# 변수 지정
MYNICK=hanship
echo $'rs.secondaryOk()\nuse doik\ndb.test.count()' | mongo --quiet "mongodb://doik:qwe123@$MYNICK-rs0-2.$MYNICK-rs0.psmdb.svc/admin?ssl=false"
while true; do echo $'rs.secondaryOk()\nuse doik\ndb.test.count()' | mongo --quiet "mongodb://doik:qwe123@$MYNICK-rs0-2.$MYNICK-rs0.psmdb.svc/admin?ssl=false" | grep -v Error; date; sleep 1; done
--------------------------------------

# [터미널1] 모니터링
watch -d "kubectl get psmdb;echo; kubectl get pod,pvc -l app.kubernetes.io/component=mongod -owide"

# [터미널2] 클러스터 접속(CLUSTER_USER) : 프라이머리 파드 확인
kubectl exec ds/myclient -it -- mongo --quiet "mongodb+srv://clusterAdmin:clusterAdmin123456@$MYNICK-rs0.psmdb.svc.cluster.local/admin?replicaSet=rs0&ssl=false"
rs0:PRIMARY> rs.status()['members']
--------------------------------------
[
	{
		"_id" : 0,
		"name" : "hanship-rs0-0.hanship-rs0.psmdb.svc.cluster.local:27017",
		"health" : 1,
		"state" : 1,
		"stateStr" : "PRIMARY",
		"uptime" : 5172,
		"optime" : {
			"ts" : Timestamp(1699192435, 1),
			"t" : NumberLong(1)
		},
...(생략)...
# 강제로 rs0-Y 프라이머리 파드 1개 삭제
kubectl delete pod $MYNICK-rs0-0
# [터미널1] 모니터링
NAME                READY   STATUS        RESTARTS   AGE   IP              NODE                                               NOMINATED NODE   READINESS GATES
pod/hanship-rs0-0   1/1     Terminating   0	     92m   192.168.1.212   ip-192-168-1-95.ap-northeast-2.compute.internal    <none>	       <none>
pod/hanship-rs0-1   1/1     Running       0	     91m   192.168.3.135   ip-192-168-3-193.ap-northeast-2.compute.internal   <none>	       <none>
pod/hanship-rs0-2   1/1     Running       0	     90m   192.168.2.223   ip-192-168-2-89.ap-northeast-2.compute.internal    <none>	       <none>

# [터미널2] 클러스터 접속(CLUSTER_USER) : 프라이머리 파드 확인
kubectl exec ds/myclient -it -- mongo --quiet "mongodb+srv://clusterAdmin:clusterAdmin123456@$MYNICK-rs0.psmdb.svc.cluster.local/admin?replicaSet=rs0&ssl=false"
rs0:PRIMARY> rs.status()['members']

{
"_id" : 1,
"name" : "hanship-rs0-1.hanship-rs0.psmdb.svc.cluster.local:27017",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 5495,
"optime" : {
"ts" : Timestamp(1699192822, 2),
"t" : NumberLong(2)
},
hanship-rs0-1 이 primary로 변경되고 아래에서 보듯이 [터미널3]에서 데이터는 정상적으로 count

100
Sun Nov 5 14:01:31 UTC 2023

🔥장애2 Node 2개 Drain 시 Write/Read 테스트

# [터미널2] 클러스터 접속(CLUSTER_USER) : 프라이머리 파드 확인
kubectl exec ds/myclient -it -- mongo --quiet "mongodb+srv://clusterAdmin:clusterAdmin123456@$MYNICK-rs0.psmdb.svc.cluster.local/admin?replicaSet=rs0&ssl=false"
--------------------------------------
rs0:PRIMARY> rs.status()['members']
{
		"_id" : 1,
		"name" : "hanship-rs0-1.hanship-rs0.psmdb.svc.cluster.local:27017",
		"health" : 1,
		"state" : 1,
		"stateStr" : "PRIMARY",

# 프라이머리 파드가 배포 정보 확인
kubectl get pod -l app.kubernetes.io/instance=$MYNICK -owide
--------------------------------------
NAME            READY   STATUS    RESTARTS   AGE     IP              NODE                                               NOMINATED NODE   READINESS GATES
hanship-rs0-0   1/1     Running   0          7m12s   192.168.1.109   ip-192-168-1-95.ap-northeast-2.compute.internal    <none>           <none>
hanship-rs0-1   1/1     Running   0          98m     192.168.3.135   ip-192-168-3-193.ap-northeast-2.compute.internal   <none>           <none>
hanship-rs0-2   1/1     Running   0          98m     192.168.2.223   ip-192-168-2-89.ap-northeast-2.compute.internal    <none>           <none>

hanship-rs0-1 이 primary 이고 node 3번에 배포되어 있음, node 2개를 drain하기 위해 node3(primary), node2(secondary) drain

# node2, node3 drain
kubectl drain ip-192-168-3-193.ap-northeast-2.compute.internal ip-192-168-2-89.ap-northeast-2.compute.internal --delete-emptydir-data --force --ignore-daemonsets
--------------------------------------
error when evicting pods/"hanship-rs0-2" -n "psmdb" (will retry after 5s): Cannot evict pod as it would violate the pod's disruption budget.

# pod, node 확인
--------------------------------------
k get po,node -owide
NAME                                                  READY   STATUS    RESTARTS   AGE     IP              NODE                                               NOMINATED NODE   READINESS GATES
pod/hanship-rs0-0                                     1/1     Running   0          20m     192.168.1.109   ip-192-168-1-95.ap-northeast-2.compute.internal    <none>           <none>
pod/hanship-rs0-1                                     0/1     Pending   0          2m55s   <none>          <none>                                             <none>           <none>
pod/hanship-rs0-2                                     1/1     Running   0          111m    192.168.2.223   ip-192-168-2-89.ap-northeast-2.compute.internal    <none>           <none>

NAME                                                    STATUS                     ROLES    AGE    VERSION               INTERNAL-IP     EXTERNAL-IP      OS-IMAGE         KERNEL-VERSION                  CONTAINER-RUNTIME
node/ip-192-168-1-95.ap-northeast-2.compute.internal    Ready                      <none>   152m   v1.27.6-eks-a5df82a   192.168.1.95    3.39.227.88      Amazon Linux 2   5.10.197-186.748.amzn2.x86_64   containerd://1.6.19
node/ip-192-168-2-89.ap-northeast-2.compute.internal    Ready,SchedulingDisabled   <none>   152m   v1.27.6-eks-a5df82a   192.168.2.89    15.165.208.199   Amazon Linux 2   5.10.197-186.748.amzn2.x86_64   containerd://1.6.19
node/ip-192-168-3-193.ap-northeast-2.compute.internal   Ready,SchedulingDisabled   <none>   151m   v1.27.6-eks-a5df82a   192.168.3.193   13.125.217.147   Amazon Linux 2   5.10.197-186.748.amzn2.x86_64   containerd://1.6.19

# [터미널1] 클러스터 접속(CLUSTER_USER) : 장애 상태 확인
rs0:PRIMARY> rs.status()['members']
--------------------------------------
no-output

# 동작 확인 후 uncordon 설정
kubectl uncordon ip-192-168-3-193.ap-northeast-2.compute.internal ip-192-168-2-89.ap-northeast-2.compute.internal

데이터 Write/Read는 정상적으로 수행되었으나 rs.status()['members'] 에서 primary 조회가 되지 않음

1900
Sun Nov 5 14:26:13 UTC 2023

두 개 노드 drain인 시 아래 에러 발생, 실제로 노드는 drain 상태인것으로 나오나 pod는 Running 상태
error when evicting pods/"hanship-rs0-2" -n "psmdb" (will retry after 5s): Cannot evict pod as it would violate the pod's disruption budget.

노드 복구 후 정상적으로 primary 조회 가능

[
{
"_id" : 0,
"name" : "hanship-rs0-0.hanship-rs0.psmdb.svc.cluster.local:27017",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 1706,
"optime" : {
"ts" : Timestamp(1699194512, 1),
"t" : NumberLong(3)
},

샤드

샤딩 테스트를 위해 sharding  enable 하여 테스트, 그 외에 운영이 아닌 실습환경이기에 backup, arbiter diasble

설치

# 신규 터미널 : 모니터링
watch kubectl get psmdb,sts,pod,svc,ep,pvc
--------------------------------------
NAME                                             ENDPOINT                                                                          STATUS   AGE
perconaservermongodb.psmdb.percona.com/hanship   k8s-psmdb-hanshipm-e1b14c3a07-25b399b1164850b6.elb.ap-northeast-2.amazonaws.com   ready    4m3s

NAME                              READY   AGE
statefulset.apps/hanship-cfg      3/3     4m2s
statefulset.apps/hanship-mongos   3/3     105s
statefulset.apps/hanship-rs0      3/3     4m1s
statefulset.apps/hanship-rs1	  3/3     4m1s

NAME                                                  READY   STATUS    RESTARTS   AGE
pod/hanship-cfg-0                                     1/1     Running   0 	   4m1s
pod/hanship-cfg-1                                     1/1     Running   0          3m35s
pod/hanship-cfg-2                                     1/1     Running   0 	   3m6s
pod/hanship-mongos-0                                  1/1     Running   0 	   105s
pod/hanship-mongos-1                                  1/1     Running   0          80s
pod/hanship-mongos-2                                  1/1     Running   0          54s
pod/hanship-rs0-0                                     1/1     Running   0          4m1s
pod/hanship-rs0-1                                     1/1     Running   0          3m22s
pod/hanship-rs0-2                                     1/1     Running   0          2m39s

# ebs gp3 스토리지 클래스 : 삭제 정책 변경 RECLAIMPOLICY Delete -> Retain 으로 변경하기 위함
kubectl get sc gp3
# The StorageClass "gp3" is invalid: reclaimPolicy: Forbidden: updates to reclaimPolicy are forbidden. 발생하므로 기존 gp3 삭제후 진행
kubectl delete sc gp3
kubectl apply -f https://raw.githubusercontent.com/gasida/DOIK/main/1/gp3-sc-retain.yaml
kubectl get sc gp3

# 클러스터 생성 : 복제 셋 2개(rs-0, rs1), mongos(파드 3개), cfg(파드 3개)
kubectl get secret $MYNICK-secrets
curl -s -O https://raw.githubusercontent.com/gasida/DOIK/main/psmdb/cluster2.yaml
cat cluster2.yaml | sed -e "s/my-cluster-name/$MYNICK/" | kubectl apply -f -
# Error from server (NotFound): services "hanship-mongos" not found 에러 발생 시 일정시간 지난 후 mongos 생성 후 실행
kubectl annotate service $MYNICK-mongos "external-dns.alpha.kubernetes.io/hostname=mongos.$MyDomain"

# 클러스터 생성 정보 확인
kubectl get psmdb
kubectl get psmdb hanship -o yaml | kubectl neat | yh

# 클러스타 파드 정보 확인
kubectl get sts,pod -owide
kubectl get svc,ep
kubectl df-pv
kubectl get pvc,pv

샤딩 정보확인

  • Shard : 데이터베이스의 Replica Set
  • Mongos : 클라이언트 애플리케이션의 쿼리를 처리하는 라우터
  • Config Servers : Replica Set 의 메타데이터와 샤드 클러스터의 정보를 저장
  • 샤드 접근 : mongos Pods - query routers, which acts as an entry point for client applications
# mongos 라우터 접속 서비스 정보 확인
kubectl get svc,ep $MYNICK-mongos
--------------------------------------
NAME                     TYPE           CLUSTER-IP       EXTERNAL-IP                                                                       PORT(S)           AGE
service/hanship-mongos   LoadBalancer   10.100.154.208   k8s-psmdb-hanshipm-e1b14c3a07-25b399b1164850b6.elb.ap-northeast-2.amazonaws.com   27017:32317/TCP   4m19s

NAME                       ENDPOINTS                                                    AGE
endpoints/hanship-mongos   192.168.1.99:27017,192.168.2.194:27017,192.168.3.141:27017   4m19s

# [터미널1] 클러스터 접속(ADMIN_USER)
kubectl exec ds/myclient -it -- mongo --quiet "mongodb://userAdmin:userAdmin123456@$MYNICK-mongos.psmdb.svc.cluster.local/admin?ssl=false"
mongos> db
mongos> show dbs
# 데이터베이스를 사용할 유저 생성
mongos> db.createUser({user: "doik" , pwd: "qwe123" , roles: [ "userAdminAnyDatabase", "dbAdminAnyDatabase","readWriteAnyDatabase"]})

# [터미널2] 클러스터 접속(CLUSTER_USER)
kubectl exec ds/myclient -it -- mongo --quiet "mongodb://clusterAdmin:clusterAdmin123456@$MYNICK-mongos.psmdb.svc.cluster.local/admin?ssl=false"
mongos> use config
# 샤드 목록 정보 확인
mongos> db.shards.find().pretty()
--------------------------------------
{
	"_id" : "rs0",
	"host" : "rs0/hanship-rs0-0.hanship-rs0.psmdb.svc.cluster.local:27017,hanship-rs0-1.hanship-rs0.psmdb.svc.cluster.local:27017,hanship-rs0-2.hanship-rs0.psmdb.svc.cluster.local:27017",
	"state" : 1,
	"topologyTime" : Timestamp(1699195400, 4)
}
{
	"_id" : "rs1",
	"host" : "rs1/hanship-rs1-0.hanship-rs1.psmdb.svc.cluster.local:27017,hanship-rs1-1.hanship-rs1.psmdb.svc.cluster.local:27017,hanship-rs1-2.hanship-rs1.psmdb.svc.cluster.local:27017",
	"state" : 1,
	"topologyTime" : Timestamp(1699195406, 5)
}

# (옵션) 설정 서버에 저장된 메타데이터 확인
mongos> show collections
mongos> show changelog  # 메타메이터가 변경된 내용을 기록한 목록
mongos> show chunks     # 샤딩된 컬렉션의 청크 정보, 어떤 샤드에 어떤 범위로 있는지 확인 가능
mongos> show collections   # 샤드 클러스터 컬렉션 목록
mongos> show lockpings  # 샤드 클러스터의 구성원이 서로의 연결상태를 확인한 일시가 있는 목록
mongos> show locks      # 컬렉션 잠금에 대한 목록. 서로 다른 mongos 가 보낸 명령 충돌을 방지한다
mongos> show mongos     # 실행중인 라우터 mongos 목록
mongos> show shards     # 샤드 클러스터에 등록된 샤드 목록
mongos> show version    # 샤드 클러스터 메타데이터 전체에 대한 버전 정보, 동기화를 위한 필요
mongos> db.changelog.find().pretty()  # 메타메이터가 변경된 내용을 기록한 목록
mongos> db.chunks.find().pretty()     # 샤딩된 컬렉션의 청크 정보, 어떤 샤드에 어떤 범위로 있는지 확인 가능

# 샤드 클러스터 상태 확인 : 기본 정보, 샤드 정보, 밸런서 정보, 샤딩 설정이 된 컬렉션 정보, 청크 정보 등 출력
mongos> sh.help()
mongos> sh.status({"verbose":1})
--- Sharding Status ---
  sharding version: {
  	"_id" : 1,
  	"minCompatibleVersion" : 5,
  	"currentVersion" : 6,
  	"clusterId" : ObjectId("6547b5c390c26679ba5549b7")
  }
  shards:
        {  "_id" : "rs0",  "host" : "rs0/hanship-rs0-0.hanship-rs0.psmdb.svc.cluster.local:27017,hanship-rs0-1.hanship-rs0.psmdb.svc.cluster.local:27017,hanship-rs0-2.hanship-rs0.psmdb.svc.cluster.local:27017",  "state" : 1,  "topologyTime" : Timestamp(1699198663, 4) }
        {  "_id" : "rs1",  "host" : "rs1/hanship-rs1-0.hanship-rs1.psmdb.svc.cluster.local:27017,hanship-rs1-1.hanship-rs1.psmdb.svc.cluster.local:27017,hanship-rs1-2.hanship-rs1.psmdb.svc.cluster.local:27017",  "state" : 1,  "topologyTime" : Timestamp(1699198658, 4) }
  active mongoses:
        {  "_id" : "hanship-mongos-0:27017",  "advisoryHostFQDNs" : [ ],  "created" : ISODate("2023-11-05T15:34:01.726Z"),  "mongoVersion" : "6.0.9-7",  "ping" : ISODate("2023-11-05T15:39:12.168Z"),  "up" : NumberLong(310),  "waiting" : true }
        {  "_id" : "hanship-mongos-1:27017",  "advisoryHostFQDNs" : [ ],  "created" : ISODate("2023-11-05T15:34:28.012Z"),  "mongoVersion" : "6.0.9-7",  "ping" : ISODate("2023-11-05T15:39:08.394Z"),  "up" : NumberLong(280),  "waiting" : true }
        {  "_id" : "hanship-mongos-2:27017",  "advisoryHostFQDNs" : [ ],  "created" : ISODate("2023-11-05T15:34:54.045Z"),  "mongoVersion" : "6.0.9-7",  "ping" : ISODate("2023-11-05T15:39:04.660Z"),  "up" : NumberLong(250),  "waiting" : true }
  autosplit:
        Currently enabled: yes
  balancer:
        Currently enabled:  yes
        Currently running:  no
        Failed balancer rounds in last 5 attempts:  0
        Migration Results for the last 24 hours:
                No recent migrations
  databases:
        {  "_id" : "config",  "primary" : "config",  "partitioned" : true }
                config.system.sessions
                        shard key: { "_id" : 1 }
                        unique: false
                        balancing: true
                        chunks:

샤딩 테스트

[터미널1]

# [터미널1] 클러스터 접속(doik)
kubectl exec ds/myclient -it -- mongo --quiet "mongodb://doik:qwe123@$MYNICK-mongos.psmdb.svc.cluster.local/admin?ssl=false"
## doik 테이터베이스 선택(없으면 데이터베이스 생성됨)
mongos> use doik
## 도큐먼트 추가
mongos> db.test.insertOne({ hello: 'world' })
## 콜렉션에서 도큐먼트 조회
mongos> db.test.find()
mongos> db.test.find({},{_id:0})
{ "hello" : "world" }

[터미널2]

# [터미널2] 클러스터 접속(CLUSTER_USER)
kubectl exec ds/myclient -it -- mongo --quiet "mongodb://clusterAdmin:clusterAdmin123456@$MYNICK-mongos.psmdb.svc.cluster.local/admin?ssl=false"
mongos> use config
## 샤드 클러스터 상태 확인 : 기본 정보, 샤드 정보, 밸런서 정보, 샤딩 설정이 된 컬렉션 정보, 청크 정보 등 출력
mongos> sh.status({"verbose":1})  # 모든 정보 출력

## doik 데이터베이스에서 샤딩을 활성화
mongos> sh.enableSharding("doik")

--------------------------------------
## chunks 사이즈가 64MB(기본값)을 테스트를 위해서 1M 줄이기 - [링크](https://www.mongodb.com/docs/manual/tutorial/modify-chunk-size-in-sharded-cluster/)
## 기본 청크사이즈가 64MB 여서 10만 도큐먼트(레코드)는 분할이 되지 않았습니다.
## 그래서 테스트를 위해 청크사이즈를 1MB로 변경하고 테스트 하시면 분할 확인이 가능합니다.
--------------------------------------

mongos> db.settings.save({_id: "chunksize", value: 1})
--------------------------------------
WriteResult({ "nMatched" : 0, "nUpserted" : 1, "nModified" : 0, "_id" : "chunksize" })
## chunks 사이즈 설정 정보 확인 
mongos> db.settings.find()
--------------------------------------
{ "_id" : "ReadWriteConcernDefaults", "defaultReadConcern" : { "level" : "majority" }, "defaultWriteConcern" : { "w" : "majority", "wtimeout" : 0 }, "updateOpTime" : Timestamp(1699196289, 1), "updateWallClockTime" : ISODate("2023-11-05T14:58:10.232Z") }
{ "_id" : "chunksize", "value" : 1 }

테스트

# [터미널1] 클러스터 접속(doik)
# 샤딩 활성화를 위해서 샤딩하려는 키에 해시 인덱스를 생성
mongos> db.test.createIndex({"username" : "hashed"})

# [터미널2] 클러스터 접속(CLUSTER_USER)
# 이제 "username" 으로 컬렉션을 샤딩할 수 있다
mongos> sh.shardCollection("doik.test", {"username" : "hashed"})
# 몇 분 기다렸다가 다시 샤드 클러스터 상태 확인 : 휠씬 많은 정보가 표시됨
mongos> sh.status()
--- Sharding Status ---
  sharding version: {
  	"_id" : 1,
  	"minCompatibleVersion" : 5,
  	"currentVersion" : 6,
  	"clusterId" : ObjectId("6547a8c49e02d8e3d56244b5")
  }
  shards:
        {  "_id" : "rs0",  "host" : "rs0/hanship-rs0-0.hanship-rs0.psmdb.svc.cluster.local:27017,hanship-rs0-1.hanship-rs0.psmdb.svc.cluster.local:27017,hanship-rs0-2.hanship-rs0.psmdb.svc.cluster.local:27017",  "state" : 1,  "topologyTime" : Timestamp(1699195400, 4) }
        {  "_id" : "rs1",  "host" : "rs1/hanship-rs1-0.hanship-rs1.psmdb.svc.cluster.local:27017,hanship-rs1-1.hanship-rs1.psmdb.svc.cluster.local:27017,hanship-rs1-2.hanship-rs1.psmdb.svc.cluster.local:27017",  "state" : 1,  "topologyTime" : Timestamp(1699195406, 5) }
  active mongoses:
        {  "_id" : "hanship-mongos-0:27017",  "advisoryHostFQDNs" : [ ],  "created" : ISODate("2023-11-05T14:39:23.405Z"),  "mongoVersion" : "6.0.9-7",  "ping" : ISODate("2023-11-05T14:54:35.024Z"),  "up" : NumberLong(911),  "waiting" : true }
        {  "_id" : "hanship-mongos-1:27017",  "advisoryHostFQDNs" : [ ],  "created" : ISODate("2023-11-05T14:39:48.836Z"),  "mongoVersion" : "6.0.9-7",  "ping" : ISODate("2023-11-05T14:54:30.485Z"),  "up" : NumberLong(881),  "waiting" : true }
        {  "_id" : "hanship-mongos-2:27017",  "advisoryHostFQDNs" : [ ],  "created" : ISODate("2023-11-05T14:40:16.349Z"),  "mongoVersion" : "6.0.9-7",  "ping" : ISODate("2023-11-05T14:54:27.865Z"),  "up" : NumberLong(851),  "waiting" : true }
  autosplit:
        Currently enabled: yes
  balancer:
        Currently enabled:  yes
        Currently running:  no
        Failed balancer rounds in last 5 attempts:  0
        Migration Results for the last 24 hours:
                No recent migrations
  databases:
        {  "_id" : "config",  "primary" : "config",  "partitioned" : true }
                config.system.sessions
                        shard key: { "_id" : 1 }
                        unique: false
                        balancing: true
                        chunks:
        {  "_id" : "doik",  "primary" : "rs1",  "partitioned" : false,  "version" : {  "uuid" : UUID("81c16b8b-e731-4de1-b966-69f7ad9aa730"),  "timestamp" : Timestamp(1699196044, 1),  "lastMod" : 1 } }

# [터미널1] 클러스터 접속(doik)
# 대량의 도큐먼트 생성: 20 분정도 시간 소요
mongos> for (i=10; i<100000; i++) {db.test.insert({"username" : "user"+i, "created_at" : new Date()})}

# [터미널3] 클러스터 접속(doik)
kubectl exec ds/myclient -it -- mongo --quiet "mongodb://doik:qwe123@MYNICK-mongos.psmdb.svc.cluster.local/admin?ssl=false" mongos> use doik mongos> db.test.count() 10001 **# 데이터가 여러 샤드에 분산됐으므로 몇 가지 쿼리를 시도해서 확인 : 쿼리 정상 작동 확인** mongos> db.test.find({username: "user1234"}) { "_id" : ObjectId("6547af867bdfebc3913bcacd"), "username" : "user1234", "created_at" : ISODate("2023-11-05T15:06:46.885Z") } **# 쿼리 내부 수행 작업 확인** mongos> db.test.find({username: "user1234"}).explain() { "queryPlanner" : { "mongosPlannerVersion" : 1, "winningPlan" : { "stage" : "SINGLE_SHARD", "shards" : [ { "shardName" : "rs1", "connectionString" : "rs1/hanship-rs1-0.hanship-rs1.psmdb.svc.cluster.local:27017,hanship-rs1-1.hanship-rs1.psmdb.svc.cluster.local:27017,hanship-rs1-2.hanship-rs1.psmdb.svc.cluster.local:27017", "serverInfo" : { "host" : "hanship-rs1-0", "port" : 27017, "version" : "6.0.9-7", "gitVersion" : "81b02fc96fb1fe0fc550b98f870e1ca01c574dd4" }, "namespace" : "doik.test", "indexFilterSet" : false, "parsedQuery" : { "username" : { "eq" : "user1234"
}
},
"queryHash" : "7D9BB680",
"planCacheKey" : "24069050",
"maxIndexedOrSolutionsReached" : false,
"maxIndexedAndSolutionsReached" : false,
"maxScansToExplodeReached" : false,
"winningPlan" : {
"stage" : "FETCH",
"filter" : {
"username" : {
"eq" : "user1234" } }, "inputStage" : { "stage" : "IXSCAN", "keyPattern" : { "username" : "hashed" }, "indexName" : "username_hashed", "isMultiKey" : false, "isUnique" : false, "isSparse" : false, "isPartial" : false, "indexVersion" : 2, "direction" : "forward", "indexBounds" : { "username" : [ "[8720327145141812260, 8720327145141812260]" ] } } }, "rejectedPlans" : [ ] } ] } }, "serverInfo" : { "host" : "hanship-mongos-1", "port" : 27017, "version" : "6.0.9-7", "gitVersion" : "81b02fc96fb1fe0fc550b98f870e1ca01c574dd4" }, "serverParameters" : { "internalQueryFacetBufferSizeBytes" : 104857600, "internalQueryFacetMaxOutputDocSizeBytes" : 104857600, "internalLookupStageIntermediateDocumentMaxSizeBytes" : 104857600, "internalDocumentSourceGroupMaxMemoryBytes" : 104857600, "internalQueryMaxBlockingSortMemoryUsageBytes" : 104857600, "internalQueryProhibitBlockingMergeOnMongoS" : 0, "internalQueryMaxAddToSetBytes" : 104857600, "internalDocumentSourceSetWindowFieldsMaxMemoryBytes" : 104857600 }, "command" : { "find" : "test", "filter" : { "username" : "user1234" }, "lsid" : { "id" : UUID("0aea0757-af0d-4fff-a1d7-812343e4e795") }, "clusterTime" : {
"clusterTime" : Timestamp(1699196973, 1),
"signature" : {
"hash" : BinData(0,"MJ7hgEub79SaLTtZscHGrWl/ZPg="),
"keyId" : NumberLong("7297987280944234512")
}
},
"db" : "doik" }, "ok" : 1, "clusterTime" : {
"clusterTime" : Timestamp(1699196985, 1),
"signature" : {
"hash" : BinData(0,"q3OTt9tN+L456QFenrsQd0xU4gk="),
"keyId" : NumberLong("7297987280944234512")
}
},
"operationTime" : Timestamp(1699196978, 1)
}

# [터미널2] 클러스터 접속(CLUSTER_USER)
# 클러스터 내 모든 샤드 정보 출력
--------------------------------------
mongos> db.shards.find()
{ "_id" : "rs0", "host" : "rs0/hanship-rs0-0.hanship-rs0.psmdb.svc.cluster.local:27017,hanship-rs0-1.hanship-rs0.psmdb.svc.cluster.local:27017,hanship-rs0-2.hanship-rs0.psmdb.svc.cluster.local:27017", "state" : 1, "topologyTime" : Timestamp(1699195400, 4) }
{ "_id" : "rs1", "host" : "rs1/hanship-rs1-0.hanship-rs1.psmdb.svc.cluster.local:27017,hanship-rs1-1.hanship-rs1.psmdb.svc.cluster.local:27017,hanship-rs1-2.hanship-rs1.psmdb.svc.cluster.local:27017", "state" : 1, "topologyTime" : Timestamp(1699195406, 5) }

## 클러스터가 알고 있는 모든 샤딩 데이터베이스 출력 : enableSharding 실행된 데이터베이스이며, partitioned 가 false
mongos> db.databases.find()
{ "_id" : "doik", "primary" : "rs1", "partitioned" : false, "version" : { "uuid" : UUID("81c16b8b-e731-4de1-b966-69f7ad9aa730"), "timestamp" : Timestamp(1699196044, 1), "lastMod" : 1 } }

## 샤딩된 컬렉션 출력
mongos> db.collections.find().pretty()
{
	"_id" : "doik.test",
	"lastmodEpoch" : ObjectId("6547adccdfcbc5c94a6e5a5e"),
	"lastmod" : ISODate("2023-11-05T14:59:24.498Z"),
	"timestamp" : Timestamp(1699196364, 7),
	"uuid" : UUID("c8b85bc2-af5a-49b3-bd58-8e81d30eb948"),
	"key" : {
		"username" : "hashed"
	},
	"unique" : false,
	"chunksAlreadySplitForDowngrade" : false,
	"noBalance" : false
}

## 모든 컬렉션 내의 청크 기록
mongos> db.chunks.find().skip(1).limit(1).pretty()
mongos> db.chunks.find().skip(1).limit(10).pretty()

## 분할과 마이그레이션 기록
mongos> db.changelog.find().pretty()

## 샤딩 안됨
mongos> use doik
mongos> db.test.getShardDistribution()
---
Collection doik.test is not sharded.

sharding으로 설정하고 배포를 했는데 샤딩 정보를 얻을 수 없었음…

+ Recent posts