08. Kubernetes μ¬ν
08. Kubernetes μ¬ν¶
νμ΅ λͺ©ν¶
- Ingressλ₯Ό ν΅ν μΈλΆ νΈλν½ λΌμ°ν
- StatefulSetμΌλ‘ μν μλ μ ν리μΌμ΄μ κ΄λ¦¬
- PersistentVolume/PersistentVolumeClaim νμ©
- ConfigMapκ³Ό Secret κ³ κΈ μ¬μ©λ²
- DaemonSetκ³Ό Job νμ©
λͺ©μ°¨¶
- Ingress
- StatefulSet
- μꡬ μ€ν 리μ§
- ConfigMap κ³ κΈ
- DaemonSetκ³Ό Job
- κ³ κΈ μ€μΌμ€λ§
- μ°μ΅ λ¬Έμ
1. Ingress¶
1.1 Ingress κ°λ ¶
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Ingress μν€ν
μ² β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β μΈν°λ· β
β β β
β βΌ β
β βββββββββββββββββββββββββββββββββββββββββββββ β
β β Ingress Controller β β
β β (nginx, traefik, haproxy λ±) β β
β βββββββββββββββββββββ¬ββββββββββββββββββββββββ β
β β β
β βββββββββββββββΌββββββββββββββ β
β β β β β
β βΌ βΌ βΌ β
β βββββββββββ βββββββββββ βββββββββββ β
β βIngress β βIngress β βIngress β β
β βResource β βResource β βResource β β
β ββββββ¬βββββ ββββββ¬βββββ ββββββ¬βββββ β
β β β β β
β βΌ βΌ βΌ β
β βββββββββββ βββββββββββ βββββββββββ β
β βService Aβ βService Bβ βService Cβ β
β βββββββββββ βββββββββββ βββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
1.2 Ingress Controller μ€μΉ¶
# NGINX Ingress Controller μ€μΉ
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.2/deploy/static/provider/cloud/deploy.yaml
# μ€μΉ νμΈ
kubectl get pods -n ingress-nginx
kubectl get svc -n ingress-nginx
# IngressClass νμΈ
kubectl get ingressclass
1.3 κΈ°λ³Έ Ingress¶
# simple-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: simple-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
rules:
- host: myapp.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: frontend-service
port:
number: 80
---
# νΈμ€νΈ κΈ°λ° λΌμ°ν
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: host-based-ingress
spec:
ingressClassName: nginx
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-service
port:
number: 8080
- host: admin.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: admin-service
port:
number: 3000
1.4 κ²½λ‘ κΈ°λ° λΌμ°ν ¶
# path-based-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: path-based-ingress
annotations:
nginx.ingress.kubernetes.io/use-regex: "true"
spec:
ingressClassName: nginx
rules:
- host: app.example.com
http:
paths:
# /api/* β api-service
- path: /api(/|$)(.*)
pathType: ImplementationSpecific
backend:
service:
name: api-service
port:
number: 8080
# /static/* β static-service
- path: /static
pathType: Prefix
backend:
service:
name: static-service
port:
number: 80
# κΈ°λ³Έ β frontend
- path: /
pathType: Prefix
backend:
service:
name: frontend-service
port:
number: 80
1.5 TLS μ€μ ¶
# tls-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: tls-ingress
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
spec:
ingressClassName: nginx
tls:
- hosts:
- secure.example.com
secretName: tls-secret # TLS Secret
rules:
- host: secure.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: secure-service
port:
number: 443
---
# TLS Secret μμ±
apiVersion: v1
kind: Secret
metadata:
name: tls-secret
type: kubernetes.io/tls
data:
tls.crt: <base64-encoded-cert>
tls.key: <base64-encoded-key>
1.6 κ³ κΈ Ingress μ€μ ¶
# advanced-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: advanced-ingress
annotations:
# κΈ°λ³Έ μ€μ
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
nginx.ingress.kubernetes.io/proxy-read-timeout: "60"
nginx.ingress.kubernetes.io/proxy-send-timeout: "60"
# Rate Limiting
nginx.ingress.kubernetes.io/limit-rps: "100"
nginx.ingress.kubernetes.io/limit-connections: "50"
# CORS
nginx.ingress.kubernetes.io/enable-cors: "true"
nginx.ingress.kubernetes.io/cors-allow-origin: "https://frontend.example.com"
nginx.ingress.kubernetes.io/cors-allow-methods: "GET, POST, PUT, DELETE, OPTIONS"
# μΈμ¦
nginx.ingress.kubernetes.io/auth-type: basic
nginx.ingress.kubernetes.io/auth-secret: basic-auth
nginx.ingress.kubernetes.io/auth-realm: "Authentication Required"
# 컀μ€ν
ν€λ
nginx.ingress.kubernetes.io/configuration-snippet: |
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
spec:
ingressClassName: nginx
tls:
- hosts:
- api.example.com
secretName: api-tls
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-service
port:
number: 8080
2. StatefulSet¶
2.1 StatefulSet κ°λ ¶
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β StatefulSet vs Deployment β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Deployment (Stateless) β
β βββββββββ βββββββββ βββββββββ β
β βpod-xyzβ βpod-abcβ βpod-123β λλ€ μ΄λ¦, κ΅μ²΄ κ°λ₯ β
β βββββββββ βββββββββ βββββββββ β
β β
β StatefulSet (Stateful) β
β βββββββββ βββββββββ βββββββββ β
β βweb-0 β βweb-1 β βweb-2 β μμ 보μ₯, κ³ μ ID β
β β β β β β β β β β β
β βpvc-0 β βpvc-1 β βpvc-2 β κ°μ μ μ© μ€ν λ¦¬μ§ β
β βββββββββ βββββββββ βββββββββ β
β β
β νΉμ§: β
β β’ μμλλ‘ μμ±/μμ (0 β 1 β 2) β
β β’ κ³ μ λ λ€νΈμν¬ ID (pod-name.service-name) β
β β’ μꡬ μ€ν λ¦¬μ§ μ°κ²° β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
2.2 StatefulSet μ μ¶
# statefulset-example.yaml
apiVersion: v1
kind: Service
metadata:
name: web-headless
labels:
app: web
spec:
ports:
- port: 80
name: web
clusterIP: None # Headless Service
selector:
app: web
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
serviceName: "web-headless" # Headless Service μ°κ²°
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80
name: web
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
# λ³Όλ₯¨ ν΄λ μ ν
νλ¦Ώ
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: standard
resources:
requests:
storage: 1Gi
# μ
λ°μ΄νΈ μ λ΅
updateStrategy:
type: RollingUpdate
rollingUpdate:
partition: 0 # μ΄ λ²νΈ μ΄μμ Podλ§ μ
λ°μ΄νΈ
# Pod κ΄λ¦¬ μ μ±
podManagementPolicy: OrderedReady # λλ Parallel
2.3 λ°μ΄ν°λ² μ΄μ€ StatefulSet¶
# mysql-statefulset.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql-config
data:
my.cnf: |
[mysqld]
bind-address = 0.0.0.0
default_authentication_plugin = mysql_native_password
---
apiVersion: v1
kind: Secret
metadata:
name: mysql-secret
type: Opaque
stringData:
root-password: "rootpass123"
user-password: "userpass123"
---
apiVersion: v1
kind: Service
metadata:
name: mysql-headless
spec:
clusterIP: None
selector:
app: mysql
ports:
- port: 3306
---
apiVersion: v1
kind: Service
metadata:
name: mysql
spec:
selector:
app: mysql
statefulset.kubernetes.io/pod-name: mysql-0 # Primaryλ§
ports:
- port: 3306
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
serviceName: mysql-headless
replicas: 3
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
initContainers:
- name: init-mysql
image: mysql:8.0
command:
- bash
- "-c"
- |
set -ex
# Pod μΈλ±μ€μμ server-id μμ±
[[ `hostname` =~ -([0-9]+)$ ]] || exit 1
ordinal=${BASH_REMATCH[1]}
echo [mysqld] > /mnt/conf.d/server-id.cnf
echo server-id=$((100 + $ordinal)) >> /mnt/conf.d/server-id.cnf
volumeMounts:
- name: conf
mountPath: /mnt/conf.d
containers:
- name: mysql
image: mysql:8.0
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: root-password
ports:
- containerPort: 3306
volumeMounts:
- name: data
mountPath: /var/lib/mysql
- name: conf
mountPath: /etc/mysql/conf.d
- name: config
mountPath: /etc/mysql/my.cnf
subPath: my.cnf
resources:
requests:
cpu: 500m
memory: 1Gi
livenessProbe:
exec:
command: ["mysqladmin", "ping"]
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
exec:
command: ["mysql", "-h", "127.0.0.1", "-e", "SELECT 1"]
initialDelaySeconds: 5
periodSeconds: 2
volumes:
- name: conf
emptyDir: {}
- name: config
configMap:
name: mysql-config
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: fast
resources:
requests:
storage: 10Gi
2.4 StatefulSet κ΄λ¦¬¶
# StatefulSet μ‘°ν
kubectl get statefulset
kubectl describe statefulset web
# Pod νμΈ (μμλλ‘ μ΄λ¦ λΆμ¬)
kubectl get pods -l app=web
# NAME READY STATUS
# web-0 1/1 Running
# web-1 1/1 Running
# web-2 1/1 Running
# DNS νμΈ
# κ° Pod: web-0.web-headless.default.svc.cluster.local
kubectl run -it --rm debug --image=busybox -- nslookup web-0.web-headless
# μ€μΌμΌλ§
kubectl scale statefulset web --replicas=5
# λ‘€λ§ μ
λ°μ΄νΈ
kubectl set image statefulset/web nginx=nginx:1.26
# νΉμ Podλ§ μ
λ°μ΄νΈ (partition μ¬μ©)
kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"rollingUpdate":{"partition":2}}}}'
# μμ (PVCλ μ μ§λ¨)
kubectl delete statefulset web
kubectl delete pvc -l app=web # PVC μμ
3. μꡬ μ€ν 리짶
3.1 μ€ν λ¦¬μ§ κ³μΈ΅ ꡬ쑰¶
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β μ€ν λ¦¬μ§ κ³μΈ΅ ꡬ쑰 β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β βββββββββββββββββββββββββββββββββββββββββββ β
β β Pod β β
β β βββββββββββββββββββββββββββββββββββ β β
β β β Volume Mount β β β
β β β /data β β β
β β βββββββββββββββ¬ββββββββββββββββββββ β β
β βββββββββββββββββββΌββββββββββββββββββββββββ β
β β β
β βΌ β
β βββββββββββββββββββββββββββββββββββββββββββ β
β β PersistentVolumeClaim (PVC) β β
β β β’ μ€ν λ¦¬μ§ μμ² β β
β β β’ λ€μμ€νμ΄μ€ λ²μ β β
β βββββββββββββββββββ¬ββββββββββββββββββββββββ β
β β λ°μΈλ© β
β βΌ β
β βββββββββββββββββββββββββββββββββββββββββββ β
β β PersistentVolume (PV) β β
β β β’ μ€μ μ€ν λ¦¬μ§ β β
β β β’ ν΄λ¬μ€ν° λ²μ β β
β βββββββββββββββββββ¬ββββββββββββββββββββββββ β
β β β
β βΌ β
β βββββββββββββββββββββββββββββββββββββββββββ β
β β StorageClass β β
β β β’ λμ νλ‘λΉμ λ β β
β β β’ μ€ν λ¦¬μ§ μ ν μ μ β β
β βββββββββββββββββββββββββββββββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
3.2 StorageClass¶
# storageclass.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast
annotations:
storageclass.kubernetes.io/is-default-class: "true"
provisioner: kubernetes.io/gce-pd # ν΄λΌμ°λμ λ°λΌ λ€λ¦
parameters:
type: pd-ssd
reclaimPolicy: Delete # Delete, Retain
allowVolumeExpansion: true
volumeBindingMode: WaitForFirstConsumer # Immediate
---
# AWS EBS StorageClass
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: aws-fast
provisioner: ebs.csi.aws.com
parameters:
type: gp3
iops: "3000"
throughput: "125"
reclaimPolicy: Delete
allowVolumeExpansion: true
---
# λ‘컬 StorageClass
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
3.3 PersistentVolume (μ μ νλ‘λΉμ λ)¶
# pv-static.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-manual
labels:
type: local
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: manual
hostPath:
path: /mnt/data
---
# NFS PV
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-nfs
spec:
capacity:
storage: 100Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
storageClassName: nfs
nfs:
server: nfs-server.example.com
path: /exports/data
---
# AWS EBS PV
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-aws-ebs
spec:
capacity:
storage: 50Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: aws-fast
awsElasticBlockStore:
volumeID: vol-0123456789abcdef
fsType: ext4
3.4 PersistentVolumeClaim¶
# pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: app-data-pvc
namespace: production
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
storageClassName: fast # λμ νλ‘λΉμ λ
# selector: # μ μ λ°μΈλ© μ μ¬μ©
# matchLabels:
# type: local
---
# Podμμ PVC μ¬μ©
apiVersion: v1
kind: Pod
metadata:
name: app-with-storage
spec:
containers:
- name: app
image: myapp:latest
volumeMounts:
- name: data
mountPath: /app/data
volumes:
- name: data
persistentVolumeClaim:
claimName: app-data-pvc
3.5 λ³Όλ₯¨ νμ₯ λ° μ€λ μ·¶
# PVC νμ₯ (allowVolumeExpansion: true νμ)
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: expandable-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi # 5Giμμ νμ₯
storageClassName: fast
---
# VolumeSnapshot (CSI λλΌμ΄λ² νμ)
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
name: data-snapshot
spec:
volumeSnapshotClassName: csi-snapclass
source:
persistentVolumeClaimName: app-data-pvc
---
# μ€λ
μ·μμ PVC 볡μ
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: restored-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
storageClassName: fast
dataSource:
name: data-snapshot
kind: VolumeSnapshot
apiGroup: snapshot.storage.k8s.io
4. ConfigMap κ³ κΈ¶
4.1 ConfigMap μμ± λ°©λ²¶
# 리ν°λ΄λ‘ μμ±
kubectl create configmap app-config \
--from-literal=LOG_LEVEL=info \
--from-literal=MAX_CONNECTIONS=100
# νμΌλ‘ μμ±
kubectl create configmap nginx-config \
--from-file=nginx.conf
# λλ ν λ¦¬λ‘ μμ±
kubectl create configmap app-configs \
--from-file=config/
# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
# λ¨μ ν€-κ°
LOG_LEVEL: "info"
DATABASE_HOST: "db.example.com"
# νμΌ νν
app.properties: |
server.port=8080
server.host=0.0.0.0
logging.level=INFO
nginx.conf: |
server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html;
}
location /api {
proxy_pass http://backend:8080;
}
}
4.2 ConfigMap μ¬μ© λ°©λ²¶
# configmap-usage.yaml
apiVersion: v1
kind: Pod
metadata:
name: app-pod
spec:
containers:
- name: app
image: myapp:latest
# νκ²½ λ³μλ‘ μ£Όμ
env:
- name: LOG_LEVEL
valueFrom:
configMapKeyRef:
name: app-config
key: LOG_LEVEL
# μ 체 ConfigMapμ νκ²½ λ³μλ‘
envFrom:
- configMapRef:
name: app-config
prefix: APP_ # μ νμ μ λμ¬
volumeMounts:
# νμΌλ‘ λ§μ΄νΈ
- name: config-volume
mountPath: /etc/app
# νΉμ ν€λ§ λ§μ΄νΈ
- name: nginx-volume
mountPath: /etc/nginx/conf.d
volumes:
- name: config-volume
configMap:
name: app-config
# μ 체 νλͺ©
- name: nginx-volume
configMap:
name: app-config
items:
- key: nginx.conf
path: default.conf
mode: 0644
4.3 ConfigMap λ³κ²½ κ°μ§¶
# μλ 리λ‘λ (Reloader μ¬μ©)
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-deployment
annotations:
# stakater/Reloader μ΄λ
Έν
μ΄μ
reloader.stakater.com/auto: "true"
# λλ νΉμ ConfigMapλ§
configmap.reloader.stakater.com/reload: "app-config"
spec:
template:
spec:
containers:
- name: app
image: myapp:latest
volumeMounts:
- name: config
mountPath: /etc/app
volumes:
- name: config
configMap:
name: app-config
---
# Sidecarλ‘ λ³κ²½ κ°μ§
apiVersion: v1
kind: Pod
metadata:
name: app-with-reloader
spec:
containers:
- name: app
image: myapp:latest
volumeMounts:
- name: config
mountPath: /etc/app
- name: config-reloader
image: jimmidyson/configmap-reload:v0.5.0
args:
- --volume-dir=/etc/app
- --webhook-url=http://localhost:8080/-/reload
volumeMounts:
- name: config
mountPath: /etc/app
readOnly: true
volumes:
- name: config
configMap:
name: app-config
5. DaemonSetκ³Ό Job¶
5.1 DaemonSet¶
# daemonset.yaml
# λͺ¨λ λ
Έλμ Pod λ°°ν¬
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: node-exporter
labels:
app: node-exporter
spec:
selector:
matchLabels:
app: node-exporter
template:
metadata:
labels:
app: node-exporter
spec:
# νΉμ λ
Έλμλ§ λ°°ν¬
nodeSelector:
monitoring: "true"
tolerations:
# λ§μ€ν° λ
Έλμλ λ°°ν¬
- key: node-role.kubernetes.io/control-plane
operator: Exists
effect: NoSchedule
containers:
- name: node-exporter
image: prom/node-exporter:v1.6.1
ports:
- containerPort: 9100
hostPort: 9100
volumeMounts:
- name: proc
mountPath: /host/proc
readOnly: true
- name: sys
mountPath: /host/sys
readOnly: true
resources:
limits:
cpu: 200m
memory: 100Mi
requests:
cpu: 100m
memory: 50Mi
hostNetwork: true
hostPID: true
volumes:
- name: proc
hostPath:
path: /proc
- name: sys
hostPath:
path: /sys
updateStrategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
5.2 Job¶
# job.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: data-migration
spec:
# μλ£ μ‘°κ±΄
completions: 1 # μ±κ³΅ν΄μΌ ν Pod μ
parallelism: 1 # λμ μ€ν Pod μ
backoffLimit: 3 # μ€ν¨ μ μ¬μλ νμ
activeDeadlineSeconds: 600 # μ΅λ μ€ν μκ°
# μλ£ ν μμ
ttlSecondsAfterFinished: 3600
template:
spec:
restartPolicy: Never # OnFailure λλ Never
containers:
- name: migrator
image: myapp/migrator:latest
command: ["python", "migrate.py"]
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: db-secret
key: url
---
# λ³λ ¬ Job
apiVersion: batch/v1
kind: Job
metadata:
name: parallel-processing
spec:
completions: 10 # μ΄ 10κ° μλ£
parallelism: 3 # λμμ 3κ°μ©
template:
spec:
restartPolicy: OnFailure
containers:
- name: worker
image: myapp/worker:latest
5.3 CronJob¶
# cronjob.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
name: db-backup
spec:
schedule: "0 2 * * *" # λ§€μΌ μλ²½ 2μ
timeZone: "Asia/Seoul"
# λμ μ€ν μ μ±
concurrencyPolicy: Forbid # Allow, Forbid, Replace
# μμ λ°λλΌμΈ
startingDeadlineSeconds: 300
# μ±κ³΅/μ€ν¨ νμ€ν 리
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 1
# μΌμ μ€μ§
suspend: false
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: backup
image: postgres:15
command:
- /bin/sh
- -c
- |
pg_dump -h $DB_HOST -U $DB_USER -d $DB_NAME > /backup/db_$(date +%Y%m%d).sql
aws s3 cp /backup/db_$(date +%Y%m%d).sql s3://backups/
env:
- name: DB_HOST
value: "postgres"
- name: DB_USER
valueFrom:
secretKeyRef:
name: db-secret
key: username
- name: PGPASSWORD
valueFrom:
secretKeyRef:
name: db-secret
key: password
volumeMounts:
- name: backup
mountPath: /backup
volumes:
- name: backup
emptyDir: {}
6. κ³ κΈ μ€μΌμ€λ§¶
6.1 Node Affinity¶
# node-affinity.yaml
apiVersion: v1
kind: Pod
metadata:
name: gpu-pod
spec:
affinity:
nodeAffinity:
# νμ 쑰건
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: gpu
operator: In
values:
- "true"
- key: kubernetes.io/arch
operator: In
values:
- amd64
# μ νΈ μ‘°κ±΄
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
preference:
matchExpressions:
- key: gpu-type
operator: In
values:
- nvidia-a100
- weight: 50
preference:
matchExpressions:
- key: gpu-type
operator: In
values:
- nvidia-v100
containers:
- name: gpu-app
image: nvidia/cuda:12.0-base
resources:
limits:
nvidia.com/gpu: 1
6.2 Pod Affinity/Anti-Affinity¶
# pod-affinity.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-server
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
affinity:
# μΊμ Podμ κ°μ λ
Έλ μ νΈ
podAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- cache
topologyKey: kubernetes.io/hostname
# κ°μ μ±μ λ€λ₯Έ Podμ λ€λ₯Έ λ
Έλμ λ°°μΉ
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- web
topologyKey: kubernetes.io/hostname
containers:
- name: web
image: nginx:latest
6.3 Taintsμ Tolerations¶
# λ
Έλμ Taint μΆκ°
kubectl taint nodes node1 dedicated=gpu:NoSchedule
kubectl taint nodes node2 special=true:PreferNoSchedule
kubectl taint nodes node3 critical=true:NoExecute
# Taint μ κ±°
kubectl taint nodes node1 dedicated=gpu:NoSchedule-
# tolerations.yaml
apiVersion: v1
kind: Pod
metadata:
name: gpu-pod
spec:
tolerations:
# μ νν μΌμΉ
- key: "dedicated"
operator: "Equal"
value: "gpu"
effect: "NoSchedule"
# ν€λ§ μ‘΄μ¬νλ©΄ λ¨
- key: "special"
operator: "Exists"
effect: "PreferNoSchedule"
# NoExecute + tolerationSeconds
- key: "critical"
operator: "Equal"
value: "true"
effect: "NoExecute"
tolerationSeconds: 3600 # 1μκ° ν μΆμΆ
containers:
- name: app
image: myapp:latest
6.4 Topology Spread Constraints¶
# topology-spread.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: distributed-app
spec:
replicas: 6
selector:
matchLabels:
app: distributed
template:
metadata:
labels:
app: distributed
spec:
topologySpreadConstraints:
# Zone κ° κ· λ± λΆλ°°
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: distributed
# λ
Έλ κ° κ· λ± λΆλ°°
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
app: distributed
containers:
- name: app
image: myapp:latest
7. μ°μ΅ λ¬Έμ ¶
μ°μ΅ 1: λ§μ΄ν¬λ‘μλΉμ€ Ingress¶
# μꡬμ¬ν:
# - api.example.com/v1/* β api-v1-service
# - api.example.com/v2/* β api-v2-service
# - TLS μ μ©, HTTPβHTTPS 리λ€μ΄λ νΈ
# - Rate limiting μ μ©
# Ingress μμ±
μ°μ΅ 2: Redis Cluster StatefulSet¶
# μꡬμ¬ν:
# - 3κ° λ
Έλ Redis Cluster
# - κ° λ
Έλμ 1Gi PVC
# - Headless Serviceλ‘ λ
Έλ κ° ν΅μ
# - μ μ ν 리μμ€ μ ν
# StatefulSet μμ±
μ°μ΅ 3: λ‘κ·Έ μμ§ DaemonSet¶
# μꡬμ¬ν:
# - λͺ¨λ λ
Έλμμ /var/log μμ§
# - Elasticsearchλ‘ μ μ‘
# - ConfigMapμΌλ‘ μ€μ κ΄λ¦¬
# - λ§μ€ν° λ
Έλμλ λ°°ν¬
# DaemonSet μμ±
μ°μ΅ 4: λ°°μΉ λ°μ΄ν° μ²λ¦¬ Job¶
# μꡬμ¬ν:
# - 100κ° λ°μ΄ν° μ²λ¦¬ (completions: 100)
# - 10κ°μ© λ³λ ¬ μ²λ¦¬ (parallelism: 10)
# - μ€ν¨ μ 3ν μ¬μλ
# - 2μκ° λ΄ μλ£ νμ
# - μλ£ ν 24μκ° λ€ μμ
# Job μμ±
λ€μ λ¨κ³¶
- 09_Helm_ν¨ν€μ§κ΄λ¦¬ - Helm μ°¨νΈ
- 10_CI_CD_νμ΄νλΌμΈ - μλν λ°°ν¬
- 07_Kubernetes_보μ - 보μ 볡μ΅
μ°Έκ³ μλ£¶
β μ΄μ : Kubernetes 보μ | λ€μ: Helm ν¨ν€μ§κ΄λ¦¬ β | λͺ©μ°¨