오늘은 쿠버네티스의 보안과 운영 관련 핵심 개념들을 집중적으로 다뤘다. 어제 배운 PV/PVC, ConfigMap, Secret을 활용한 3 Tier 과제 풀이로 시작해서, 접근 제어(RBAC), 자동 스토리지 프로비저닝, 그리고 Pod 상태 관리까지 이어졌다.
공식 정의: RBAC는 조직 내 개별 사용자의 역할(Role)을 기반으로 컴퓨터 또는 네트워크 리소스에 대한 접근을 규제하는 방법이다. 쿠버네티스에서는
rbac.authorization.k8s.ioAPI 그룹을 통해 구현되며, 관리자가 어떤 사용자나 서비스 계정이 어떤 리소스에 어떤 작업을 수행할 수 있는지 동적으로 구성할 수 있다.
— Kubernetes 공식 문서
쿠버네티스에서 누가 무엇을 할 수 있는지 제어하는 메커니즘이다.
AWS IAM과 비교하면 이해가 쉽다.
| 구분 | k8s | AWS |
|---|---|---|
| 권한 정의 | Role / ClusterRole | Policy |
| 권한 부여 | RoleBinding / ClusterRoleBinding | Role에 Policy 연결 |
| 대상 | ServiceAccount (SA) | IAM User / Role |
주의: k8s의 Role과 AWS의 Role은 이름은 같지만 개념이 다르다. k8s Role은 AWS의 Policy에 가깝다.
쿠버네티스 RBAC의 4가지 핵심 오브젝트는 다음과 같다.
| 오브젝트 | 범위 | 설명 |
|---|---|---|
| Role | 네임스페이스 | 특정 네임스페이스 내 리소스에 대한 권한 집합 |
| ClusterRole | 클러스터 전체 | 모든 네임스페이스 또는 클러스터 수준 리소스에 대한 권한 집합 |
| RoleBinding | 네임스페이스 | Role을 특정 사용자/SA에 연결 |
| ClusterRoleBinding | 클러스터 전체 | ClusterRole을 특정 사용자/SA에 연결 |
구성 순서는 이렇다.
SA 생성 → ClusterRole(권한) 생성 → ClusterRoleBinding으로 SA에 연결
전체 구성 순서는 SA → Secret(토큰) → ClusterRole → ClusterRoleBinding 순이다.
# 1. ServiceAccount 생성
apiVersion: v1
kind: ServiceAccount
metadata:
name: pod-reader-sa
---
# 2. SA 인증용 Secret(토큰) 생성
apiVersion: v1
kind: Secret
metadata:
name: pod-reader-token
namespace: default
annotations:
kubernetes.io/service-account.name: pod-reader-sa
type: kubernetes.io/service-account-token
---
# 3. ClusterRole - pod 조회만 허용
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: pod-reader-crole
rules:
- apiGroups: [""] # "" = core API group (Pod, Service, ConfigMap 등)
resources: ["pods"]
verbs: ["get", "list"] # 허용할 동작
---
# 4. ClusterRoleBinding - SA에 ClusterRole 연결
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: pod-reader-crolebinding
subjects:
- kind: ServiceAccount
name: pod-reader-sa
namespace: default
roleRef:
kind: ClusterRole
name: pod-reader-crole
apiGroup: rbac.authorization.k8s.io
# SA 토큰 추출 (base64 디코딩)
kubectl get secret pod-reader-token -o jsonpath='{.data.token}' | base64 --decode
# 기존 kubeconfig를 복사해서 토큰만 교체
cp ~/.kube/config ./myconfig
# 특정 kubeconfig로 명령 실행
kubectl get pods --kubeconfig=myconfig # ✅ 성공 (pod 조회 권한 있음)
kubectl get svc --kubeconfig=myconfig # ❌ 실패 (svc 조회 권한 없음)
결과: pod는 조회되지만 svc는
Error from server (Forbidden)오류가 발생한다. RBAC가 정확히 동작하는 것을 확인할 수 있다.
공식 정의: Dynamic Volume Provisioning은 스토리지 볼륨을 온디맨드로 자동 생성할 수 있게 해준다. 정적 프로비저닝에서는 클러스터 관리자가 PV를 수동으로 미리 생성해야 했지만, 동적 프로비저닝을 사용하면 사용자가 PVC를 생성할 때 StorageClass에 정의된 프로비저너가 자동으로 PV를 생성한다.
— Kubernetes 공식 문서
어제 배운 PV/PVC는 관리자가 PV를 미리 만들어둬야 했다. Dynamic Provisioner는 PVC 요청이 오면 자동으로 PV와 디렉토리를 생성해주는 기능이다.
PVC 생성 요청
↓
StorageClass가 Dynamic Provisioner에 전달
↓
Provisioner가 자동으로 PV + NFS 디렉토리 생성
↓
PVC와 PV 자동 Bind
| 구분 | 정적(Static) | 동적(Dynamic) |
|---|---|---|
| PV 생성 시점 | PVC 요청 전 관리자가 미리 생성 | PVC 요청 시 자동 생성 |
| 관리 부담 | 높음 | 낮음 |
| 유연성 | 낮음 | 높음 |
| 필요 조건 | 없음 | StorageClass + Provisioner |
EKS에서는 EFS CSI 드라이버를 통해 동적 프로비저닝을 구현한다. gp2, gp3 같은 스토리지 클래스가 바로 이 개념이다.
핵심은 StorageClass에 provisioner를 지정하는 것이다. PVC에서 storageClassName을 명시하면 해당 프로비저너가 자동으로 PV를 만들어준다.
공식 정의: StatefulSet은 상태가 있는(stateful) 애플리케이션을 관리하기 위한 워크로드 API 오브젝트다. Deployment와 달리 각 Pod에 고유하고 지속적인 식별자(이름, 네트워크 ID, 스토리지)를 부여하며, Pod가 재스케줄링되어도 이 식별자는 유지된다.
— Kubernetes 공식 문서
일반 Deployment로 Pod를 띄우면 이름이 랜덤하게 생성된다. StatefulSet은 Pod 이름을 규칙적으로 생성한다.
# Deployment로 생성 시 (이름 예측 불가)
my-dep-7d4f9b8c6-xk2pq
my-dep-7d4f9b8c6-mn8rt
# StatefulSet으로 생성 시 (이름이 규칙적)
sts-mysql-0 ← 0번부터 순서대로 생성
sts-mysql-1
sts-mysql-2 ← 삭제는 역순 (2 → 1 → 0)
Deployment와의 차이점:
| 구분 | Deployment | StatefulSet |
|---|---|---|
| Pod 이름 | 랜덤 해시 | 순번 인덱스 (0, 1, 2...) |
| 스케일링 순서 | 동시 생성/삭제 | 순서대로 생성, 역순으로 삭제 |
| 스토리지 | 공유 가능 | 각 Pod마다 독립적인 PVC |
| 적합한 앱 | 무상태(stateless) | 상태 있는(stateful) DB, 캐시 |
serviceName을 반드시 지정해야 하고, 해당 이름의 Headless Service도 함께 만들어줘야 한다.
공식 정의: DaemonSet은 모든(또는 일부) 노드가 Pod의 복사본을 실행하도록 보장한다. 클러스터에 노드가 추가되면 해당 노드에 Pod가 자동으로 추가되고, 노드가 제거되면 Pod도 가비지 컬렉션된다.
— Kubernetes 공식 문서
모든 노드에 반드시 하나씩 Pod를 배치하고 싶을 때 사용한다.
대표적인 사용 사례:
glusterd, cephfluentd, logstashPrometheus Node Exporter, Datadog agentkube-proxy, metallb speaker노드가 추가되면 자동으로 해당 노드에도 Pod가 생성된다.
공식 정의: kubelet은 컨테이너의 상태를 진단하기 위해 Probe를 주기적으로 실행한다. 진단 결과에 따라 컨테이너를 재시작하거나 트래픽 라우팅에서 제외하는 등의 조치를 취한다.
— Kubernetes 공식 문서
| 구분 | livenessProbe | readinessProbe | startupProbe |
|---|---|---|---|
| 실패 시 동작 | 컨테이너 재시작 | 서비스에서 트래픽 차단 | liveness/readiness 비활성화 |
| 용도 | 컨테이너 이상 감지 (데드락 등) | 트래픽 수신 준비 여부 | 느린 시작 앱 보호 |
| 실패해도 Pod 삭제? | X (재시작만) | X | X |
readinessProbe가 실패하면 Pod는 살아있지만 Service의 Endpoint에서 제외된다. 즉, 트래픽이 해당 Pod로 가지 않는다.
실제로 확인해보면, readinessProbe가 실패한 Pod는 kubectl get pods에서 READY 0/1 상태로 표시된다. 그리고 kubectl describe svc로 확인하면 해당 Pod의 IP가 Endpoints에 잡히지 않는다. Pod 자체는 Running이지만 서비스에서 완전히 격리된 상태다.
# 1. httpGet - HTTP 200~399 응답 확인 (웹 서버에 적합)
readinessProbe:
httpGet:
path: /
port: 80
periodSeconds: 5 # 체크 주기 (초)
initialDelaySeconds: 2 # 컨테이너 시작 후 첫 체크까지 대기 시간
# 2. tcpSocket - TCP 포트 연결 확인 (HTTP가 아닌 앱: MySQL, Redis 등)
readinessProbe:
tcpSocket:
port: 6379
# 3. exec - 명령어 종료 코드로 확인 (0이면 성공, 그 외 실패)
readinessProbe:
exec:
command: ["mysqladmin", "ping", "-ppassword"]
| 파라미터 | 설명 | 기본값 |
|---|---|---|
initialDelaySeconds | 컨테이너 시작 후 첫 Probe까지 대기 시간 | 0 |
periodSeconds | Probe 실행 주기 | 10 |
timeoutSeconds | Probe 타임아웃 | 1 |
failureThreshold | 연속 실패 횟수 (이후 조치 실행) | 3 |
successThreshold | 연속 성공 횟수 (정상으로 판단) | 1 |
공식 정의: HPA는 CPU 사용률 또는 커스텀 메트릭을 기반으로 Deployment, ReplicaSet, StatefulSet의 Pod 수를 자동으로 스케일링한다. 수직 스케일링(VPA, 리소스 증가)과 달리 수평 스케일링(Pod 수 증가)을 담당한다.
— Kubernetes 공식 문서
트래픽에 따라 Pod 개수를 자동으로 조절하는 기능이다.
Metrics Server → 각 Pod의 CPU/메모리 수집
↓
HPA Controller → 현재 사용률 vs 목표 사용률 비교
↓
목표 Pod 수 계산: ceil(현재 Pod 수 × (현재 사용률 / 목표 사용률))
↓
Deployment replicas 자동 조정
동작하려면 Metrics Server가 필요하다. Pod의 CPU/메모리 사용량을 수집해서 HPA가 판단 기준으로 삼는다.
# Metrics Server 설치 (공식 GitHub에서 manifest 다운로드)
curl -Ls https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml -o metric.yml
# insecure-tls 옵션 추가 후 적용 (실습 환경)
kubectl apply -f metric.yml
# CPU 50% 초과 시 최소 1개, 최대 5개로 자동 스케일링
kubectl autoscale deploy my-dep --min=1 --max=5 --cpu-percent=50
# HPA 상태 확인
kubectl get hpa
# NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS
# my-dep Deployment/my-dep 8%/50% 1 5 3
# 트래픽 부하 발생 (ClusterIP로 반복 요청)
i=1; while true; do sleep 0.001; echo $((i++)) `curl -s <svc의 ClusterIP>`; done
resources.requests가 반드시 설정되어 있어야 한다. requests가 없으면 CPU 사용률 계산이 불가능하다.replicas 설정과 무관하게 HPA가 우선 적용된다.| 구분 | HPA | VPA | KEDA |
|---|---|---|---|
| 스케일링 방향 | 수평 (Pod 수) | 수직 (CPU/메모리 크기) | 이벤트 기반 수평 |
| 기준 메트릭 | CPU, 메모리 | CPU, 메모리 | 큐 길이, HTTP 요청 수 등 |
| 적합한 경우 | 웹 서버, API | 단일 Pod 앱 | 메시지 큐, 배치 처리 |
오늘은 쿠버네티스 운영에서 실제로 많이 쓰이는 개념들을 다뤘다. RBAC는 AWS IAM과 비교해서 이해하니까 훨씬 빠르게 잡혔고, Dynamic Provisioner는 EKS에서 EFS 쓸 때 바로 연결되는 개념이라 더 와닿았다. HPA는 오토스케일링 개념 자체는 알고 있었는데 실제로 메트릭 서버 설치부터 직접 해보니까 내부 동작이 명확해졌다.
이번 velog도 AWS Kiro를 통해 작성하였다. 이번에는 3Tier를 구현한 실습도있어서 같이 올리도록 하겠다.