You are currently viewing GL-Docker & Kubernetes week3

GL-Docker & Kubernetes week3

1. Kubernetes Availability & Network

  • ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค ํด๋Ÿฌ์Šคํ„ฐ์˜ ์•ˆ์ •์ ์ธ ์šด์˜์„ ์œ„ํ•ด ํ•„์š”ํ•œ ๊ฐ€์šฉ์„ฑ ํ™•๋ณด ๋ฐฉ๋ฒ•
    • HPA์™€ VPA๋ฅผ ํ†ตํ•ด ๋ถ€ํ•˜์— ๋”ฐ๋ฅธ ์›Œํฌ๋กœ๋“œ์˜ ์ˆ˜์ง ๋ฐ ์ˆ˜ํ‰ ์ž๋™ ํ™•์žฅ
  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„ค์ •๊ณผ ๋ณด์•ˆ ์ •๋ณด๋ฅผ ํ™˜๊ฒฝ์— ๋งž๊ฒŒ ๋ถ„๋ฆฌ ๊ด€๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•
    • ConfigMap๊ณผ Secret์„ ํ™œ์šฉํ•ด ์„ค์ •๊ฐ’๊ณผ ๋ฏผ๊ฐ ์ •๋ณด๋ฅผ ๋ถ„๋ฆฌํ•˜๊ณ  ์•ˆ์ „ํ•˜๊ฒŒ ๊ด€๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ฐฐ์›๋‹ˆ๋‹ค
  • ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค ์„œ๋น„์Šค ํƒ€์ž…์„ ํ†ตํ•œ ๋‚ด๋ถ€ ๋ฐ ์™ธ๋ถ€ ํŠธ๋ž˜ํ”ฝ ์—ฐ๊ฒฐ ๊ตฌ์กฐ ์ดํ•ด
    • ClusterIP, NodePort, Ingress ๋“ฑ ๋‹ค์–‘ํ•œ ์„œ๋น„์Šค ์œ ํ˜•์„ ํ†ตํ•ด ํด๋Ÿฌ์Šคํ„ฐ ๋‚ด๋ถ€ ๋ฐ ์™ธ๋ถ€์™€ ํ†ต์‹ ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ตํž™๋‹ˆ๋‹ค
  • ๋„ค์ดํ‹ฐ๋ธŒ ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค ํ™˜๊ฒฝ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ์Šคํ† ๋ฆฌ์ง€์˜ ๊ฐœ๋…๊ณผ ํŠน์ง• ๊ฐ„๋‹จ ์ •๋ฆฌ

์‹ค์Šต ํ™˜๊ฒฝ ๊ตฌ์„ฑ (Kind, Controlplane 1 + Worker 1)

  • Windows WSL2 ๊ธฐ์ค€, kind ๋ฐ ํ™˜๊ฒฝ๊ตฌ์„ฑ
# Windows WSL2 (Ubuntu) - PowerShell / CMD (๊ด€๋ฆฌ์ž ๊ถŒํ•œ)
dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart
dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart
wsl --list -o
wsl --install -d ubuntu

# WSL2 Ubuntu
sudo snap install docker
sudo groupadd docker
sudo usermod -aG docker $USER
docker --version

# Kind ์„ค์น˜
[ $(uname -m) = x86_64 ] && curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.27.0/kind-linux-amd64
sudo chmod +x ./kind
sudo mv ./kind /usr/local/bin/kind

# Kind Cluster ์ƒ์„ฑ
kind create cluster

# kubectl ์„ค์น˜
sudo snap install kubectl --classic
kubectl get pods -A

# Krew ์„ค์น˜
wget https://github.com/kubernetes-sigs/krew/releases/download/v0.4.5/krew-linux_amd64.tar.gz
tar zxvf krew-linux_amd64
./krew-linux_amd64 install krew
~/.bashrc >> export PATH="${KREW_ROOT:-$HOME/.krew}/bin:$PATH"
source ~/.bashrc
kubectl krew

# k9s ์„ค์น˜
wget https://github.com/derailed/k9s/releases/download/v0.50.4/k9s_linux_amd64.deb
sudo dpkg -i k9s_linux_amd64.deb
sudo apt-get install -f
k9s

# Helm ์„ค์น˜
sudo snap install helm --classic
helm ls
  • ๋ฉ€ํ‹ฐ๋…ธ๋“œ ํด๋Ÿฌ์Šคํ„ฐ with kube-ops-view & Mapping ports
# '์ปจํŠธ๋กคํ”Œ๋ ˆ์ธ, ์›Œ์ปค ๋…ธ๋“œ 1๋Œ€' ํด๋Ÿฌ์Šคํ„ฐ ๋ฐฐํฌ : ํŒŒ๋“œ์— ์ ‘์†ํ•˜๊ธฐ ์œ„ํ•œ ํฌํŠธ ๋งตํ•‘ ์„ค์ •
cat <<EOT> kind-2node.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
  extraPortMappings:
  - containerPort: 30000
    hostPort: 30000
    listenAddress: "0.0.0.0" # Optional, defaults to "0.0.0.0"
    protocol: tcp # Optional, defaults to tcp
  - containerPort: 30001
    hostPort: 30001
EOT

kind create cluster --config kind-2node.yaml

# ๋ฐฐํฌ ํ™•์ธ
kind get clusters
kind get nodes

# ๋…ธ๋“œ ํ™•์ธ
kubectl get nodes -o wide

# ๋…ธ๋“œ์— Taints ์ •๋ณด ํ™•์ธ
kubectl describe node kind-control-plane | grep Taints
Taints:             node-role.kubernetes.io/control-plane:NoSchedule

kubectl describe node kind-worker | grep Taints
Taints:             <none>

# ์ปจํ…Œ์ด๋„ˆ ํ™•์ธ : ์ปจํ…Œ์ด๋„ˆ ๊ฐฏ์ˆ˜, ์ปจํ…Œ์ด๋„ˆ ์ด๋ฆ„ ํ™•์ธ
# kind yaml ์— ํฌํŠธ ๋งตํ•‘ ์ •๋ณด ์ฒ˜๋Ÿผ, ์ž์‹ ์˜ PC ํ˜ธ์ŠคํŠธ์— 30000 ํฌํŠธ ์ ‘์† ์‹œ, ์›Œ์ปค๋…ธ๋“œ(์‹ค์ œ๋กœ๋Š” ์ปจํ…Œ์ด๋„ˆ)์— TCP 30000 ํฌํŠธ๋กœ ์—ฐ๊ฒฐ
# ์ฆ‰, ์›Œ์ปค๋…ธ๋“œ์— NodePort TCP 31000 ์„ค์ • ์‹œ ์ž์‹ ์˜ PC ํ˜ธ์ŠคํŠธ์—์„œ ์ ‘์† ๊ฐ€๋Šฅ!
docker ps
docker port kind-worker
30000/tcp -> 0.0.0.0:30000
30001/tcp -> 0.0.0.0:30001

๋ฐฐํฌ ํ™•์ธ

  • controlplane, worker ๊ฐ 1๋Œ€์˜ ๋…ธ๋“œ ํ™•์ธ
  • worker์˜ ๊ฐ 30000/30001 ํฌํŠธ๋Š” ํ˜ธ์ŠคํŠธ์™€ ๋งคํ•‘๋˜์–ด์žˆ์Œ

2. ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค ๊ฐ€์šฉ์„ฑ (Availability)

