콘텐츠로 이동

19. 시험 전략과 치트시트

아는 것을 2시간 안에 꺼내기

  1. 별칭과 단축 변수

    Terminal window
    alias k=kubectl # 보통 이미 있다
    export do='--dry-run=client -o yaml'
    export now='--force --grace-period=0'
    source <(kubectl completion bash)
    complete -F __start_kubectl k
  2. vim 설정 — 탭 문자로 YAML을 깨뜨리지 않기 위해

    Terminal window
    cat <<'EOF' > ~/.vimrc
    set expandtab
    set tabstop=2
    set shiftwidth=2
    set number
    EOF

이 30초가 남은 2시간의 속도를 정한다. 특히 $do는 거의 모든 문제에서 쓰게 된다.

flowchart LR
    Q["문제를 연다"] --> S1["① 컨텍스트 전환<br/>지문의 명령을 그대로 복사"]
    S1 --> S2["② 네임스페이스 확인<br/>지문이 지정했으면 반드시 -n"]
    S2 --> SOLVE["푼다"]
    SOLVE --> S3["③ 검산<br/>만든 것을 눈으로 확인"]
    S3 --> NEXT["다음 문제"]
    SKIP1["① 을 빠뜨리면"] -.->|"정답을 만들어도 0점 ❌"| S1
    SKIP2["② 를 틀리면"] -.->|"그 문제 전부 0점 ❌"| S2

    classDef key fill:#dbeafe,stroke:#2563eb,color:#1e3a8a
    classDef bad fill:#fee2e2,stroke:#dc2626,color:#7f1d1d
    classDef ok fill:#dcfce7,stroke:#16a34a,color:#14532d
    classDef mute fill:#f1f5f9,stroke:#94a3b8,color:#334155
    class S1,S2 key
    class SKIP1,SKIP2 bad
    class S3 ok
    class Q,SOLVE,NEXT mute
Terminal window
# ① 컨텍스트 전환
kubectl config use-context k8s-c1-A
# ② 네임스페이스
kubectl config set-context --current --namespace=<지정된ns>
# ③ 검산
kubectl get <리소스> -n <ns>
Terminal window
# Pod
k run nginx --image=nginx
k run nginx --image=nginx --port=80 --labels=app=web
k run nginx --image=nginx $do > pod.yaml
k run busy --image=busybox --restart=Never -- sleep 3600
k run tmp --image=busybox --rm -it --restart=Never -- sh # 일회용 셸
# Deployment
k create deploy web --image=nginx --replicas=3
k create deploy web --image=nginx --replicas=3 $do > deploy.yaml
k scale deploy web --replicas=5
k set image deploy/web nginx=nginx:1.28
k rollout status deploy/web
k rollout undo deploy/web
k rollout restart deploy/web
# Job / CronJob
k create job hello --image=busybox -- echo hi
k create job manual --from=cronjob/nightly
k create cronjob nightly --image=busybox --schedule="0 3 * * *" -- echo backup

생성 치트시트 — 네트워크 · 설정

