07. Kubernetes λ³΄μ•ˆ

07. Kubernetes λ³΄μ•ˆ

ν•™μŠ΅ λͺ©ν‘œ

  • Kubernetes λ³΄μ•ˆ μ•„ν‚€ν…μ²˜ 이해
  • RBAC을 ν†΅ν•œ μ ‘κ·Ό μ œμ–΄ κ΅¬ν˜„
  • NetworkPolicy둜 λ„€νŠΈμ›Œν¬ 격리
  • Secrets 및 민감 정보 관리
  • Pod λ³΄μ•ˆ μ •μ±… 적용

λͺ©μ°¨

  1. Kubernetes λ³΄μ•ˆ κ°œμš”
  2. RBAC (μ—­ν•  기반 μ ‘κ·Ό μ œμ–΄)
  3. ServiceAccount
  4. NetworkPolicy
  5. Secrets 관리
  6. Pod λ³΄μ•ˆ
  7. μ—°μŠ΅ 문제

1. Kubernetes λ³΄μ•ˆ κ°œμš”

1.1 4C λ³΄μ•ˆ λͺ¨λΈ

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                     Cloud (ν΄λΌμš°λ“œ)                         β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚                 Cluster (ν΄λŸ¬μŠ€ν„°)                    β”‚   β”‚
β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚   β”‚
β”‚  β”‚  β”‚              Container (μ»¨ν…Œμ΄λ„ˆ)             β”‚   β”‚   β”‚
β”‚  β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚   β”‚   β”‚
β”‚  β”‚  β”‚  β”‚            Code (μ½”λ“œ)               β”‚   β”‚   β”‚   β”‚
β”‚  β”‚  β”‚  β”‚  - 취약점 μŠ€μΊλ‹                      β”‚   β”‚   β”‚   β”‚
β”‚  β”‚  β”‚  β”‚  - μ˜μ‘΄μ„± 관리                        β”‚   β”‚   β”‚   β”‚
β”‚  β”‚  β”‚  β”‚  - λ³΄μ•ˆ μ½”λ”©                          β”‚   β”‚   β”‚   β”‚
β”‚  β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚   β”‚   β”‚
β”‚  β”‚  β”‚  - 이미지 λ³΄μ•ˆ                              β”‚   β”‚   β”‚
β”‚  β”‚  β”‚  - λŸ°νƒ€μž„ λ³΄μ•ˆ                              β”‚   β”‚   β”‚
β”‚  β”‚  β”‚  - λ¦¬μ†ŒμŠ€ μ œν•œ                              β”‚   β”‚   β”‚
β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚   β”‚
β”‚  β”‚  - RBAC, NetworkPolicy                            β”‚   β”‚
β”‚  β”‚  - Secrets 관리                                    β”‚   β”‚
β”‚  β”‚  - Pod λ³΄μ•ˆ                                        β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚  - λ„€νŠΈμ›Œν¬ λ³΄μ•ˆ                                           β”‚
β”‚  - IAM, λ°©ν™”λ²½                                            β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

1.2 인증과 인가

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚    User     │────▢│   API μ„œλ²„   │────▢│   λ¦¬μ†ŒμŠ€    β”‚
β”‚  (kubectl)  β”‚     β”‚              β”‚     β”‚   (Pods)    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                           β”‚
              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
              β–Ό            β–Ό            β–Ό
        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
        β”‚   인증   β”‚ β”‚   인가   β”‚ β”‚ Admissionβ”‚
        β”‚(AuthN)   β”‚ β”‚(AuthZ)   β”‚ β”‚ Control  β”‚
        β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
        β”‚β€’ μΈμ¦μ„œ  β”‚ β”‚β€’ RBAC    β”‚ β”‚β€’ 검증    β”‚
        β”‚β€’ 토큰    β”‚ β”‚β€’ ABAC    β”‚ β”‚β€’ λ³€ν™˜    β”‚
        β”‚β€’ OIDC    β”‚ β”‚β€’ Webhook β”‚ β”‚β€’ μ •μ±…    β”‚
        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

1.3 λ³΄μ•ˆ ꡬ성 μš”μ†Œ

# ν˜„μž¬ ν΄λŸ¬μŠ€ν„° λ³΄μ•ˆ μƒνƒœ 확인
# API μ„œλ²„ μ„€μ • 확인
kubectl describe pod kube-apiserver-<master-node> -n kube-system

# 인증 λͺ¨λ“œ 확인
kubectl api-versions | grep rbac
# rbac.authorization.k8s.io/v1