AutoScaler – HPA
  • ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค ํ™˜๊ฒฝ์—์„œ๋Š” ๊ฐ€์šฉ์„ฑ (Availability) ์„ ํ™•๋ณดํ•˜๊ธฐ ์œ„ํ•œ ๋‹ค์–‘ํ•œ ๋ฐฉ๋ฒ•์ด ์กด์žฌ
    • HPA (Horizontal Pod Autoscaling) : Pod ์ˆ˜ํ‰ ํ™•์žฅ (์Šค์ผ€์ผ In – Out)
      • Pod ๊ฐœ์ˆ˜ ์ฆ๊ฐ์„ ํ†ตํ•œ ๋ถ€ํ•˜ ๋ถ„์‚ฐ
    • VPA (Vertical Pod Autoscaling) : Pod ์ˆ˜์ง ํ™•์žฅ (์Šค์ผ€์ผ Up)
      • Pod ๋ฆฌ์†Œ์Šค(CPU, Mem ๋“ฑ) ์ฆ๊ฐ์„ ํ†ตํ•œ ์„ฑ๋Šฅ ์กฐ์ •
    • CA (Cluster Autoscaling) : Node ํ™•์žฅ (Cloud ํ™˜๊ฒฝ)
      • Worker ๋…ธ๋“œ ๊ฐœ์ˆ˜ ์ฆ๊ฐ์„ ํ†ตํ•œ ๋ถ€ํ•˜ ๋ถ„์‚ฐ
  • Metrics Server ์„ค์น˜
    • k8s ํด๋Ÿฌ์Šคํ„ฐ ๋‚ด Pod/Node ๋ฆฌ์†Œ์Šค ์‚ฌ์šฉ๋Ÿ‰(CPU, ๋ฉ”๋ชจ๋ฆฌ ๋“ฑ)์„ ์ˆ˜์ง‘ํ•˜๋Š” ๊ฒฝ๋Ÿ‰ํ™”๋œ ๋ชจ๋“ˆ
    • ๊ฐ ๋…ธ๋“œ์˜ Kubelet์—์„œ ์ง€ํ‘œ๋ฅผ ์ฃผ๊ธฐ์ ์œผ๋กœ ์ˆ˜์ง‘ํ•ด API Server์— ์ œ๊ณต
    • HPA๋‚˜ VPA ๊ฐ™์€ ์ž๋™ ํ™•์žฅ ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ๋ฆฌ์†Œ์Šค ์‚ฌ์šฉ๋Ÿ‰์„ ๊ธฐ์ค€์œผ๋กœ ๋™์ž‘ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘
    • ์‹ค์‹œ๊ฐ„ ๋ชจ๋‹ˆํ„ฐ๋ง ๋„๊ตฌ๊ฐ€ ์•„๋‹Œ, ์ž๋™ํ™”๋œ ๋ฆฌ์†Œ์Šค ์กฐ์ •์„ ์œ„ํ•œ ์šฉ๋„๋กœ ์‚ฌ์šฉ
# Metrics Server ์„ค์น˜
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

# Metrics Server SSL ๋ฌด์‹œ
kubectl patch deployment metrics-server -n kube-system --type=json \
  -p='[{"op": "add", "path": "/spec/template/spec/containers/0/args/-", "value": "--kubelet-insecure-tls"}]'

# Metrics Server ๋ฐฐํฌ ํ™•์ธ
kubectl get pods -n kube-system -l k8s-app=metrics-server

# ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค ๋ฆฌ์†Œ์Šค ์ž์› ์‚ฌ์šฉ๋Ÿ‰ ํ™•์ธ
kubectl top node
kubectl top pods -A

# CPU, Memory ๋‚ด๋ฆผ์ฐจ์ˆœ
kubectl top pods -A --sort-by=cpu
kubectl top pods -A --sort-by=memory

2.1. HPA (Horizontal Pod Autoscaling)

How does a HorizontalPodAutoscaler work?
  • ์ˆ˜ํ‰ ์Šค์ผ€์ผ๋ง(HPA, Horizontal Pod Autoscaling)
    • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋ถ€ํ•˜์— ๋”ฐ๋ผ ํŒŒ๋“œ์˜ ๊ฐœ์ˆ˜๋ฅผ ์ž๋™์œผ๋กœ ์กฐ์ ˆ
  • CPU ์‚ฌ์šฉ๋ฅ , ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰, ๋˜๋Š” ์‚ฌ์šฉ์ž ์ •์˜ ๋ฉ”ํŠธ๋ฆญ์„ ๊ธฐ์ค€์œผ๋กœ ๋™์ž‘
  • Metrics Server๋กœ๋ถ€ํ„ฐ ์ˆ˜์ง‘ํ•œ ์ง€ํ‘œ๊ฐ€ ์„ค์ •๋œ ์ž„๊ณ„์น˜๋ฅผ
    • ์ดˆ๊ณผํ•˜๋ฉด ํŒŒ๋“œ์˜ replicas ์ˆ˜๋ฅผ ์ฆ๊ฐ€
    • ์ค„์–ด๋“ค๋ฉด ํŒŒ๋“œ์˜ replicas ์ˆ˜๋ฅผ ๊ฐ์†Œ
  • ์›Œํฌ๋กœ๋“œ ๋ณ€ํ™”์— ๋”ฐ๋ผ ๋ฆฌ์†Œ์Šค๋ฅผ ํšจ์œจ์ ์œผ๋กœ ํ• ๋‹น / ๊ณผ๋„ํ•œ ๋ฆฌ์†Œ์Šค ๋‚ญ๋น„ ๋ฐฉ์ง€
  • ๊ธฐ๋ณธ YAML spec
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: hpa-sample
spec:
  scaleTargetRef:          # Scale ํƒ€๊ฒŸ ์ง€์ •
    apiVersion: apps/v1
    kind: Deployment
    name: my-app           # Deployment ์ด๋ฆ„
  minReplicas: 2           # ์ตœ์†Œ Pod
  maxReplicas: 10          # ์ตœ๋Œ€ Pod
  metrics:                 # Scale ๊ธฐ์ค€ ์ง€ํ‘œ ์„ค์ •
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 50  # CPU ์‚ฌ์šฉ๋ฅ  50% ๊ธฐ์ค€
    - type: Resource
      resource:
        name: memory
        target:
          type: Utilization
          averageUtilization: 70  # ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋ฅ  70% ๊ธฐ์ค€

HPA ๊ตฌ์„ฑ

  • nginx deploy ๋ฐฐํฌ
cat << EOF >> hpa-nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hpa-nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: hpa-nginx
  template:
    metadata:
      labels:
        app: hpa-nginx
    spec:
      containers:
      - name: hpa-nginx
        image: nginx
        resources:
          requests:
            cpu: 50m
          limits:
            cpu: 100m
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: hpa-nginx
  labels:
    app: hpa-nginx
spec:
  ports:
  - port: 80
  selector:
    app: hpa-nginx
EOF

cat hpa-nginx.yaml

# Deployment ๋ฐฐํฌ
kubectl apply -f hpa-nginx.yaml

kubectl get deploy,pod
  • ๋ช…๋ นํ˜•์œผ๋กœ hpa ์„ค์ •
# HPA ์ƒ์„ฑ
kubectl autoscale deployment hpa-nginx --cpu-percent=50 --min=1 --max=10

# HPA ํ™•์ธ
kubectl get hpa
...
NAME        REFERENCE              TARGETS       MINPODS   MAXPODS   REPLICAS   AGE
hpa-nginx   Deployment/hpa-nginx   cpu: 0%/50%   1         10        1          26s
....

# HPA ์ƒ์„ธ ์ •๋ณด ํ™•์ธ
kubectl describe hpa
  • pod ๋ถ€ํ•˜ ์ƒ์„ฑ
# ํ„ฐ๋ฏธ๋„ 1๋ฒˆ
while true; do kubectl get hpa; kubectl top pods; sleep 1s; done

# ํ„ฐ๋ฏธ๋„ 2๋ฒˆ
kubectl run -i --tty load-generator --rm --image=busybox:1.28 --restart=Never -- /bin/sh -c "while true; do wget -q -O- http://hpa-nginx.default.svc.cluster.local; done"

# ์‹ค์Šต ์ข…๋ฃŒ ํ›„ ๋ฆฌ์†Œ์Šค ์‚ญ์ œ
kubectl delete hpa --all
kubectl delete -f hpa-nginx.yaml

๊ฒฐ๊ณผ ํ™•์ธ

  • http ์š”์ฒญ์— ๋”ฐ๋ฅธ nginx ํŒŒ๋“œ์˜ CPU ๋ถ€ํ•˜ ์ฆ๊ฐ€
  • metrics-server์— ๊ธฐ๋ก ํ›„ scailng triggered

2.2. VPA (Vertical Pod Autoscaling)

