CKA 1주차 - 클러스터 구조와 Pod · Deployment, kubectl 기본기
이 시리즈에 대해
AWS DevOps Engineer Professional에 합격한 뒤 다음 목표로 CKA(Certified Kubernetes Administrator) 를 준비하고 있다. 지금 회사가 AWS EKS로 인프라를 운영하고 있어서, 시험 준비가 곧 회사 인프라를 이해하는 공부가 된다는 점이 컸다.
나는 프론트엔드 개발자이고 쿠버네티스는 처음이다. 그래서 개념을 읽기만 하지 않고, 노트북의 minikube 클러스터에서 명령어를 하나씩 직접 쳐보면서 공부했다. 이 시리즈는 그 과정을 주차별로 정리한 기록이다.
- 1주차: 클러스터 구조, Pod · Deployment, kubectl 기본기 (이번 글)
- 2주차: ConfigMap · Secret, 리소스 requests/limits, 스케줄링(nodeSelector · taint · affinity), DaemonSet
- 3주차: Service 4종, EndpointSlice, kube-proxy, CoreDNS, Service 장애 진단
0. CKA는 어떤 시험인가
CKA는 객관식이 아니다. 원격 감독관이 웹캠으로 지켜보는 상태에서, 브라우저 안의 리눅스 터미널로 실제 클러스터를 조작해서 과제를 해결하는 실습형 시험이다.
| 항목 | 내용 |
|---|---|
| 형식 | 실습형(performance-based), 2시간 동안 15~20개 과제 |
| 합격선 | 66점 |
| 허용 자료 | 시험 중 kubernetes.io 공식 문서 열람 가능 |
| 언어 | 문제는 영어 (Create a pod named ... in namespace ... 같은 정형화된 문장) |
출제 비중은 다음과 같다.
| 도메인 | 비중 |
|---|---|
| Troubleshooting | 30% |
| Cluster Architecture, Installation & Configuration | 25% |
| Services & Networking | 20% |
| Workloads & Scheduling | 15% |
| Storage | 10% |
명령어를 손에 익히는 속도가 곧 점수다. 그래서 공부 시간의 대부분을 실습에 쓰기로 했다.
합격선·응시료·허용 문서 범위는 바뀔 수 있으니 응시 전에 Linux Foundation 공식 페이지에서 다시 확인해야 한다.
실습 환경
- Docker Desktop 위에서 minikube 2노드 클러스터를 띄웠다.
minikube(control-plane)와minikube-m02(worker) 두 대이고, CNI는 flannel, 쿠버네티스 버전은 v1.37.0이다. - 명령어는 EKS와 똑같다. 로컬 클러스터에서 익힌
kubectl명령을 회사 EKS에서도 그대로 쓸 수 있다.
minikube start --driver=docker --nodes=2 --cni=flannel \
--kubernetes-version=v1.37.0 --cpus=2 --memory=30721. 쿠버네티스 리소스의 계층
처음 쿠버네티스를 보면 Pod, ReplicaSet, Deployment, Node 같은 단어가 한꺼번에 나와서 헷갈린다. 가장 먼저 "무엇이 무엇을 감싸고 있는가"를 정리했다. 왼쪽이 가장 바깥, 오른쪽이 가장 안쪽이다.
- Cluster
- Node
- Deployment
- ReplicaSet
- Pod
- Container
안쪽부터 하나씩 보면 이렇다.
- Container: 앱과 실행 환경을 이미지로 묶어 실행한 것이다. 이미지는 Docker Hub 같은 레지스트리에서 받아온다.
- Pod: 보통 컨테이너 1개를 감싼다. 클러스터 내부에서만 쓰는 IP(
10.244.x.x)를 하나 받는다. - ReplicaSet: "Pod를 항상 N개 유지"를 실제로 수행한다. Pod 이름 가운데 해시(
web-68d995574f-xxxxx의68d995574f)가 이 ReplicaSet의 ID다. - Deployment: ReplicaSet을 관리한다. 스케일 조정, 버전 업데이트, 롤백을 Deployment에 시킨다.
- Node / Cluster: Node는 앱이 실행되는 서버이고(EKS에서는 보통 EC2 인스턴스), Cluster는 Node들과 이들을 관리하는 control plane을 합친 것이다.
실무에서는 Pod를 직접 만들지 않는다. Deployment를 만들면 Deployment → ReplicaSet → Pod 순서로 쿠버네티스가 알아서 만들어준다. 왜 그런지는 아래 자가 치유 실습에서 확인했다.
2. 명령형과 선언형
리소스를 만드는 방법은 두 가지다.
| 방식 | 명령 | 의미 |
|---|---|---|
| 명령형 (imperative) | k run, k create | "지금 이걸 하나 만들어" |
| 선언형 (declarative) | k apply -f x.yaml | "이 파일에 적힌 상태로 맞춰줘" |
# 명령형: Pod를 바로 만든다
k run myweb --image=nginx
# 선언형: YAML 파일을 만든 뒤 그 내용대로 반영한다
k run web2 --image=nginx --dry-run=client -o yaml > web2.yaml
k apply -f web2.yaml--dry-run=client -o yaml이 이번 주에 가장 많이 쓴 옵션이다.
--dry-run=client: 클러스터에 실제로 만들지 않고 결과만 계산한다.-o yaml: 그 결과를 YAML로 출력한다.> web2.yaml: 화면 대신 파일로 저장한다.
시험에서 YAML을 처음부터 손으로 쓰면 들여쓰기 실수도 나고 시간도 오래 걸린다. 명령형으로 뼈대를 뽑은 뒤 필요한 부분만 고치는 방식이 훨씬 빠르다.
apply는 무조건 새로 만드는 명령이 아니다. YAML에 적힌 상태와 클러스터의 현재 상태를 비교해서 다른 부분만 바꾼다. 같은 파일로 apply를 여러 번 실행해도 리소스가 늘어나거나 망가지지 않는다. 이 성질을 멱등성(idempotency) 이라고 한다. 실제로 내용이 같은 파일을 다시 적용하면 unchanged가 출력된다.
모든 매니페스트는 4개의 최상위 필드로 시작한다.
apiVersion: v1 # 이 리소스를 정의한 API 버전
kind: Pod # 리소스 종류
metadata: # 이름, 라벨 같은 식별 정보
name: web2
spec: # 원하는 상태 (어떤 컨테이너를 어떻게 실행할지)
containers:
- name: web2
image: nginx3. Pod 하나를 만들고 들여다보기
Pod를 하나 만들고 상태를 확인하는 명령부터 익혔다.
k run myweb --image=nginx # Pod 생성
k get pod myweb -o wide # IP와 배치된 노드까지 표시
k describe pod myweb # 상세 정보와 이벤트
k logs myweb # 컨테이너 로그
k exec myweb -- hostname # 컨테이너 안에서 명령 실행
k get pods -A -o wide # 모든 네임스페이스의 Pod 조회이 중에서 describe가 가장 중요하다. 출력 맨 아래의 Events 에 "이미지를 받는 중", "스케줄링 실패", "컨테이너 재시작" 같은 기록이 시간 순서로 남는다. Pod가 이상하면 제일 먼저 Events를 본다. 2주차와 3주차의 장애 실습에서도 원인은 전부 여기서 찾았다.
4. Deployment: 자가 치유, 스케일, 롤링 업데이트
자가 치유 (self-healing)
k create deployment web --image=nginx --replicas=3
k get deployment,replicaset,podPod 3개가 뜬 상태에서 Pod 하나를 직접 지워봤다.
k delete pod web-68d995574f-abcde
k get pod지운 Pod는 사라지지만 곧바로 다른 이름의 새 Pod 가 생겨서 다시 3개가 된다. ReplicaSet이 "원하는 개수는 3개인데 현재 2개"라는 차이를 발견하고 하나를 새로 만든 것이다.
반대로 k run으로 직접 만든 Pod는 지우면 그걸로 끝이다. 다시 만들어줄 관리자가 없다. 실무에서 Pod를 직접 만들지 않고 Deployment를 쓰는 이유가 이것이다.
스케일
k scale deployment web --replicas=5
k scale deployment web --replicas=2명령 한 줄로 개수가 3 → 5 → 2로 바뀌었다.
비슷해 보이지만 결과가 다른 두 명령도 비교했다.
| 명령 | 결과 |
|---|---|
k scale deployment web --replicas=0 | Deployment와 ReplicaSet은 남고 Pod만 0개가 된다. 다시 --replicas=2로 올리면 같은 ReplicaSet이 Pod를 새로 만든다 |
k delete deployment web | Deployment, ReplicaSet, Pod가 모두 삭제된다 |
잠시 트래픽을 멈추고 싶을 때는 scale 0, 완전히 걷어낼 때는 delete를 쓴다.
롤링 업데이트와 롤백
k set image deployment/web nginx=nginx:1.27 # 이미지 버전 변경
k rollout status deployment/web # 배포 진행 상황
k rollout history deployment/web # 리비전 목록
k rollout undo deployment/web # 직전 리비전으로 되돌리기이미지를 바꾸면 Deployment는 기존 ReplicaSet을 수정하지 않는다. 새 ReplicaSet을 만들어 Pod를 하나씩 옮기고, 기존 ReplicaSet은 Pod 0개로 남겨둔다.
- rev1: ReplicaSet A(
nginx)의 Pod가 3개 → 0개가 된다. ReplicaSet A는 지우지 않고 남겨둔다. - rev2: ReplicaSet B(
nginx:1.27)의 Pod가 0개 → 3개가 된다. rollout undo후 rev3: 남겨둔 ReplicaSet A를 다시 사용해서 Pod를 0개 → 3개로 늘린다.
rollout undo가 빠른 이유가 여기에 있다. 이전 버전의 ReplicaSet이 그대로 남아 있어서 그 ReplicaSet의 Pod 개수만 다시 늘리면 된다. k get rs로 보면 Pod 0개짜리 ReplicaSet이 남아 있는 것을 확인할 수 있다.
실습하면서 하나 더 알게 된 점이 있다. nginx처럼 태그 없이 쓰면 latest가 되는데, latest는 받는 시점마다 실제 버전이 달라질 수 있다. 로그를 보니 nginx/1.31.3이 실행되고 있었다. 언제 배포하든 같은 결과가 나오게 하려면 nginx:1.27처럼 태그를 명시해야 한다.
5. kubectl은 어느 클러스터를 조작하고 있나: context
kubectl은 ~/.kube/config(kubeconfig) 파일에 적힌 클러스터 목록 중 current-context 로 지정된 클러스터에 명령을 보낸다.
k config current-context # 지금 조작 중인 클러스터
k config get-contexts # 전체 목록
k config use-context <이름> # 전환
aws eks update-kubeconfig --name <클러스터> # EKS 클러스터를 kubeconfig에 추가시험에서는 문제마다 대상 클러스터가 다르다. 각 문제 상단에 적힌 k config use-context ...를 먼저 실행하지 않으면, 엉뚱한 클러스터에서 작업하게 되고 그 문제는 0점이 된다.
6. 시험용 셸 세팅
alias k=kubectl
source <(kubectl completion bash)
export do='--dry-run=client -o yaml'
export now='--force --grace-period=0'k run web --image=nginx $do > web.yaml처럼 YAML 뼈대를 빠르게 만들 수 있다.k delete pod web $now는 Pod가 종료될 때까지 기다리지 않고 바로 지운다.- 리소스 이름은 줄여 쓸 수 있다.
po(pods),deploy(deployments),rs(replicasets),svc(services),no(nodes),ns(namespaces),cm(configmaps).
내 맥은 zsh라서 $do가 하나의 문자열로 넘어가 옵션이 쪼개지지 않는 문제가 있었다. zsh에서는 ${=do}로 쓰거나 옵션을 풀어서 쳐야 한다. 시험 환경은 bash라서 $do를 그대로 쓰면 된다.
7. 1주차 명령어 정리
| 목적 | 명령 |
|---|---|
| 노드 · Pod 조회 | k get no · k get po -A -o wide |
| 상세 정보 · 이벤트 | k describe pod <이름> |
| 로그 · 내부 명령 | k logs <pod> · k exec <pod> -- <cmd> |
| Pod 생성 | k run myweb --image=nginx |
| YAML 뼈대 | k run myweb --image=nginx --dry-run=client -o yaml > p.yaml |
| 선언형 적용 | k apply -f p.yaml |
| Deployment 생성 | k create deployment web --image=nginx --replicas=3 |
| 스케일 | k scale deployment web --replicas=5 |
| 버전 변경 | k set image deployment/web nginx=nginx:1.27 |
| 롤아웃 상태 · 이력 · 롤백 | k rollout status | history | undo deployment/web |
| 삭제 | k delete pod <이름> · k delete -f p.yaml |
정리
- 쿠버네티스에서는 Pod를 직접 다루기보다 Deployment에 "원하는 상태"를 알려주고, 실제 맞추는 일은 ReplicaSet에 맡긴다.
- YAML은
--dry-run=client -o yaml로 뼈대를 만들고 고친다.apply는 원하는 상태와 현재 상태의 차이만 반영한다. - 문제가 생기면
describe의 Events부터 본다.
다음 글에서는 앱 설정을 이미지 밖으로 빼는 ConfigMap · Secret과, Pod를 어느 노드에 배치할지 정하는 스케줄링을 정리한다.