# ν΄λŸ¬μŠ€ν„° κΆŒν•œ 확인
kubectl auth can-i --list

2. RBAC (μ—­ν•  기반 μ ‘κ·Ό μ œμ–΄)

2.1 RBAC 핡심 κ°œλ…

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                      RBAC ꡬ성 μš”μ†Œ                          β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                             β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”‚
β”‚  β”‚     Role      β”‚                  β”‚  ClusterRole  β”‚      β”‚
β”‚  β”‚  (λ„€μž„μŠ€νŽ˜μ΄μŠ€)β”‚                  β”‚   (ν΄λŸ¬μŠ€ν„°)   β”‚      β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜                  β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜      β”‚
β”‚          β”‚                                  β”‚               β”‚
β”‚          β”‚ 바인딩                           β”‚ 바인딩        β”‚
β”‚          β–Ό                                  β–Ό               β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”‚
β”‚  β”‚ RoleBinding   β”‚                  β”‚ClusterRole    β”‚      β”‚
β”‚  β”‚               β”‚                  β”‚   Binding     β”‚      β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜                  β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜      β”‚
β”‚          β”‚                                  β”‚               β”‚
β”‚          β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜               β”‚
β”‚                         β–Ό                                   β”‚
β”‚                 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                           β”‚
β”‚                 β”‚   Subjects    β”‚                           β”‚
β”‚                 β”‚ β€’ User        β”‚                           β”‚
β”‚                 β”‚ β€’ Group       β”‚                           β”‚
β”‚                 β”‚ β€’ ServiceAcc  β”‚                           β”‚
β”‚                 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                           β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

2.2 Role μ •μ˜

# role-pod-reader.yaml
# νŠΉμ • λ„€μž„μŠ€νŽ˜μ΄μŠ€μ—μ„œ Pod 읽기 κΆŒν•œ
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: development
  name: pod-reader
rules:
- apiGroups: [""]          # "" = core API group
  resources: ["pods"]
  verbs: ["get", "watch", "list"]

---
# role-deployment-manager.yaml
# Deployment 관리 κΆŒν•œ
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: development
  name: deployment-manager
rules:
- apiGroups: ["apps"]
  resources: ["deployments"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "list", "watch"]
- apiGroups: [""]
  resources: ["pods/log"]
  verbs: ["get"]

---
# role-secret-reader.yaml
# νŠΉμ • Secret만 읽기 (resourceNames μ‚¬μš©)
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: production
  name: specific-secret-reader
rules:
- apiGroups: [""]
  resources: ["secrets"]
  resourceNames: ["app-config", "db-credentials"]  # νŠΉμ • λ¦¬μ†ŒμŠ€λ§Œ
  verbs: ["get"]

2.3 ClusterRole μ •μ˜

# clusterrole-node-reader.yaml
# ν΄λŸ¬μŠ€ν„° μ „μ²΄μ—μ„œ λ…Έλ“œ 정보 읽기
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: node-reader
rules:
- apiGroups: [""]
  resources: ["nodes"]
  verbs: ["get", "watch", "list"]

---
# clusterrole-pv-manager.yaml
# PersistentVolume 관리 (ν΄λŸ¬μŠ€ν„° λ²”μœ„ λ¦¬μ†ŒμŠ€)
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: pv-manager
rules:
- apiGroups: [""]
  resources: ["persistentvolumes"]
  verbs: ["get", "list", "watch", "create", "delete"]
- apiGroups: [""]
  resources: ["persistentvolumeclaims"]
  verbs: ["get", "list", "watch"]

---
# clusterrole-namespace-admin.yaml
# λͺ¨λ“  λ„€μž„μŠ€νŽ˜μ΄μŠ€μ—μ„œ κ΄€λ¦¬μž μ—­ν• 
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: namespace-admin
rules:
- apiGroups: [""]
  resources: ["namespaces"]
  verbs: ["get", "list", "watch", "create", "delete"]
- apiGroups: [""]
  resources: ["*"]
  verbs: ["*"]

---
# μ§‘κ³„λœ ClusterRole (Aggregation)
# clusterrole-monitoring.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: monitoring
  labels:
    rbac.example.com/aggregate-to-monitoring: "true"
aggregationRule:
  clusterRoleSelectors:
  - matchLabels:
      rbac.example.com/aggregate-to-monitoring: "true"
rules: []  # κ·œμΉ™μ€ μžλ™μœΌλ‘œ 집계됨

