brunch

You can make anything
by writing

C.S.Lewis

by Master Seo Nov 05. 2023

EKS9탄-10. EKS DB-MongoDB-10/18

<1> 몽고DB 소개

<2> Percona Operator for MongoDB

<3> 설치  : Install Percona server for MongoDB on Kubernetes 

<4> 헤드리스 서비스 접속 정보 확인

<5> 몽고DB 기본 사용

<6> Percona Monitoring and Management (PMM)



<1> 몽고DB 소개


1

NoSQL 이해 : Not Only SQL

RDBMS 처럼 고정된 스키마이 존재하지 않음,



2

https://www.mongodb.com/docs/



몽고DB 데이터의 기본 단위는 도큐먼트이며, 이는 관계형 데이터베이스의 행과 유사함 = 행


와이어드타이거 WiredTiger 스토리지 엔진


컬렉션collection  = 동적 스키마 dynamic schema 가 있는 테이블과 같다 = 테이블


몽고DB의 핵심은 정렬된 키와 연결된 값의 집합으로 이뤄진 도큐먼트다. 

표현 방식은 맵 map, 해시 hash, 딕셔너리 dictionary 와 같은 자료 구조를 가짐.



3

몽고 쉘 ??


# 연결없이 셸 실행

mongosh --nodb

exit



# 셸에 전달하여 스크립트 실행

mongosh server-1:30000/foo --quite script1.js



mongosh

--------

help

db.help()     # 데이터베이스 수준의 도움말

db.foo.help() # 컬렉션 수준의 도움말

db.movies.updateOne # 함수의 기능 도움말, 함수명을 괄호 없이 입력

# load 함수로 스크립트 실행

load("script1.js")



4

몽고DB를 사용을 권장하지 않는 경우??


다양한 유형의 데이터를 여러 차원에 걸쳐 조인하는 작업은 관계형 데이터베이스에 적합하다. 

몽고DB는 이러한 작업을 잘 처리하지 못하며 거의 다룰 일이 없다.


몽고DB보다 관계형 데이터베이스를 사용하는 가장 큰 이유는 몽고DB를 지원하지 않는 도구를 사용할 수 있기 때문이다. 워드프레스등 아직 관계형 데이터베이스 생태계를 따라가지 못한다.



5

복제 세트에는 프라이머리Primary, 세컨더리Secondary, 아비터Arbiter 구성원이 있다.

프라이머리 : 클라이언트와 읽기 및 쓰기 작업  - RW

세컨더리 : 프라이머리 구성원의 정보를 동기화  

아비터 : 정보를 저장하지는 않고 복제 세트의 복구를 돕는다. -복제는 일어나지 않는다.




<2> Percona Operator for MongoDB



1

Percona Server for MongoDB (PSMDB) vs MongoDB - 링크

Percona Server for MongoDB 6.0 is based on MongoDB 6.0.


Percona Operator for MongoDB

https://docs.percona.com/percona-operator-for-mongodb/index.html


https://github.com/percona/percona-server-mongodb-operator


https://docs.percona.com/percona-operator-for-mongodb/RN/index.html


2

복제 

Replica set cluster 구성



3

샤딩 - 쓰기를 분산 시킬때.

앞단에 라우터 몽고스를 둔다.

설정을 관리하는 설정 서버가 있다.

Sharded cluster 구성



샤드 Shard : 애플리케이션이 사용할 데이터를 나눠서 저장. 애플리케이션과 직접 연결할 수 없음.            샤드는 라우터를 통해서만 애플리케이션과 정보 교환 가능, 각각의 샤드는 복제 세트를 가질 수 있음  


설정 서버 Config Server : 

애플리케이션 데이터를 제외하고 샤드 클러스터가 작동하기 위해 필요한 모든 정보를 저장. 이런 정보를 메타데이터라고 함.            

각각의 샤드에 어떤 정보가 들어있는지, 샤드 클러스터가 동작한 로그와 같은 정보를 보관.       

이 때문에 라우터가 원하는 정보를 불러오기 위해서는 사전에 설정 서버의 메타데이터를 알고 있어야 함.

하나의 샤드 클러스터는 하나의 설정 서버 복제 세트를 가질 수 있음.  


라우터 Router : 

라우터는 직접적으로 드라이버의 명령을 받아서 어떤 작업을 수행할지 판단하고 결과물을 가져다 드라이버에 넘겨주는 역할을 함.            

설정 서버의 메타데이터를 캐싱하여 알맞은 샤드에 명령을 보내 원하는 정보를 가져옴.  


