coredot.today
쿠버네티스 첫걸음 3편 — kubectl로 파드와 노드 다루기: get·describe·logs·exec, 그리고 파드가 안 뜰 때 읽는 법
블로그로 돌아가기
Kuberneteskubectl파드노드kubectl getkubectl describekubectl logsCrashLoopBackOffImagePullBackOff튜토리얼

쿠버네티스 첫걸음 3편 — kubectl로 파드와 노드 다루기: get·describe·logs·exec, 그리고 파드가 안 뜰 때 읽는 법

kubectl 명령은 '동사 + 리소스 종류 + 이름 + 옵션' 한 가지 문법으로 되어 있습니다. 이 문법 하나로 노드를 살펴보고(get nodes, describe node), 파드를 띄우고(run, apply), 들여다보고(get -o wide, describe, logs, exec), 내 컴퓨터에서 접속하고(port-forward), 지우는(delete) 과정을 실습합니다. 파드 안에 컨테이너가 여럿 들어가는 경우가 무엇인지, 파드 YAML을 한 줄씩 읽는 법, 직접 만든 파드가 왜 되살아나지 않는지도 확인합니다. STATUS를 고르면 원인과 확인 명령이 나오는 파드 진단기와 kubectl 치트시트를 담았습니다.

코어닷투데이2026-09-2725분

들어가며 — kubectl은 문법 하나만 알면 된다

kubectl 익히기크게 보기

kubectl 치트시트를 검색하면 수백 개의 명령이 나옵니다. 외우려고 하면 끝이 없습니다. 하지만 거의 모든 kubectl 명령은 한 가지 문법으로 되어 있습니다.

kubectl의 문법
kubectl [동사] [리소스 종류] [이름] [옵션]

kubectl get pods
kubectl describe node lima-rancher-desktop
kubectl logs web -f
kubectl delete pod web -n dev

동사는 무엇을 할지(get, describe, apply, delete…), 리소스 종류는 무엇에 대해(pod, node, deployment, service…), 이름은 어느 것에(생략하면 전부), 옵션은 어떻게(-n 네임스페이스, -o 출력 형식…)입니다. 이 문법 하나로 이 글의 모든 명령이 읽힙니다.

📚
쿠버네티스 첫걸음 시리즈 (6편)
1편 개념 잡기 · 2편 로컬 클러스터 만들기 · 3편 kubectl로 파드와 노드 다루기(이 글) · 4편 디플로이먼트와 서비스로 앱 배포 · 5편 관리 도구 비교 · 6편 팀 협업

실습하려면 2편의 로컬 클러스터가 켜져 있어야 합니다. kubectl get nodes에 Ready가 보이면 준비 완료입니다.

1. 시작 전에 — 편하게 만드는 두 가지

① 짧은 이름. 리소스 종류는 줄여 쓸 수 있습니다. pods는 po, services는 svc, deployments는 deploy, namespaces는 ns, nodes는 no입니다. 전체 목록은 이 명령으로 봅니다.

bash
kubectl api-resources

② 자동 완성. Tab 키로 리소스 이름까지 완성되게 합니다. 맥의 기본 셸(zsh)이라면:

bash
echo 'source <(kubectl completion zsh)' >> ~/.zshrc
echo 'alias k=kubectl' >> ~/.zshrc
echo 'compdef __start_kubectl k' >> ~/.zshrc
source ~/.zshrc

이제 k get po처럼 짧게 칠 수 있습니다. WSL2 우분투(bash)라면 completion zsh 대신 completion bash를, compdef 줄 대신 complete -o default -F __start_kubectl k를 ~/.bashrc에 넣습니다. 이 글에서는 알아보기 쉽게 kubectl을 다 적겠습니다.

2. 노드 살펴보기 — 우리 클러스터에는 컴퓨터가 몇 대?

bash
kubectl get nodes -o wide