Pod CPU/Memory ๋ฆฌ์†Œ์Šค ์ตœ์ ํ™”ํ•˜๊ธฐ (VPA ๋ฐ Kubecost ์ถ”์ฒœ๋กœ์ง ๋ถ„์„)
  • ์ˆ˜์ง ์Šค์ผ€์ผ๋ง(VPA, Vertical Pod Autoscaling)
    • ํŒŒ๋“œ์˜ ๋ฆฌ์†Œ์Šค ์š”์ฒญ๊ฐ’(CPU, ๋ฉ”๋ชจ๋ฆฌ)์„ ์ž๋™์œผ๋กœ ์กฐ์ •
  • HPA๊ฐ€ ํŒŒ๋“œ ๊ฐœ์ˆ˜๋ฅผ ์กฐ์ •ํ•˜๋Š” ๊ฒƒ๊ณผ ๋‹ฌ๋ฆฌ, VPA๋Š” ๊ฐ ํŒŒ๋“œ๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” ๋ฆฌ์†Œ์Šค์˜ ํฌ๊ธฐ๋ฅผ ์กฐ์ ˆํ•จ
  • VPA Recommender๊ฐ€ ๋ฆฌ์†Œ์Šค ์‚ฌ์šฉ๋Ÿ‰์„ ๋ถ„์„ํ•ด ์ตœ์ ์˜ ์š”์ฒญ๊ฐ’์„ ๊ณ„์‚ฐํ•˜๊ณ  ๋ฐ˜์˜ํ•จ
    • HPA์™€ VPA๋Š” ๊ฐ™์€ Deployment์— ๋™์‹œ์— ์ ์šฉํ•  ์ˆ˜ ์—†์Œ
  • Kubernetes v1.33๋ถ€ํ„ฐ๋Š” VPA ๊ธฐ๋Šฅ์ด ๊ธฐ๋ณธ ํ™œ์„ฑํ™”๋˜์–ด ์žˆ์œผ๋‚˜, kind์—์„œ๋Š” v1.32๊นŒ์ง€ ์ง€์›๋˜๋ฏ€๋กœ ๋ณ„๋„์˜ ์ปจํŠธ๋กค๋Ÿฌ ์„ค์น˜๊ฐ€ ํ•„์š”ํ•จ
  • ๊ธฐ๋ณธ ๊ตฌ์„ฑ ์š”์†Œ๋Š” VPA Recommender, Updater, Admission Plugin ์„ธ ๊ฐ€์ง€๋กœ ์ด๋ฃจ์–ด์ง
  • ๊ธฐ๋ณธ YAML ๊ตฌ์„ฑ
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
  name: my-app-vpa
spec:
  targetRef:                   # Scale ๋Œ€์ƒ
    apiVersion: apps/v1
    kind: Deployment
    name: my-app               # Deployment ๋ช…์นญ
  updatePolicy:
    updateMode: "Auto"         # VPA Recommender ์— ์˜ํ•ด ์ž๋™ ์กฐ์ • ํ™œ์„ฑํ™”
  resourcePolicy:
    containerPolicies:
      - containerName: my-app-container # Container ๋ช…์นญ "*" ์‚ฌ์šฉ ๊ฐ€๋Šฅ
        minAllowed:            # ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ํ• ๋‹น๋ฐ›์„ ์ˆ˜ ์žˆ๋Š” ์ตœ์†Œ ๋ฆฌ์†Œ์Šค
          cpu: "200m"
          memory: "512Mi"
        maxAllowed:            # ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ํ• ๋‹น๋ฐ›์„ ์ˆ˜ ์žˆ๋Š” ์ตœ๋Œ€ ๋ฆฌ์†Œ์Šค
          cpu: "2"
          memory: "2Gi"

VPA ๊ตฌ์„ฑ ๋ฐ ํ…Œ์ŠคํŠธ

  • EKS Workshop ์†Œ์Šค๋ฅผ ์‚ฌ์šฉํ•œ VPA ๋ฐฐํฌ
# EKS Workshop ์†Œ์Šค ์‚ฌ์šฉ
git clone https://github.com/kubernetes/autoscaler.git

# VPA ๋ฐฐํฌ
cd autoscaler/vertical-pod-autoscaler/
./hack/vpa-up.sh

# VPA Controller ํ™•์ธ
kubectl get pods -n kube-system | grep vpa

# VPA ์ œ๊ฑฐ
./hack/vpa-down.sh
  • 0.1 cpu ๋ฅผ ์š”์ฒญํ•œ 2๊ฐœ Pod ๋ฐฐํฌ (์‹ค์ œ ์‚ฌ์šฉ๋Ÿ‰๋ณด๋‹ค ๋ถ€์กฑํ•œ ์ƒํƒœ)
apiVersion: "autoscaling.k8s.io/v1"
kind: VerticalPodAutoscaler
metadata:
  name: hamster-vpa
spec:
  targetRef:
    apiVersion: "apps/v1"
    kind: Deployment
    name: hamster
  resourcePolicy:
    containerPolicies:
      - containerName: '*'
        minAllowed:
          cpu: 100m
          memory: 50Mi
        maxAllowed:
          cpu: 1
          memory: 500Mi
        controlledResources: ["cpu", "memory"]
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hamster
spec:
  selector:
    matchLabels:
      app: hamster
  replicas: 2
  template:
    metadata:
      labels:
        app: hamster
    spec:
      securityContext:
        runAsNonRoot: true
        runAsUser: 65534 # nobody
      containers:
        - name: hamster
          image: registry.k8s.io/ubuntu-slim:0.14
          resources:
            requests:
              cpu: 100m
              memory: 50Mi
          command: ["/bin/sh"]
          args:
            - "-c"
            - "while true; do timeout 0.5s yes >/dev/null; sleep 0.5s; done"

๊ฒฐ๊ณผ ํ™•์ธ

  • ๋ฆฌ์†Œ์Šค ๋ถ€์กฑํ•œ pod ์ œ๊ฑฐ(evict) ์ดํ›„ ์ ์ ˆํ•œ resource request ๋ฅผ ๊ฐ–๊ณ  ์žฌ์ƒ์„ฑ

2.3. CA (Cluster Autoscaler)

Kubernetes Cluster Autoscaler, Kubecost
  • Cluster Autoscaler๋Š” ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค ํด๋Ÿฌ์Šคํ„ฐ์—์„œ ์›Œ์ปค ๋…ธ๋“œ์˜ ์ˆ˜๋ฅผ ์ž๋™์œผ๋กœ ์กฐ์ ˆํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ๋กœ, ํด๋Ÿฌ์Šคํ„ฐ์— ์Šค์ผ€์ค„ํ•  ์—ฌ์œ ๊ฐ€ ์—†๋Š” ํŒŒ๋“œ๊ฐ€ ์กด์žฌํ•  ๊ฒฝ์šฐ ๋…ธ๋“œ๋ฅผ ์ž๋™์œผ๋กœ ์ถ”๊ฐ€ํ•˜๊ณ , ์‚ฌ์šฉ๋˜์ง€ ์•Š๋Š” ๋…ธ๋“œ๋Š” ์ž๋™์œผ๋กœ ์ œ๊ฑฐํ•จ
  • HPA๋‚˜ VPA๋Š” ํŒŒ๋“œ ๋‹จ์œ„์—์„œ์˜ ๋ฆฌ์†Œ์Šค ํ™•์žฅ์„ ๋‹ค๋ฃจ์ง€๋งŒ, CA๋Š” ํŒŒ๋“œ๊ฐ€ ๋ฐฐ์น˜๋  ๊ณต๊ฐ„ ์ž์ฒด๊ฐ€ ์—†์„ ๋•Œ ํด๋Ÿฌ์Šคํ„ฐ์˜ ์ธํ”„๋ผ ๋ ˆ๋ฒจ(๋…ธ๋“œ ์ˆ˜)์„ ์กฐ์ •ํ•จ
  • ์ผ๋ฐ˜์ ์œผ๋กœ GCP, AWS, Azure ๊ฐ™์€ ํด๋ผ์šฐ๋“œ ํ™˜๊ฒฝ์—์„œ ์‚ฌ์šฉ๋˜๋ฉฐ, ํด๋ผ์šฐ๋“œ ํ”„๋กœ๋ฐ”์ด๋” API๋ฅผ ํ†ตํ•ด ์‹ค์ œ VM ์ธ์Šคํ„ด์Šค๋ฅผ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•˜๊ฑฐ๋‚˜ ์‚ญ์ œํ•จ
  • ์„ค์ •๋œ ๋…ธ๋“œ ๊ทธ๋ฃน์ด๋‚˜ Auto Scaling Group ๋‹จ์œ„๋กœ ๋™์ž‘ํ•˜๋ฉฐ, ๋…ธ๋“œ์˜ ์ข…๋ฅ˜๋‚˜ ๊ฐœ์ˆ˜๋Š” ์‚ฌ์ „์— ์ •ํ•ด์ ธ ์žˆ์Œ
  • ๋™์ž‘ ๋ฐฉ์‹
  1. Cluster Autoscaler๊ฐ€ ํ™œ์„ฑํ™”๋˜๋ฉด, ๋Œ€๊ธฐ ์ƒํƒœ(pending)์˜ ํŒŒ๋“œ๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธํ•œ๋‹ค. ๊ธฐ๋ณธ ์Šค์บ” ์ฃผ๊ธฐ๋Š” 10์ดˆ์ด๋ฉฐ, --scan-interval ํ”Œ๋ž˜๊ทธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.
  2. ๋Œ€๊ธฐ ์ค‘์ธ ํŒŒ๋“œ๊ฐ€ ์žˆ๊ณ  ํด๋Ÿฌ์Šคํ„ฐ์— ๋” ๋งŽ์€ ๋ฆฌ์†Œ์Šค๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ, CA๋Š” ๊ด€๋ฆฌ์ž๊ฐ€ ์„ค์ •ํ•œ ์ œ์•ฝ ์กฐ๊ฑด ๋‚ด์—์„œ ์ƒˆ๋กœ์šด ๋…ธ๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ํด๋Ÿฌ์Šคํ„ฐ๋ฅผ ํ™•์žฅํ•œ๋‹ค. AWS, Azure, GCP ๊ฐ™์€ ํผ๋ธ”๋ฆญ ํด๋ผ์šฐ๋“œ ๊ณต๊ธ‰์ž๋“ค์€ Kubernetes Cluster Autoscaler ๊ธฐ๋Šฅ์„ ์ง€์›ํ•˜๋ฉฐ, ์˜ˆ๋ฅผ ๋“ค์–ด AWS EKS๋Š” Auto Scaling Group ๊ธฐ๋Šฅ์„ ํ†ตํ•ด EC2 ๊ฐ€์ƒ ๋จธ์‹ ์„ ์ž๋™์œผ๋กœ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ ์ œ๊ฑฐํ•œ๋‹ค.
  3. Kubernetes๋Š” ์ƒˆ๋กœ ํ”„๋กœ๋น„์ €๋‹๋œ ๋…ธ๋“œ๋ฅผ ์ปจํŠธ๋กค ํ”Œ๋ ˆ์ธ์— ๋“ฑ๋กํ•˜์—ฌ ์Šค์ผ€์ค„๋Ÿฌ๊ฐ€ ํ•ด๋‹น ๋…ธ๋“œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๋งŒ๋“ ๋‹ค.
  4. ๋งˆ์ง€๋ง‰์œผ๋กœ Kubernetes ์Šค์ผ€์ค„๋Ÿฌ๊ฐ€ ๋Œ€๊ธฐ ์ค‘์ธ ํŒŒ๋“œ๋ฅผ ์ƒˆ ๋…ธ๋“œ์— ํ• ๋‹นํ•œ๋‹ค.

