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

보안 계층 구조

  1. Host Security (인프라 보안) — root 접근 비활성화 / SSH 키 기반 인증
  2. API Server Access Control
    • Authentication (누가 접근?) — 인증서, 토큰, LDAP, ServiceAccount
    • Authorization (무엇을 할 수 있나?) — RBAC, ABAC, Node, Webhook
  3. TLS Encryption — 컴포넌트 간 통신 암호화 (etcd, API Server, Kubelet)
  4. Network Policies — Pod 간 트래픽 제어

145. Authentication

공식문서: Authenticating

Authentication vs Authorization 개념 구분

혼동하기 쉬운 두 개념을 먼저 정리:

용어한국어질문예시
Authentication (AuthN)인증”너 누구?”인증서/토큰으로 신원 확인
Authorization (AuthZ)인가”뭐 할 수 있어?”RBAC으로 권한 확인

흐름: kubectl 요청 → [인증: 너 Alice 맞아?] → [인가: Alice는 Pod 생성 권한 있어?] → 실행

현관에서 신분증 확인(AuthN) 하고, 안에 들어가서 어느 방 들어갈 수 있나(AuthZ) 묻는 것과 같음.

접근 주체

주체유형관리 방식
Admin / DeveloperUser Account외부 관리 (인증서, LDAP 등)
Bot / CI/CDService Accountkubectl 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 serviceaccounts

146. TLS Introduction

  • 대칭 키 암호화: 같은 키로 암호화/복호화 → 키 전송 시 탈취 위험
  • 비대칭 키 암호화: Public Key(암호화) + Private Key(복호화) → TLS의 기반

147. TLS Basics

인증서 파일 명명 규칙

확장자유형
*.crt, *.pemPublic Key (인증서)
*-key.pem, *.keyPrivate 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.crt

API 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 IP

API 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 key

154. Certificates API

  • 새 관리자가 클러스터에 접근하려면 → CSR(Certificate Signing Request)을 통해 인증서 발급

CSR 처리 흐름

  1. 사용자: Private Key 생성 (openssl genrsa -out jane.key 2048)
  2. 사용자: CSR 생성 (openssl req -new -key jane.key -subj '/CN=jane' -out jane.csr)
  3. 관리자: CSR 객체 생성 (Kubernetes API)
  4. 관리자: CSR 승인
  5. 사용자에게 인증서 전달
# 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.crt

Controller 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.key

KubeConfig 구성: 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 ~/.bashrc

160. Persistent Key/Value Store

  • ETCD = 클러스터의 모든 데이터를 저장하는 Persistent Key/Value Store
  • ETCD 데이터 보호 = Encryption at Rest 활성화 (CKA-5 §112 참고)

161. API Groups

Kubernetes API 구조

Group경로주요 리소스
Core/api/v1pods, services, configmaps, secrets, namespaces, nodes, persistentvolumes, events
apps/apis/apps/v1deployments, replicasets, statefulsets
networking.k8s.io/apis/networking.k8s.io/v1networkpolicies, ingresses
certificates.k8s.io/apis/certificates.k8s.io/v1certificatesigningrequests
rbac.authorization.k8s.io/apis/rbac.authorization.k8s.io/v1roles, rolebindings, clusterroles
storage.k8s.io/apis/storage.k8s.io/v1storageclasses
# 모든 API 그룹 확인
kubectl api-resources
 
# 특정 API 그룹의 리소스
kubectl api-resources --api-group=apps
 
# kubectl proxy로 API 탐색
kubectl proxy &
curl http://localhost:8001/apis

162. Authorization

공식문서: Authorization

Authorization 메커니즘

메커니즘설명
Nodekubelet 요청 인가 (system:node 그룹)
ABAC사용자별 JSON 정책 파일 (비추천)
RBACRole 기반 권한 관리 (표준/권장)
Webhook외부 인가 서비스 (OPA 등)
AlwaysAllow모든 요청 허용 (기본값)
AlwaysDeny모든 요청 거부

다중 Authorization Mode

--authorization-mode=Node,RBAC,Webhook

처리 흐름: 요청 → Node Authorizer → 거부 시 RBAC → 승인 시 접근 허용 / 거부 시 Webhook 판단

순서대로 처리하며, 하나의 모듈이 승인하면 나머지는 건너뜀


163. Role Based Access Controls (RBAC)

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-sa

Role 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, ServicesNodes, PersistentVolumes
ConfigMaps, SecretsClusterRoles, ClusterRoleBindings
Roles, RoleBindingsNamespaces, CertificateSigningRequests
# Namespaced 리소스 목록
kubectl api-resources --namespaced=true
 
# Cluster-Scoped 리소스 목록
kubectl api-resources --namespaced=false

ClusterRole & 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.io

ClusterRole로 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 AccountService 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-sa

Pod에서 Service Account 지정

apiVersion: v1
kind: Pod
metadata:
  name: my-dashboard