-o wide는 더 많은 열을 보여 줍니다. 노드의 내부 IP, 운영체제, 커널 버전, 컨테이너 런타임(containerd 등)까지 나옵니다.

노드 하나를 자세히 봅니다. 노드 이름은 get nodes에 나온 것을 씁니다.

bash
kubectl describe node <노드이름>

출력이 길지만 처음에는 세 부분만 보면 됩니다.

describe node의 절무엇을 보나
ConditionsReady True면 정상. MemoryPressure·DiskPressure가 True면 노드가 힘들다는 신호
Capacity / Allocatable이 노드의 CPU·메모리 총량과, 그중 파드에 줄 수 있는 양
Allocated resources이미 파드들이 예약(requests)한 양. 이게 꽉 차면 새 파드가 Pending이 된다
💡
개발자가 노드를 볼 일은 많지 않습니다. 노드는 보통 클러스터 운영자(또는 클라우드)가 관리합니다. 개발자가 노드를 보는 때는 거의 하나 — "내 파드가 왜 Pending이지?"를 조사할 때입니다. 그때 describe node의 Allocated resources가 답을 줍니다.

3. 첫 파드 — 명령 한 줄로

bash
kubectl run web --image=nginx:1.29 --port=80

도커의 docker run과 비슷해 보이죠. 상태를 봅니다.

bash
kubectl get pods
kubectl get pods -o wide

-o wide를 붙이면 파드의 IP와 어느 노드에서 돌고 있는지(NODE)가 나옵니다. 1편의 그림처럼 파드는 노드 위에서 돌고, 자기 IP를 하나 가집니다.

⏱️
실시간으로 지켜보기. kubectl get pods -w(watch)를 켜 두면 상태가 바뀔 때마다 한 줄씩 찍힙니다. Pending → ContainerCreating → Running으로 넘어가는 과정을 볼 수 있습니다. Ctrl+C로 빠져나옵니다.

4. 파드 들여다보기 — describe · logs · exec

도커 3편에서 쓰던 docker logs, docker exec가 쿠버네티스에서도 거의 그대로 있습니다.

bash
kubectl describe pod web

맨 아래의 Events가 가장 중요합니다. 파드가 어느 노드에 배치됐고(Scheduled), 이미지를 받았고(Pulling, Pulled), 컨테이너를 만들고 시작했는지(Created, Started)가 시간순으로 적혀 있습니다. 무언가 잘못되면 여기에 이유가 나옵니다.

로그를 봅니다. -f는 실시간으로 따라갑니다.

bash
kubectl logs web
kubectl logs -f web

파드 안에 들어가 셸을 엽니다. -- 뒤가 컨테이너 안에서 실행할 명령입니다.

bash
kubectl exec -it web -- sh

안에서 cat /etc/nginx/nginx.conf, hostname 등을 쳐 보고 exit로 나옵니다. hostname이 파드 이름(web)과 같다는 것도 확인해 보세요.

5. 내 컴퓨터에서 접속하기 — port-forward

파드의 IP는 클러스터 안의 주소라 내 브라우저에서 바로 열 수 없습니다(도커에서 -p를 안 준 것과 비슷). 개발 중에 잠깐 접속할 때는 port-forward를 씁니다.

bash
kubectl port-forward pod/web 8080:80

순서는 도커와 같습니다 — 내 쪽:파드 쪽. http://localhost:8080을 열면 nginx 페이지가 보입니다. 터미널을 닫거나 Ctrl+C를 누르면 연결이 끊깁니다. port-forward는 개발·디버깅용 임시 통로이고, 제대로 된 공개 방법(Service, Ingress)은 4편에서 다룹니다.

6. 직접 만든 파드는 되살아나지 않는다

1편 시뮬레이터에서 파드를 죽이면 새로 생겼습니다. 여기서도 그럴까요?

bash
kubectl delete pod web
kubectl get pods