Karpenter ์†Œ๊ฐœ โ€“ ์˜คํ”ˆ ์†Œ์Šค ๊ณ ์„ฑ๋Šฅ Kubernetes ํด๋Ÿฌ์Šคํ„ฐ ์˜คํ† ์Šค์ผ€์ผ๋Ÿฌ, AWS

  • Karpenter๋Š” Cluster Autoscaler์™€ ๋™์ผํ•˜๊ฒŒ ๋…ธ๋“œ์˜ ์ž๋™ ํ™•์žฅ์„ ์ˆ˜ํ–‰ํ•˜์ง€๋งŒ, ๋” ์œ ์—ฐํ•˜๊ณ  ๋น ๋ฅธ ๋ฆฌ์†Œ์Šค ํ”„๋กœ๋น„์ €๋‹์„ ์ง€์›ํ•˜๋Š” ์ฐจ์„ธ๋Œ€ ์˜คํ† ์Šค์ผ€์ผ๋Ÿฌ์ž„
  • ๊ธฐ์กด CA๋Š” ์ •ํ•ด์ง„ ๋…ธ๋“œ ๊ทธ๋ฃน ๋‚ด์—์„œ ํ™•์žฅ์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๋ฐ˜๋ฉด, Karpenter๋Š” ํŒŒ๋“œ์˜ ์š”๊ตฌ์‚ฌํ•ญ์— ๋”ฐ๋ผ ๊ฐ€์žฅ ์ ์ ˆํ•œ ์ธ์Šคํ„ด์Šค ํƒ€์ž…๊ณผ ํฌ๊ธฐ๋ฅผ ์„ ํƒํ•ด ๋…ธ๋“œ๋ฅผ ์ƒ์„ฑํ•จ
  • ์ฆ‰, HPA์˜ ์ˆ˜ํ‰ ํ™•์žฅ + VPA์˜ ๋ฆฌ์†Œ์Šค ๋ถ„์„ ์—ญํ• ์„ ํฌํ•จํ•œ ๋ฐฉ์‹์œผ๋กœ, ํด๋Ÿฌ์Šคํ„ฐ์˜ ๋ฆฌ์†Œ์Šค๋ฅผ ๋” ์ •๋ฐ€ํ•˜๊ฒŒ ์ตœ์ ํ™”ํ•จ
  • ํƒ„๋ ฅ์ ์ธ ๋ฆฌ์†Œ์Šค ํ™œ์šฉ๊ณผ ํ•จ๊ป˜ ๋ถˆํ•„์š”ํ•œ ๋…ธ๋“œ๋ฅผ ๋น ๋ฅด๊ฒŒ ์ œ๊ฑฐํ•  ์ˆ˜ ์žˆ์–ด, ๋น„์šฉ ํšจ์œจ์„ฑ๊ณผ ์‘๋‹ต ์†๋„ ๋ฉด์—์„œ ์œ ๋ฆฌํ•จ
  • AWS Fargate, EC2, Bottlerocket ๋“ฑ ๋‹ค์–‘ํ•œ ๋ฐฑ์—”๋“œ๋ฅผ ์ง€์›ํ•˜๋ฉฐ, ์ ์  ๊ธฐ์กด CA๋ฅผ ๋Œ€์ฒดํ•˜๋Š” ๋ฐฉํ–ฅ์œผ๋กœ ํ™•์‚ฐ๋˜๊ณ  ์žˆ์Œ

2.4. KEDA (Kubernetes Event-Driven Autoscaler)

KEDA Architecture, KEDA.sh

KEDA (Kubernetes Event-Driven Autoscaler)

  • KEDA(Kubernetes Event-Driven Autoscaler)๋Š” ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค์—์„œ ์ด๋ฒคํŠธ ๊ธฐ๋ฐ˜์œผ๋กœ ์›Œํฌ๋กœ๋“œ๋ฅผ ์ž๋™ ํ™•์žฅํ•ด์ฃผ๋Š” ํ™•์žฅ ๋„๊ตฌ์ž„
  • ๊ธฐ์กด์˜ HPA๋Š” CPU, ๋ฉ”๋ชจ๋ฆฌ์™€ ๊ฐ™์€ ๋ฆฌ์†Œ์Šค ์‚ฌ์šฉ๋Ÿ‰์„ ๊ธฐ์ค€์œผ๋กœ๋งŒ ํ™•์žฅ ์กฐ๊ฑด์„ ํŒ๋‹จํ•˜์ง€๋งŒ, KEDA๋Š” ์™ธ๋ถ€ ์ด๋ฒคํŠธ๋‚˜ ๋ฉ”์‹œ์ง€ ํ ์ƒํƒœ ๋“ฑ ๋‹ค์–‘ํ•œ ์‹ ํ˜ธ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ™•์žฅ์„ ์ˆ˜ํ–‰ํ•จ
  • Kafka, RabbitMQ, AWS SQS, Azure Event Hub, Prometheus ์ฟผ๋ฆฌ ๋“ฑ 50๊ฐœ ์ด์ƒ์˜ ์ด๋ฒคํŠธ ์†Œ์Šค๋ฅผ ์ง€์›ํ•˜์—ฌ, ์‹ค์‹œ๊ฐ„ ํŠธ๋ž˜ํ”ฝ ๋ณ€ํ™”๋‚˜ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์ž‘์—…์— ์œ ์—ฐํ•˜๊ฒŒ ๋Œ€์‘ ๊ฐ€๋Šฅํ•จ
  • ๋‚ด๋ถ€์ ์œผ๋กœ HPA๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ๊ด€๋ฆฌํ•˜๋ฉฐ, ์ด๋ฒคํŠธ ์กฐ๊ฑด์ด ์ถฉ์กฑ๋˜๋ฉด HPA๋ฅผ ํ™œ์„ฑํ™”ํ•˜์—ฌ Pod ์ˆ˜๋ฅผ ์ž๋™ ์กฐ์ ˆํ•จ
  • ์˜ˆ๋ฅผ ๋“ค์–ด, ํŠน์ • ์‹œ๊ฐ„๋Œ€์— ๋ฉ”์‹œ์ง€ ํ์— ์Œ“์ธ ์ž‘์—…๋Ÿ‰์ด ๋งŽ์•„์ง€๋Š” ๊ฒฝ์šฐ ์ด๋ฅผ ๊ฐ์ง€ํ•ด ํŒŒ๋“œ๋ฅผ ๋ฏธ๋ฆฌ ํ™•์žฅํ•˜๊ฑฐ๋‚˜, ์™ธ๋ถ€ ์š”์ฒญ ์ˆ˜๊ฐ€ ๊ธ‰์ฆํ•  ๋•Œ ๋น ๋ฅด๊ฒŒ ๋Œ€์‘ํ•  ์ˆ˜ ์žˆ์Œ
  • ์ •์  ์Šค์ผ€์ค„๋ง(HPA + cronjob)์ด๋‚˜ ๋‹จ์ˆœ ๋ฆฌ์†Œ์Šค ์‚ฌ์šฉ๋ฅ  ๊ธฐ๋ฐ˜ ํ™•์žฅ์˜ ํ•œ๊ณ„๋ฅผ ๋ณด์™„ํ•˜๋ฉฐ, ํด๋ผ์šฐ๋“œ ๋„ค์ดํ‹ฐ๋ธŒ ํ™˜๊ฒฝ์— ์ ํ•ฉํ•œ ํ™•์žฅ ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ์ œ๊ณตํ•จ

3. ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ณ€์ˆ˜ ๊ด€๋ฆฌ

3.1. ConfigMap

About ConfigMap in Kubernetes
  • Kubernetes ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์„ค์ • ์ •๋ณด๋ฅผ Key-Value ํ˜•ํƒœ๋กœ ์ €์žฅํ•˜๊ณ  ์™ธ๋ถ€์—์„œ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก ์ง€์›ํ•˜๋Š” ๋ฆฌ์†Œ์Šค
  • ์„ค์ • ๋ฐ์ดํ„ฐ๋ฅผ ์ด๋ฏธ์ง€์— ์ง์ ‘ ํฌํ•จํ•˜์ง€ ์•Š๊ณ  ๋ถ„๋ฆฌํ•จ์œผ๋กœ์จ, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์žฌ๋นŒ๋“œํ•˜์ง€ ์•Š๊ณ ๋„ ์„ค์ •์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Œ
  • ํŒŒ๋“œ์˜ ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ๋˜๋Š” ๋ณผ๋ฅจ ๋งˆ์šดํŠธ๋ฅผ ํ†ตํ•ด ์ปจํ…Œ์ด๋„ˆ ๋‚ด๋ถ€์—์„œ ConfigMap ๊ฐ’์„ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ์Œ
  • ์ฃผ์š” ์‚ฌ์šฉ ๋ชฉ์ 
  1. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„ค์ • ๊ด€๋ฆฌ
    • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค URL, ์™ธ๋ถ€ API ์ฃผ์†Œ ๋“ฑ ๊ตฌ์„ฑ ์ •๋ณด๋ฅผ ConfigMap์— ์ €์žฅํ•˜๊ณ  ํŒŒ๋“œ์— ์ฃผ์ž…ํ•˜์—ฌ ์‚ฌ์šฉ
  2. ํ™˜๊ฒฝ๋ณ„ ์„ค์ • ๋ถ„๋ฆฌ
    • ๊ฐœ๋ฐœ, ์Šคํ…Œ์ด์ง•, ์šด์˜ ํ™˜๊ฒฝ๋ณ„๋กœ ๋‹ค๋ฅธ ์„ค์ • ๊ฐ’์„ ๊ด€๋ฆฌํ•˜์—ฌ ๋™์ผํ•œ ์ปจํ…Œ์ด๋„ˆ ์ด๋ฏธ์ง€๋ฅผ ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•จ
# ConfigMap ์ƒ˜ํ”Œ ๊ตฌ์„ฑ
apiVersion: v1
kind: ConfigMap
metadata:
  name: my-config # ConfigMap ๋ช…์นญ
data:
  key1: value1    # Key : Value ํ˜•ํƒœ ๊ฐ’ ์ฃผ์ž…
  key2: value2
# ConfigMap ์‚ฌ์šฉ ์˜ˆ์‹œ
apiVersion: v1
kind: Pod
metadata:
  name: my-app
spec:
  containers:
    - name: my-container
      image: my-image
      env:
        - name: MY_CONFIG_KEY  # ์ปจํ…Œ์ด๋„ˆ์—์„œ ์‚ฌ์šฉํ•  ๋ณ€์ˆ˜ Key ๊ฐ’
          valueFrom:
            configMapKeyRef:
              name: my-config  # ์‚ฌ์šฉํ•  ConfigMap์˜ ์ด๋ฆ„
              key: key1        # ConfigMap ๋‚ด์˜ ํ‚ค -> ๊ฐ’: value1

ConfigMap ๊ธฐ๋ณธ ํ™œ์šฉ

  • ๊ธฐ๋ณธ ์ƒ์„ฑ ๋ฐ ํ™•์ธ
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
  name: mysql
data:
  DBNAME: mydatabase
---
apiVersion: v1
kind: Pod
metadata:
  name: nginx-configmap
spec:
  containers:
  - image: nginx
    name: nginx-configmap
    env:
    - name: DB
      valueFrom:
        configMapKeyRef:
          name: mysql
          key: DBNAME
EOF

# ์˜ค๋ธŒ์ ํŠธ ํ™•์ธ
kubectl get cm,pod

# ์ƒ์„ธ ์ •๋ณด ์กฐํšŒ
kubectl describe cm mysql
kubectl describe pod nginx-configmap

# pod ๋‚ด๋ถ€ ๋ณ€์ˆ˜ ํ™•์ธ
kubectl exec -it nginx-configmap -- /bin/bash -c env
...
DB=mydatabase
...

# ๋ฆฌ์†Œ์Šค ์‚ญ์ œ
kubectl delete pod --all
kubectl delete cm nginx-configmap

ConfigMap ์œผ๋กœ ์„ค์ • ํŒŒ์ผ ๊ด€๋ฆฌ

  • yaml ์ƒ์„ฑ
# ํ…Œ์ŠคํŠธ ํŒŒ์ผ ์ƒ์„ฑ
cat << EOF >> config-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-configmap-deploy
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx-configmap
  template:
    metadata:
      labels:
        app: nginx-configmap
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80
        volumeMounts:
        - name: config-volume
          mountPath: /etc/nginx/nginx.conf
          subPath: nginx.conf
      volumes:
      - name: config-volume
        configMap:
          name: nginx-config
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  selector:
    app: nginx-configmap
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
      nodePort: 31001
  type: NodePort
EOF

cat << EOF >> configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  nginx.conf: |
    events {}
    http {
        server {
            listen 80;
            location / {
                return 200 'Hello from nginx configmap!';
            }
        }
    }
EOF
  • ๋ฆฌ์†Œ์Šค ๋ฐฐํฌ
kubectl apply -f configmap.yaml -f config-deploy.yaml

#
kubectl get cm,deploy,pod
kubectl describe deploy
...
    Mounts:
      /etc/nginx/nginx.conf from config-volume (rw,path="nginx.conf")
  Volumes:
   config-volume:
    Type:          ConfigMap (a volume populated by a ConfigMap)
    Name:          nginx-config
    Optional:      false
...

# Nginx ์ ‘์†
open http://localhost:31001

# Nginx ConfigMap ๋ณ€๊ฒฝ
vim configmap.yaml
...
return 200 'Modify from nginx configmap!';
...

#
kubectl apply -f configmap.yaml

# pod ์žฌ์‹œ์ž‘
kubectl rollout restart deploy nginx-configmap-deploy

# ๋ฆฌ์†Œ์Šค ์‚ญ์ œ
kubectl delete -f configmap.yaml -f config-deploy.yaml

๊ฒฐ๊ณผ ํ™•์ธ

  • cm ์ˆ˜์ • ํ›„ deploy ์žฌ์‹œ์ž‘ํ•˜์—ฌ response ๋ณ€๊ฒฝ ํ™•์ธ
  • nginx ์‰˜ ๋‚ด๋ถ€์—์„œ ๋กœ์ปฌํ˜ธ์ถœ kubectl exec -it $(kubectl get pod -l app=nginx-configmap -o jsonpath='{.items[0].metadata.name}') -- curl localhost:80
