kubectl describe
사람이 읽으라고 만든 요약. 끝에 Events가 붙는다 ← 진단의 핵심. 관련 오브젝트 정보를 합쳐서 보여준다.
같은 결과를 30초 만에 만드는 법
flowchart LR
U["당신"] --> KC["kubectl"]
CFG["kubeconfig<br/>~/.kube/config"] -->|"어디로 · 누구로"| KC
KC -->|"HTTPS REST 요청"| API["kube-apiserver"]
API --> ETCD[("etcd")]
ERR1["연결이 안 된다"] -.->|"= server 주소 문제"| CFG
ERR2["권한이 없다 403"] -.->|"= user 신원 문제"| CFG
classDef key fill:#dbeafe,stroke:#2563eb,color:#1e3a8a
classDef bad fill:#fee2e2,stroke:#dc2626,color:#7f1d1d
classDef mute fill:#f1f5f9,stroke:#94a3b8,color:#334155
class CFG key
class ERR1,ERR2 bad
class U,KC,API,ETCD mute
~/.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이걸 알면 **“권한이 없다”**거나 **“연결이 안 된다”**는 에러를 “어느 주소로 어떤 신원으로 갔는가”로 환원해서 볼 수 있다.
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 # 지금 쓰는 조합flowchart LR
CL["clusters<br/>어디로 · server + CA"] --> CTX["contexts<br/>조합"]
US["users<br/>누구로 · 인증서 · 토큰"] --> CTX
NS["namespace<br/>기본 네임스페이스"] --> CTX
CTX --> CUR["current-context<br/>지금 쓰는 조합"]
classDef key fill:#dbeafe,stroke:#2563eb,color:#1e3a8a
classDef mute fill:#f1f5f9,stroke:#94a3b8,color:#334155
class CTX,CUR key
class CL,US,NS mute
context = cluster + user + namespace. 셋을 묶은 것이 컨텍스트다.
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.confkubectl 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}'값 하나를 정확히 꺼낼 때. 다른 명령에 넘겨 쓰기 좋다.
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 # 정확히 어떤 값이 들어갔는가flowchart LR
I["명령형<br/>kubectl create · scale · expose"] -->|"빠르다 · 표현이 제한적"| R1["바로 만들어진다"]
D["선언형<br/>kubectl apply -f"] -->|"모든 필드 표현 가능 · 파일이 필요"| R2["파일 기준으로 맞춘다"]
BEST["★ 시험 전략"] --> S1["① 명령형 + dry-run 으로 뼈대 생성"]
S1 --> S2["② 필요한 필드만 편집"]
S2 --> S3["③ kubectl apply -f"]
classDef key fill:#dbeafe,stroke:#2563eb,color:#1e3a8a
classDef ok fill:#dcfce7,stroke:#16a34a,color:#14532d
classDef mute fill:#f1f5f9,stroke:#94a3b8,color:#334155
class BEST key
class S1,S2,S3 ok
class I,D,R1,R2 mute
# 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"이 목록을 손에 붙이는 것이 곧 시험 시간이다. 19장에 전체 치트시트가 있다.
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.yamlflowchart LR
C["kubectl create …"] --> Q{"--dry-run"}
Q -->|client| CL["클라이언트에서만 만든다<br/>서버에 안 보낸다 → YAML 생성용"]
Q -->|server| SV["서버에 보내되 저장은 안 한다<br/>admission·검증까지 통과하는지 확인"]
Q -->|"생략"| REAL["실제로 생성된다"]
classDef key fill:#dbeafe,stroke:#2563eb,color:#1e3a8a
classDef warn fill:#fef3c7,stroke:#d97706,color:#78350f
classDef mute fill:#f1f5f9,stroke:#94a3b8,color:#334155
class CL key
class SV warn
class C,Q,REAL mute
전용 서브커맨드 — 가장 빠르다
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 # 지우고 다시 만든다flowchart LR
F["① 내 파일 web.yaml"] --> M{"3-way merge"}
C["② 클러스터의 현재 상태"] --> M
L["③ 마지막으로 적용한 것<br/>last-applied-configuration 애노테이션"] --> M
M --> R["파일에 없는 필드를<br/>함부로 지우지 않는다 ✅"]
REP["kubectl replace"] -.->|"3-way merge 를 안 한다"| R2["통째로 교체<br/>파일에 없는 필드가 사라진다 ❌"]
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 R ok
class R2 bad
class M key
class F,C,L,REP mute
apply는 파일 / 클러스터의 현재 / 마지막으로 적용한 것 셋을 비교한다kubectl.kubernetes.io/last-applied-configuration 에 저장kubectl diff -f web.yaml # 적용하면 무엇이 바뀌는지 미리 본다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)을 남긴다flowchart TD
D["kubectl delete pod web"] --> Q{"상위 컨트롤러가 있는가"}
Q -->|"있다 · Deployment 등"| BACK["즉시 다시 생긴다 ⚠️<br/>→ 컨트롤러를 지워야 한다"]
Q -->|"없다 · 단독 Pod"| GONE["사라진다 ✅"]
NS["네임스페이스가 Terminating 에서 안 끝난다"] --> FIN["finalizer 를 의심한다 · 18장"]
classDef ok fill:#dcfce7,stroke:#16a34a,color:#14532d
classDef warn fill:#fef3c7,stroke:#d97706,color:#78350f
classDef bad fill:#fee2e2,stroke:#dc2626,color:#7f1d1d
classDef mute fill:#f1f5f9,stroke:#94a3b8,color:#334155
class GONE ok
class BACK warn
class FIN bad
class D,Q,NS mute
terminationGracePeriodSeconds(기본 30초)를 기다린다--force --grace-period=0이 유용하다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가 안 될 때 직접 읽는다.
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-svcflowchart LR
Q{"컨테이너 안을 봐야 한다"} -->|"셸이 있다"| E["kubectl exec -it … -- sh ✅"]
Q -->|"distroless · 셸이 없다"| D["kubectl debug -it web<br/>--image=busybox --target=web"]
D --> EPH["임시 컨테이너 ephemeral container 를 붙인다"]
classDef ok fill:#dcfce7,stroke:#16a34a,color:#14532d
classDef key fill:#dbeafe,stroke:#2563eb,color:#1e3a8a
classDef mute fill:#f1f5f9,stroke:#94a3b8,color:#334155
class E ok
class D,EPH key
class Q mute
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 # 새 전용 명령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로 다시 만든다