애플리케이션 서버에서 실행되는 mongos 프로세스가 샤드 클러스터의 라우터 역할을 수행함.            

따라서 샤드 클러스터가 구성된 몽고DB를 애플리케이션에 연결하기 위해서는 mongod 프로세스와 mongos 프로세스가 서로 통신하게 만들어야 함.  


샤드키 : 

샤드 클러스터에서 정보를 어떻게 나눠어 저장하는지에 따라서 성능상의 문제가 발생할 수 있다. 예를 들면 정보가 몰릴 경우.            

샤드에 정보를 나누기 위해서는 기준이 되는 값이 필요. 이 기준이 되는 필드를 샤드키라고 함.       예를 들어 사람들을 조별로 분배하기 위해 이름의 성씨별로 묶어서 나누었다. 이 경우 이름의 성씨가 샤드키가 된다.       

샤드키는 인덱스를 반드시 갖는다. 이 때문에 샤드키는 인덱싱이 되어 있는 필드에 설정하도록 몽고DB가 제한함.       

샤드크의 인덱스가 해시 인덱스인지 아닌지에 따라서 샤드에 정보를 분할하는 방식이 달라짐  




<3> 설치  : Install Percona server for MongoDB on Kubernetes 



1

Percona server for MongoDB가 지원을 많이 한다. = 샤딩을 지원한다.

PSMDB


Percona Operator for MongoDB   지원 되는 기능  = 샤딩 지원함.

MogoDB Commuity Operator = 샤딩 지원 안함.


https://docs.percona.com/percona-operator-for-mongodb/compare.html#maintenance



https://docs.percona.com/percona-operator-for-mongodb/eks.html



https://docs.percona.com/percona-operator-for-mongodb/operator.html#operator-custom-resource-options



2

# CRD 설치

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


kubectl get crd | grep psmdb



# namespace 생성

kubectl create ns psmdb



# 실습 편리를 위해서 네임스페이스 변경

kubectl get pod

kubectl ns psmdb       #  psmdb 로 네임스페이스 변경을 하시고 실습 진행을 하셔야 됩니다!

kubectl get pod



# RBAC 설치

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

kubectl get-all -n psmdb



# 오퍼레이터 설치

curl -s -O https://raw.githubusercontent.com/gasida/DOIK/main/psmdb/operator.yaml

cat operator.yaml | yh


kubectl apply -f operator.yaml


kubectl get deploy,pod

kubectl get-all -n psmdb



# 각자 닉네임 변수 지정 : 클러스터 이름으로 사용됨

MYNICK=<각자 자신의 닉네임>

echo "export MYNICK=<각자 자신의 닉네임>" >> /etc/profile


MYNICK=masterseo

echo "export MYNICK=masterseo" >> /etc/profile



# 계정 정보를 위한 secret 생성

curl -s -O https://raw.githubusercontent.com/gasida/DOIK/main/psmdb/secrets.yaml

cat secrets.yaml

cat secrets.yaml | sed -e "s/my-cluster-name/$MYNICK/" | kubectl apply -f -

cat secrets.yaml


kubectl get secret $MYNICK-secrets

kubectl get secret $MYNICK-secrets -o json | jq .data

kubectl get secret $MYNICK-secrets -o json | jq -r .data.MONGODB_DATABASE_ADMIN_USER | base64 -d ; echo

kubectl get secret $MYNICK-secrets -o json | jq -r .data.MONGODB_DATABASE_ADMIN_PASSWORD| base64 -d ; echo

kubectl get secret $MYNICK-secrets -o json | jq -r .data.MONGODB_CLUSTER_ADMIN_USER | base64 -d ; echo

kubectl get secret $MYNICK-secrets -o json | jq -r .data.MONGODB_CLUSTER_ADMIN_PASSWORD| base64 -d ; echo

kubectl get secret $MYNICK-secrets -o json | jq -r .data.MONGODB_USER_ADMIN_USER | base64 -d ; echo

kubectl get secret $MYNICK-secrets -o json | jq -r .data.MONGODB_USER_ADMIN_PASSWORD| base64 -d ; echo



# 신규 터미널 : 모니터링

watch kubectl get psmdb,sts,pod,svc,ep,pvc




# 클러스터 생성 : 복제 세트(3개 파드) replsets(rs0, size 3)

curl -s -O https://raw.githubusercontent.com/gasida/DOIK/main/psmdb/cr.yaml

cat cr.yaml