섹션 제목: “생성 치트시트 — 네트워크 · 설정”
Terminal window
# Service
k expose deploy web --port=80 --target-port=8080 --name=web-svc
k expose deploy web --port=80 --type=NodePort
k create svc clusterip web-svc --tcp=80:8080
k create svc nodeport web-svc --tcp=80:8080 --node-port=30080
# Ingress
k create ingress web --class=nginx --rule="example.com/*=web-svc:80"
k create ingress web --class=nginx \
--rule="example.com/api*=api-svc:8080" \
--rule="example.com/*=web-svc:80"
# ConfigMap / Secret
k create cm app-config --from-literal=KEY=val --from-file=./conf/
k create cm app-env --from-env-file=./app.env
k create secret generic db --from-literal=password=s3cr3t
k create secret tls web-tls --cert=./tls.crt --key=./tls.key
k create secret docker-registry regcred --docker-server=r.io --docker-username=u --docker-password=p
# 네임스페이스 / 쿼터
k create ns dev
k create quota dev-quota --hard=cpu=4,memory=8Gi,pods=20 -n dev
Terminal window
# ServiceAccount
k create sa deploy-bot
# Role / ClusterRole
k create role pod-reader --verb=get,list,watch --resource=pods -n dev
k create role pod-editor --verb=get,list,create,delete --resource=pods,deployments -n dev
k create clusterrole node-reader --verb=get,list,watch --resource=nodes
# Binding
k create rolebinding dev-rb --role=pod-reader --user=dev -n dev
k create rolebinding sa-rb --role=pod-reader --serviceaccount=dev:deploy-bot -n dev
k create clusterrolebinding ops-crb --clusterrole=cluster-admin --user=ops
k create rolebinding dev-edit --clusterrole=edit --user=bob -n dev # ClusterRole + RoleBinding
# 검산 ★ 반드시 한다
k auth can-i list pods -n dev --as=dev
k auth can-i create deploy -n dev --as=system:serviceaccount:dev:deploy-bot
k auth can-i --list -n dev
Terminal window
# 기본
k get pods -o wide
k get pods -A
k get all -n dev
k get pods --show-labels
k get pods -l env=prod --no-headers | wc -l # 개수 세기
# 정렬
k get pods --sort-by=.metadata.creationTimestamp
k get events -A --sort-by=.lastTimestamp | tail -30
k get nodes --sort-by=.metadata.name
# 필드 추출 — "파일에 저장하시오" 문제용
k get pods -o custom-columns='NAME:.metadata.name,NODE:.spec.nodeName' --no-headers
k get pods -o jsonpath='{.items[*].spec.containers[*].image}'
k get pv --sort-by=.spec.capacity.storage -o custom-columns='NAME:.metadata.name,SIZE:.spec.capacity.storage'
k get nodes -o jsonpath='{.items[*].status.addresses[?(@.type=="InternalIP")].address}'
# 필터
k get pods --field-selector status.phase=Running
k get pods --field-selector spec.nodeName=node01
k get events --field-selector type=Warning
Terminal window
# 순서: describe → logs → yaml
k describe pod web | grep -A20 Events
k logs web --previous
k logs -l app=web --all-containers --prefix --tail=50
k get pod web -o yaml
# 노드
k describe node node01 | grep -A15 Conditions
k describe node node01 | grep -A10 'Allocated resources'
k top nodes ; k top pods -A --sort-by=memory
# 노드 안에서
sudo systemctl status kubelet
sudo journalctl -u kubelet -n 100 --no-pager
sudo crictl ps -a
sudo crictl logs <id>
# 네트워크
k get endpoints web-svc
k run tmp --image=busybox:1.36 --rm -it --restart=Never -- nslookup web-svc
k run tmp --image=busybox:1.36 --rm -it --restart=Never -- wget -qO- http://web-svc
Terminal window
# 노드 관리
k drain node01 --ignore-daemonsets --delete-emptydir-data
k uncordon node01
k taint node node01 key=value:NoSchedule
k taint node node01 key=value:NoSchedule-
k label node node01 disktype=ssd
# 업그레이드 (노드에서)
sudo sed -i 's/v1.34/v1.35/' /etc/apt/sources.list.d/kubernetes.list && sudo apt-get update
sudo apt-mark unhold kubeadm && sudo apt-get install -y kubeadm=1.35.1-1.1 && sudo apt-mark hold kubeadm
sudo kubeadm upgrade plan
sudo kubeadm upgrade apply v1.35.1 # 첫 컨트롤 플레인
sudo kubeadm upgrade node # 나머지 전부
sudo apt-mark unhold kubelet kubectl && sudo apt-get install -y kubelet=1.35.1-1.1 kubectl=1.35.1-1.1 && sudo apt-mark hold kubelet kubectl
sudo systemctl daemon-reload && sudo systemctl restart kubelet
# 조인 / 인증서
kubeadm token create --print-join-command
sudo kubeadm certs check-expiration
sudo kubeadm certs renew all

etcd 백업·복구 — 통째로 외울 것

섹션 제목: “etcd 백업·복구 — 통째로 외울 것”
Terminal window
# 경로 확인 (추측하지 말 것)
sudo grep -E 'cert-file|key-file|trusted-ca-file|data-dir|listen-client-urls' \
/etc/kubernetes/manifests/etcd.yaml
# 백업
sudo ETCDCTL_API=3 etcdctl \
--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 \
snapshot save /opt/backup.db
# 확인
sudo ETCDCTL_API=3 etcdctl --write-out=table snapshot status /opt/backup.db
# 복구
sudo ETCDCTL_API=3 etcdctl snapshot restore /opt/backup.db --data-dir=/var/lib/etcd-restore
sudo vim /etc/kubernetes/manifests/etcd.yaml # volumes.hostPath.path 를 새 디렉터리로
# → kubelet이 자동으로 etcd Pod을 다시 만든다. 1~2분 대기

