143. Security - Section Introduction
- Kubernetes 보안 기본 원칙 (Security Primitives)
- 인증 (Authentication) & 인가 (Authorization)
- TLS 인증서 관리
- RBAC (Role-Based Access Control)
- Network Policy, Security Context
- CRD / Custom Controller / Operator Framework (2025 업데이트)
144. Kubernetes Security Primitives
보안 계층 구조
- Host Security (인프라 보안) — root 접근 비활성화 / SSH 키 기반 인증
- API Server Access Control
- Authentication (누가 접근?) — 인증서, 토큰, LDAP, ServiceAccount
- Authorization (무엇을 할 수 있나?) — RBAC, ABAC, Node, Webhook
- TLS Encryption — 컴포넌트 간 통신 암호화 (etcd, API Server, Kubelet)
- Network Policies — Pod 간 트래픽 제어
145. Authentication
공식문서: Authenticating
Authentication vs Authorization 개념 구분
혼동하기 쉬운 두 개념을 먼저 정리:
| 용어 | 한국어 | 질문 | 예시 |
|---|---|---|---|
| Authentication (AuthN) | 인증 | ”너 누구?” | 인증서/토큰으로 신원 확인 |
| Authorization (AuthZ) | 인가 | ”뭐 할 수 있어?” | RBAC으로 권한 확인 |
흐름: kubectl 요청 → [인증: 너 Alice 맞아?] → [인가: Alice는 Pod 생성 권한 있어?] → 실행
현관에서 신분증 확인(AuthN) 하고, 안에 들어가서 어느 방 들어갈 수 있나(AuthZ) 묻는 것과 같음.
접근 주체
| 주체 | 유형 | 관리 방식 |
|---|---|---|
| Admin / Developer | User Account | 외부 관리 (인증서, LDAP 등) |
| Bot / CI/CD | Service Account | kubectl create serviceaccount |
Kubernetes는 User Account를 직접 관리하지 않음 → 외부 인증 시스템 연동
인증 메커니즘
| 메커니즘 | 용도 | 상태 |
|---|---|---|
| Static Password File | 테스트용 | Deprecated |
| Static Token File | 자동화용 | Deprecated |
| TLS Client Certificates | 프로덕션 | 권장 |
| External Identity (OIDC/LDAP) | 엔터프라이즈 SSO | 권장 |
# Service Account 생성/조회
kubectl create serviceaccount sa1
kubectl get serviceaccounts146. TLS Introduction
- 대칭 키 암호화: 같은 키로 암호화/복호화 → 키 전송 시 탈취 위험
- 비대칭 키 암호화: Public Key(암호화) + Private Key(복호화) → TLS의 기반
147. TLS Basics
인증서 파일 명명 규칙
| 확장자 | 유형 |
|---|---|
*.crt, *.pem | Public Key (인증서) |
*-key.pem, *.key | Private Key |
파일명에 "key"가 없으면 Public Key(인증서)
TLS 핸드셰이크 흐름
sequenceDiagram participant C as Client participant S as Server C->>S: 1. HTTPS 연결 요청 S->>C: 2. Server 공개 인증서 (server.crt) 전송 Note over C: 3. CA를 통해 인증서 검증 C->>S: 4. 대칭 키를 Server 공개키로 암호화하여 전송 Note over C,S: 5. 이후 대칭 키로 암호화된 통신
Certificate Authority (CA)
- 인증서의 신뢰성을 보증하는 기관
- 자체 서명(Self-signed) 인증서 → 신뢰할 수 없음
- CA 서명 인증서 → 브라우저/클라이언트가 신뢰
148. TLS in Kubernetes
인증서 전체 맵
- Server Certificates: kube-apiserver (
apiserver.crt/key), etcd-server, kubelet - Client Certificates: admin (kubectl), kube-scheduler, controller-mgr, kube-proxy, apiserver→etcd, apiserver→kubelet
- CA: Cluster CA (
ca.crt/ca.key) — 위 모든 인증서에 서명 (ETCD용 별도 CA 가능)
| 카테고리 | 용도 |
|---|---|
| Client Certificates | 컴포넌트가 API Server에 인증할 때 사용 |
| Server Certificates | 서버 컴포넌트가 HTTPS 서비스 제공 시 사용 |
149. TLS in Kubernetes - Certificate Creation
OpenSSL로 인증서 생성
# 1. CA 인증서 생성
openssl genrsa -out ca.key 2048
openssl req -new -key ca.key -subj "/CN=KUBERNETES-CA" -out ca.csr
openssl x509 -req -in ca.csr -signkey ca.key -out ca.crt
# 2. Admin 클라이언트 인증서
openssl genrsa -out admin.key 2048
openssl req -new -key admin.key -subj "/CN=kube-admin/O=system:masters" -out admin.csr
openssl x509 -req -in admin.csr -CA ca.crt -CAkey ca.key -out admin.crt
# 3. API Server 인증서 (SAN 포함)
openssl genrsa -out apiserver.key 2048
openssl req -new -key apiserver.key -subj "/CN=kube-apiserver" \
-config openssl.cnf -out apiserver.csr
openssl x509 -req -in apiserver.csr -CA ca.crt -CAkey ca.key \
-extensions v3_req -extfile openssl.cnf -out apiserver.crtAPI Server SAN (Subject Alternative Names)
# openssl.cnf
[alt_names]
DNS.1 = kubernetes
DNS.2 = kubernetes.default
DNS.3 = kubernetes.default.svc
DNS.4 = kubernetes.default.svc.cluster.local
IP.1 = 10.96.0.1 # Service CIDR 첫 번째 IP
IP.2 = 172.17.0.87 # Master Node IPAPI Server 인증서는 반드시 SAN에 모든 접근 가능한 이름/IP를 포함해야 함
150. View Certificate Details
# 인증서 상세 확인
openssl x509 -in /etc/kubernetes/pki/apiserver.crt -text -noout
# 확인해야 할 항목:
# - Subject: CN (Common Name)
# - Issuer: 누가 서명했는지 (CA)
# - Validity: 만료일
# - Subject Alternative Name: SAN 목록kubeadm 환경 인증서 경로
| 인증서 | 경로 |
|---|---|
| CA | /etc/kubernetes/pki/ca.crt |
| API Server | /etc/kubernetes/pki/apiserver.crt |
| API Server Key | /etc/kubernetes/pki/apiserver.key |
| ETCD Server | /etc/kubernetes/pki/etcd/server.crt |
트러블슈팅
# kubeadm 환경: 컨테이너 로그
kubectl logs etcd-master -n kube-system
# 또는
docker logs <container-id>
# crictl 사용
crictl logs <container-id>151-153. Labs - View Certificates
# API Server 인증서 만료일 확인
openssl x509 -in /etc/kubernetes/pki/apiserver.crt -text -noout | grep -A2 "Validity"
# ETCD 인증서 확인
openssl x509 -in /etc/kubernetes/pki/etcd/server.crt -text -noout
# kube-apiserver.yaml에서 인증서 경로 확인
cat /etc/kubernetes/manifests/kube-apiserver.yaml | grep -i cert
cat /etc/kubernetes/manifests/kube-apiserver.yaml | grep -i key154. Certificates API
- 새 관리자가 클러스터에 접근하려면 → CSR(Certificate Signing Request)을 통해 인증서 발급
CSR 처리 흐름
- 사용자: Private Key 생성 (
openssl genrsa -out jane.key 2048) - 사용자: CSR 생성 (
openssl req -new -key jane.key -subj '/CN=jane' -out jane.csr) - 관리자: CSR 객체 생성 (Kubernetes API)
- 관리자: CSR 승인
- 사용자에게 인증서 전달
# CertificateSigningRequest 생성
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
name: jane
spec:
groups:
- system:authenticated
usages:
- digital signature
- key encipherment
- server auth
request: <base64-encoded-csr>
signerName: kubernetes.io/kube-apiserver-client# CSR의 request 필드 값 생성
cat jane.csr | base64 -w 0
# CSR 조회
kubectl get csr
# CSR 승인
kubectl certificate approve jane
# CSR 거부
kubectl certificate deny jane
# 승인된 인증서 추출
kubectl get csr jane -o jsonpath='{.status.certificate}' | base64 --decode > jane.crtController Manager가 CA 인증서를 사용하여 CSR 서명을 처리함
155-156. Labs - Certificates API
# CSR 목록 확인
kubectl get csr
# CSR 상태 확인
kubectl describe csr <name>
# CSR 승인/거부
kubectl certificate approve <name>
kubectl certificate deny <name>157. KubeConfig
KubeConfig 파일 구조
# ~/.kube/config
apiVersion: v1
kind: Config
current-context: my-kube-admin@my-kube-playground
clusters: # 접속할 클러스터 정보
- name: my-kube-playground
cluster:
certificate-authority: /etc/kubernetes/pki/ca.crt
server: https://my-kube-playground:6443
contexts: # 클러스터 + 사용자 조합
- name: my-kube-admin@my-kube-playground
context:
cluster: my-kube-playground
user: my-kube-admin
namespace: default # 기본 namespace 지정 가능
users: # 인증 정보
- name: my-kube-admin
user:
client-certificate: /etc/kubernetes/pki/admin.crt
client-key: /etc/kubernetes/pki/admin.keyKubeConfig 구성: Contexts (누가 어디에 접속) → Clusters (서버 주소, CA) + Users (인증서, 키)
KubeConfig 명령어
# 현재 config 보기
kubectl config view
# 특정 config 파일 지정
kubectl config view --kubeconfig=my-custom-config
# Context 전환
kubectl config use-context prod-user@production
# 현재 Context 확인
kubectl config current-context
# Context 목록
kubectl config get-contexts인증서 지정 방법 2가지
# 방법 1: 파일 경로
certificate-authority: /etc/kubernetes/pki/ca.crt
# 방법 2: Base64 인코딩 데이터 (파일 경로 대신)
certificate-authority-data: LS0tLS1CRUdJTi...158-159. Labs - KubeConfig
# KubeConfig 파일 위치 확인
ls -la ~/.kube/config
# 클러스터 수 확인
kubectl config view | grep -c "cluster:"
# Context 전환 후 확인
kubectl config use-context research
kubectl config current-context
# 특정 config에 있는 context로 변경
kubectl config use--context research \
--kubeconfig /root/my-kube-config
# config 파일 위치 영구적으로 바꾸기
export KUBECONFIG=/root/test-config
source ~/.bashrc160. Persistent Key/Value Store
- ETCD = 클러스터의 모든 데이터를 저장하는 Persistent Key/Value Store
- ETCD 데이터 보호 = Encryption at Rest 활성화 (CKA-5 §112 참고)
161. API Groups
공식문서: Kubernetes API Concepts
Kubernetes API 구조
| Group | 경로 | 주요 리소스 |
|---|---|---|
| Core | /api/v1 | pods, services, configmaps, secrets, namespaces, nodes, persistentvolumes, events |
apps | /apis/apps/v1 | deployments, replicasets, statefulsets |
networking.k8s.io | /apis/networking.k8s.io/v1 | networkpolicies, ingresses |
certificates.k8s.io | /apis/certificates.k8s.io/v1 | certificatesigningrequests |
rbac.authorization.k8s.io | /apis/rbac.authorization.k8s.io/v1 | roles, rolebindings, clusterroles |
storage.k8s.io | /apis/storage.k8s.io/v1 | storageclasses |
# 모든 API 그룹 확인
kubectl api-resources
# 특정 API 그룹의 리소스
kubectl api-resources --api-group=apps
# kubectl proxy로 API 탐색
kubectl proxy &
curl http://localhost:8001/apis162. Authorization
공식문서: Authorization
Authorization 메커니즘
| 메커니즘 | 설명 |
|---|---|
| Node | kubelet 요청 인가 (system:node 그룹) |
| ABAC | 사용자별 JSON 정책 파일 (비추천) |
| RBAC | Role 기반 권한 관리 (표준/권장) |
| Webhook | 외부 인가 서비스 (OPA 등) |
| AlwaysAllow | 모든 요청 허용 (기본값) |
| AlwaysDeny | 모든 요청 거부 |
다중 Authorization Mode
--authorization-mode=Node,RBAC,Webhook
처리 흐름: 요청 → Node Authorizer → 거부 시 RBAC → 승인 시 접근 허용 / 거부 시 Webhook 판단
순서대로 처리하며, 하나의 모듈이 승인하면 나머지는 건너뜀
163. Role Based Access Controls (RBAC)
공식문서: Using RBAC Authorization
RBAC란?
“사용자/SA에게 직접 권한을 주지 않고, 역할(Role)을 만들고 그 역할을 부여하는 방식”
왜 이 방식인가?
- 사용자 100명에게 매번 “Pod 읽기 권한” 붙이면 관리 지옥
- “developer” 역할을 하나 만들고, 100명을 그 역할에 바인딩만 하면 됨
- 역할 내용만 바꾸면 100명 권한이 한 번에 갱신
구성 요소 4개:
- Role / ClusterRole — “무엇을 할 수 있나”의 정의 (권한 자체)
- RoleBinding / ClusterRoleBinding — “누구에게 이 역할을 부여할지”의 연결
Role/Binding은 동사와 주어의 조합. Role만 만들면 아무도 권한 없음. Binding으로 연결해야 발효.
Role 생성
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: developer
namespace: default
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["list", "get", "create", "update", "delete"]
- apiGroups: [""]
resources: ["ConfigMap"]
verbs: ["create"]RoleBinding 생성
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: devuser-developer-binding
namespace: default
subjects:
- kind: User
name: dev-user
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: developer
apiGroup: rbac.authorization.k8s.io권한 확인
# 내 권한 확인
kubectl auth can-i create deployments # → yes/no
kubectl auth can-i delete nodes # → yes/no
# 특정 사용자 권한 확인 (admin만 가능)
kubectl auth can-i create pods --as dev-user # → yes
kubectl auth can-i create deployments --as dev-user # → no
kubectl auth can-i create pods --as dev-user -n test # namespace 지정특정 리소스만 접근 제한
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "create", "update"]
resourceNames: ["blue", "orange"] # 이 이름의 Pod만 접근 허용RBAC imperative
시험에서 RBAC 문제는 거의 100% 출제. imperative 명령으로 뼈대 만들고 필요 부분만 YAML 수정하는 것이 가장 빠름.
# ── Role 생성 ──────────────────────────────────────────────
kubectl create role developer \
--verb=get,list,watch,create,update,delete \
--resource=pods,pods/log
# 여러 apiGroup: --resource=deployments.apps,pods
# resourceNames 제한: --resource-name=blue --resource-name=orange
# ── RoleBinding 생성 ──────────────────────────────────────
# User에게 바인딩
kubectl create rolebinding dev-binding \
--role=developer \
--user=dev-user
# Group에게 바인딩
kubectl create rolebinding dev-binding \
--role=developer \
--group=developers
# ServiceAccount에게 바인딩
kubectl create rolebinding dev-binding \
--role=developer \
--serviceaccount=default:my-sa
# 형식: --serviceaccount=<namespace>:<sa-name>
# ── ClusterRole 생성 ──────────────────────────────────────
kubectl create clusterrole node-reader \
--verb=get,list,watch \
--resource=nodes
# PodSecurityPolicy / 하위 리소스 같은 특수 케이스
kubectl create clusterrole pod-exec \
--verb=create \
--resource=pods/exec
# ── ClusterRoleBinding 생성 ────────────────────────────────
kubectl create clusterrolebinding cluster-admin-binding \
--clusterrole=cluster-admin \
--user=admin-user
kubectl create clusterrolebinding ci-binding \
--clusterrole=node-reader \
--serviceaccount=ci:ci-runner
# ── YAML로 뽑아서 세밀 수정 ────────────────────────────────
kubectl create role dev \
--verb=get,list --resource=pods \
--dry-run=client -o yaml > role.yaml
kubectl create rolebinding dev-bind \
--role=dev --user=dev-user \
--dry-run=client -o yaml > rolebinding.yaml권한 확인 빠른 명령
# 내 권한 확인
kubectl auth can-i create pods
kubectl auth can-i delete nodes
# 특정 사용자로 가장하여 확인 (가장 많이 쓰임)
kubectl auth can-i get pods --as=dev-user
kubectl auth can-i '*' '*' --as=admin-user # 전부 가능한지
# 네임스페이스 지정
kubectl auth can-i list pods --as=dev-user -n production
# ServiceAccount 권한 확인 (형식 주의)
kubectl auth can-i list pods \
--as=system:serviceaccount:default:my-saRole vs ClusterRole 선택 기준
| 상황 | 선택 |
|---|---|
| 특정 namespace 내부 리소스 권한 | Role + RoleBinding |
| 모든 namespace의 동일 권한 | ClusterRole + RoleBinding (네임스페이스별로) |
| 클러스터 전역 리소스 (nodes, PV 등) | ClusterRole + ClusterRoleBinding |
| 여러 namespace에서 재사용 | ClusterRole (namespace 없이 정의) |
164-165. Labs - RBAC
kubectl get roles
kubectl get rolebindings
kubectl describe role <name>
kubectl describe rolebinding <name>166. Cluster Roles
Namespaced vs Cluster-Scoped 리소스
| Namespaced (namespace별) | Cluster-Scoped (클러스터 전체) |
|---|---|
| Pods, Deployments, Services | Nodes, PersistentVolumes |
| ConfigMaps, Secrets | ClusterRoles, ClusterRoleBindings |
| Roles, RoleBindings | Namespaces, CertificateSigningRequests |
# Namespaced 리소스 목록
kubectl api-resources --namespaced=true
# Cluster-Scoped 리소스 목록
kubectl api-resources --namespaced=falseClusterRole & ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: cluster-administrator
rules:
- apiGroups: [""]
resources: ["nodes"]
verbs: ["list", "get", "create", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: cluster-admin-role-binding
subjects:
- kind: User
name: cluster-admin
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: cluster-administrator
apiGroup: rbac.authorization.k8s.ioClusterRole로 namespaced 리소스(예: pods)에 권한을 부여하면 모든 namespace의 해당 리소스에 접근 가능
167-168. Labs - Cluster Roles
kubectl get clusterroles --no-headers | wc -l
kubectl get clusterrolebindings --no-headers | wc -l
kubectl describe clusterrole <name>
kubectl describe clusterrolebinding <name>169. Service Accounts
User Account vs Service Account
| 구분 | User Account | Service Account |
|---|---|---|
| 대상 | 사람 (admin, developer) | 머신 (Prometheus, Jenkins) |
| 관리 | 외부 시스템 | Kubernetes API |
| 토큰 | kubeconfig 기반 | 자동 마운트 토큰 |
Service Account 사용
# 생성
kubectl create serviceaccount dashboard-sa
# 토큰 생성 (v1.24+)
kubectl create token dashboard-sa
# 조회
kubectl get serviceaccount
kubectl describe serviceaccount dashboard-saPod에서 Service Account 지정
apiVersion: v1
kind: Pod
metadata:
name: my-dashboard
spec:
serviceAccountName: dashboard-sa # 기본값: default
containers:
- name: my-dashboard
image: my-dashboardServiceAccount 관련 imperative
# ── SA 생성 ────────────────────────────────────────────────
kubectl create serviceaccount my-sa
kubectl create sa my-sa -n my-ns
# ── 토큰 생성 (v1.24+) ─────────────────────────────────────
kubectl create token my-sa
kubectl create token my-sa --duration=1h
kubectl create token my-sa --audience=prometheus
# ── Pod에 SA 지정 (set으로 변경 가능) ──────────────────────
kubectl set serviceaccount deploy/web my-sa
# → deployment의 spec.template.spec.serviceAccountName 변경토큰 자동 마운트
# 토큰 위치
/var/run/secrets/kubernetes.io/serviceaccount/token
# 자동 마운트 비활성화
spec:
automountServiceAccountToken: falsev1.22 / v1.24 변경사항
| 버전 | 변경 |
|---|---|
| v1.22 | TokenRequest API 도입 → 시간/대상 제한 토큰 |
| v1.24 | Secret 기반 비만료 토큰 자동 생성 중단 → kubectl create token 사용 |
170-171. Labs - Service Accounts
kubectl get sa
kubectl describe sa <name>
kubectl create token <sa-name>
# Pod에서 사용 중인 SA 확인
kubectl describe pod <name> | grep -i "service account"172. Image Security
이미지 이름 구조
docker.io / library / nginx
──────── ─────── ─────
Registry Account Image
# 생략 시: docker.io/library/nginx
# 사설: private-registry.io/apps/internal-app
Private Registry 접근 설정
# 1. Docker Registry Secret 생성
kubectl create secret docker-registry regcred \
--docker-server=private-registry.io \
--docker-username=registry-user \
--docker-password=registry-password \
[email protected]# 2. Pod에서 imagePullSecrets 지정
apiVersion: v1
kind: Pod
metadata:
name: my-app
spec:
containers:
- name: my-app
image: private-registry.io/apps/internal-app
imagePullSecrets:
- name: regcred173-174. Labs - Image Security
# Pod 이미지 확인
kubectl describe pod <name> | grep -i image
# imagePullSecrets 확인
kubectl get pod <name> -o yaml | grep -A3 imagePullSecrets175. Pre-requisite - Security in Docker
Linux Capabilities
# 기본: 제한된 capabilities로 실행
docker run ubuntu
# capability 추가
docker run --cap-add MAC_ADMIN ubuntu
# capability 제거
docker run --cap-drop KILL ubuntu
# 모든 권한 (위험!)
docker run --privileged ubuntu176. Security Contexts
Security Context란?
“컨테이너를 얼마나 제한된 권한으로 실행할지 정하는 설정”
왜 필요한가?
- 컨테이너가 root로 실행되면 → 탈출 공격 시 호스트 전체 장악 가능
- 특정 커널 capability(예: 네트워크 조작) 만 필요한 앱에 전체 권한을 주면 위험
- 파일 시스템을 읽기 전용으로 만들면 악성 코드 주입 방지
Security Context로 설정 가능한 것:
-
runAsUser: 1000— 비 root 사용자로 실행 -
readOnlyRootFilesystem: true— 루트 파일 시스템 읽기 전용 -
capabilities.drop: ["ALL"]— 모든 권한 제거 후 필요한 것만 추가 -
allowPrivilegeEscalation: false— 권한 상승 금지 -
Docker의 보안 설정을 Kubernetes Pod/Container 레벨에서 구성
# Container 레벨 Security Context (우선순위 높음)
apiVersion: v1
kind: Pod
metadata:
name: web-pod
spec:
containers:
- name: ubuntu
image: ubuntu
command: ["sleep", "3600"]
securityContext:
runAsUser: 1000
capabilities: # capabilities는 Container 레벨에서만 설정 가능
add: ["MAC_ADMIN"]
# Pod 레벨 Security Context (모든 컨테이너에 적용)
spec:
securityContext:
runAsUser: 1000
containers:
- name: ubuntu
image: ubuntu
capabilities는 Container 레벨에서만 설정 가능 (Pod 레벨 X)
177-178. Labs - Security Contexts
# Pod의 Security Context 확인
kubectl describe pod <name> | grep -A5 "Security"
# 컨테이너 내부에서 사용자 확인
kubectl exec <pod-name> -- whoami
kubectl exec <pod-name> -- id179. Network Policy
Network Policy란?
“Pod 사이의 트래픽에 적용하는 방화벽 규칙”
왜 필요한가?
- Kubernetes 기본값: 모든 Pod가 모든 Pod와 통신 가능 (All-Allow)
- 하지만 보안적으로는 “DB Pod는 API Pod만 접근 허용” 같은 제한이 필요
- 전통 방화벽은 IP 기반 — but Pod IP는 계속 바뀜 → 라벨 기반 방화벽이 필요
Network Policy 핵심:
- podSelector: 이 규칙이 적용되는 Pod (대상)
- ingress/egress: 들어오는/나가는 트래픽 규칙
- 같은 namespace/Pod 라벨 조합으로 세밀하게 제어
Network Policy는 CNI 플러그인이 지원해야 효과 있음. Flannel은 지원 안 함 (Calico/Weave/Cilium 사용).
기본 동작
- Kubernetes는 기본적으로 All Allow → 모든 Pod가 서로 통신 가능
- Network Policy로 트래픽 제한 가능
Ingress vs Egress
- 들어오는 트래픽 (Ingress) → Pod
- Pod → 나가는 트래픽 (Egress)
Network Policy 예시
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: db-policy
spec:
podSelector: # 이 정책이 적용될 Pod
matchLabels:
role: db
policyTypes:
- Ingress
ingress:
- from:
- podSelector: # 허용할 소스 Pod
matchLabels:
name: api-pod
ports:
- protocol: TCP
port: 3306적용 전 (All Allow): Web → DB (허용) ✅
적용 후 (Network Policy):
- Web → DB 차단 ❌
- API → DB 허용 ✅
Flannel은 Network Policy를 지원하지 않음. Calico, Weave Net, Kube-router 등 사용
180. Developing Network Policies
공식문서: Network Policies
복합 규칙 (AND vs OR)
# OR 조건: podSelector 또는 namespaceSelector 중 하나만 만족하면 허용
ingress:
- from:
- podSelector:
matchLabels:
name: api-pod
- namespaceSelector:
matchLabels:
name: prod
# AND 조건: 두 조건 모두 만족해야 허용
ingress:
- from:
- podSelector:
matchLabels:
name: api-pod
namespaceSelector:
matchLabels:
name: prod
- from:리스트 항목이 별도 → OR, 같은 항목 내 → AND
Egress Policy
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: db-policy
spec:
podSelector:
matchLabels:
role: db
policyTypes:
- Egress
egress:
- to:
- ipBlock:
cidr: 192.168.5.10/32
ports:
- protocol: TCP
port: 80181-182. Labs - Network Policy
# Network Policy 조회
kubectl get networkpolicy
kubectl describe networkpolicy <name>
# Pod 라벨 확인
kubectl get pods --show-labels183. Kubectx and Kubens
# kubectx: context 빠른 전환
kubectx # 목록 보기
kubectx my-context # 전환
kubectx - # 이전 context로 복귀
# kubens: namespace 빠른 전환
kubens # 목록 보기
kubens my-namespace # 전환
kubens - # 이전 namespace로 복귀184. (2025) Custom Resource Definition (CRD)
CRD가 왜 필요한가? — Kubernetes 확장 철학
“Kubernetes에 새로운 리소스 종류를 내가 추가할 수 있게 해주는 장치”
왜 필요한가?
- 기본 리소스(Pod, Deployment, Service 등)로는 도메인 특화 개념을 표현 못함
- 예: “데이터베이스 클러스터”, “항공권”, “머신러닝 모델”, “Kafka 토픽”
kubectl apply -f db-cluster.yaml로 DB 클러스터 전체를 선언적으로 관리하고 싶음- Kubernetes의 감시-조정(reconcile) 패턴을 내 도메인에도 적용하고 싶음
CRD는 “새로운 kind를 API에 등록” 하는 행위:
kind: FlightTicket같은 새 타입 생성kubectl get flightticket같이 기본 리소스처럼 다룸- ETCD에 저장, RBAC 적용, Watch 가능 — 모든 Kubernetes 메커니즘 그대로 적용
3가지 핵심 용어 구분 (자주 혼동)
| 용어 | 무엇 | 예시 |
|---|---|---|
| CRD (Custom Resource Definition) | 새 타입 자체의 정의 | ”FlightTicket이라는 kind를 만들겠다” |
| CR (Custom Resource) | 그 타입의 실제 인스턴스 | ”SEL → LAX 비행기표 1개” |
| Custom Controller | CR을 감시하고 실제 동작을 수행하는 프로그램 | ”FlightTicket 생성되면 항공사 API 호출해 예약” |
비유: CRD = 자동차 설계도, CR = 실제 자동차 1대, Controller = 자동차를 작동시키는 운전사. CRD만 있으면 데이터만 저장될 뿐 아무 일도 안 일어남.
기본 리소스 vs 커스텀 리소스
기본 리소스 (K8s 내장):
- Deployment → Deployment Controller (built-in) → ReplicaSet / Pod 자동 관리
커스텀 리소스:
- FlightTicket (CR) — 정의: CRD, API Server가 kind 인식
- → Custom Controller (내가 개발/배포) → 실제 동작 (외부 API 호출 등)
Controller의 Reconciliation Loop (핵심 개념)
Controller는 무한 루프로 돌면서:
- 관찰 (Observe): 현재 CR 상태를 API Server에서 읽음
- 비교 (Diff): 원하는 상태(spec) 과 실제 상태(status) 를 비교
- 조정 (Act): 차이가 있으면 실제 세계를 spec에 맞게 바꿈 (외부 API, Pod 생성 등)
- 상태 업데이트: status 필드 갱신 후 다시 관찰
이게 Kubernetes의 핵심 철학: “선언적 원하는 상태를 쓰면, 컨트롤러가 계속 실제 상태를 원하는 상태로 만들어준다.”
CRD 정의 상세
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
# 이름 형식 필수: <plural>.<group>
name: flighttickets.flights.com
spec:
group: flights.com # API 그룹 (도메인 형식 권장)
scope: Namespaced # Namespaced 또는 Cluster
names:
plural: flighttickets # kubectl get 복수형
singular: flightticket # 단수형
kind: FlightTicket # Pascal 케이스 (YAML의 kind:)
shortNames:
- ft # kubectl get ft 로 짧게 호출
categories:
- all # kubectl get all 에 포함시킬지
versions:
- name: v1
served: true # 이 버전 API로 제공
storage: true # ETCD 저장 시 사용할 버전 (딱 하나만 true)
schema:
openAPIV3Schema: # 필드 검증 스키마
type: object
properties:
spec:
type: object
required: ["from", "to"]
properties:
from:
type: string
to:
type: string
number:
type: integer
minimum: 1
maximum: 10
class:
type: string
enum: ["economy", "business", "first"]
status:
type: object
properties:
bookingRef:
type: string
confirmed:
type: boolean
subresources: # 선택: status/scale 서브리소스 활성화
status: {}
scale: # HPA와 연동 가능
specReplicasPath: .spec.replicas
statusReplicasPath: .status.replicas
additionalPrinterColumns: # kubectl get 에서 추가 컬럼 표시
- name: From
type: string
jsonPath: .spec.from
- name: To
type: string
jsonPath: .spec.to
- name: Confirmed
type: boolean
jsonPath: .status.confirmed
- name: Age
type: date
jsonPath: .metadata.creationTimestamp주요 필드 의미
| 필드 | 의미 | 주의점 |
|---|---|---|
group | API 그룹 (도메인 형식) | flights.com, mycompany.io 등 고유성 확보 |
scope | Namespaced or Cluster | namespace 범위 결정 |
names.plural | kubectl get <이것> | 소문자만 |
names.kind | YAML의 kind: 값 | PascalCase |
versions[].served | 이 버전을 API로 제공할지 | 여러 버전을 동시에 served: true 가능 |
versions[].storage | ETCD에 저장할 정식 버전 | 반드시 1개만 true |
schema.openAPIV3Schema | 필드 검증 | 잘못된 YAML 거부 |
subresources.status | /status 서브리소스 분리 | status 변경 권한 분리용 |
subresources.scale | HPA와 연동 | replicas 개념이 있을 때 |
additionalPrinterColumns | kubectl get 테이블 커스터마이징 | 가독성 향상 |
Custom Resource (CR) 인스턴스 생성
CRD가 “타입 정의”라면 CR은 “실제 데이터”.
# CR 예시 (CRD 등록 후 사용 가능)
apiVersion: flights.com/v1
kind: FlightTicket
metadata:
name: my-flight-seoul-la
spec:
from: ICN
to: LAX
number: 2
class: business# CRD 먼저 생성
kubectl apply -f flightticket-crd.yaml
# 스키마 등록 확인
kubectl api-resources | grep flightticket
kubectl api-versions | grep flights.com
# CR 생성 (일반 리소스처럼)
kubectl apply -f my-flight.yaml
kubectl get flighttickets # 또는 kubectl get ft
kubectl describe ft my-flight-seoul-la
kubectl delete ft my-flight-seoul-laCRD의 Scope 비교
| Scope | 예시 | 접근 |
|---|---|---|
| Namespaced | 개별 앱 설정, 예약 데이터 | kubectl get ft -n my-ns |
| Cluster | 클러스터 전역 정책, ClusterIssuer (cert-manager) | kubectl get xxx (namespace 무의미) |
Versioning - 여러 버전 공존
스키마가 진화하면 v1alpha1 → v1beta1 → v1 순서로 버전을 올림.
versions:
- name: v1alpha1
served: false # 더 이상 제공 안함
storage: false
- name: v1beta1
served: true # 여전히 제공 (하위 호환)
storage: false
- name: v1
served: true
storage: true # 새로 저장되는 것은 모두 v1
# conversion 전략 (version 간 변환 로직)CRD는 imperative 생성 명령이 없음 (YAML 필수).
kubectl create불가.
185. Labs - CRD
# ── CRD 조회 ──────────────────────────────────────────────
kubectl get crd
kubectl get crd -A # 특정 네임스페이스 없이 전체
kubectl describe crd flighttickets.flights.com
# ── API에 등록됐는지 확인 ──────────────────────────────────
kubectl api-resources --api-group=flights.com
kubectl api-versions | grep flights.com
# ── CR 스키마 확인 ────────────────────────────────────────
kubectl explain flightticket
kubectl explain flightticket.spec
kubectl explain flightticket.spec.class
# ── 내가 정의한 리소스가 Namespaced인지 Cluster인지 ────────
kubectl api-resources --namespaced=true | grep flights
kubectl api-resources --namespaced=false | grep flights
# ── CR 검증 (schema 위반 시) ───────────────────────────────
# 잘못된 YAML을 apply 하면 서버가 거부
kubectl apply -f invalid-ft.yaml
# error: ... validation failed: spec.class: Unsupported value: "luxury"CRD 트러블슈팅
| 증상 | 원인 | 해결 |
|---|---|---|
no matches for kind "X" | CRD가 등록 안됐거나 삭제됨 | kubectl get crd 확인 |
| CR 생성 시 validation 에러 | schema 위반 | kubectl explain으로 필드 확인 |
kubectl get ft 아무것도 없음 | 정말 없거나, namespace 문제 | kubectl get ft -A |
| CR은 있는데 아무 일도 안 일어남 | Controller 미설치 | Custom Controller 배포 여부 확인 |
| CRD 삭제했는데 CR이 남아있음 | finalizer가 붙어있음 | CRD 삭제 시 CR도 자동 cascading delete |
186. (2025) Custom Controllers
공식문서: Operator pattern / Custom Resources
Controller가 하는 일
“CR만 만들면 ETCD에 데이터가 저장될 뿐 아무 일도 안 일어남. Controller가 이 CR을 보고 실제 작업을 수행해야 함.”
전형적인 Controller 동작:
[FlightTicket 생성 이벤트]
↓
[Controller가 Watch로 감지]
↓
[spec 읽음: ICN → LAX, 비즈니스 클래스 2석]
↓
[외부 항공사 API 호출해 실제 예약]
↓
[응답받은 bookingRef를 status.bookingRef에 저장]
↓
[status.confirmed: true 로 업데이트]
구현 방법
| 방법 | 언어 | 난이도 | 비고 |
|---|---|---|---|
| client-go + informer | Go | 높음 | 정석, 성능 좋음 |
| Kubebuilder | Go | 중간 | 스캐폴딩 자동화 (가장 인기) |
| Operator SDK | Go/Ansible/Helm | 낮음 | CNCF 공식 툴킷 |
| Kopf | Python | 낮음 | Python 친숙하면 |
| Metacontroller | Any (Webhook 기반) | 낮음 | 언어 상관없이 HTTP |
Controller 배포
Custom Controller도 결국 Pod로 배포:
# Controller 자체는 보통 Deployment로 운영
apiVersion: apps/v1
kind: Deployment
metadata:
name: flight-controller
namespace: flight-system
spec:
replicas: 1
selector:
matchLabels:
app: flight-controller
template:
metadata:
labels:
app: flight-controller
spec:
serviceAccountName: flight-controller # RBAC 필요
containers:
- name: controller
image: my-registry/flight-controller:v1Controller 배포 시 꼭 필요한 것:
- ServiceAccount (Pod의 신원)
- ClusterRole (CR 전체 감시 권한 — 보통 get/list/watch/update)
- ClusterRoleBinding (SA ↔ ClusterRole 연결)
187. (2025) Operator Framework
공식문서: Operator pattern / OperatorHub.io
Operator란?
“CRD + Custom Controller를 하나의 패키지로 묶은 것 + 전문 지식이 코드화된 것”
왜 Operator가 Controller보다 한 단계 위냐면:
- 단순 Controller: “CR이 바뀌면 동작” (기계적 조정)
- Operator: 운영자(SRE)의 도메인 지식까지 내장
- “Postgres HA 클러스터 구성 시 마스터 먼저 승격, 팔로워는 따라오게”
- “Kafka 업그레이드 시 컨트롤러 브로커를 마지막에”
- “ETCD 백업은 새벽 3시, 3일치 보관”
“Operator = 코드로 작성된 운영 전문가”. 운영자가 수행할 복잡한 절차를 코드가 대신함.
Operator의 성숙도 레벨 (CNCF 모델)
| Level | 능력 |
|---|---|
| 1. Basic Install | 설치/삭제 자동화 |
| 2. Seamless Upgrades | 업그레이드 로직 |
| 3. Full Lifecycle | 백업/복구/장애 복구 |
| 4. Deep Insights | 메트릭/알림 |
| 5. Auto Pilot | 자가 진단/자가 치유 |
Operator = CRD + Custom Controller 패키지
Operator = CRD (리소스 정의) + Custom Controller + 설치/업그레이드 로직 을 하나로 묶은 패키지
유명 Operator 예시
| Operator | 역할 |
|---|---|
| Prometheus Operator | Prometheus 인스턴스, ServiceMonitor 등을 CR로 관리 |
| cert-manager | TLS 인증서 자동 발급/갱신 (Let’s Encrypt 연동) |
| Strimzi (Kafka) | Kafka 클러스터 CR로 선언 → 자동 구성 |
| Postgres Operator | HA PostgreSQL 클러스터 관리 |
| ETCD Operator | etcd 클러스터 배포 및 스냅샷 백업 |
| Istio Operator | Istio 서비스 메시 설치/업그레이드 |
| ArgoCD | GitOps 배포 (Application CR 사용) |
Operator Lifecycle Manager (OLM)
“Operator들을 또 Operator로 관리하는 메타 툴”
OLM이 해주는 일:
- Operator 설치/업그레이드 자동화
- 의존성 해결 (Operator A가 Operator B를 필요로 하면 함께 설치)
- Subscription 개념으로 자동 업데이트
# OLM 설치
curl -sL https://github.com/operator-framework/operator-lifecycle-manager/releases/download/v0.25.0/install.sh | bash -s v0.25.0
# OperatorHub.io에서 Operator 설치
kubectl apply -f https://operatorhub.io/install/prometheus.yaml
# 설치된 Operator 확인
kubectl get csv -A # ClusterServiceVersion
kubectl get subscription -A
kubectl get operatorgroup -AOperator 사용 예시 (Prometheus)
# Operator 설치 후, CR 한 줄로 Prometheus 전체 배포
apiVersion: monitoring.coreos.com/v1
kind: Prometheus
metadata:
name: my-prometheus
spec:
replicas: 2
serviceAccountName: prometheus
serviceMonitorSelector:
matchLabels:
team: backend
resources:
requests:
memory: 400MiOperator가 이 CR을 보고 알아서:
- StatefulSet 생성 (데이터 디스크 포함)
- Service, ServiceAccount 생성
- ConfigMap (prometheus.yml) 자동 생성
- ServiceMonitor 라벨 맞는 것 자동 스크레이핑 설정
기존에 수백 줄 YAML이 필요했던 Prometheus 설정이 10줄 CR로 끝남.
CRD 전체 흐름 요약
- CRD YAML 작성 (새 kind 정의)
kubectl apply→ CRD 등록 (API Server가 새 kind 인식)- Custom Resource YAML 작성 (실제 인스턴스)
kubectl apply→ CR이 ETCD에 저장- Custom Controller가 Watch로 감지
- Controller가 실제 동작 수행 (외부 API / Pod 생성 등)
- status 필드 업데이트 → 다시 5단계로 (loop)
CKA 시험에서의 CRD
| 출제 범위 | 난이도 |
|---|---|
kubectl get crd 로 리소스 찾기 | 낮음 |
kubectl explain <custom-kind> | 낮음 |
| 주어진 CRD YAML을 apply하고 CR 생성 | 중간 |
| Operator 설치 확인 (kubectl 명령) | 낮음 |
| Custom Controller 직접 작성 | 시험 범위 아님 |
CKA 시험은 CRD/Operator 개념을 아는지 정도만 물어봄. Go로 Controller 구현하는 건 안 나옴.
kubectl명령으로 CRD/CR 다루는 것만 익히면 충분.