grep ^[^#] cr.yaml | yh

curl -s -O https://raw.githubusercontent.com/gasida/DOIK/main/psmdb/cluster1.yaml

cat cluster1.yaml | yh

cat cluster1.yaml | sed -e "s/my-cluster-name/$MYNICK/" | kubectl apply -f - && kubectl get psmdb -w


만들어 지는중  3대까지 만들어짐.


스페이트 풀셋을 사용한다!!!



# 클러스터 생성 정보 확인 : 약자 psmdb

kubectl get perconaservermongodbs

NAME        ENDPOINT                                STATUS   AGE

masterseo   masterseo-rs0.psmdb.svc.cluster.local   ready    4m25s


kubectl get psmdb

NAME        ENDPOINT                                STATUS   AGE

masterseo   masterseo-rs0.psmdb.svc.cluster.local   ready    4m48s




## 클러스터 상세 정보 확인

kubectl get psmdb masterseo -o yaml | kubectl neat | yh



# 클러스타 파드 정보 확인

kubectl get sts,pod -owide

kubectl get svc,ep

kubectl df-pv

kubectl get pvc,pv



# 노드 정보 확인 : affinity.antiAffinityTopologyKey: "kubernetes.io/hostname" 이해하기

# https://docs.percona.com/percona-operator-for-mongodb/constraints.html#affinity-and-anti-affinity


anti-affinity = 같은 영역에 생성되지 않도록 한다.


kubectl describe node | more

kubectl get node --label-columns=kubernetes.io/hostname,topology.kubernetes.io/zone






# mongodb 이미지 버전 확인

kubectl get perconaservermongodbs $MYNICK -o jsonpath={.spec.image} ; echo

percona/percona-server-mongodb:6.0.9-7



# (옵션) 설치 리소스 확인

kubectl get-all --namespace=psmdb


10분내 만들어진 리소스만 보기

kubectl get-all --since 10m



클러스테 레벨의 리소스만 보기

kubectl get-all --only-scope=cluster



## (참고) psmdb 클러스터 삭제 시

cat cluster1.yaml | sed -e "s/my-cluster-name/$MYNICK/" | kubectl delete -f -







<4> 헤드리스 서비스 접속 정보 확인



1

# 헤드리스 서비스 확인 : ClusterIP None

kubectl get svc,ep



# 엔드포인트 슬라이스 정보 확인 

kubectl describe endpointslices

kubectl get endpointslices



# netshoot 이미지로 netdebug 파드에 zsh 실행

kubectl run -it --rm netdebug --image=nicolaka/netshoot --restart=Never -- zsh


--------------------


## 변수 지정

MYNICK=<각자 자신의 닉네임>

MYNICK=masterseo



## 헤드리스 서비스 접속 도메인 확인

nslookup $MYNICK-rs0


Server:         10.100.0.10

Address:        10.100.0.10#53

Name:   masterseo-rs0.psmdb.svc.cluster.local

Address: 192.168.1.102

Name:   masterseo-rs0.psmdb.svc.cluster.local

Address: 192.168.3.51

Name:   masterseo-rs0.psmdb.svc.cluster.local

Address: 192.168.2.228




서비스 라는 타입으로 지정하고 확인

nslookup -type=srv $MYNICK-rs0

nslookup $MYNICK-rs0-0.$MYNICK-rs0

nslookup $MYNICK-rs0-1.$MYNICK-rs0

nslookup $MYNICK-rs0-2.$MYNICK-rs0

exit

--------------------




<5> 몽고DB 기본 사용


https://docs.percona.com/percona-operator-for-mongodb/users.html#system-users


https://linux-operator.tistory.com/12


https://www.mongodb.com/docs/manual/reference/configuration-options/


1


# myclient 데몬셋 배포

curl -s -O https://raw.githubusercontent.com/gasida/DOIK/main/psmdb/myclient.yaml

cat myclient.yaml | yh


VERSION=4.4.24-23 envsubst < myclient.yaml | kubectl apply -f -


kubectl get pod -l name=mongodb -owide



# 몽고 config 확인 : CLUSTER_USER로 접속 후 확인

kubectl get cm $MYNICK-rs0-mongod -o yaml | kubectl neat | yh



# [터미널1] 클러스터 접속(ADMIN_USER)

mogo라는 쉘을 싱행 ,  cluster admin으로 로그인한다.

서비스명으로 접속 한다.


cat secrets.yaml | yh

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

admin   0.000GB

config  0.000GB

local   0.001GB

rs0:PRIMARY> db

admin  // 현재 사용하고 있는 데이터베이스.


rs0:PRIMARY> db.getUsers()

// 사용자 계정들 나옴.




2

# 클러스터  admin으로 사용할 user 만들자.


## 데이터베이스를 사용할 유저 생성

doik , qwe123

rs0:PRIMARY> db.createUser({user: "doik" , pwd: "qwe123" , roles: [ "userAdminAnyDatabase", "dbAdminAnyDatabase","readWriteAnyDatabase"]})



## 복제 정보 확인 시도

rs0:PRIMARY> rs.status()



3

# [터미널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> db

admin


rs0:PRIMARY> rs.status()

rs0:PRIMARY> rs.status()['members']



# 몽고 config 적용 확인

rs0:PRIMARY> db.getProfilingLevel()




4

몽고DB CRUD 1편 : db, collection 으로 구성 → 데이터는 각 collection 에 document 형식(python dictionary) 로 저장, collection 들의 논리적인 집합이 database


# [터미널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



# 콜렉션 확인

rs0:PRIMARY> show collections



# 콜렉션 생성

## (옵션) capped:true 최초 제한된 크기로 생성된 공간에서만 데이터를 저장하는 설정 (고성능, 저장공간차면 기존 공간 재사용, 일정시간만 저장하는 로그에 적합)

rs0:PRIMARY> db.createCollection("test", {capped:true, size:10000})



# 콜렉션 확인

rs0:PRIMARY> show collections

test



# 콜렉션에서 도큐먼트 조회

rs0:PRIMARY> db.test.find()



# 콜렉션에 도큐먼트 추가

rs0:PRIMARY> db.test.insertOne({ hello: 'world' })

{

        "acknowledged" : true,

        "insertedId" : ObjectId("65560b59fafdce7b727051fa")

}



# 콜렉션에서 도큐먼트 조회

rs0:PRIMARY> db.test.find()

{ "_id" : ObjectId("65560b59fafdce7b727051fa"), "hello" : "world" }


rs0:PRIMARY> db.test.find({},{_id:0})

{ "hello" : "world" }



# 현재 데이터베이스의 콜렉션 통계 정보 확인

rs0:PRIMARY> db.test.stats()



# 콜렉션 삭제하기

rs0:PRIMARY> db.test.drop()

true



# 콜렉션 확인

rs0:PRIMARY> show collections



# 현재 데이터베이스의 통계 정보 확인

rs0:PRIMARY> db.stats()




3

Create 생성 Document 입력 : insertOne, insertMany



# 콜렉션 생성

db.createCollection("users")



# Document 입력

db.users.insertOne(

 {

  name: "gasida",

  age: 30,

  status: "pending",

 }

)



# 콜렉션 확인

db.users.find()


# insertMany : 파이썬의 List [] 문법을 사용 >> RDBMS 처럼 컬럼(스키마)를 편하게 입력 가능

db.users.insertMany(

 [

  { subject: "abc1", author: "xyz1", views: 10 },

  { subject: "abc2", author: "xyz2", views: 20 },

  { subject: "abc3", author: "xyz3", views: 30 }

 ]

)



# 콜렉션 확인

db.users.find()

...




4

Search 읽기/검색 Document 읽기(검색) : find(), findOne



# 콜렉션 생성

db.createCollection("employees")



# insertMany

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" }

]

)