Kubernetes: Auto Restart Deployments when K8s ConfigMap or Secret is Updated
  • ConfigMap ๋ณ€๊ฒฝ ์‹œ Pod ์ˆ˜๋™ ์žฌ๋ฐฐํฌ๊ฐ€ ํ•„์š” โ†’ ๋น„ํšจ์œจ์ 
  • ConfigMap, Secret ์˜ ๋ณ€๋™ ์‚ฌํ•ญ์„ ์ฃผ๊ธฐ์ ์œผ๋กœ ํ™•์ธํ•ด์„œ ์ž๋™์œผ๋กœ Rollout ์„ ํ•ด์ฃผ๋Š” ์˜คํ”ˆ์†Œ์Šค ๋„๊ตฌ

3.2. Secret

How to K8s: Kubernetes Secrets Made Simple
  • Kubernetes์˜ Secret์€ ๋น„๋ฐ€๋ฒˆํ˜ธ, ํ† ํฐ, SSH ํ‚ค ๋“ฑ ๋ฏผ๊ฐํ•œ ์ •๋ณด๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ๋ฆฌ์†Œ์Šค
  • ConfigMap๊ณผ ์œ ์‚ฌํ•œ ๋ฐฉ์‹์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜์ง€๋งŒ, ๋ชจ๋“  ๊ฐ’์€ Base64 ์ธ์ฝ”๋”ฉ๋œ ์ƒํƒœ๋กœ ์ €์žฅ
  • Base64๋Š” ๋‹จ์ˆœ ์ธ์ฝ”๋”ฉ์ผ ๋ฟ ์•”ํ˜ธํ™”๊ฐ€ ์•„๋‹ˆ๋ฉฐ, ์ „์†ก ์ค‘ ๋…ธ์ถœ์„ ๋ง‰๊ธฐ ์œ„ํ•œ ํ˜•์‹์ผ ๋ฟ ๋ณด์•ˆ์„ฑ์„ ๋ณด์žฅํ•˜์ง€ ์•Š์Œ
  • Secret์€ ํŒŒ๋“œ์˜ ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋กœ ์ฃผ์ž…ํ•˜๊ฑฐ๋‚˜ ํŒŒ์ผ๋กœ ๋งˆ์šดํŠธํ•˜์—ฌ ์ปจํ…Œ์ด๋„ˆ ๋‚ด๋ถ€์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Œ
  • ์ฃผ์š” ์‚ฌ์šฉ ์‚ฌ๋ก€
    • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋น„๋ฐ€๋ฒˆํ˜ธ, API ํ‚ค, TLS ์ธ์ฆ์„œ ๋“ฑ ์™ธ๋ถ€์— ๋…ธ์ถœ๋˜๋ฉด ์•ˆ ๋˜๋Š” ์ •๋ณด ์ €์žฅ
    • ConfigMap๊ณผ ๋™์ผํ•œ ๋ฐฉ์‹์œผ๋กœ ์ฐธ์กฐ ๊ฐ€๋Šฅํ•˜์ง€๋งŒ, ๋ณด๋‹ค ๋ฏผ๊ฐํ•œ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ์— ์ ํ•ฉ
# Secret ์ƒ˜ํ”Œ
apiVersion: v1
kind: Secret
metadata:
  name: my-secret
type: Opaque
data:
  username: bXl1c2Vy  # base64๋กœ ์ธ์ฝ”๋”ฉ๋œ ๊ฐ’
  password: bXlwYXNzd29yZA==  # base64๋กœ ์ธ์ฝ”๋”ฉ๋œ ๊ฐ’
# Secret ์‚ฌ์šฉ ์˜ˆ์‹œ
apiVersion: v1
kind: Pod
metadata:
  name: my-app
spec:
  containers:
    - name: my-container
      image: my-image
      env:
        - name: DB_USER        # Container ์—์„œ ์‚ฌ์šฉํ•  ๋ณ€์ˆ˜๋ช…
          valueFrom:
            secretKeyRef:
              name: my-secret  # ์‚ฌ์šฉํ•  Secret์˜ ์ด๋ฆ„
              key: username    # Secret ๋‚ด์˜ ํ‚ค
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: my-secret  # ์‚ฌ์šฉํ•  Secret์˜ ์ด๋ฆ„
              key: password    # Secret ๋‚ด์˜ ํ‚ค
...
# ๋งˆ์šดํŠธ ๋ฐฉ๋ฒ•
      volumeMounts:
        - name: secret-volume     # Volume ๋ช…์นญ
          mountPath: /etc/secrets # ์ปจํ…Œ์ด๋„ˆ ๋‚ด๋ถ€ ๋งˆ์šดํŠธ ์œ„์น˜
  volumes:
    - name: secret-volume         # Volume ๋ช…์นญ
      secret:
        secretName: my-secret     # ์‚ฌ์šฉํ•  Secret์˜ ์ด๋ฆ„

Secret ๊ธฐ๋ณธ ํ™œ์šฉ

  • secret ๋ฆฌ์†Œ์Šค ์ƒ์„ฑ ๋ฐ ์กฐํšŒ
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Secret
metadata:
  name: secret-test
type: Opaque
data:
  username: YWRtaW4=  # 'admin'์„ base64 ์ธ์ฝ”๋”ฉํ•œ ๊ฐ’
  password: cGFzc3dvcmQ=  # 'password'๋ฅผ base64 ์ธ์ฝ”๋”ฉํ•œ ๊ฐ’
EOF

# Base64 ์ธ์ฝ”๋”ฉ ๋ฐฉ๋ฒ•
echo -n 'admin' | base64
echo -n 'password' | base64

#
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: secret-pod
spec:
  containers:
  - name: nginx
    image: nginx
    env:
    - name: DB_USER
      valueFrom:
        secretKeyRef:
          name: secret-test
          key: username
    - name: DB_PASS
      valueFrom:
        secretKeyRef:
          name: secret-test
          key: password
EOF

#
kubectl get pod,secret

# ์ƒ์„ธ ์ •๋ณด ์กฐํšŒ
kubectl describe secret secret-test
kubectl describe pod secret-pod

# pod ๋‚ด๋ถ€ ๋ณ€์ˆ˜ ํ™•์ธ
kubectl exec -it secret-pod -- /bin/bash -c env
...
DB_USER=admin
DB_PASS=password
...

# ๋ฆฌ์†Œ์Šค ์‚ญ์ œ
kubectl delete pod --all
kubectl delete secret secret-test

๊ฒฐ๊ณผ ํ™•์ธ

  • secret์— ์ €์žฅ๋˜์–ด์žˆ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ base64 decode ํ•˜์—ฌ ํ™•์ธ
  • pod ๋‚ด๋ถ€์— ์ ์šฉ๋œ env ๊ฐ’ ํ™•์ธ

4. Network

A figure illustrating the different network ranges in a kubernetes cluster
Kubernetes IP address ranges
  • Servicek8s docs
    • ์™ธ๋ถ€์™€ ์ ‘ํ•˜๋Š” ๋‹จ์ผ ์—”๋“œํฌ์ธํŠธ
    • ์„œ๋น„์Šค ๋’ท๋‹จ์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์œผ๋กœ ์™ธ๋ถ€ ํŠธ๋ž˜ํ”ฝ์„ ์ „์†ก
  • ClusterIP์ฐธ๊ณ 
    • ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค ํด๋Ÿฌ์Šคํ„ฐ ๋ฒ”์œ„์˜ ๊ฐ€์ƒ IP ๋ถ€์—ฌ
    • ํด๋Ÿฌ์Šคํ„ฐ ๋‚ด๋ถ€์—์„œ๋งŒ ClusterIP ๋กœ ์ ‘๊ทผ ๊ฐ€๋Šฅ
    • ์„œ๋น„์Šค ํƒ€์ž…์„ ์ง€์ •ํ•˜์ง€ ์•Š์„ ๊ฒฝ์šฐ ๊ธฐ๋ณธ๊ฐ’
  • NodePort์ฐธ๊ณ 
    • ๊ณ ์ • ํฌํŠธ๋กœ ๊ฐ ๋…ธ๋“œ(Host) ์˜ ํฌํŠธ๋ฅผ ๋…ธ์ถœ
    • ํด๋Ÿฌ์Šคํ„ฐ ์™ธ๋ถ€์—์„œ ๋…ธ๋“œ(Host) ์˜ IP:Port ๋ฅผ ํ†ตํ•ด ์ ‘๊ทผ ๊ฐ€๋Šฅ
  • LoadBalancer์ฐธ๊ณ 
    • ์™ธ๋ถ€ ๋กœ๋“œ ๋ฐธ๋Ÿฐ์„œ๋ฅผ ์ง€์›ํ•˜๋Š” ํด๋ผ์šฐ๋“œ ๊ณต๊ธ‰์ž ์ƒ์—์„œ ํ™œ์šฉ
    • AWS, Azure, GCP ๋“ฑ์˜ LB ์„œ๋น„์Šค์™€ ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค ์„œ๋น„์Šค๋ฅผ ์—ฐ๊ฒฐ