아무것도 남지 않습니다. kubectl run으로 만든 파드는 관리해 줄 상위 부품(디플로이먼트)이 없는 "맨 파드"라서, 지우면 그걸로 끝입니다. 노드가 죽어도 마찬가지입니다. 그래서 실제 앱은 절대 맨 파드로 띄우지 않고 디플로이먼트로 띄웁니다(4편). 맨 파드는 지금처럼 실험하거나, 잠깐 디버깅용 셸을 띄울 때만 씁니다.

bash
kubectl run tmp --rm -it --image=busybox:1.37 -- sh

--rm은 도커와 같습니다. 셸에서 나오면 파드가 자동으로 지워집니다. 클러스터 안에서 wget -qO- http://<다른파드IP>로 통신을 확인할 때 유용합니다.

7. 파드를 YAML로 — 쿠버네티스의 진짜 방식

kubectl run은 편하지만 협업에는 맞지 않습니다. 무엇을 띄웠는지 기록이 남지 않기 때문입니다. 쿠버네티스의 기본 방식은 YAML 파일에 원하는 상태를 적고 apply 하는 것입니다. pod.yaml을 만듭니다.

yaml
apiVersion: v1
kind: Pod
metadata:
  name: web
  labels:
    app: web
spec:
  containers:
    - name: nginx
      image: nginx:1.29
      ports:
        - containerPort: 80
      resources:
        requests:
          cpu: 100m
          memory: 64Mi
        limits:
          memory: 128Mi
줄뜻
apiVersion: v1 · kind: Pod1편에서 말한 네 칸 중 둘. "v1 API의 Pod 리소스"
metadata.name파드 이름. 같은 네임스페이스 안에서 겹치면 안 된다
metadata.labels라벨 — 파드에 붙이는 이름표. 4편에서 서비스가 이 라벨로 파드를 찾는다
spec.containers이 파드에 들어갈 컨테이너 목록 (보통 하나). -는 목록의 한 항목
image · ports도커의 이미지 이름과 컨테이너 포트
resources.requests"최소 이만큼은 필요해" — 스케줄러가 이걸 보고 자리가 있는 노드를 고른다. 100m은 CPU 0.1개
resources.limits"이 이상은 못 써" — 메모리 한도를 넘으면 OOMKilled
bash
kubectl apply -f pod.yaml
kubectl get pods --show-labels

YAML을 고친 뒤 다시 apply하면 차이만 반영됩니다(단, 파드는 대부분의 필드를 바꿀 수 없어 지우고 다시 만들어야 합니다 — 이것도 디플로이먼트를 쓰는 이유입니다). 지울 때도 파일로 지웁니다.

bash
kubectl delete -f pod.yaml
📖
YAML 필드가 기억 안 날 때. kubectl explain pod.spec.containers처럼 치면 그 필드의 설명이 나옵니다. 또 이미 떠 있는 리소스의 전체 YAML은 kubectl get pod web -o yaml로 볼 수 있습니다. 새 파일을 만들 때는 kubectl run web --image=nginx:1.29 --dry-run=client -o yaml > pod.yaml로 뼈대를 뽑는 것이 빠릅니다.

8. 파드 안에 컨테이너가 여럿? — 사이드카

대부분의 파드는 컨테이너 하나지만, 가끔 둘 이상을 넣습니다.

파드 하나 안의 앱 컨테이너와 보조 컨테이너가 IP와 볼륨을 공유크게 보기

같은 파드 안의 컨테이너들은 IP 하나(localhost로 서로 통신)와 볼륨을 공유하고, 항상 같은 노드에 함께 배치되고 함께 지워집니다. 그래서 떼려야 뗄 수 없는 관계일 때만 한 파드에 넣습니다. 예를 들면 앱의 로그 파일을 읽어 수집 서버로 보내는 로그 수집기, 앱 앞에서 암호화를 대신 처리하는 프록시 같은 사이드카(sidecar) 컨테이너입니다.