빈 파일에서 시작하지 않는 세 경로.

flowchart LR
    NEED["YAML 이 필요하다"] --> P1["① 명령형 생성기<br/>k create … $do > f.yaml"]
    NEED --> P2["② 공식 문서 복사<br/>NetworkPolicy · affinity · PV …"]
    NEED --> P3["③ 기존 리소스 복사<br/>k get pod web -o yaml > new.yaml"]
    P3 --> CLEAN["uid · resourceVersion<br/>creationTimestamp · status 를 지운다"]
    P1 --> EDIT["필요한 필드만 편집"]
    P2 --> EDIT
    CLEAN --> EDIT
    EDIT --> V["k apply --dry-run=server<br/>k diff -f"]
    BAD["빈 파일에 직접 타이핑"] -.->|"가장 느리고 가장 틀리기 쉽다 ❌"| NEED

    classDef ok fill:#dcfce7,stroke:#16a34a,color:#14532d
    classDef bad fill:#fee2e2,stroke:#dc2626,color:#7f1d1d
    classDef key fill:#dbeafe,stroke:#2563eb,color:#1e3a8a
    classDef mute fill:#f1f5f9,stroke:#94a3b8,color:#334155
    class P1,P2,P3 ok
    class BAD bad
    class V key
    class NEED,EDIT,CLEAN mute
Terminal window
# 1) 명령형으로 뼈대를 뽑는다
k run web --image=nginx $do > web.yaml
k create deploy web --image=nginx $do > deploy.yaml
# 2) 기존 리소스를 복사해서 고친다
k get pod web -o yaml > new.yaml
# → metadata의 uid, resourceVersion, creationTimestamp, status 를 지운다
# 3) 필드 이름이 기억 안 나면
k explain pod.spec.containers.resources
k explain deployment.spec.strategy --recursive | head -30
# 4) 적용 전에 검증
k apply -f web.yaml --dry-run=server
k diff -f web.yaml
유형 핵심
클러스터 업그레이드 저장소 URL → apply/node → kubelet
etcd 백업·복구 인증서 3종, 새 data-dir
고장난 컨트롤 플레인 /etc/kubernetes/manifests/ 의 값 하나
노드 NotReady journalctl -u kubelet
RBAC 생성 Role + Binding + auth can-i 검산
CSR 승인 certificate approve
PV/PVC 연결 storageClassName·accessModes·용량
NetworkPolicy 문서 복사, AND/OR 주의
Ingress / Gateway API 문서 복사
스케줄링(taint·affinity) 문서 복사
사이드카·initContainer 추가 kubectl edit 또는 재생성
DNS 확인 nslookup, CoreDNS ConfigMap
특정 조건의 Pod 찾아 파일에 저장 -l, --sort-by, custom-columns
HPA 구성 metrics-server + requests
노드 drain / 스케줄 제어 --ignore-daemonsets
flowchart LR
    S["시험 시작"] --> R0["30초 루틴<br/>alias · do · vimrc"]
    R0 --> R1["1차 순회<br/>3분 안에 풀리는 것을 전부 처리"]
    R1 --> RULE{"5분 룰"}
    RULE -->|"실마리가 안 보인다"| FLAG["플래그 걸고 넘어간다"]
    RULE -->|"풀린다"| SOLVE["푼다"]
    FLAG --> R2["2차 순회<br/>남은 문제를 배점 순으로"]
    SOLVE --> R2
    R2 --> LAST["마지막 10분<br/>검산"]

    classDef key fill:#dbeafe,stroke:#2563eb,color:#1e3a8a
    classDef ok fill:#dcfce7,stroke:#16a34a,color:#14532d
    classDef warn fill:#fef3c7,stroke:#d97706,color:#78350f
    classDef mute fill:#f1f5f9,stroke:#94a3b8,color:#334155
    class R0,RULE key
    class LAST ok
    class FLAG warn
    class S,R1,R2,SOLVE mute
  • 문제당 평균 7분. 하지만 균일하지 않다
  • 1차 순회 — 전체를 빠르게 훑으며 3분 안에 풀리는 것을 전부 처리
  • 막히면 5분 룰 — 5분 안에 실마리가 안 보이면 플래그 걸고 넘어간다
  • 2차 순회 — 남은 문제를 배점 순으로
  • 마지막 10분은 검산

그리고 완벽주의를 버려라. 66%면 합격이다. 어려운 두 문제를 포기하고 나머지를 확실히 하는 편이 낫다.

