- v1.33 "Octarine" 릴리스 핵심 변경 사항
- 플레이 1: 업그레이드 사전 준비
- 플레이 2: Control Plane 업그레이드
- 플레이 3: Worker 노드 롤링 업그레이드
- 플레이 4: In-Place Pod Resize 활용
- 플레이 5: User Namespaces 보안 강화
- 플레이 6: nftables kube-proxy 전환
- 플레이 7: 업그레이드 후 검증
- 업그레이드 롤백 절차
- 참고 자료

v1.33 "Octarine" 릴리스 핵심 변경 사항
Kubernetes v1.33은 2025년 4월 23일에 릴리스되었으며, 코드명은 "Octarine: The Color of Magic"이다. 64개의 개선 사항이 포함되어 있고, 그 중 18개가 Stable로 졸업, 20개가 Beta 진입, 24개가 새로운 Alpha 기능이다.
이 플레이북은 v1.33으로 업그레이드하는 운영팀이 알아야 할 것, 해야 할 것, 주의해야 할 것을 실행 가능한 단위로 정리한 문서다.
Stable로 졸업한 핵심 기능
| 기능 | KEP | 운영 영향도 | 필요 조치 |
|---|---|---|---|
| Sidecar Containers | KEP-753 | 높음 | initContainers에 restartPolicy: Always 사용 가능. 기존 sidecar injection 워크어라운드 정리 |
| User Namespaces | KEP-127 | 높음 | Pod에서 hostUsers: false 기본 활성화. 보안 격리 강화 |
| nftables kube-proxy | KEP-3866 | 중간 | iptables에서 nftables로 전환 가능. 대규모 Service 환경에서 성능 향상 |
| CRD Validation Ratcheting | KEP-4008 | 중간 | 기존 CRD 필드값이 새 스키마에 안 맞아도 변경 안 하면 통과 |
Beta로 진입한 주요 기능
| 기능 | KEP | 운영 영향도 | 설명 |
|---|---|---|---|
| In-Place Pod Resize | KEP-1287 | 높음 | Pod 재시작 없이 CPU/Memory 조정 가능. 기본 활성화 |
| DRA Driver-owned Status | KEP-4817 | 중간 | DRA 드라이버가 ResourceClaim 상태를 직접 업데이트 |
| Pod-level Resource Limits | KEP-2837 | 중간 | 컨테이너 단위가 아닌 Pod 단위로 리소스 상한 설정 |
Deprecated/Removed
| 항목 | 상태 | 대응 |
|---|---|---|
| Endpoints API | Deprecated | EndpointSlice API로 마이그레이션 |
| flowcontrol.apiserver.k8s.io/v1beta3 | Removed | v1으로 업데이트 |
Node Status conditions 필드 중 일부 | 변경 | 모니터링 쿼리 수정 확인 |
플레이 1: 업그레이드 사전 준비
Step 1. 현재 클러스터 상태 점검
#!/bin/bash
# pre-upgrade-check.sh - 업그레이드 전 클러스터 상태 점검
echo "=== 1. 현재 버전 확인 ==="
kubectl version --short 2>/dev/null || kubectl version
echo ""
echo "=== 2. 노드 상태 확인 ==="
kubectl get nodes -o wide
NOT_READY=$(kubectl get nodes --no-headers | grep -v " Ready " | wc -l)
if [ "$NOT_READY" -gt 0 ]; then
echo "WARNING: $NOT_READY 개 노드가 NotReady 상태입니다. 업그레이드 전 해결 필요."
fi
echo ""
echo "=== 3. Deprecated API 사용 확인 ==="
# v1.33에서 제거된 API를 사용하는 리소스 탐색
kubectl get --raw /metrics | grep -E 'apiserver_requested_deprecated_apis'
echo ""
echo "=== 4. PodDisruptionBudget 확인 ==="
kubectl get pdb --all-namespaces -o wide
echo "PDB가 없는 critical 워크로드가 있으면 업그레이드 중 downtime 발생 가능"
echo ""
echo "=== 5. etcd 상태 확인 ==="
kubectl -n kube-system exec -it etcd-$(hostname) -- \
etcdctl endpoint health --cluster \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key
echo ""
echo "=== 6. 버전 skew 확인 ==="
echo "Control Plane:"
kubectl get nodes -l node-role.kubernetes.io/control-plane -o jsonpath='{.items[*].status.nodeInfo.kubeletVersion}'
echo ""
echo "Workers:"
kubectl get nodes -l '!node-role.kubernetes.io/control-plane' -o jsonpath='{.items[*].status.nodeInfo.kubeletVersion}'
Step 2. Deprecated API 마이그레이션
v1.33에서 가장 중요한 API 변경은 Endpoints API의 deprecation이다. 기존에 Endpoints를 직접 읽고 있는 컨트롤러나 스크립트가 있다면 EndpointSlice로 전환해야 한다.
# Endpoints API를 사용하는 리소스 찾기
kubectl get endpoints --all-namespaces -o name | wc -l
# EndpointSlice로 읽는 방법
kubectl get endpointslices --all-namespaces -o wide
코드에서 Endpoints를 직접 참조하는 경우 변경 예시:
# Before: Endpoints API
from kubernetes import client
v1 = client.CoreV1Api()
endpoints = v1.read_namespaced_endpoints("my-service", "default")
for subset in endpoints.subsets:
for address in subset.addresses:
print(address.ip)
# After: EndpointSlice API
discovery_v1 = client.DiscoveryV1Api()
slices = discovery_v1.list_namespaced_endpoint_slice(
"default",
label_selector="kubernetes.io/service-name=my-service"
)
for ep_slice in slices.items:
for endpoint in ep_slice.endpoints:
for address in endpoint.addresses:
print(address)
Step 3. etcd 백업
업그레이드 전 반드시 etcd 스냅샷을 생성한다. 이것이 최후의 롤백 수단이다.
# etcd 스냅샷 생성
ETCDCTL_API=3 etcdctl snapshot save /backup/etcd-pre-v133-$(date +%Y%m%d%H%M).db \
--endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key
# 스냅샷 검증
ETCDCTL_API=3 etcdctl snapshot status /backup/etcd-pre-v133-*.db --write-out=table
플레이 2: Control Plane 업그레이드
kubeadm 기반 클러스터에서의 업그레이드 절차다. 관리형 서비스(EKS, GKE, AKS)는 콘솔이나 CLI에서 버전을 지정하면 자동 처리된다.
Control Plane 노드 업그레이드
# 1. kubeadm 업그레이드
sudo apt-get update
sudo apt-get install -y kubeadm=1.33.0-1.1
# 2. 업그레이드 계획 확인
sudo kubeadm upgrade plan v1.33.0
# 3. 업그레이드 실행 (첫 번째 control plane 노드)
sudo kubeadm upgrade apply v1.33.0
# 4. kubelet과 kubectl 업그레이드
sudo apt-get install -y kubelet=1.33.0-1.1 kubectl=1.33.0-1.1
sudo systemctl daemon-reload
sudo systemctl restart kubelet
# 5. 추가 control plane 노드 (두 번째, 세 번째)
sudo kubeadm upgrade node
업그레이드 후 Control Plane 검증
# API Server 버전 확인
kubectl version
# 컴포넌트 상태 확인
kubectl get componentstatuses 2>/dev/null || \
kubectl get --raw /readyz?verbose
# etcd 클러스터 상태
kubectl -n kube-system exec -it etcd-cp-01 -- \
etcdctl member list --write-out=table \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key
플레이 3: Worker 노드 롤링 업그레이드
Worker 노드는 한 번에 하나씩 업그레이드하여 서비스 가용성을 유지한다.
#!/bin/bash
# worker-upgrade.sh <node-name>
NODE=$1
echo "=== $NODE 업그레이드 시작 ==="
# 1. 노드 cordon (새 Pod 스케줄링 차단)
kubectl cordon "$NODE"
# 2. 노드 drain (기존 Pod 퇴거)
kubectl drain "$NODE" \
--ignore-daemonsets \
--delete-emptydir-data \
--timeout=300s \
--grace-period=120
# 3. 노드에서 패키지 업그레이드 (SSH)
ssh "$NODE" "sudo apt-get update && \
sudo apt-get install -y kubeadm=1.33.0-1.1 && \
sudo kubeadm upgrade node && \
sudo apt-get install -y kubelet=1.33.0-1.1 && \
sudo systemctl daemon-reload && \
sudo systemctl restart kubelet"
# 4. 노드 uncordon
kubectl uncordon "$NODE"
# 5. 노드 상태 확인
kubectl get node "$NODE" -o wide
echo "=== $NODE 업그레이드 완료 ==="
롤링 업그레이드 병렬도 결정
| 클러스터 규모 | 동시 업그레이드 노드 수 | 근거 |
|---|---|---|
| 3-10 노드 | 1 | 여유 노드가 적으므로 보수적 진행 |
| 10-50 노드 | 2-3 | PDB 준수 확인 후 병렬화 |
| 50-200 노드 | 5-10 | 노드 그룹별 순차 진행 |
| 200+ 노드 | 노드 풀 단위 | Blue-green 노드 풀 교체 권장 |
플레이 4: In-Place Pod Resize 활용
v1.33에서 Beta로 승격된 In-Place Pod Resize는 운영팀에게 가장 실용적인 기능이다. Pod를 재시작하지 않고 CPU/Memory를 조정할 수 있다.
리사이즈 실행
# 현재 리소스 확인
kubectl get pod my-app-xyz -o jsonpath='{.spec.containers[0].resources}'
# 리사이즈 요청 (resize subresource 사용)
kubectl patch pod my-app-xyz --subresource=resize --type=merge -p '{
"spec": {
"containers": [{
"name": "app",
"resources": {
"requests": {"cpu": "500m", "memory": "512Mi"},
"limits": {"cpu": "1000m", "memory": "1Gi"}
}
}]
}
}'
# 리사이즈 상태 확인
kubectl get pod my-app-xyz -o jsonpath='{.status.resize}'
# "Proposed" → "InProgress" → "Completed" 순서로 전환
Resize Policy 설정
Container spec에서 리사이즈 정책을 지정할 수 있다:
apiVersion: v1
kind: Pod
metadata:
name: resizable-app
spec:
containers:
- name: app
image: nginx:1.27
resources:
requests:
cpu: '250m'
memory: '256Mi'
limits:
cpu: '500m'
memory: '512Mi'
resizePolicy:
- resourceName: cpu
restartPolicy: NotRequired # CPU는 재시작 없이 조정
- resourceName: memory
restartPolicy: RestartContainer # Memory는 컨테이너 재시작 필요
자동 리사이즈 연동 (VPA + In-Place Resize)
VerticalPodAutoscaler가 In-Place Resize를 활용하도록 설정:
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: my-app-vpa
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: my-app
updatePolicy:
updateMode: 'InPlaceOrRecreate' # v1.33에서 추가된 모드
resourcePolicy:
containerPolicies:
- containerName: app
minAllowed:
cpu: '100m'
memory: '128Mi'
maxAllowed:
cpu: '2000m'
memory: '4Gi'
플레이 5: User Namespaces 보안 강화
v1.33에서 User Namespaces가 기본 활성화되었다. 컨테이너 내부에서 root로 실행되더라도 호스트에서는 비권한(unprivileged) UID로 매핑되어 보안이 강화된다.
User Namespaces 적용
apiVersion: v1
kind: Pod
metadata:
name: secure-app
spec:
hostUsers: false # User Namespaces 활성화
containers:
- name: app
image: myapp:v2.0
securityContext:
runAsUser: 0 # 컨테이너 내부에서는 root
# 하지만 hostUsers: false이므로 호스트에서는 고유 UID로 매핑
보안 정책과 결합
User Namespaces와 Pod Security Standards를 함께 사용:
# Namespace에 Pod Security 레벨 적용
apiVersion: v1
kind: Namespace
metadata:
name: production
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/warn: restricted
Kyverno로 User Namespaces 강제화:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-user-namespaces
spec:
validationFailureAction: Enforce
rules:
- name: require-hostusers-false
match:
any:
- resources:
kinds: ['Pod']
namespaces: ['production', 'staging']
validate:
message: '프로덕션/스테이징 Pod는 hostUsers: false를 설정해야 합니다.'
pattern:
spec:
hostUsers: false
플레이 6: nftables kube-proxy 전환
v1.33에서 nftables 기반 kube-proxy가 Stable이 되었다. iptables보다 성능이 좋고, 특히 Service 수가 많은 클러스터에서 큰 차이가 난다.
전환 전 확인
# 현재 kube-proxy 모드 확인
kubectl -n kube-system get configmap kube-proxy -o yaml | grep mode
# nftables 지원 확인 (노드에서)
nft --version
# nftables v1.0.6 이상 필요
kube-proxy ConfigMap 수정
apiVersion: v1
kind: ConfigMap
metadata:
name: kube-proxy
namespace: kube-system
data:
config.conf: |
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
mode: nftables # iptables → nftables
nftables:
masqueradeAll: false
syncPeriod: 30s
minSyncPeriod: 1s
전환 후 kube-proxy를 롤링 재시작:
kubectl -n kube-system rollout restart daemonset kube-proxy
kubectl -n kube-system rollout status daemonset kube-proxy
플레이 7: 업그레이드 후 검증
업그레이드가 완료되었다고 끝이 아니다. 아래 검증 단계를 반드시 거친다.
자동화된 검증 스크립트
#!/bin/bash
# post-upgrade-verify.sh
ERRORS=0
echo "=== Post-Upgrade Verification ==="
# 1. 모든 노드 Ready 확인
echo "[1/7] Node Status..."
NOT_READY=$(kubectl get nodes --no-headers | grep -v " Ready " | wc -l)
if [ "$NOT_READY" -gt 0 ]; then
echo "FAIL: $NOT_READY nodes not ready"
ERRORS=$((ERRORS + 1))
else
echo "PASS: All nodes ready"
fi
# 2. 모든 시스템 Pod Running 확인
echo "[2/7] System Pods..."
FAILING=$(kubectl -n kube-system get pods --no-headers | grep -v "Running\|Completed" | wc -l)
if [ "$FAILING" -gt 0 ]; then
echo "FAIL: $FAILING system pods not running"
kubectl -n kube-system get pods | grep -v "Running\|Completed"
ERRORS=$((ERRORS + 1))
else
echo "PASS: All system pods healthy"
fi
# 3. CoreDNS 동작 확인
echo "[3/7] DNS Resolution..."
kubectl run dns-test --image=busybox:1.36 --restart=Never --rm -it -- \
nslookup kubernetes.default.svc.cluster.local 2>/dev/null
if [ $? -eq 0 ]; then
echo "PASS: DNS working"
else
echo "FAIL: DNS resolution failed"
ERRORS=$((ERRORS + 1))
fi
# 4. API Server 버전 확인
echo "[4/7] API Server Version..."
SERVER_VERSION=$(kubectl version -o json | jq -r '.serverVersion.gitVersion')
echo "Server version: $SERVER_VERSION"
if [[ "$SERVER_VERSION" == *"1.33"* ]]; then
echo "PASS: Correct version"
else
echo "FAIL: Unexpected version"
ERRORS=$((ERRORS + 1))
fi
# 5. 핵심 워크로드 상태 확인
echo "[5/7] Critical Workloads..."
for ns in production staging; do
UNAVAIL=$(kubectl -n "$ns" get deployments --no-headers 2>/dev/null | \
awk '{if ($2 != $4) print $1}')
if [ -n "$UNAVAIL" ]; then
echo "FAIL: Unavailable deployments in $ns: $UNAVAIL"
ERRORS=$((ERRORS + 1))
fi
done
echo "PASS: Critical workloads healthy"
# 6. Ingress/Service 동작 확인
echo "[6/7] Service Connectivity..."
kubectl get svc -A --no-headers | awk '{print $1"/"$2}' | head -5 | while read svc; do
echo " Checking $svc..."
done
echo "PASS: Services enumerated"
# 7. 메트릭 파이프라인 확인
echo "[7/7] Metrics Pipeline..."
kubectl top nodes 2>/dev/null
if [ $? -eq 0 ]; then
echo "PASS: Metrics working"
else
echo "WARN: Metrics pipeline may need time to recover"
fi
echo ""
echo "=== Verification Complete: $ERRORS errors ==="
exit $ERRORS
Conformance Test 실행
업그레이드 후 Kubernetes Conformance Test를 실행하여 클러스터가 스펙을 준수하는지 확인:
# Sonobuoy로 Conformance Test 실행
sonobuoy run --mode=certified-conformance --wait
# 결과 확인
sonobuoy status
sonobuoy results $(sonobuoy retrieve)
업그레이드 롤백 절차
문제가 발견되면 즉시 롤백할 수 있어야 한다.
Worker 노드 롤백
# Worker 노드를 이전 버전으로 다운그레이드
ssh worker-node-01 "sudo apt-get install -y \
kubeadm=1.32.x-1.1 kubelet=1.32.x-1.1 && \
sudo kubeadm upgrade node && \
sudo systemctl daemon-reload && \
sudo systemctl restart kubelet"
Control Plane 롤백 (최후의 수단)
Control Plane 롤백은 etcd 복원을 동반하므로 위험하다. 반드시 사전에 백업을 확인한 상태에서만 진행한다.
# etcd 스냅샷에서 복원
ETCDCTL_API=3 etcdctl snapshot restore /backup/etcd-pre-v133-*.db \
--data-dir=/var/lib/etcd-restored
# etcd 데이터 디렉토리 교체 (모든 control plane 노드에서)
sudo systemctl stop etcd
sudo mv /var/lib/etcd /var/lib/etcd-broken
sudo mv /var/lib/etcd-restored /var/lib/etcd
sudo systemctl start etcd
퀴즈
Q1. v1.33에서 Endpoints API가 deprecated된 후 대체해야 할 API는?
정답: ||EndpointSlice API. v1.21부터 사용 가능했으며, dual-stack 네트워킹 등 새로운 기능을
지원한다.||
Q2. In-Place Pod Resize에서 CPU와 Memory의 리사이즈 동작 차이는?
정답: ||CPU는 컨테이너 재시작 없이(NotRequired) 조정 가능하지만, Memory는 경우에 따라 컨테이너
재시작(RestartContainer)이 필요하다.||
Q3. User Namespaces에서 hostUsers: false를 설정하면 보안상 어떤 이점이 있는가?
정답: ||컨테이너 내부에서 root(UID 0)로 실행되더라도 호스트에서는 비권한 UID로 매핑되어, 컨테이너
탈출(container escape) 시에도 호스트 권한을 얻지 못한다.||
Q4. 업그레이드 전 etcd 백업이 "최후의 롤백 수단"인 이유는?
정답: ||Control Plane 롤백은 API Server 바이너리만 되돌리면 안 되고, etcd에 저장된 클러스터 상태도
함께 복원해야 한다. etcd 스냅샷이 없으면 이전 상태로 돌아갈 방법이 없다.||
Q5. nftables kube-proxy가 iptables보다 대규모 클러스터에서 유리한 이유는?
정답: ||iptables는 규칙이 선형 체인으로 평가되어 Service 수에 비례하여 지연이 증가하지만,
nftables는 맵/셋 기반으로 O(1)에 가까운 룩업이 가능하다.||
Q6. Worker 노드 업그레이드 시 drain 전에 cordon을 먼저 하는 이유는?
정답: ||cordon 없이 drain만 하면, drain 중에 새로운 Pod가 해당 노드에 스케줄링될 수 있다.
cordon으로 먼저 새 Pod 배치를 차단한 후 drain으로 기존 Pod를 퇴거해야 한다.||
Q7. CRD Validation Ratcheting이 업그레이드에서 중요한 이유는?
정답: ||CRD 스키마를 강화해도, 기존에 저장된 리소스가 변경되지 않는 한 새 스키마 규칙에 걸리지
않는다. 이것은 기존 리소스를 깨뜨리지 않으면서 점진적으로 스키마를 개선할 수 있게 해준다.||
참고 자료
현재 단락 (1/355)
Kubernetes v1.33은 2025년 4월 23일에 릴리스되었으며, 코드명은 "Octarine: The Color of Magic"이다. 64개의 개선 사항이 포함되어 있고,...