⚠️
앱과 DB를 한 파드에 넣지 마세요. 도커 compose에서 api와 db가 별개 서비스였듯, 쿠버네티스에서도 각자 따로입니다. 한 파드에 넣으면 앱만 늘리고 싶어도 DB까지 함께 늘어나고, 앱을 재시작하면 DB도 함께 재시작됩니다. 컨테이너가 여럿인 파드에서 로그를 볼 때는 kubectl logs web -c 컨테이너이름처럼 -c로 고릅니다.

9. 파드가 안 뜰 때 — STATUS 읽는 법

파드 문제의 90%는 kubectl get pods의 STATUS 열과 describe의 Events, logs 세 가지로 풀립니다. 아래 진단기에서 STATUS를 골라 보세요. 실습으로 일부러 문제를 만들어 볼 수도 있습니다.

실습: 일부러 ImagePullBackOff 만들기

bash
kubectl run broken --image=nginx:9.99-notexist
kubectl get pods -w

ErrImagePull → ImagePullBackOff로 바뀌는 것을 보고, Events에서 이유를 찾아봅니다.

bash
kubectl describe pod broken | tail -n 10
kubectl delete pod broken

실습: 일부러 CrashLoopBackOff 만들기 — 시작하자마자 에러로 끝나는 컨테이너입니다.

bash
kubectl run crash --image=busybox:1.37 -- sh -c "echo 설정 파일이 없습니다; exit 1"
kubectl get pods -w

RESTARTS가 늘면서 CrashLoopBackOff가 됩니다. 죽기 직전의 로그를 봅니다.

bash
kubectl logs crash --previous
kubectl delete pod crash

설정 파일이 없습니다가 보입니다. 실제 앱에서도 이 자리에 "DB 연결 실패", "환경변수 누락" 같은 진짜 이유가 찍힙니다.

10. kubectl 치트시트 — 이 표만 있으면 된다

하고 싶은 일명령도커로 치면
목록 보기kubectl get pods (-o wide, -w, -A)docker ps
자세히 보기 (Events)kubectl describe pod 이름docker inspect (+ 이유)
로그kubectl logs -f 이름 (--previous, -c)docker logs -f
안에서 명령kubectl exec -it 이름 -- shdocker exec -it
내 컴퓨터에서 접속kubectl port-forward pod/이름 8080:80docker run -p
파일대로 만들기·고치기kubectl apply -f 파일.yamldocker compose up
지우기kubectl delete -f 파일.yaml / kubectl delete pod 이름docker rm
자원 사용량kubectl top pod (metrics-server 필요)docker stats
최근 사건kubectl get events --sort-by=.lastTimestamp—
파일 복사kubectl cp 이름:/경로 ./로컬docker cp
ℹ️
kubectl top은 클러스터에 metrics-server가 있어야 합니다. Rancher Desktop·k3s 계열은 기본으로 들어 있고, kind는 따로 설치해야 합니다(error: Metrics API not available가 나면 없는 것).

마치며

🧠
3편 요약
· kubectl 문법은 하나 — 동사 + 리소스 종류 + 이름 + 옵션
· 노드는 get nodes -o wide, Pending 조사는 describe node의 Allocated resources
· 파드 조사 순서: get → describe(Events) → logs(--previous)
· 맨 파드는 되살아나지 않는다 — 실제 앱은 디플로이먼트로
· 협업은 kubectl run이 아니라 YAML + apply
· 한 파드에 여러 컨테이너는 사이드카처럼 뗄 수 없는 관계일 때만

다음 편에서는 드디어 내 앱을 쿠버네티스에 올립니다. 도커 시리즈에서 만든 hello-api 이미지를 디플로이먼트로 배포하고, 서비스로 고정 주소를 주고, 개수를 늘리고, 새 버전으로 무중단 교체하고, 잘못되면 되돌리는 것까지 합니다.

👉 4편: 디플로이먼트와 서비스로 내 앱 배포하기


참고 자료