spec:
  serviceAccountName: dashboard-sa   # 기본값: default
  containers:
  - name: my-dashboard
    image: my-dashboard

ServiceAccount 관련 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: false

v1.22 / v1.24 변경사항

버전변경
v1.22TokenRequest API 도입 → 시간/대상 제한 토큰
v1.24Secret 기반 비만료 토큰 자동 생성 중단 → 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: regcred

173-174. Labs - Image Security

# Pod 이미지 확인
kubectl describe pod <name> | grep -i image
 
# imagePullSecrets 확인
kubectl get pod <name> -o yaml | grep -A3 imagePullSecrets

175. 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 ubuntu

176. 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

capabilitiesContainer 레벨에서만 설정 가능 (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> -- id

179. 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: 80

181-182. Labs - Network Policy

# Network Policy 조회
kubectl get networkpolicy
kubectl describe networkpolicy <name>
 
# Pod 라벨 확인
kubectl get pods --show-labels

183. 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 ControllerCR을 감시하고 실제 동작을 수행하는 프로그램”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는 무한 루프로 돌면서:

  1. 관찰 (Observe): 현재 CR 상태를 API Server에서 읽음
  2. 비교 (Diff): 원하는 상태(spec)실제 상태(status) 를 비교
  3. 조정 (Act): 차이가 있으면 실제 세계를 spec에 맞게 바꿈 (외부 API, Pod 생성 등)
  4. 상태 업데이트: 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

주요 필드 의미

필드의미주의점
groupAPI 그룹 (도메인 형식)flights.com, mycompany.io 등 고유성 확보
scopeNamespaced or Clusternamespace 범위 결정
names.pluralkubectl get <이것>소문자만
names.kindYAML의 kind:PascalCase
versions[].served이 버전을 API로 제공할지여러 버전을 동시에 served: true 가능
versions[].storageETCD에 저장할 정식 버전반드시 1개만 true
schema.openAPIV3Schema필드 검증잘못된 YAML 거부
subresources.status/status 서브리소스 분리status 변경 권한 분리용
subresources.scaleHPA와 연동replicas 개념이 있을 때
additionalPrinterColumnskubectl 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-la

CRD의 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

Controller가 하는 일

“CR만 만들면 ETCD에 데이터가 저장될 뿐 아무 일도 안 일어남. Controller가 이 CR을 보고 실제 작업을 수행해야 함.”

전형적인 Controller 동작:

[FlightTicket 생성 이벤트]
      ↓
[Controller가 Watch로 감지]
      ↓
[spec 읽음: ICN → LAX, 비즈니스 클래스 2석]
      ↓
[외부 항공사 API 호출해 실제 예약]
      ↓
[응답받은 bookingRef를 status.bookingRef에 저장]
      ↓
[status.confirmed: true 로 업데이트]

구현 방법

방법언어난이도비고
client-go + informerGo높음정석, 성능 좋음
KubebuilderGo중간스캐폴딩 자동화 (가장 인기)
Operator SDKGo/Ansible/Helm낮음CNCF 공식 툴킷
KopfPython낮음Python 친숙하면
MetacontrollerAny (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:v1

Controller 배포 시 꼭 필요한 것:

  • ServiceAccount (Pod의 신원)
  • ClusterRole (CR 전체 감시 권한 — 보통 get/list/watch/update)
  • ClusterRoleBinding (SA ↔ ClusterRole 연결)

187. (2025) Operator Framework

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 OperatorPrometheus 인스턴스, ServiceMonitor 등을 CR로 관리
cert-managerTLS 인증서 자동 발급/갱신 (Let’s Encrypt 연동)
Strimzi (Kafka)Kafka 클러스터 CR로 선언 → 자동 구성
Postgres OperatorHA PostgreSQL 클러스터 관리
ETCD Operatoretcd 클러스터 배포 및 스냅샷 백업
Istio OperatorIstio 서비스 메시 설치/업그레이드
ArgoCDGitOps 배포 (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 -A

Operator 사용 예시 (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: 400Mi

Operator가 이 CR을 보고 알아서:

  • StatefulSet 생성 (데이터 디스크 포함)
  • Service, ServiceAccount 생성
  • ConfigMap (prometheus.yml) 자동 생성
  • ServiceMonitor 라벨 맞는 것 자동 스크레이핑 설정

기존에 수백 줄 YAML이 필요했던 Prometheus 설정이 10줄 CR로 끝남.


CRD 전체 흐름 요약

  1. CRD YAML 작성 (새 kind 정의)
  2. kubectl apply → CRD 등록 (API Server가 새 kind 인식)
  3. Custom Resource YAML 작성 (실제 인스턴스)
  4. kubectl apply → CR이 ETCD에 저장
  5. Custom Controller가 Watch로 감지
  6. Controller가 실제 동작 수행 (외부 API / Pod 생성 등)
  7. 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 다루는 것만 익히면 충분.