4.1. ClusterIP

Kubernetes โ€“ ClusterIP vs NodePort vs LoadBalancer

ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ ๋ฐฐํฌ

# ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ฐฐํฌ
cat << EOF >> cluster-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: cluster-pod-1
  labels:
    app: cluster-pod
spec:
  containers:
  - name: container
    image: traefik/whoami
---
apiVersion: v1
kind: Pod
metadata:
  name: cluster-pod-2
  labels:
    app: cluster-pod
spec:
  containers:
  - name: container
    image: traefik/whoami
EOF

# Test ํŒŒ๋“œ
cat << EOF >> netshoot-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: netshoot-pod
spec:
  containers:
  - name: netshoot-pod
    image: nicolaka/netshoot
    command: ["tail"]
    args: ["-f", "/dev/null"]
EOF

# ClusterIP ์„œ๋น„์Šค ์ƒ์„ฑ
cat <<EOF>> cluster-svc.yaml
apiVersion: v1
kind: Service
metadata:
  name: cluster-svc
spec:
  type: ClusterIP
  selector:
    app: cluster-pod
  ports:
    - name: cluster
      port: 8080
      targetPort: 80
EOF

# ๋ฐฐํฌ
kubectl apply -f cluster-pod.yaml -f cluster-svc.yaml -f netshoot-pod.yaml

pod/ClusterIP ํ™•์ธ

  • pod ์ƒ์„ฑ ํ™•์ธ
# ํŒŒ๋“œ ๋Œ€์—ญ ํ™•์ธ
kubectl get nodes -o jsonpath='{.items[*].spec.podCIDR}'
...
10.244.0.0/24 10.244.1.0/24
...

# SVC ๋Œ€์—ญ ํ™•์ธ
kubectl -n kube-system get pods -l component=kube-controller-manager -o yaml | grep service-cluster-ip-range
...
--service-cluster-ip-range=10.96.0.0/16
...

# ํ™•์ธ
kubectl get pod -o wide
...
NAME                                         READY   STATUS    RESTARTS   AGE     IP            NODE          NOMINATED NODE   READINESS GATES
cluster-pod-1                                1/1     Running   0          67s   10.244.1.26   kind-worker   <none>           <none>
cluster-pod-2                                1/1     Running   0          67s   10.244.1.27   kind-worker   <none>           <none>
netshoot-pod                                 1/1     Running   0          67s   10.244.1.28   kind-worker   <none>           <none>
...

# ์„œ๋น„์Šค ํ™•์ธ
kubectl get svc cluster-svc
...
NAME          TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
cluster-svc   ClusterIP   10.96.193.184   <none>        8080/TCP   98s
...

# Endpoint ํ™•์ธ (Pod IP:Port)
kubectl get endpoints cluster-svc
...
NAME                                ENDPOINTS                       AGE
cluster-svc                         10.244.1.26:80,10.244.1.27:80   113s
...
  • ClusterIP ํ™•์ธ
# ํด๋ผ์ด์–ธํŠธ(TestPod) Shell ์‹คํ–‰
kubectl exec -it netshoot-pod -- zsh

# ์„œ๋น„์Šค ClusterIP ์ฃผ์ž…
SVC=10.96.193.184

curl $SVC:8080
curl -s $SVC:8080 | grep Hostname

# 100 ๋ฒˆ ๋ฐ˜๋ณต ํ˜ธ์ถœ
for i in {1..100}; do curl -s $SVC:8080 | grep Hostname; done | sort | uniq -c | sort -nr
...
55 Hostname: cluster-pod-1
45 Hostname: cluster-pod-2
...

๊ฒฐ๊ณผ ํ™•์ธ

  • svc rr ๋™์ž‘ ํ™•์ธ

4.2. NodePort

Kubernetes โ€“ NodePort Service

ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ ๋ฐฐํฌ

  • yaml ์ƒ์„ฑ
# ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ฐฐํฌ
cat << EOF > nodeport-pod.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nodeport-deploy
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nodeport-deploy
  template:
    metadata:
      labels:
        app: nodeport-deploy
    spec:
      containers:
      - name: container
        image: traefik/whoami
EOF

# ClusterIP ์„œ๋น„์Šค ์ƒ์„ฑ
cat <<EOF> nodeport-svc.yaml
apiVersion: v1
kind: Service
metadata:
  name: nodeport-svc
spec:
  type: NodePort
  selector:
    app: nodeport-deploy
  ports:
    - name: nodeport-svc
      port: 80          # ์„œ๋น„์Šค ํฌํŠธ (Cluster ๋‚ด๋ถ€์—์„œ ์‚ฌ์šฉ)
      targetPort: 80    # ์‹ค์ œ ์ปจํ…Œ์ด๋„ˆ ํฌํŠธ
      nodePort: 31001   # ์™ธ๋ถ€์—์„œ ์ ‘๊ทผํ•  NodePort
EOF
  • ์ƒ์„ฑ ๋ฐ ๋™์ž‘ ํ™•์ธ
# ์ƒ์„ฑ
kubectl apply -f nodeport-pod.yaml -f nodeport-svc.yaml

# ํ™•์ธ
kubectl get pod,svc
...
NAME                   TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
service/nodeport-svc   NodePort    10.96.115.75   <none>        80:31001/TCP   10s
...

kubectl get endpoints nodeport-svc
NAME           ENDPOINTS                       AGE
nodeport-svc   10.244.1.29:80,10.244.1.30:80   28s

NodePort ๋™์ž‘ ํ™•์ธ

  • WSL์—์„œ nodeport ์ง์ ‘ ์ฐŒ๋ฅด๊ธฐ๊ฐ€ ์•ˆ๋˜์–ด netshoot์—์„œ ๋…ธ๋“œ์˜ ๋…ธ๋“œํฌํŠธ ์ฐŒ๋ฅด๊ธฐ
# ๋…ธ๋“œ์˜ Port ๋กœ curl ์š”์ฒญ
curl http://localhost:31001
curl -s http://localhost:31001 | grep Hostname

# 100 ๋ฒˆ ๋ฐ˜๋ณต ํ˜ธ์ถœ
for i in {1..100}; do curl -s http://localhost:31001 | grep Hostname; done | sort | uniq -c | sort -nr
...
58 Hostname: nodeport-deploy-59b68567d7-6h562
42 Hostname: nodeport-deploy-59b68567d7-k2cpb
...

๊ฒฐ๊ณผ ํ™•์ธ

4.3. Ingress

ingress-fanout-diagram
Ingress – Simple fanout
  • ํด๋Ÿฌ์Šคํ„ฐ ์™ธ๋ถ€์—์„œ ๋‚ด๋ถ€ ์„œ๋น„์Šค๋กœ ํŠธ๋ž˜ํ”ฝ์„ ๋ผ์šฐํŒ… ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ œ๊ณตํ•˜๋Š” ๋ฆฌ์†Œ์Šค
  • ํด๋Ÿฌ์Šคํ„ฐ ๋‚ด๋ถ€ ์„œ๋น„์Šค (ClusterIP, NodePort, LoadBalancer) ๋ฅผ ์™ธ๋ถ€๋กœ ๋…ธ์ถœ (HTTP / HTTPS) – Web Proxy ์—ญํ• 
  • Ingress ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” Ingress Controller ๊ฐ€ ํ•„์š”
  • ๋Œ€ํ‘œ์ ์ธ Ingress Controller
    • Nginx Ingress Controller
    • Cloud Provider Ingress Controllers
      • (ex. AWS ALB Ingress Controller, GCP Ingress Controller)
  • ์ฃผ์š” ๊ธฐ๋Šฅ
    1. ํ˜ธ์ŠคํŠธ ๊ธฐ๋ฐ˜ ๋ผ์šฐํŒ…
      • ํ˜ธ์ŠคํŠธ ์ด๋ฆ„ (๋„๋ฉ”์ธ)์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํŠธ๋ž˜ํ”ฝ ๋ผ์šฐํŒ… ๊ฐ€๋Šฅ
      • api.example.com / www.example.com ์„ ๊ฐ๊ฐ ๋‹ค๋ฅธ Service ๋ฆฌ์†Œ์Šค์™€ ์—ฐ๊ฒฐ
    2. ๊ฒฝ๋กœ ๊ธฐ๋ฐ˜ ๋ผ์šฐํŒ…
      • ์š”์ฒญ ๊ฒฝ๋กœ ๊ธฐ๋ฐ˜์œผ๋กœ ํŠธ๋ž˜ํ”ฝ ๋ผ์šฐํŒ… ๊ฐ€๋Šฅ
      • /growth , /log ๊ฒฝ๋กœ๋ฅผ ๊ฐ๊ฐ ๋‹ค๋ฅธ Service ๋ฆฌ์†Œ์Šค์™€ ์—ฐ๊ฒฐ
    3. TLS ์„ค์ •
      • TLS ์ธ์ฆ์„œ๋ฅผ ํ™œ์šฉํ•˜์—ฌ HTTPS ๊ตฌ์„ฑ ๊ฐ€๋Šฅ
    4. ๋กœ๋“œ ๋ฐธ๋Ÿฐ์‹ฑ
      • ๋‚ด๋ถ€ ์„œ๋น„์Šค์— ๋Œ€ํ•œ ๋กœ๋“œ ๋ฐธ๋Ÿฐ์‹ฑ
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: example-ingress
  namespace: default