2.4 RoleBinding & ClusterRoleBinding

# rolebinding-pod-reader.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-pods
  namespace: development
subjects:
- kind: User
  name: jane
  apiGroup: rbac.authorization.k8s.io
- kind: Group
  name: developers
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

---
# rolebinding-sa.yaml
# ServiceAccount에 μ—­ν•  바인딩
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: app-deployment-binding
  namespace: development
subjects:
- kind: ServiceAccount
  name: app-deployer
  namespace: development
roleRef:
  kind: Role
  name: deployment-manager
  apiGroup: rbac.authorization.k8s.io

---
# clusterrolebinding.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: node-reader-binding
subjects:
- kind: Group
  name: ops-team
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: node-reader
  apiGroup: rbac.authorization.k8s.io

---
# ClusterRole을 νŠΉμ • λ„€μž„μŠ€νŽ˜μ΄μŠ€μ— 바인딩
# (ClusterRole μž¬μ‚¬μš©)
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: admin-binding
  namespace: staging
subjects:
- kind: User
  name: admin-user
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole      # ClusterRoleμ΄μ§€λ§Œ
  name: admin            # RoleBinding으둜 λ²”μœ„ μ œν•œ
  apiGroup: rbac.authorization.k8s.io

2.5 RBAC ν…ŒμŠ€νŠΈ 및 디버깅

# κΆŒν•œ 확인
kubectl auth can-i create pods --namespace development
# yes

kubectl auth can-i delete pods --namespace production --as jane
# no

kubectl auth can-i '*' '*' --all-namespaces --as system:serviceaccount:default:admin
# yes

# νŠΉμ • μ‚¬μš©μžμ˜ λͺ¨λ“  κΆŒν•œ 확인
kubectl auth can-i --list --as jane --namespace development

# RBAC λ¦¬μ†ŒμŠ€ 쑰회
kubectl get roles -n development
kubectl get rolebindings -n development
kubectl get clusterroles
kubectl get clusterrolebindings

# 상세 정보
kubectl describe role pod-reader -n development
kubectl describe rolebinding read-pods -n development

3. ServiceAccount

3.1 ServiceAccount κΈ°λ³Έ

# serviceaccount.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: app-service-account
  namespace: production
  annotations:
    description: "Application service account for production"
# Kubernetes 1.24+μ—μ„œλŠ” 토큰이 μžλ™ μƒμ„±λ˜μ§€ μ•ŠμŒ

---
# 토큰 생성 (Kubernetes 1.24+)
apiVersion: v1
kind: Secret
metadata:
  name: app-sa-token
  namespace: production
  annotations:
    kubernetes.io/service-account.name: app-service-account
type: kubernetes.io/service-account-token

3.2 Podμ—μ„œ ServiceAccount μ‚¬μš©

# pod-with-sa.yaml
apiVersion: v1
kind: Pod
metadata:
  name: app-pod
  namespace: production
spec:
  serviceAccountName: app-service-account
  automountServiceAccountToken: true  # 토큰 μžλ™ 마운트
  containers:
  - name: app
    image: myapp:latest
    # 토큰은 /var/run/secrets/kubernetes.io/serviceaccount/에 마운트됨

---
# 토큰 마운트 λΉ„ν™œμ„±ν™” (λ³΄μ•ˆ κ°•ν™”)
apiVersion: v1
kind: Pod
metadata:
  name: secure-pod
spec:
  serviceAccountName: restricted-sa
  automountServiceAccountToken: false  # 토큰 λΉ„λ§ˆμš΄νŠΈ
  containers:
  - name: app
    image: myapp:latest

3.3 ServiceAccountλ₯Ό μœ„ν•œ RBAC