컨텍스트 전환 안 함

그 문제 전부 0점. 가장 흔하다.

네임스페이스 틀림

그 문제 전부 0점. 두 번째로 흔하다.

vimrc 없이 YAML 편집

탭 문자가 들어가 파싱 에러. 문제를 통째로 날린다.

한 문제에 20분

뒤의 쉬운 문제 3개를 못 푼다.

실수 대가
컨텍스트 전환 안 함 그 문제 전부 0점
네임스페이스 틀림 그 문제 전부 0점
vim에 expandtab 없이 YAML 편집 파싱 에러로 문제 통째로 날림
노드에서 exit 안 하고 다음 문제 명령이 안 먹혀 시간 낭비
drain--ignore-daemonsets 누락 에러에서 헤맴
업그레이드 시 저장소 URL 안 바꿈 패키지를 못 찾아 막힘
만들고 확인 안 함 롤아웃 중인 상태로 넘어가 감점
한 문제에 20분 매달림 뒤의 쉬운 문제 3개를 못 품

위 8개 중 앞의 두 개가 압도적으로 많다. 이것만 안 해도 합격선에 가까워진다.

Terminal window
# 만들었으면 본다
k get <리소스> -n <ns>
k describe <리소스> <이름> -n <ns>
# 롤아웃은 끝날 때까지 기다린다
k rollout status deploy/web
k wait --for=condition=ready pod -l app=web --timeout=60s
# 연결은 실제로 해본다
k get endpoints web-svc
k run tmp --image=busybox:1.36 --rm -it --restart=Never -- wget -qO- http://web-svc
# 권한은 흉내내서 확인한다
k auth can-i <verb> <resource> -n <ns> --as=<user>
# 노드 작업은 최종 상태를 본다
k get nodes

“만들었다”와 “동작한다”는 다르다. 채점은 최종 상태만 보므로, 최종 상태를 본 사람만 점수를 가져간다.

공식 문서에서 바로 가져올 것들

섹션 제목: “공식 문서에서 바로 가져올 것들”

손으로 치면 틀리는 것들이다. 무조건 복사한다.

필요한 것 검색어 (kubernetes.io/docs)
NetworkPolicy network policies — deny-all 포함 예제 5종
nodeAffinity / podAffinity assign pods nodes
PV / PVC / StorageClass persistent volumes
Ingress + TLS ingress
RBAC Role/Binding rbac
CSR certificate signing requests
etcd 백업·복구 operating etcd
업그레이드 upgrade kubeadm clusters
probe 문법 configure liveness readiness startup probes
CronJob 스케줄 cronjob
HTTPRoute (Gateway API 사이트) http routing
  1. D-14 — killer.sh 1세션

    무엇이 약한지만 확인한다. 점수는 신경 쓰지 말 것.

  2. D-13 ~ D-8 — 틀린 영역 집중

    특히 15장(라이프사이클) 을 반복한다.

  3. D-7 ~ D-4 — killercoda 반복

    업그레이드 · etcd 백업복구 · 컨트롤 플레인 복구를 각 5회.

  4. D-3 — killer.sh 2세션

    이번엔 시간 안에 푸는 연습.

  5. D-2 — 요약과 치트시트

    각 장의 요약 절만 훑고 치트시트를 손에 붙인다.

  6. D-1 — 새 걸 공부하지 말 것

    환경 점검(웹캠·신분증·책상)만 한다.

killer.sh는 실제 시험보다 어렵다. 거기서 60%를 받아도 실제로는 합격하는 경우가 많다. 점수에 낙담하지 말고 틀린 것의 해설을 전부 읽는 용도로 쓰자.

  • 시작 30초에 alias · $do · ~/.vimrc(expandtab) 를 만든다
  • 문제마다 ① 컨텍스트 ② 네임스페이스 ③ 검산 3단계를 반복한다
  • 빈 파일에 YAML을 치지 말 것 — 생성기 / 문서 / 기존 리소스 복사
  • 빨리 풀리는 것부터. 5분 룰로 넘기고 2차 순회에서 돌아온다
  • 가장 비싼 실수는 컨텍스트·네임스페이스. 이 둘만 안 틀려도 크게 유리하다
  • “만들었다”가 아니라 “동작한다”를 확인하고 넘어간다
  • 문서 복사 대상(NetworkPolicy·affinity·PV·etcd·업그레이드)은 경로를 미리 익혀둔다
  • 66%면 합격이다. 어려운 문제를 버리는 것도 전략이다