Chapter 24. RBAC — 누가 무엇을 할 수 있는가
용어: RBAC(Role-Based Access Control) — 역할 기반 접근 제어. 사용자나 서비스 계정에게 역할(Role)을 부여하고, 역할에 권한을 정의하는 방식이다.
* RBAC의 핵심 4가지 리소스
1) Role: 특정 네임스페이스 안에서의 권한 정의.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: pod-reader
namespace: production
rules:
- apiGroups: [""] # 코어 API 그룹 (Pod, Service 등)
resources: ["pods"] # 대상 리소스
verbs: ["get", "list", "watch"] # 허용하는 동작
2) ClusterRole: 클러스터 전체에 적용되는 권한.
3) RoleBinding: Role을 사용자/그룹/서비스계정에 연결.
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: read-pods
namespace: production
subjects:
- kind: User
name: jane
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
4) ClusterRoleBinding: ClusterRole을 연결.
* ServiceAccount
용어: ServiceAccount — Pod 안의 프로세스가 API 서버에 접근할 때 사용하는 신원이다. 사람이 아닌 프로그램용 계정이라고 생각하면 된다.
apiVersion: v1
kind: ServiceAccount
metadata:
name: monitoring-sa
namespace: production
모든 Pod은 기본적으로 default ServiceAccount를 사용한다. 보안을 위해 필요한 최소 권한만 가진 ServiceAccount를 별도로 만들어 사용하는 것이 좋다.
Chapter 25. ResourceQuota와 LimitRange
* ResourceQuota — 네임스페이스별 자원 한도
용어: ResourceQuota — 네임스페이스가 사용할 수 있는 전체 자원의 상한선을 설정한다.
apiVersion: v1
kind: ResourceQuota
metadata:
name: compute-quota
namespace: development
spec:
hard:
requests.cpu: "4" # 이 네임스페이스의 모든 Pod의 CPU requests 합이 4코어 이하
requests.memory: 8Gi # 메모리 requests 합이 8Gi 이하
limits.cpu: "8" # CPU limits 합이 8코어 이하
limits.memory: 16Gi # 메모리 limits 합이 16Gi 이하
pods: "20" # Pod 최대 20개
services: "10" # Service 최대 10개
* LimitRange — Pod/Container 단위 기본값과 제한
용어: LimitRange — 네임스페이스 안에서 개별 Pod이나 컨테이너가 요청할 수 있는 자원의 기본값과 최소/최대를 설정한다.
apiVersion: v1
kind: LimitRange
metadata:
name: cpu-mem-limit
namespace: development
spec:
limits:
- type: Container
default: # limits 기본값 (명시하지 않으면 이 값 적용)
cpu: "500m"
memory: "256Mi"
defaultRequest: # requests 기본값
cpu: "100m"
memory: "128Mi"
max: # 최대값
cpu: "2"
memory: "2Gi"
min: # 최소값
cpu: "50m"
memory: "64Mi"
ResourceQuota가 네임스페이스 “전체”의 한도라면, LimitRange는 “개별” 컨테이너의 한도다.
Chapter 26. HPA와 VPA — 오토스케일링
* HPA — 수평 확장/축소
용어: HPA(Horizontal Pod Autoscaler) — Pod의 CPU 사용률, 메모리 사용률, 또는 사용자 정의 메트릭을 기반으로 Pod의 수(replicas)를 자동으로 늘리거나 줄이는 리소스다.
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: api-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api-server
minReplicas: 2 # 최소 2개
maxReplicas: 10 # 최대 10개
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70 # CPU 사용률 70% 기준
HPA의 동작: 평균 CPU가 70%를 넘으면 Pod 수를 늘리고, 70% 아래로 내려가면 Pod 수를 줄인다.
필수 조건: HPA가 동작하려면 Metrics Server가 클러스터에 설치되어 있어야 한다. Metrics Server가 각 Pod의 CPU/메모리 사용량을 수집하고, HPA가 이 데이터를 참조한다.
* VPA — 수직 확장/축소
용어: VPA(Vertical Pod Autoscaler) — Pod의 수가 아니라, 개별 Pod의 자원 할당량(requests/limits)을 자동으로 조정하는 것이다.
VPA는 K8s 기본 내장이 아니라 별도 설치가 필요하다. 동작 모드가 세 가지 있다:
- Off: 추천값만 제공하고 적용하지 않는다 (모니터링 용도).
- Initial: Pod이 생성될 때만 적용한다.
- Auto: 필요하면 기존 Pod을 재시작하면서 적용한다.
주의: HPA와 VPA를 같은 메트릭(CPU)으로 동시에 사용하면 충돌할 수 있다. HPA는 “Pod 수를 늘려”라고 하고, VPA는 “Pod 크기를 키워”라고 하면 혼란이 생긴다. 일반적으로 HPA를 메인으로 쓰고, VPA는 추천 모드(Off)로 적절한 requests/limits 값을 파악하는 데 활용한다.
Chapter 27. DaemonSet — 모든 노드에 하나씩
용어: DaemonSet — 클러스터의 모든 노드(또는 특정 노드)에 Pod을 딱 하나씩 실행하는 리소스다.
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: log-collector
spec:
selector:
matchLabels:
app: log-collector
template:
metadata:
labels:
app: log-collector
spec:
containers:
- name: fluentd
image: fluentd:v1.16
volumeMounts:
- name: varlog
mountPath: /var/log
volumes:
- name: varlog
hostPath:
path: /var/log
DaemonSet의 전형적인 사용 사례:
- 로그 수집: 모든 노드의 로그를 수집하는 에이전트 (Fluentd, Fluent Bit)
- 모니터링: 모든 노드의 메트릭을 수집하는 에이전트 (Prometheus Node Exporter, WhaTap 에이전트)
- 네트워크: CNI 플러그인, kube-proxy도 사실 DaemonSet으로 돌아간다
- 스토리지: 분산 스토리지 클라이언트
새 노드가 클러스터에 추가되면 자동으로 해당 노드에도 DaemonSet Pod이 생성된다. 노드가 제거되면 해당 Pod도 삭제된다.
Chapter 28. StatefulSet — 상태가 있는 애플리케이션
용어: StatefulSet — 상태가 있는(Stateful) 애플리케이션을 관리하기 위한 리소스다. Deployment와 비슷하지만 중요한 차이점이 있다.
* Deployment vs StatefulSet
Deployment로 관리하는 Pod은 “이름 없는 일꾼”이다. pod-abc123, pod-def456 같은 랜덤 이름이 붙고, 서로 구분할 필요가 없다. 웹서버처럼 어떤 Pod이든 같은 응답을 하는 Stateless 앱에 적합하다.
StatefulSet은 다르다:
- 고정된 순서와 이름: Pod 이름이 mysql-0, mysql-1, mysql-2 같이 순번이 붙는다.
- 순서대로 생성/삭제: 0번이 먼저 생성되고 정상이 돼야 1번이 생성된다. 삭제는 역순.
- 고유한 PVC: 각 Pod마다 자기만의 PV가 바인딩된다. Pod이 재생성되어도 같은 PV에 연결된다.
- 안정적인 네트워크 ID: Pod이 재생성되어도 같은 DNS 이름을 가진다.
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
serviceName: mysql-headless # Headless Service 이름 (필수)
replicas: 3
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8.0
ports:
- containerPort: 3306
volumeMounts:
- name: data
mountPath: /var/lib/mysql
volumeClaimTemplates: # 각 Pod마다 PVC를 자동 생성
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: fast-ssd
resources:
requests:
storage: 10Gi
* Headless Service
StatefulSet은 Headless Service가 필요하다.
용어: Headless Service — ClusterIP가 없는(None으로 설정된) Service다. 로드밸런싱을 하지 않고, 각 Pod에 직접 접근할 수 있는 DNS 레코드를 제공한다.
apiVersion: v1
kind: Service
metadata:
name: mysql-headless
spec:
clusterIP: None # Headless
selector:
app: mysql
ports:
- port: 3306
이렇게 하면 각 Pod에 mysql-0.mysql-headless.default.svc.cluster.local 같은 DNS 이름이 부여된다. 특정 Pod에 직접 접근해야 하는 DB 마스터/슬레이브 같은 구성에 필수다.
* 어떤 앱에 StatefulSet을 써야 하는가
- MySQL, PostgreSQL, MongoDB 같은 데이터베이스
- Kafka, RabbitMQ 같은 메시지 큐
- Elasticsearch, Redis Cluster 같이 노드마다 고유한 역할이 있는 분산 시스템
- Zookeeper, etcd 같은 분산 합의(consensus) 시스템
Chapter 29. Job과 CronJob — 일회성/주기적 작업
* Job — 한 번 실행하고 끝
용어: Job — 하나 이상의 Pod을 생성하여 지정된 수의 Pod이 성공적으로 종료될 때까지 관리하는 리소스다.
apiVersion: batch/v1
kind: Job
metadata:
name: data-migration
spec:
completions: 1 # 성공해야 하는 Pod 수
parallelism: 1 # 동시에 실행할 Pod 수
backoffLimit: 3 # 실패 시 최대 재시도 횟수
activeDeadlineSeconds: 600 # 최대 실행 시간 (초)
template:
spec:
containers:
- name: migration
image: my-migration:1.0
command: ["python", "migrate.py"]
restartPolicy: Never # Job에서는 Never 또는 OnFailure만 가능
Deployment와 달리 Job의 Pod은 작업이 끝나면 종료된다. 종료된 Pod은 삭제되지 않고 남아있어서 로그를 확인할 수 있다.
* CronJob — 주기적으로 실행
용어: CronJob — Linux의 crontab처럼 스케줄에 따라 Job을 주기적으로 실행하는 리소스다.
apiVersion: batch/v1
kind: CronJob
metadata:
name: daily-backup
spec:
schedule: "0 2 * * *" # 매일 새벽 2시 (cron 문법)
jobTemplate:
spec:
template:
spec:
containers:
- name: backup
image: backup-tool:1.0
command: ["./backup.sh"]
restartPolicy: OnFailure
successfulJobsHistoryLimit: 3 # 성공한 Job 히스토리 유지 개수
failedJobsHistoryLimit: 1 # 실패한 Job 히스토리 유지 개수
Cron 문법 빠른 참조: 분 시 일 월 요일. 예: 0 2 * * * = 매일 02:00, */5 * * * * = 5분마다, 0 0 * * 0 = 매주 일요일 자정.
Chapter 30. 프로브(Probe) — 컨테이너 상태 확인
K8s는 컨테이너가 건강한지 세 가지 방법으로 확인한다.
1) livenessProbe — 살아있는가?
컨테이너가 살아있는지 확인한다. 실패하면 kubelet이 컨테이너를 재시작한다.
spec:
containers:
- name: app
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 15 # 첫 체크까지 대기 시간
periodSeconds: 10 # 체크 간격
failureThreshold: 3 # 연속 3번 실패하면 재시작
2) readinessProbe — 트래픽 받을 준비가 됐는가?
컨테이너가 요청을 처리할 준비가 됐는지 확인한다. 실패하면 Service의 엔드포인트에서 제외된다 (트래픽을 보내지 않는다). 컨테이너를 재시작하지는 않는다.
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
3) startupProbe — 시작됐는가?
느리게 시작하는 앱을 위한 프로브다. startupProbe가 성공할 때까지 livenessProbe와 readinessProbe는 비활성화된다.
startupProbe:
httpGet:
path: /healthz
port: 8080
failureThreshold: 30 # 30번까지 실패 허용
periodSeconds: 10 # 10초마다 → 최대 5분까지 기다림
* 프로브 방식
프로브는 세 가지 방식으로 실행할 수 있다:
- httpGet: HTTP 요청을 보내서 2xx/3xx 응답이면 성공
- tcpSocket: TCP 연결이 되면 성공
- exec: 컨테이너 안에서 명령을 실행해서 종료 코드 0이면 성공
실무 팁: livenessProbe와 readinessProbe를 모두 설정하자. livenessProbe만 설정하면 앱이 아직 준비되지 않았는데 트래픽이 들어올 수 있다. readinessProbe만 설정하면 앱이 교착 상태(deadlock)에 빠졌을 때 재시작되지 않는다.
Chapter 31. Taints, Tolerations, Affinity — Pod 배치 제어
* Taints와 Tolerations
용어: Taint — 노드에 붙이는 “기피 딱지”다. Taint가 있는 노드에는 일반 Pod이 스케줄링되지 않는다.
용어: Toleration — Pod에 붙이는 “면역”이다. 특정 Taint를 용인(tolerate)하는 Pod만 해당 노드에 스케줄링된다.
# 노드에 Taint 추가
kubectl taint nodes node1 gpu=true:NoSchedule
# 이 Taint를 용인하는 Pod
spec:
tolerations:
- key: "gpu"
operator: "Equal"
value: "true"
effect: "NoSchedule"
Effect 종류:
- NoSchedule: 새 Pod 스케줄링 금지. 이미 있는 Pod은 그대로.
- PreferNoSchedule: 가능하면 스케줄링하지 않지만, 다른 곳이 없으면 허용.
- NoExecute: 새 Pod 스케줄링 금지 + 이미 있는 Pod도 퇴거(evict).
* Node Affinity — 선호하는 노드 지정
용어: Node Affinity — Pod이 특정 조건의 노드에 배치되도록 선호도를 설정하는 것이다. nodeSelector보다 표현력이 풍부하다.
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution: # 반드시 (hard)
nodeSelectorTerms:
- matchExpressions:
- key: disk-type
operator: In
values: ["ssd"]
preferredDuringSchedulingIgnoredDuringExecution: # 가능하면 (soft)
- weight: 1
preference:
matchExpressions:
- key: zone
operator: In
values: ["az-1"]
* Pod Affinity / Anti-Affinity
- Pod Affinity: “특정 Pod이 있는 노드에 같이 배치해줘” (예: 웹서버와 캐시를 같은 노드에)
- Pod Anti-Affinity: “특정 Pod이 있는 노드에는 배치하지 마” (예: 같은 앱의 복제본을 다른 노드에 분산)
Chapter 32. Helm — K8s의 패키지 매니저
* Helm이란?
용어: Helm — K8s 애플리케이션을 패키징, 배포, 관리하는 도구다. Linux의 apt/yum, Node.js의 npm과 비슷한 역할이다.
복잡한 앱(예: Prometheus 모니터링 스택)은 Deployment, Service, ConfigMap, ServiceAccount, ClusterRole 등 수십 개의 YAML 파일이 필요하다. 이걸 하나하나 만들고 관리하는 건 고통스럽다.
Helm은 이것들을 하나의 차트(Chart)로 패키징한다.
# Helm으로 Prometheus 설치 (예시)
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm install prometheus prometheus-community/kube-prometheus-stack
한 줄이면 Prometheus, Grafana, AlertManager, Node Exporter 등이 전부 설치된다.
* Chart의 구조
my-chart/
├── Chart.yaml # 차트 메타데이터 (이름, 버전)
├── values.yaml # 기본 설정값
├── templates/ # K8s YAML 템플릿
│ ├── deployment.yaml
│ ├── service.yaml
│ └── configmap.yaml
└── charts/ # 의존성 차트
values.yaml이 핵심이다. 템플릿에서 {{ .Values.replicas }} 같이 참조하고, 사용자는 values.yaml만 수정해서 원하는 설정으로 배포할 수 있다.
# values.yaml
replicas: 3
image:
repository: nginx
tag: "1.25"
resources:
requests:
cpu: 100m
memory: 128Mi
# 커스텀 값으로 설치
helm install my-app ./my-chart -f custom-values.yaml
# 업그레이드
helm upgrade my-app ./my-chart -f custom-values.yaml
# 롤백
helm rollback my-app 1
# 삭제
helm uninstall my-app
Chapter 33. 모니터링 기초
* Metrics Server
K8s 내장은 아니지만 거의 필수인 컴포넌트다. 각 노드의 kubelet에서 CPU, 메모리 사용량을 수집한다. HPA, kubectl top 명령의 데이터 소스다.
kubectl top nodes # 노드별 자원 사용량
kubectl top pods # Pod별 자원 사용량
Prometheus + Grafana (업계 표준)
- Prometheus: 메트릭 수집 및 저장. Pull 방식(Prometheus가 대상에서 데이터를 가져옴)으로 동작한다.
- Grafana: 메트릭을 시각화하는 대시보드. Prometheus 등 다양한 데이터 소스를 연결할 수 있다.
K8s 환경에서의 모니터링 구성:
Node Exporter (노드 메트릭) ──┐
kube-state-metrics (K8s 상태) ──┤──→ Prometheus ──→ Grafana (대시보드)
cAdvisor (컨테이너 메트릭) ──┘ │
└──→ AlertManager (알람)
- Node Exporter: 노드의 CPU, 메모리, 디스크, 네트워크 메트릭
- kube-state-metrics: K8s 오브젝트의 상태 (Pod 수, Deployment 상태 등)
- cAdvisor: 컨테이너 수준의 자원 사용량 (kubelet에 내장)
실무 포인트: WhaTap 같은 상용 모니터링 도구를 쓰더라도, Prometheus 기반의 메트릭 체계를 이해하고 있으면 커스텀 메트릭 설정이나 알람 설정에 큰 도움이 된다.
Chapter 34. 실무에서 알아두면 좋은 것들
* kubectl 자주 쓰는 팁
# 컨텍스트/네임스페이스 관리
kubectl config get-contexts # 사용 가능한 컨텍스트 목록
kubectl config use-context my-cluster # 컨텍스트 전환
kubectl config set-context --current --namespace=production # 기본 네임스페이스 변경
# 디버깅
kubectl describe pod 이름 # 이벤트, 상태 상세
kubectl logs 이름 -c 컨테이너명 # 멀티컨테이너 Pod에서 특정 컨테이너 로그
kubectl logs 이름 --previous # 이전(죽은) 컨테이너의 로그
kubectl get events --sort-by=.metadata.creationTimestamp # 이벤트 시간순 정렬
# 리소스 확인
kubectl explain deployment.spec.strategy # 특정 필드의 문서 확인
kubectl api-resources # 사용 가능한 리소스 종류 목록
# 빠른 YAML 생성 (dry-run으로 YAML 템플릿 뽑기)
kubectl create deployment nginx --image=nginx --dry-run=client -o yaml > deploy.yaml
* Pod이 안 뜰 때 체크리스트
- kubectl get pods — STATUS 확인 (Pending, CrashLoopBackOff, ImagePullBackOff 등)
- kubectl describe pod 이름 — Events 섹션 확인
- Pending: 스케줄링 실패 → 노드 자원 부족? nodeSelector/taint 문제?
- ImagePullBackOff: 이미지를 못 가져옴 → 이미지 이름/태그 오타? 레지스트리 접근 권한?
- CrashLoopBackOff: 컨테이너가 시작되자마자 죽음 → kubectl logs 이름으로 로그 확인
- Running인데 접속 안 됨: Service의 selector, port, targetPort 확인
* K8s 핵심 설계 원칙 요약
- 선언적 관리: 원하는 상태를 YAML로 선언하면 K8s가 맞춰준다.
- Reconciliation Loop: 현재 상태 → 원하는 상태 비교 → 차이 조정, 무한 반복.
- 모든 것이 API 객체: Pod, Service, Deployment 전부 API 서버에 등록된 객체다.
- 라벨과 셀렉터: 리소스 간 연결은 라벨로 한다. 이름이 아니라 라벨이다.
- Pod은 일시적이다: Pod에 의존하지 말고, Deployment + Service + PVC를 조합해서 안정성을 확보한다.
- 네임스페이스로 분리: 환경·팀·프로젝트별로 나누고, RBAC + ResourceQuota로 격리한다.
'k8s 이론 공부' 카테고리의 다른 글
| Deployment와 rollout 이해하기 (0) | 2026.04.10 |
|---|---|
| 부록: 핵심 용어 사전 (가나다순) (0) | 2026.04.02 |
| Part 4 — 네트워킹과 스토리지 (0) | 2026.04.02 |
| Part 3 — 핵심 리소스 (0) | 2026.04.02 |
| Part 2 — K8s 아키텍처 (0) | 2026.04.02 |