# CI/CD νŒŒμ΄ν”„λΌμΈμš© ServiceAccount μ˜ˆμ‹œ
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: cicd-deployer
  namespace: cicd

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: cicd-deployer-role
rules:
# Deployment 관리
- apiGroups: ["apps"]
  resources: ["deployments", "replicasets"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
# Service 관리
- apiGroups: [""]
  resources: ["services"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
# ConfigMap, Secret 읽기
- apiGroups: [""]
  resources: ["configmaps", "secrets"]
  verbs: ["get", "list", "watch"]
# Pod μƒνƒœ 확인
- apiGroups: [""]
  resources: ["pods", "pods/log"]
  verbs: ["get", "list", "watch"]

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: cicd-deployer-binding
subjects:
- kind: ServiceAccount
  name: cicd-deployer
  namespace: cicd
roleRef:
  kind: ClusterRole
  name: cicd-deployer-role
  apiGroup: rbac.authorization.k8s.io

3.4 ServiceAccount 토큰 μ‚¬μš©

# ServiceAccount 토큰 κ°€μ Έμ˜€κΈ°
TOKEN=$(kubectl create token app-service-account -n production)

# λ˜λŠ” Secretμ—μ„œ κ°€μ Έμ˜€κΈ°
TOKEN=$(kubectl get secret app-sa-token -n production -o jsonpath='{.data.token}' | base64 -d)

# ν† ν°μœΌλ‘œ API 호좜
curl -k -H "Authorization: Bearer $TOKEN" \
  https://kubernetes.default.svc/api/v1/namespaces/production/pods

# kubeconfig 생성
kubectl config set-credentials sa-user --token=$TOKEN
kubectl config set-context sa-context --cluster=my-cluster --user=sa-user

4. NetworkPolicy

4.1 NetworkPolicy κ°œμš”

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    NetworkPolicy λ™μž‘                        β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                             β”‚
β”‚  NetworkPolicy μ—†μŒ:                                        β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”                           β”‚
β”‚  β”‚Pod A│◀───▢│Pod B│◀───▢│Pod Cβ”‚  λͺ¨λ“  νŠΈλž˜ν”½ ν—ˆμš©          β”‚
β”‚  β””β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”˜                           β”‚
β”‚                                                             β”‚
β”‚  NetworkPolicy 적용:                                        β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”                           β”‚
β”‚  β”‚Pod A│────▢│Pod Bβ”‚  βœ—  β”‚Pod Cβ”‚  정책에 따라 μ œν•œ          β”‚
β”‚  β””β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”˜                           β”‚
β”‚                                                             β”‚
β”‚  ⚠️  주의: CNI ν”ŒλŸ¬κ·ΈμΈμ΄ NetworkPolicyλ₯Ό 지원해야 함        β”‚
β”‚      (Calico, Cilium, Weave Net λ“±)                        β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

4.2 κΈ°λ³Έ NetworkPolicy

# deny-all-ingress.yaml
# 기본적으둜 λͺ¨λ“  μΈλ°”μš΄λ“œ νŠΈλž˜ν”½ κ±°λΆ€
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-all-ingress
  namespace: production
spec:
  podSelector: {}  # λͺ¨λ“  Pod에 적용
  policyTypes:
  - Ingress
  # ingress κ·œμΉ™ μ—†μŒ = λͺ¨λ“  μΈλ°”μš΄λ“œ κ±°λΆ€

---
# deny-all-egress.yaml
# λͺ¨λ“  μ•„μ›ƒλ°”μš΄λ“œ νŠΈλž˜ν”½ κ±°λΆ€
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-all-egress
  namespace: production
spec:
  podSelector: {}
  policyTypes:
  - Egress
  # egress κ·œμΉ™ μ—†μŒ = λͺ¨λ“  μ•„μ›ƒλ°”μš΄λ“œ κ±°λΆ€

---
# default-deny-all.yaml
# λͺ¨λ“  νŠΈλž˜ν”½ κ±°λΆ€ (κ°€μž₯ μ œν•œμ )
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: production
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress

4.3 ν—ˆμš© μ •μ±…

# allow-frontend-to-backend.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-frontend-to-backend
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: backend
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: frontend
    ports:
    - protocol: TCP
      port: 8080

---
# allow-backend-to-database.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-backend-to-database
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: database
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: backend
    ports:
    - protocol: TCP
      port: 5432

---
# λ‹€λ₯Έ λ„€μž„μŠ€νŽ˜μ΄μŠ€μ—μ„œμ˜ μ ‘κ·Ό ν—ˆμš©
# allow-from-monitoring.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-from-monitoring
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: backend
  policyTypes:
  - Ingress
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          name: monitoring
      podSelector:
        matchLabels:
          app: prometheus
    ports:
    - protocol: TCP
      port: 9090

4.4 볡합 μ •μ±…

# comprehensive-policy.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: api-server-policy
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: api-server
  policyTypes:
  - Ingress
  - Egress
  ingress:
  # 1. 같은 λ„€μž„μŠ€νŽ˜μ΄μŠ€μ˜ frontendμ—μ„œ ν—ˆμš©
  - from:
    - podSelector:
        matchLabels:
          app: frontend
    ports:
    - protocol: TCP
      port: 443
  # 2. Ingress Controllerμ—μ„œ ν—ˆμš©
  - from:
    - namespaceSelector:
        matchLabels:
          name: ingress-nginx
    ports:
    - protocol: TCP
      port: 443
  # 3. νŠΉμ • IP λŒ€μ—­μ—μ„œ ν—ˆμš©
  - from:
    - ipBlock:
        cidr: 10.0.0.0/8
        except:
        - 10.0.1.0/24  # 이 λŒ€μ—­μ€ μ œμ™Έ
    ports:
    - protocol: TCP
      port: 443
  egress:
  # 1. λ°μ΄ν„°λ² μ΄μŠ€λ‘œ μ•„μ›ƒλ°”μš΄λ“œ
  - to:
    - podSelector:
        matchLabels:
          app: database
    ports:
    - protocol: TCP
      port: 5432
  # 2. μΊμ‹œ μ„œλ²„λ‘œ μ•„μ›ƒλ°”μš΄λ“œ
  - to:
    - podSelector:
        matchLabels:
          app: redis
    ports:
    - protocol: TCP
      port: 6379
  # 3. DNS ν—ˆμš© (ν•„μˆ˜!)
  - to:
    - namespaceSelector: {}
      podSelector:
        matchLabels:
          k8s-app: kube-dns
    ports:
    - protocol: UDP
      port: 53
    - protocol: TCP
      port: 53

4.5 NetworkPolicy 디버깅

# NetworkPolicy 쑰회
kubectl get networkpolicy -n production
kubectl describe networkpolicy api-server-policy -n production

# Pod λ ˆμ΄λΈ” 확인
kubectl get pods -n production --show-labels

# μ—°κ²° ν…ŒμŠ€νŠΈ
kubectl run test-pod --rm -it --image=busybox -n production -- /bin/sh
# Pod λ‚΄λΆ€μ—μ„œ
wget -qO- --timeout=2 http://backend-service:8080
nc -zv database-service 5432

# CNI ν”ŒλŸ¬κ·ΈμΈ 확인
kubectl get pods -n kube-system | grep -E "calico|cilium|weave"

5. Secrets 관리

5.1 Secret μœ ν˜•

# 1. Opaque (일반 데이터)
apiVersion: v1
kind: Secret
metadata:
  name: app-secrets
  namespace: production
type: Opaque
data:
  # base64 인코딩 ν•„μš”
  username: YWRtaW4=         # admin
  password: cGFzc3dvcmQxMjM=  # password123
stringData:
  # stringDataλŠ” 인코딩 λΆˆν•„μš”
  api-key: my-secret-api-key

---
# 2. kubernetes.io/dockerconfigjson (μ»¨ν…Œμ΄λ„ˆ λ ˆμ§€μŠ€νŠΈλ¦¬)
apiVersion: v1
kind: Secret
metadata:
  name: docker-registry-secret
type: kubernetes.io/dockerconfigjson
data:
  .dockerconfigjson: eyJhdXRocyI6eyJodHRwczovL2luZGV4LmRvY2tlci5pby92MS8iOnsidXNlcm5hbWUiOiJ1c2VyIiwicGFzc3dvcmQiOiJwYXNzIiwiYXV0aCI6ImRYTmxjanB3WVhOeiJ9fX0=

---
# 3. kubernetes.io/tls (TLS μΈμ¦μ„œ)
apiVersion: v1
kind: Secret
metadata:
  name: tls-secret
type: kubernetes.io/tls
data:
  tls.crt: LS0tLS1CRUdJTi...
  tls.key: LS0tLS1CRUdJTi...

---
# 4. kubernetes.io/basic-auth
apiVersion: v1
kind: Secret
metadata:
  name: basic-auth
type: kubernetes.io/basic-auth
stringData:
  username: admin
  password: t0p-Secret

5.2 Secret 생성 λͺ…λ Ήμ–΄

# Opaque Secret (literal)
kubectl create secret generic db-credentials \
  --from-literal=username=admin \
  --from-literal=password=secret123 \
  -n production

# νŒŒμΌμ—μ„œ 생성
kubectl create secret generic ssh-key \
  --from-file=ssh-privatekey=~/.ssh/id_rsa \
  --from-file=ssh-publickey=~/.ssh/id_rsa.pub

# Docker λ ˆμ§€μŠ€νŠΈλ¦¬ μ‹œν¬λ¦Ώ
kubectl create secret docker-registry regcred \
  --docker-server=ghcr.io \
  --docker-username=myuser \
  --docker-password=mytoken \
  --docker-email=user@example.com

# TLS μ‹œν¬λ¦Ώ
kubectl create secret tls app-tls \
  --cert=path/to/cert.pem \
  --key=path/to/key.pem

5.3 Secret μ‚¬μš©

# ν™˜κ²½ λ³€μˆ˜λ‘œ μ‚¬μš©
apiVersion: v1
kind: Pod
metadata:
  name: app-with-secrets
spec:
  containers:
  - name: app
    image: myapp:latest
    env:
    # νŠΉμ • ν‚€λ§Œ μ‚¬μš©
    - name: DB_USERNAME
      valueFrom:
        secretKeyRef:
          name: db-credentials
          key: username
    - name: DB_PASSWORD
      valueFrom:
        secretKeyRef:
          name: db-credentials
          key: password
    # 전체 Secret을 ν™˜κ²½λ³€μˆ˜λ‘œ
    envFrom:
    - secretRef:
        name: app-secrets

---
# λ³Όλ₯¨μœΌλ‘œ 마운트
apiVersion: v1
kind: Pod
metadata:
  name: app-with-secret-volume
spec:
  containers:
  - name: app
    image: myapp:latest
    volumeMounts:
    - name: secret-volume
      mountPath: /etc/secrets
      readOnly: true
    - name: tls-volume
      mountPath: /etc/tls
      readOnly: true
  volumes:
  - name: secret-volume
    secret:
      secretName: app-secrets
      # νŠΉμ • ν‚€λ§Œ 마운트
      items:
      - key: api-key
        path: api-key.txt
        mode: 0400  # 파일 κΆŒν•œ
  - name: tls-volume
    secret:
      secretName: tls-secret

---
# 이미지 Pull Secret
apiVersion: v1
kind: Pod
metadata:
  name: private-image-pod
spec:
  containers:
  - name: app
    image: ghcr.io/myorg/private-app:latest
  imagePullSecrets:
  - name: regcred

5.4 Secret λ³΄μ•ˆ κ°•ν™”

# Secret μ•”ν˜Έν™” μ„€μ • (kube-apiserver)
# /etc/kubernetes/encryption-config.yaml
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
      - secrets
    providers:
      - aescbc:
          keys:
            - name: key1
              secret: <base64-encoded-32-byte-key>
      - identity: {}  # 폴백 (μ•”ν˜Έν™” μ•ˆ 됨)

---
# RBAC으둜 Secret μ ‘κ·Ό μ œν•œ
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: secret-reader
  namespace: production
rules:
- apiGroups: [""]
  resources: ["secrets"]
  resourceNames: ["app-secrets"]  # νŠΉμ • Secret만
  verbs: ["get"]

5.5 μ™ΈλΆ€ Secret 관리 도ꡬ

# External Secrets Operator μ˜ˆμ‹œ
# AWS Secrets Managerμ—μ„œ κ°€μ Έμ˜€κΈ°
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: aws-secret
  namespace: production
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: aws-secretsmanager
    kind: SecretStore
  target:
    name: db-credentials  # 생성될 K8s Secret 이름
  data:
  - secretKey: username
    remoteRef:
      key: production/db-credentials
      property: username
  - secretKey: password
    remoteRef:
      key: production/db-credentials
      property: password

---
# Sealed Secrets (GitOps용)
# kubeseal둜 μ•”ν˜Έν™”
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
  name: mysecret
  namespace: production
spec:
  encryptedData:
    password: AgBy8hCi...μ•”ν˜Έν™”λœλ°μ΄ν„°...

6. Pod λ³΄μ•ˆ

6.1 Pod Security Standards

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚              Pod Security Standards (PSS)                    β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                             β”‚
β”‚  Privileged (κΆŒν•œ)                                          β”‚
β”‚  β”œβ”€β”€ μ œν•œ μ—†μŒ                                              β”‚
β”‚  └── μ‹œμŠ€ν…œ Pod용                                           β”‚
β”‚                                                             β”‚
β”‚  Baseline (κΈ°μ€€)                                            β”‚
β”‚  β”œβ”€β”€ μ•Œλ €μ§„ κΆŒν•œ μƒμŠΉ λ°©μ§€                                   β”‚
β”‚  β”œβ”€β”€ hostNetwork, hostPID κΈˆμ§€                              β”‚
β”‚  └── λŒ€λΆ€λΆ„μ˜ μ›Œν¬λ‘œλ“œμ— 적합                                β”‚
β”‚                                                             β”‚
β”‚  Restricted (μ œν•œ)                                          β”‚
β”‚  β”œβ”€β”€ κ°•λ ₯ν•œ λ³΄μ•ˆ μ •μ±…                                       β”‚
β”‚  β”œβ”€β”€ λΉ„root μ‹€ν–‰ ν•„μˆ˜                                       β”‚
β”‚  β”œβ”€β”€ 읽기 μ „μš© 루트 νŒŒμΌμ‹œμŠ€ν…œ                              β”‚
β”‚  └── λ³΄μ•ˆ 민감 μ›Œν¬λ‘œλ“œμš©                                   β”‚
β”‚                                                             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

6.2 Pod Security Admission

# λ„€μž„μŠ€νŽ˜μ΄μŠ€μ— λ³΄μ•ˆ 레벨 적용
apiVersion: v1
kind: Namespace
metadata:
  name: production
  labels:
    # enforce: μœ„λ°˜ μ‹œ κ±°λΆ€
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/enforce-version: latest
    # audit: 감사 λ‘œκ·Έμ— 기둝
    pod-security.kubernetes.io/audit: restricted
    pod-security.kubernetes.io/audit-version: latest
    # warn: κ²½κ³  λ©”μ‹œμ§€ ν‘œμ‹œ
    pod-security.kubernetes.io/warn: restricted
    pod-security.kubernetes.io/warn-version: latest

---
# baseline 레벨 λ„€μž„μŠ€νŽ˜μ΄μŠ€
apiVersion: v1
kind: Namespace
metadata:
  name: staging
  labels:
    pod-security.kubernetes.io/enforce: baseline
    pod-security.kubernetes.io/warn: restricted

6.3 λ³΄μ•ˆ μ»¨ν…μŠ€νŠΈ

# secure-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: secure-pod
spec:
  # Pod 레벨 λ³΄μ•ˆ μ»¨ν…μŠ€νŠΈ
  securityContext:
    runAsNonRoot: true
    runAsUser: 1000
    runAsGroup: 3000
    fsGroup: 2000
    seccompProfile:
      type: RuntimeDefault

  containers:
  - name: app
    image: myapp:latest
    # μ»¨ν…Œμ΄λ„ˆ 레벨 λ³΄μ•ˆ μ»¨ν…μŠ€νŠΈ
    securityContext:
      allowPrivilegeEscalation: false
      readOnlyRootFilesystem: true
      capabilities:
        drop:
          - ALL
        # ν•„μš”ν•œ capability만 μΆ”κ°€
        # add:
        #   - NET_BIND_SERVICE

    # λ¦¬μ†ŒμŠ€ μ œν•œ
    resources:
      limits:
        cpu: "500m"
        memory: "128Mi"
      requests:
        cpu: "250m"
        memory: "64Mi"

    # μž„μ‹œ λ³Όλ₯¨ (읽기 μ „μš© λ£¨νŠΈμ—μ„œ μ“°κΈ° ν•„μš” μ‹œ)
    volumeMounts:
    - name: tmp
      mountPath: /tmp
    - name: cache
      mountPath: /app/cache

  volumes:
  - name: tmp
    emptyDir: {}
  - name: cache
    emptyDir:
      sizeLimit: 100Mi

6.4 κ³ κΈ‰ λ³΄μ•ˆ μ„€μ •

# highly-secure-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: secure-app
  namespace: production
spec:
  replicas: 3
  selector:
    matchLabels:
      app: secure-app
  template:
    metadata:
      labels:
        app: secure-app
    spec:
      # ServiceAccount 토큰 λΉ„λ§ˆμš΄νŠΈ
      automountServiceAccountToken: false

      # Pod λ³΄μ•ˆ μ»¨ν…μŠ€νŠΈ
      securityContext:
        runAsNonRoot: true
        runAsUser: 65534  # nobody
        runAsGroup: 65534
        fsGroup: 65534
        seccompProfile:
          type: RuntimeDefault

      containers:
      - name: app
        image: myapp:latest
        imagePullPolicy: Always

        securityContext:
          allowPrivilegeEscalation: false
          readOnlyRootFilesystem: true
          capabilities:
            drop:
              - ALL

        # 포트
        ports:
        - containerPort: 8080
          protocol: TCP

        # λ¦¬μ†ŒμŠ€ μ œν•œ
        resources:
          limits:
            cpu: "1"
            memory: "512Mi"
          requests:
            cpu: "100m"
            memory: "128Mi"

        # ν—¬μŠ€ 체크
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 10
          periodSeconds: 10

        readinessProbe:
          httpGet:
            path: /ready
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 5

        volumeMounts:
        - name: tmp
          mountPath: /tmp
        - name: config
          mountPath: /etc/app
          readOnly: true

      volumes:
      - name: tmp
        emptyDir:
          medium: Memory
          sizeLimit: 64Mi
      - name: config
        configMap:
          name: app-config

      # 호슀트 λ„€νŠΈμ›Œν¬/PID μ‚¬μš© κΈˆμ§€
      hostNetwork: false
      hostPID: false
      hostIPC: false

      # DNS μ •μ±…
      dnsPolicy: ClusterFirst

6.5 λ³΄μ•ˆ μŠ€μΊλ‹

# 이미지 취약점 μŠ€μΊλ‹ (Trivy)
trivy image myapp:latest

# ν΄λŸ¬μŠ€ν„° λ³΄μ•ˆ μŠ€μΊ” (kubescape)
kubescape scan framework nsa --exclude-namespaces kube-system

# Pod λ³΄μ•ˆ 검사 (kube-bench)
kubectl apply -f https://raw.githubusercontent.com/aquasecurity/kube-bench/main/job.yaml
kubectl logs job/kube-bench

# OPA/Gatekeeper μ •μ±… 확인
kubectl get constrainttemplates
kubectl get constraints

7. μ—°μŠ΅ 문제

μ—°μŠ΅ 1: κ°œλ°œνŒ€ RBAC ꡬ성

# μš”κ΅¬μ‚¬ν•­:
# - development λ„€μž„μŠ€νŽ˜μ΄μŠ€μ—μ„œ κ°œλ°œμžλŠ” Pod, Deployment, Service 관리 κ°€λŠ₯
# - production λ„€μž„μŠ€νŽ˜μ΄μŠ€μ—μ„œλŠ” Pod 쑰회만 κ°€λŠ₯
# - Secret μ ‘κ·Ό λΆˆκ°€

# Role 및 RoleBinding μž‘μ„±

μ—°μŠ΅ 2: λ§ˆμ΄ν¬λ‘œμ„œλΉ„μŠ€ NetworkPolicy

# μš”κ΅¬μ‚¬ν•­:
# - frontend -> api-gateway -> backend -> database μˆœμ„œλ‘œλ§Œ 톡신
# - monitoring λ„€μž„μŠ€νŽ˜μ΄μŠ€μ—μ„œ λͺ¨λ“  Pod의 /metrics μ ‘κ·Ό ν—ˆμš©
# - μ™ΈλΆ€μ—μ„œ frontend만 μ ‘κ·Ό κ°€λŠ₯

# NetworkPolicy μž‘μ„±

μ—°μŠ΅ 3: μ•ˆμ „ν•œ μ• ν”Œλ¦¬μΌ€μ΄μ…˜ 배포

# μš”κ΅¬μ‚¬ν•­:
# - λΉ„root μ‚¬μš©μžλ‘œ μ‹€ν–‰
# - 읽기 μ „μš© 루트 νŒŒμΌμ‹œμŠ€ν…œ
# - λͺ¨λ“  capability 제거
# - λ¦¬μ†ŒμŠ€ μ œν•œ μ„€μ •
# - Secret을 ν™˜κ²½λ³€μˆ˜μ™€ λ³Όλ₯¨μœΌλ‘œ 마운트

# Deployment μž‘μ„±

μ—°μŠ΅ 4: λ³΄μ•ˆ 감사

# λ‹€μŒ ν•­λͺ© 점검:
# 1. ν΄λŸ¬μŠ€ν„°μ—μ„œ privileged Pod μ°ΎκΈ°
# 2. default ServiceAccount μ‚¬μš©ν•˜λŠ” Pod μ°ΎκΈ°
# 3. Secret이 ν™˜κ²½λ³€μˆ˜λ‘œ λ…ΈμΆœλœ Pod μ°ΎκΈ°
# 4. NetworkPolicyκ°€ μ—†λŠ” λ„€μž„μŠ€νŽ˜μ΄μŠ€ μ°ΎκΈ°

# λͺ…λ Ήμ–΄ μž‘μ„±

λ‹€μŒ 단계

참고 자료


← 이전: Docker Compose | λ‹€μŒ: Kubernetes 심화 β†’ | λͺ©μ°¨

to navigate between lessons