# 검색

db.employees.find()



# 검색 예제 : 몽고DB vs RDBMS

db.employees.find()  # SELECT * FROM people

db.employees.find({ }, { user_id: 1, status: 1 })  # SELECT _id, user_id, status FROM people

db.employees.find({ },{ user_id: 1, status: 1, _id: 0 })  # SELECT user_id, status FROM people

db.employees.find({ status: "A" })  # SELECT * FROM people WHERE status = "A"

db.employees.find({ status: "A", age: 20 }) # SELECT * FROM people WHERE status = "A" AND age = 20

db.employees.find({ $or: [ { status: "A" } , { age: 25 } ] }) # SELECT * FROM people WHERE status = "A" OR age = 25



# 비교 문법 : equal , greater than , greater than equal , less than , less than or equal , not equal, nin

$eq     =    Matches values that are equal to a specified value.

$gt     >    Matches values that are greater than a specified value.

$gte    >=   Matches values that are greater than or equal to a specified value.

$in          Matches any of the values specified in an array.

$lt     <    Matches values that are less than a specified value.

$lte    <=   Matches values that are less than or equal to a specified value.

$ne     !=   Matches all values that are not equal to a specified value.

$nin         Matches none of the values specified in an array.



# 비교 예제

db.employees.find({ age: { $gt: 25 } })  # SELECT * FROM people WHERE age > 25

