본문으로 건너뛰기

3.3 kubectl 사용법

kubectl은 API server의 REST API를 호출하는 클라이언트다. 접속할 클러스터와 자격 증명은 kubeconfig(~/.kube/config)의 context가 정하고, kubectl config use-context로 전환한다. kubectl이 하는 모든 일은 결국 오브젝트를 읽고 쓰는 것이므로, 동사 몇 개가 손에 붙으면 나머지는 플래그의 조합이다. 이 장은 그 동사들과, 매일 반복하게 되는 조합을 정리한다.

핵심 동사 여덟 개

동사역할자주 쓰는 플래그
get목록·요약 조회-o, -l, -A, -w
describe상세와 관련 Event 출력-n
logs컨테이너 로그-f, --previous, -c
exec컨테이너 안에서 명령 실행-it, -c
port-forward로컬 포트를 Pod/Service에 연결
apply매니페스트 적용-f, --server-side
delete오브젝트 삭제-f, -l
edit라이브 오브젝트를 에디터로 수정

출발점은 언제나 get이다.

$ kubectl get pods
NAME                   READY   STATUS             RESTARTS   AGE
web-7c9d6bf8c5-2xkqv   1/1     Running            0          2d
web-7c9d6bf8c5-9wl4t   1/1     Running            0          2d
db-0                   0/1     CrashLoopBackOff   12         3h

문제가 보이면 다음 명령은 describe다. describe는 spec과 status를 풀어 보여주는 데서 그치지 않고 그 오브젝트와 관련된 Event를 끝에 붙인다. 스케줄 실패, 이미지 pull 실패, probe 실패처럼 원인 진단에 필요한 정보 대부분이 이 Event 목록에 있다.

$ kubectl describe pod db-0
...
Events:
  Type     Reason   Age                 From     Message
  ----     ------   ----                ----     -------
  Warning  BackOff  2m (x32 over 3h)    kubelet  Back-off restarting failed container

logs는 기본으로 현재 컨테이너의 로그를 출력하고, --previous를 붙이면 재시작 직전 컨테이너의 마지막 로그가 나온다. CrashLoopBackOff 진단에서 가장 먼저 실행할 명령이다. exec는 컨테이너 안에서 명령을 실행하고, port-forward는 클러스터 밖에서 접근할 수 없는 Pod나 Service를 로컬 포트에 연결해 확인하게 해 준다.

kubectl logs db-0 --previous
kubectl exec -it db-0 -- /bin/sh
kubectl port-forward svc/web 8080:80

edit는 라이브 오브젝트를 에디터로 열어 바로 수정한다. 즉효가 있지만 변경이 파일로 남지 않아, git으로 관리하는 매니페스트와 어긋나기 시작하는 지점이 된다. 급한 조치에 썼다면 같은 변경을 매니페스트에도 반영해 둔다.

출력 포맷

get의 기본 출력은 요약이고, -o가 깊이를 조절한다. -o wide는 Pod의 IP와 배치된 노드처럼 한 단계 더 깊은 컬럼을 붙인다.

$ kubectl get pods -o wide
NAME                   READY   STATUS    RESTARTS   AGE   IP           NODE
web-7c9d6bf8c5-2xkqv   1/1     Running   0          2d    10.244.1.7   node-2

-o yaml(또는 -o json)은 시스템이 채운 필드까지 포함한 오브젝트 전문을 보여준다. 매니페스트에 적은 것과 실제 저장된 것의 차이를 확인할 때 쓴다. 스크립트에서 값 하나만 꺼낼 때는 jsonpath, 원하는 컬럼으로 표를 만들 때는 custom-columns를 쓴다.

$ kubectl get pod web-7c9d6bf8c5-2xkqv -o jsonpath='{.status.podIP}'
10.244.1.7

$ kubectl get pods -o custom-columns='NAME:.metadata.name,IMAGE:.spec.containers[0].image'
NAME                   IMAGE
web-7c9d6bf8c5-2xkqv   nginx:1.29
web-7c9d6bf8c5-9wl4t   nginx:1.29

jsonpath의 경로는 -o json 출력의 구조 그대로다. 어떤 경로를 적어야 할지 모르겠으면 먼저 -o json으로 전문을 확인하고 경로를 따라간다.

label selector와 dry-run

-l은 label selector로 대상을 거른다. 이름을 하나하나 나열하는 대신 무리를 지정하는 방식이고, get뿐 아니라 delete와 logs에도 통한다.

kubectl get pods -l app=web
kubectl get pods -l 'env in (dev,stage)'
kubectl delete pods -l app=web,track=canary

label과 selector의 문법 규칙은 3.4 레이블, 셀렉터, 애너테이션 장에서 다룬다.

--dry-run은 변경 명령을 실행하지 않고 결과만 검토한다. client는 kubectl이 로컬에서 오브젝트를 구성해 보는 수준이고, server는 API server에 제출해 admission과 검증까지 통과시키되 저장만 하지 않는다. defaulting이나 quota 검사처럼 서버에서만 일어나는 일은 server라야 확인된다.

kubectl apply -f web.yaml --dry-run=server

dry-run과 -o yaml의 조합은 매니페스트 생성기로 널리 쓰인다. 빈 파일에서 시작하는 대신 명령형 명령으로 골격을 뽑고 파일을 다듬는 방식이다.

kubectl create deployment web --image=nginx:1.29 --dry-run=client -o yaml > web.yaml

명령형과 선언형 사이에서

kubectl run이나 scale 같은 명령형 명령은 빠르다. 실험, 일회성 디버깅, 학습에는 그 속도가 장점이고 파일을 남길 이유도 없다. 반면 운영 환경의 변경은 선언형으로 몰아간다. 매니페스트가 git에 있으면 변경 이력이 리뷰와 롤백의 단위가 되고, 같은 파일을 다른 클러스터에 적용해 환경을 재현한다. 경계선을 어디에 둘지는 팀마다 다르지만, 명령형으로 반복하던 변경이 두 번을 넘어가면 파일로 옮길 시점이라는 감각 정도는 공유할 만하다.

자주 쓰는 조합

# 워크로드 재시작 (rolling restart)
kubectl rollout restart deployment/web

# 모든 namespace에서 Running이 아닌 Pod 검색
kubectl get pods -A --field-selector=status.phase!=Running

# 최근 이벤트를 시간순으로
kubectl get events --sort-by=.lastTimestamp

# 전체 Pod의 컨테이너 이미지 일람
kubectl get pods -o custom-columns='POD:.metadata.name,IMAGE:.spec.containers[*].image'

# 변화를 지켜보기 (watch)
kubectl get pods -w

# 리소스 사용량 (metrics-server 필요)
kubectl top pods

이 조합들은 전부 지금까지 본 동사와 플래그의 재조합이다. 새 조합이 필요해지면 외우는 대신 kubectl <동사> --help로 플래그 목록을 확인하는 습관이 오래 간다.