kubectl describe
사람이 읽으라고 만든 요약. 끝에 Events가 붙는다 ← 진단의 핵심. 관련 오브젝트 정보를 합쳐서 보여준다.
같은 결과를 30초 만에 만드는 법
아키텍처의 그림에서 모든 화살표는 API 서버를 향했다 — kubectl은 그중 사람이 쏘는 화살표다. 시험의 모든 조작이 이 클라이언트를 거치니, 이 장은 곧 손 속도를 버는 장이다.
~/.kube/config. KUBECONFIG 환경변수나 --kubeconfig로 바꾼다kubectl get pods -v=6 # 실제로 어떤 URL을 호출하는지 보인다# GET https://10.0.0.1:6443/api/v1/namespaces/default/pods?limit=500 200 OK이걸 알면 “권한이 없다”거나 “연결이 안 된다”는 에러를 “어느 주소로 어떤 신원으로 갔는가”로 환원해서 볼 수 있다.
API 서버는 아무 요청이나 받지 않는다 — 아키텍처에서 본 첫 관문(인증)을 통과해야 한다. 그렇다고 매 명령마다 서버 주소와 인증서를 플래그로 줄 수는 없으니, “어디로 · 누구로”를 파일 하나에 묶어 둔다. 그게 kubeconfig다.
apiVersion: v1kind: Configclusters: # 어디로: 주소 + CA - name: prod cluster: server: https://10.0.0.1:6443 certificate-authority: /etc/kubernetes/pki/ca.crtusers: # 누구로: 인증 정보 - name: admin user: client-certificate: /etc/kubernetes/pki/admin.crt client-key: /etc/kubernetes/pki/admin.keycontexts: # 조합: cluster + user + namespace - name: prod-admin context: cluster: prod user: admin namespace: defaultcurrent-context: prod-admin # 지금 쓰는 조합context = cluster + user + namespace. 셋을 묶은 것이 컨텍스트다.
certificate-authority는 CA(Certificate Authority · 인증 기관) 인증서다 —
“이 API 서버가 진짜인가”를 클라이언트 쪽에서 검증하는 뿌리이고,
반대로 client-certificate는 서버에게 “나는 admin이다”를 증명한다. 인증서 체계 자체는 사용자 인증.
kubectl config get-contexts # 목록 (*가 현재)kubectl config current-context # 현재 이름만kubectl config use-context prod-admin # 전환kubectl config set-context --current --namespace=dev # 현재 컨텍스트의 ns 변경kubectl config view # 전체 보기 (민감정보 마스킹)kubectl config view --raw # 마스킹 없이여러 kubeconfig를 합쳐 쓸 수도 있다.
KUBECONFIG=~/.kube/config:~/other.conf kubectl config view --flatten > merged.conf합치는 문법까지 외울 필요는 없다 — “이런 것도 된다” 정도만 알아두면 된다. 시험에서는 kubeconfig가 이미 세팅된 채로 시작한다.
트러블슈팅은 실제 상태(status)를 읽는 것에서 시작한다 (커리큘럼의 30%가 여기다).
같은 get이라도 출력 형식을 바꾸면 보이는 정보가 달라지므로, 조회는
“무엇을 보고 싶은가”에 맞춰 형식을 고르는 일이다.
kubectl get pods # 기본kubectl get pods -o wide # + IP, 노드, NOMINATED NODEkubectl get pods -o yaml # 전체 오브젝트kubectl get pods -o jsonkubectl get pods --show-labelskubectl get pods -A # 모든 네임스페이스kubectl get pod,svc,deploy # 여러 종류 한 번에kubectl get all # 주요 워크로드 리소스 (전부는 아니다)시험은 “보시오”가 아니라 “파일에 쓰시오”로 묻는다 — 이름만, 이미지만, 정렬해서. 사람 눈으로 골라 옮겨 적으면 오타로 점수를 잃으니, 처음부터 필요한 값만 뽑는 형식을 쓴다.
kubectl get pods --sort-by=.metadata.creationTimestampkubectl get nodes --sort-by=.metadata.namekubectl get events --sort-by=.lastTimestampkubectl get pods -o jsonpath='{.items[*].metadata.name}'kubectl get pods -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.podIP}{"\n"}{end}'kubectl get node node01 -o jsonpath='{.status.capacity.cpu}'JSONPath는 -o json으로 받은 응답 안에서 경로로 값을 지목하는 문법이다.
.items[*]는 목록 전체, {range}…{end}는 그 목록을 한 줄씩 돌며 찍으라는 뜻이고,
{"\t"} · {"\n"} 같은 리터럴로 구분자를 직접 넣는다.
값 하나를 정확히 꺼낼 때. 다른 명령에 넘겨 쓰기 좋다.
range나 필터 문법이 기억나지 않으면 시험 중에는 공식 JSONPath 문서의 예시를 복사해 경로만 바꾸는 게 가장 빠르다.
kubectl get pods -o custom-columns='NAME:.metadata.name,NODE:.spec.nodeName,IMAGE:.spec.containers[0].image'kubectl get pods -o custom-columns='NAME:.metadata.name,IMAGE:.spec.containers[0].image' --no-headers여러 열을 표로 뽑을 때. jsonpath보다 읽기 쉽다.
kubectl describe
사람이 읽으라고 만든 요약. 끝에 Events가 붙는다 ← 진단의 핵심. 관련 오브젝트 정보를 합쳐서 보여준다.
kubectl get -o yaml
API가 저장한 원본 그대로.
status 안의 정확한 값·조건을 본다.
복사해서 새 오브젝트를 만들 때 쓴다.
kubectl describe pod web # 왜 안 뜨는가 → Events를 읽는다kubectl get pod web -o yaml # 정확히 어떤 값이 들어갔는가만드는 것은 결국 spec을 API 서버에 밀어 넣는 일이다 (아키텍처). 방법은 두 갈래인데, 시험에서 갈리는 것은 “무엇을 만드느냐”가 아니라 몇 초 만에 만드느냐다 — 그래서 두 방식의 장단을 알고 섞어 쓰는 것이 이 절의 목적이다.
생성기는 create·run·expose가 플래그 몇 개로 오브젝트 뼈대를 대신 써 주는 기능이다.
손으로 YAML을 치면 20줄인 것이 한 줄이 된다. 지금의 kubectl run은 Pod만 만든다 —
Deployment가 필요하면 kubectl create deploy를 쓴다.
# Podkubectl run nginx --image=nginxkubectl run nginx --image=nginx --port=80 --labels=app=webkubectl run tmp --image=busybox --rm -it --restart=Never -- sh # 일회용 디버그 셸
# Deploymentkubectl create deploy web --image=nginx --replicas=3
# Servicekubectl expose deploy web --port=80 --target-port=8080 --name=web-svckubectl create svc clusterip web-svc --tcp=80:8080
# 그 외kubectl create ns devkubectl create cm app-config --from-literal=KEY=value --from-file=./conf/kubectl create secret generic db --from-literal=password=s3cr3tkubectl create sa deploy-botkubectl create job hello --image=busybox -- echo hikubectl create cronjob hello --image=busybox --schedule="*/1 * * * *" -- echo hikubectl create ingress web --rule="example.com/*=web-svc:80"이 목록을 손에 붙이는 것이 곧 시험 시간이다. 시험 전략에 전체 치트시트가 있다. 막히면 시험 중에도 열 수 있는 공식 kubectl Quick Reference에 같은 목록이 정리되어 있다.
생성기만으로는 표현할 수 없는 필드(볼륨, 프로브, tolerations…)가 많다.
그렇다고 빈 파일에서 시작할 필요는 없다 — 만들지 말고 YAML만 내놓으라고 하면
생성기가 뼈대를 써 주고, 거기에 필요한 필드만 얹으면 된다. 그게 --dry-run=client -o yaml이다.
kubectl create deploy web --image=nginx --dry-run=client -o yaml > web.yamlkubectl run nginx --image=nginx --dry-run=client -o yaml > pod.yamlkubectl expose deploy web --port=80 --dry-run=client -o yaml > svc.yaml=로 잇는다kubectl·kubeadm은 --flag=value와 --flag value를 둘 다 받는다. 하지만 값 생략이 허용되는
플래그는 =만 안전하다 — --dry-run client라고 띄면 client가 플래그 값이 아니라 별도
인자로 해석된다(값 없는 --dry-run도 유효한 표기라서, 뒤의 단어를 값으로 붙여 주지 않는다).
불리언 플래그(--dry-run false 시절 표기)와 값이 -로 시작하는 경우도 같은 함정이다.
어느 플래그가 어느 형태였는지 고민하지 않도록 습관을 = 하나로 통일한다.
따옴표는 CLI가 아니라 셸을 위한 것이다. --pod-network-cidr=172.17.0.0/16 같은 값은
점·슬래시가 셸에 평범한 글자라 안 묶어도 되고, 공백이나 *·?·$ 같은 셸 특수문자가
들어 있을 때만 묶는다(apt-get install kubeadm='1.35.0-*'의 따옴표가 그 경우 — 없으면 셸이
*를 파일명 글롭으로 펼치려 든다).
만드는 문제만큼 자주 나오는 것이 “이미 있는 것을 고치시오”다. 고치는 길이 넷이나 되는 이유는 각각 대가가 다르기 때문이다 — 빠르지만 표현이 제한적인 것부터, 느리지만 무엇이든 되는 것까지 순서대로 본다.
전용 서브커맨드 — 가장 빠르다
kubectl scale deploy web --replicas=5kubectl set image deploy/web nginx=nginx:1.27kubectl set env deploy/web LOG_LEVEL=debugkubectl set resources deploy/web --limits=cpu=500m,memory=256Mikubectl set serviceaccount deploy/web deploy-botedit — 에디터로 연다
kubectl edit deploy webpatch — 한 필드만 정확히
kubectl patch deploy web -p '{"spec":{"replicas":5}}'kubectl patch pod web --type=json -p='[{"op":"replace","path":"/spec/containers/0/image","value":"nginx:1.27"}]'apply / replace — 파일 기준
kubectl apply -f web.yamlkubectl replace -f web.yaml --force # 지우고 다시 만든다patch의 기본 형식은 strategic merge다 — 준 필드만 병합하고 나머지는 그대로 둔다.
--type=json은 op(연산)와 path(위치)로 지목하는 방식이라, 위 예처럼
배열 원소 하나(/spec/containers/0/image)를 정확히 바꿀 때 쓴다.
세 번째 형식인 --type=merge도 있지만 깊게 팔 필요는 없다 — 이름만 알아두자.
같은 오브젝트를 파일과 명령형 조작이 번갈아 건드린다. 파일을 그대로 덮어쓰면
파일에 안 적힌 것(다른 도구가 넣은 필드)이 통째로 날아간다.
apply가 단순 덮어쓰기가 아닌 이유가 이것이다.
apply는 파일 / 클러스터의 현재 / 마지막으로 적용한 것 셋을 비교한다kubectl.kubernetes.io/last-applied-configuration 에 저장kubectl diff -f web.yaml # 적용하면 무엇이 바뀌는지 미리 본다병합을 클라이언트가 아니라 서버가 하는 --server-side 방식도 있다.
시험에서는 기본(클라이언트 쪽 3-way merge)으로 충분하다 — 이름만 알아두자.
kubectl delete pod webkubectl delete -f web.yamlkubectl delete pod -l app=web # 라벨로kubectl delete pods --all -n devkubectl delete pod web --force --grace-period=0 # 즉시 (graceful 생략)kubectl delete deploy web --cascade=orphan # 자식(Pod)을 남긴다terminationGracePeriodSeconds(기본 30초)를 기다린다--force --grace-period=0이 유용하다여기부터는 루프가 어디서 멈췄는지를 알아내는 도구다 (시작하기 전에의 축, 커리큘럼의 30%).
순서가 있다 — 이벤트는 “스케줄링·이미지·볼륨” 같은 Pod이 뜨기까지의 실패를,
로그는 “떴는데 앱이 죽는” 뜬 다음의 실패를 보여준다. exec·debug는 그래도 모를 때 안으로 들어가는 것이다.
kubectl logs webkubectl logs web -c sidecar # 멀티 컨테이너면 -c 필수kubectl logs web --previous # 죽기 직전 컨테이너의 로그 ★kubectl logs web -f # 따라가기kubectl logs web --tail=50kubectl logs web --since=10mkubectl logs web --timestampskubectl logs -l app=web --all-containers --prefix # 라벨로 여러 Pod 한 번에kubectl logs deploy/web # 컨트롤러를 지정해도 된다로그의 실체는 노드의 /var/log/pods/<ns>_<pod>_<uid>/<container>/*.log 다.
API 서버가 죽어 kubectl logs가 안 될 때 직접 읽는다.
셋 다 컨테이너 안쪽에 손을 넣는 명령이다 — 역할이 다르다.
exec는 이미 도는 컨테이너에서 명령을 실행하고, cp는 파일을 넣거나 꺼내고,
port-forward는 클러스터 안의 포트를 내 로컬 포트로 끌어온다.
port-forward는 Service를 만들지 않고도 앱에 붙어 볼 때 쓰지만, 시험에서는
클러스터 안에서 임시 Pod으로 확인하는 쪽이 더 흔하다 — 쓰임을 알아 두는 정도면 된다.
kubectl exec web -- ls /appkubectl exec -it web -- sh # 대화형kubectl exec -it web -c sidecar -- sh
kubectl cp ./local.txt web:/tmp/local.txtkubectl cp web:/var/log/app.log ./app.log
kubectl port-forward pod/web 8080:80 # 로컬 8080 → Pod 80kubectl port-forward svc/web-svc 8080:80임시 디버그 Pod — 클러스터 안에서 네트워크를 확인할 때.
kubectl run tmp --image=busybox:1.36 --rm -it --restart=Never -- sh# 안에서: wget -qO- http://web-svc.default.svc.cluster.local# nslookup web-svc임시 컨테이너는 Pod을 다시 만들지 않고 나중에 끼워 넣는 컨테이너다.
--target=web을 주면 대상 컨테이너와 프로세스 네임스페이스를 공유해 그쪽 프로세스까지 보인다.
한 번 붙인 것은 뗄 수 없고, Pod을 지우면 같이 사라진다.
kubectl get events --sort-by=.lastTimestampkubectl get events -A --sort-by=.lastTimestamp | tail -30kubectl get events --field-selector type=Warningkubectl get events --field-selector involvedObject.name=webkubectl events --for pod/web # 새 전용 명령이벤트도 etcd에 저장되는 오브젝트다 — 스케줄러·kubelet·컨트롤러가 “무엇을 했는지, 왜 못 했는지”를 남긴 기록이다. 로그와 달리 컴포넌트를 가리지 않고 한 줄로 모이기 때문에 “Pod이 안 뜬다”류에서 가장 먼저 볼 곳이 된다.
describe의 아래쪽 Events 섹션이 사실 이것이다--dry-run=client -o yaml로 뽑아 고친다 — 이게 가장 빠르다-o wide → describe(Events) → -o yaml 순으로 좁혀 간다--sort-by, custom-columns, jsonpath 는 “파일에 저장하시오” 문제 전용 무기logs --previous 와 get events --sort-by 는 진단의 두 기둥--force로 다시 만든다 (리소스만 --subresource=resize 예외)