db.employees.find({ age: { $lt: 25 } })  # SELECT * FROM people WHERE age < 25

db.employees.find({ age: { $gt: 25, $lte: 50 } }) # SELECT * FROM people WHERE age > 25 AND age <= 50

db.employees.find( { user_id: /use/ } )

db.employees.find( { user_id: { $regex: /use/ } } )  # SELECT * FROM people WHERE user_id like "%use%"

db.employees.find( { user_id: /^use/ } )

db.employees.find( { user_id: { $regex: /^use/ } } )  # SELECT * FROM people WHERE user_id like "use%"

db.employees.find( { status: "A" } ).sort( { user_id: 1 } )  # SELECT * FROM people WHERE status = "A" ORDER BY user_id ASC 

db.employees.find( { status: "A" } ).sort( { user_id: -1 } )  # SELECT * FROM people WHERE status = "A" ORDER BY user_id DESC

db.employees.find().count()  # SELECT COUNT(*) FROM people

db.employees.count()

db.employees.count( { user_id: { $exists: true } } )

db.employees.find( { user_id: { $exists: true } } ).count()  # SELECT COUNT(user_id) FROM people

db.employees.count( { age: { $gt: 30 } } )

db.employees.find( { age: { $gt: 30 } } ).count()  # SELECT COUNT(*) FROM people WHERE age > 30

db.employees.distinct( "status" )  # SELECT DISTINCT(status) FROM people

db.employees.findOne()

db.employees.find().limit(1)       # SELECT * FROM people LIMIT 1





5


Update 수정 Document 수정: updateOne, updateMany


$set: field 값 설정

$inc: field 값을 증가시키거나, 감소시킴

 예) $inc: { age: 2} - age 값을 본래의 값에서 2 증가



# 다음 Document 데이터 수정하기

age 가 30 보다 큰 Document 의 status 를 B 로 변환하기

db.employees.updateMany( { age: {$gt: 30} }, { $set: {status: "B"} } )



# 실습 결과 확인

db.employees.find({}, {_id:0})




6

Delete 삭제 Document 삭제 - removeOne, removeMany



# 삭제1

db.employees.deleteMany( { status: "A" } )  # SQL로 변환하면, DELETE FROM people WHERE status = "A"



# 실습 결과 확인

db.employees.find({}, {_id:0})



# 삭제2

db.employees.deleteMany({})  # SQL로 변환하면, DELETE FROM people



# 실습 결과 확인

db.employees.find({}, {_id:0})



# 콜렉션 삭제하기

rs0:PRIMARY> db.users.drop()

rs0:PRIMARY> db.employees.drop()




7

로깅https://docs.percona.com/percona-operator-for-mongodb/debug-logs.html




# 오퍼레이터 로그

kubectl logs -l name=percona-server-mongodb-operator -f

# Check logs of the mongod container, parsing the output with jq JSON processor

kubectl logs gasida-rs0-0 -c mongod --since=1m | jq -R 'fromjson?'

kubectl logs gasida-rs0-1 -c mongod --since=1m | jq -R 'fromjson?'

kubectl logs gasida-rs0-2 -c mongod --since=1m | jq -R 'fromjson?'



#

kubectl logs gasida-rs0-0 -c mongod -f | jq -R 'fromjson?'

kubectl logs gasida-rs0-1 -c mongod -f | jq -R 'fromjson?'

kubectl logs gasida-rs0-2 -c mongod -f | jq -R 'fromjson?'



#

kubectl logs -l app.kubernetes.io/component=mongod -c mongod --since=1m | jq -R 'fromjson?'

kubectl logs -l app.kubernetes.io/component=mongod -c mongod --since=10s | jq -R 'fromjson?'

kubectl logs -l app.kubernetes.io/component=mongod -c mongod -f | jq -R 'fromjson?'



Pause/resume Percona Server for MongoDB - Link





<6> Percona Monitoring and Management (PMM)


모니터링!


1

https://docs.percona.com/percona-monitoring-and-management/index.html





위 내용은  주말 CloudNet  스터디 내용 참고하여  정리한 부분입니다.

https://gasidaseo.notion.site/gasidaseo/CloudNet-Blog-c9dfa44a27ff431dafdd2edacc8a1863  


다음

https://brunch.co.kr/@topasvga/3516





브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari