1
NoSQL 이해 : Not Only SQL
RDBMS 처럼 고정된 스키마이 존재하지 않음,
2
몽고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
세컨더리 : 프라이머리 구성원의 정보를 동기화
아비터 : 정보를 저장하지는 않고 복제 세트의 복구를 돕는다. -복제는 일어나지 않는다.
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가 제한함.
샤드크의 인덱스가 해시 인덱스인지 아닌지에 따라서 샤드에 정보를 분할하는 방식이 달라짐
1
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 -
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
--------------------
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
모니터링!
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