spec:
  rules:
  - host: example.com       # Domain Host
    http:
      paths:
      - path: /service1     # URL Path (example.com/service1)
        pathType: Prefix
        backend:
          service:
            name: service1  # /service1 ๋กœ ๋“ค์–ด์˜จ ํŠธ๋ž˜ํ”ฝ์„ ์ „์†กํ•  service ๋ช…
            port:
              number: 80
      - path: /service2     # URL Path (example.com/service2)
        pathType: Prefix
        backend:
          service:
            name: service2
            port:
              number: 80    # /service2 ๋กœ ๋“ค์–ด์˜จ ํŠธ๋ž˜ํ”ฝ์„ ์ „์†กํ•  service ๋ช…

Nginx Ingress Controller ์„ค์น˜ ๋ฐ ๊ตฌ์„ฑ

  • Ingress Controller ์ค‘ ๊ฐ€์žฅ ๋Œ€์ค‘์ ์ด๋ฉฐ Ingress ๋™์ž‘ ๋ฐฉ์‹ ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅ
# ๊ธฐ์กด ๋ฆฌ์†Œ์Šค ์‚ญ์ œ
kind delete cluster

# kind cluster ์žฌ๋ฐฐํฌ
kind create cluster --config kind-2node.yaml

# Nginx Ingress Controller ์„ค์น˜
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/cloud/deploy.yaml

# Service ํƒ€์ž… ๋ณ€๊ฒฝ
kubectl patch svc ingress-nginx-controller -n ingress-nginx -p \
'{"spec":{"type":"NodePort","ports":[{"port":80,"targetPort":80,"nodePort":31000},{"port":443,"targetPort":443,"nodePort":31001}]}}'

# Nginx Ingress Controller ๋ฆฌ์†Œ์Šค ํ™•์ธ
kubectl get -n ingress-nginx svc,deploy,pod

Service ๋ฐ Ingress ์ƒ์„ฑ

  • ์„œ๋น„์Šค ์ƒ์„ฑ
# Growth ์„œ๋น„์Šค
cat << EOF > svc-growth.yaml
apiVersion: v1
kind: Service
metadata:
  name: growth-service
spec:
  selector:
    app: growth
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: growth-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: growth
  template:
    metadata:
      labels:
        app: growth
    spec:
      containers:
        - name: nginx
          image: nginx
          ports:
            - containerPort: 80
          volumeMounts:
            - name: growth-html
              mountPath: /usr/share/nginx/html
      volumes:
        - name: growth-html
          configMap:
            name: growth-html
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: growth-html
data:
  index.html: |
    <html>
    <body>
      <h1>hello growth</h1>
    </body>
    </html>
EOF

kubectl apply -f svc-growth.yaml

# Log ์„œ๋น„์Šค
cat << EOF > svc-log.yaml
apiVersion: v1
kind: Service
metadata:
  name: log-service
spec:
  selector:
    app: log
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: log-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: log
  template:
    metadata:
      labels:
        app: log
    spec:
      containers:
        - name: nginx
          image: nginx
          ports:
            - containerPort: 80
          volumeMounts:
            - name: log-html
              mountPath: /usr/share/nginx/html
      volumes:
        - name: log-html
          configMap:
            name: log-html
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: log-html
data:
  index.html: |
    <html>
    <body>
      <h1>hello log</h1>
    </body>
    </html>

EOF

kubectl apply -f svc-log.yaml

# ๋ฐฐํฌ ํ™•์ธ
kubectl get pod,svc,cm

# ConfigMap ํ™•์ธ
kubectl describe cm growth-html
kubectl describe cm log-html
  • Ingress ๋ฐฐํฌ
cat << EOF > ingress-sample.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: growth-log-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: / # /growth, /log ์š”์ฒญ์„ ์„œ๋น„์Šค๋กœ ์ „๋‹ฌํ•  ๋•Œ ์ ‘๋‘์‚ฌ ์ œ๊ฑฐ. ex) /growth -> growth-service '/'
spec:
  ingressClassName: nginx
  rules:
  - http:
      paths:
      - path: /growth
        pathType: Prefix
        backend:
          service:
            name: growth-service
            port:
              number: 80
      - path: /log
        pathType: Prefix
        backend:
          service:
            name: log-service
            port:
              number: 80
EOF

kubectl apply -f ingress-sample.yaml

kubectl get ing,svc

#
kubectl describe ingress growth-log-ingress

Ingress ๋™์ž‘ ํ™•์ธ

  • ๊ฒฝ๋กœ ๊ธฐ๋ฐ˜ ์‘๋‹ต ํ™•์ธ
# growth ๊ฒฝ๋กœ ํ˜ธ์ถœ
curl http://localhost:31000/growth
...
<html>
<body>
  <h1>hello growth</h1>
</body>
</html>
...

# Log ๊ฒฝ๋กœ ํ˜ธ์ถœ
curl http://localhost:31000/log
...
<html>
<body>
  <h1>hello log</h1>
</body>
</html>
...

๊ฒฐ๊ณผ ํ™•์ธ

  • nginx ingress pod์—์„œ ๊ฐ ๊ฒฝ๋กœ ํ˜ธ์ถœ์‹œ ํ•ด๋‹น pod์˜ ์‘๋‹ต ํ™•์ธ

5. Storage

Volume

  • emptyDir
    • Pod ๋‚ด๋ถ€์— ์กด์žฌํ•˜๋ฉฐ ๋™์ผํ•œ Pod ์˜ ์ปจํ…Œ์ด๋„ˆ ๊ฐ„ ๊ณต์œ ๋  ์ˆ˜ ์žˆ๋Š” ์Šคํ† ๋ฆฌ์ง€
    • Pod ์‚ญ์ œ ์‹œ ํ•จ๊ป˜ ์‚ญ์ œ
    • Container ๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” ์ž„์‹œ ์ €์žฅ์†Œ
  • hostPath
    • Pod ๊ฐ€ ๋ฐฐํฌ๋œ Worker Node ์˜ Directory Path ๋ฅผ Pod ์— ๋งˆ์šดํŠธ
    • Pod ๊ฐ€ ์‚ญ์ œ๋˜์–ด๋„ ๋ฐ์ดํ„ฐ๋Š” Worker Node ์— ์กด์žฌ
    • ํ•˜์ง€๋งŒ ํ•ด๋‹น Pod ๊ฐ€ ๋ฐฐํฌ๋œ Node ์—๋งŒ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๋ฏ€๋กœ, ๋‹ค๋ฅธ ๋…ธ๋“œ์— ๋ฐฐํฌ๋œ ํŒŒ๋“œ์™€๋Š” ๊ณต์œ ํ•  ์ˆ˜ ์—†์Œ
  • Persistent volume (PV)
    • ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค ํด๋Ÿฌ์Šคํ„ฐ ์ „์ฒด์˜ ๊ณต์œ  ๋ณผ๋ฅจ
    • ๋‹ค์–‘ํ•œ ํฌ๊ธฐ์˜ ๋ณผ๋ฅจ์„ ๊ฐ€์ง„ PV ๋ฅผ ๋ฏธ๋ฆฌ ๋งŒ๋“ค์–ด ๋‘๊ณ  Pod ๊ฐ€ ํ•„์š”ํ•  ๋•Œ ์—ฐ๊ฒฐํ•˜์—ฌ ์‚ฌ์šฉ
      • ์ด ๋•Œ, Pod ์™€ PV ๋ฅผ ์—ฐ๊ฒฐํ•˜๋Š” ์ž‘์—…์ด PVC(PersistentVolumeClaim)

Leave a Reply