[TIL] kubectl

2026. 07. 10.Yeji Kim
Platform

kubectl은 결국 API 서버에 보내는 요청

kubectl은 쿠버네티스 API 서버(kube-apiserver)와 HTTP로 대화하는 REST 클라이언트이다.

클러스터의 모든 상태는 API 서버 뒤의 저장소(etcd)에 있고, kubectl은 그 앞에 놓인 잘 만들어진 curl 인 셈이다. --v=8 을 붙이면 오가는 HTTP 요청이 그대로 보인다.

bash
kubectl get pods --v=8
# GET https://kubernetes.example.com:6443/api/v1/namespaces/default/pods
# Request Headers: Authorization: Bearer <REDACTED>
# Response Status: 200 OK

이 관점 하나만 잡으면 나머지가 전부 쉬워진다.


kubectl get pods 한 줄의 여정

명령 하나가 결과를 돌려주기까지 대략 이런 길을 지난다.

text
kubectl get pods

   ├─ 1. kubeconfig 읽기 (~/.kube/config)
   │      → 어느 클러스터로? 누구로? 어떤 네임스페이스?

   ├─ 2. API 서버로 HTTPS 요청
   │      GET /api/v1/namespaces/default/pods


kube-apiserver
   ├─ 3. 인증 (authn)   : 너 누구야? (인증서/토큰)
   ├─ 4. 인가 (authz)   : 그거 봐도 돼? (RBAC)
   ├─ 5. 어드미션        : 정책 통과? (주로 쓰기 때 작동)
   ├─ 6. etcd 조회       : 실제 데이터 읽기

   응답(JSON) → kubectl이 표로 예쁘게 출력

여기서 기억할 두 가지.

  • kubectl은 클러스터에 직접 접속하지 않는다. 오직 API 서버하고만 이야기한다. 노드나 파드에 SSH로 들어가는 게 아니다.
  • 모든 권한 검사는 API 서버에서 한다. kubectl은 요청을 보낼 뿐, 봐도 되는지는 클러스터의 RBAC이 판단한다.

kubectl auth can-i delete pods 처럼 물어보면 실제로 지우지 않고 인가 결과(yes/no)만 확인할 수 있다.


kubeconfig와 context - 지금 어디에 쏘고 있나

kubectl 사고의 절반은 내 명령이 지금 어느 클러스터로 가고 있는가 다. 이걸 정하는 게 kubeconfig(기본 경로 ~/.kube/config)이고, 세 부분으로 이뤄진다.

yaml
# ~/.kube/config (값은 예시로 마스킹)
clusters:            # 어디로 (API 서버 주소)
  - name: prod
    cluster:
      server: https://kubernetes.example.com:6443
users:               # 누구로 (인증 정보)
  - name: alice
    user:
      token: <REDACTED>
contexts:            # (클러스터 + 유저 + 네임스페이스) 묶음
  - name: prod-alice
    context:
      cluster: prod
      user: alice
      namespace: web
current-context: prod-alice   # 지금 활성화된 묶음

context어느 클러스터, 누구로, 어느 네임스페이스 의 조합이다. 실무 사고는 대부분 dev인 줄 알고 prod에 명령을 날리는 데서 난다. 그래서 습관 하나가 중요하다 — 명령 전에 현재 컨텍스트 확인하기.

bash
kubectl config current-context     # 지금 활성 컨텍스트
kubectl config get-contexts        # 전체 목록 (* 표시가 현재)
kubectl config use-context dev     # 컨텍스트 전환

-n <namespace> 를 안 붙이면 컨텍스트의 기본 네임스페이스로 간다. 특정 네임스페이스는 -n kube-system, 전체는 -A(--all-namespaces)로 본다.


리소스 모델 - 모든 것은 spec과 status

쿠버네티스의 모든 오브젝트(Pod, Deployment, Service…)는 같은 뼈대를 가진다.

yaml
apiVersion: apps/v1     # 이 오브젝트의 종류/버전
kind: Deployment
metadata:               # 이름, 네임스페이스, 라벨
  name: web
spec:                   # 내가 원하는 상태 (desired)
  replicas: 3
status:                 # 클러스터의 실제 상태 (current) — 시스템이 채움
  readyReplicas: 3

핵심은 spec(원하는 상태)status(실제 상태) 의 구분이다. 나는 spec 만 선언하고, 컨트롤러라는 백그라운드 루프가 끊임없이 실제를 원하는 상태에 맞추려 조정(reconcile)한다. 파드가 죽으면 다시 만들고, replicas: 3 인데 2개면 하나 더 띄운다.

그래서 kubectl 사용법도 명령형선언형 두 갈래로 나뉜다.


명령형 vs 선언형 (apply가 표준인 이유)

bash
# 명령형(imperative): 지금 이걸 해 — 빠른 실험/디버깅용
kubectl create deployment web --image=nginx
kubectl scale deployment web --replicas=3
 
# 선언형(declarative): 최종 상태는 이거야 — 실무 표준
kubectl apply -f deployment.yaml

apply 는 이 YAML이 최종 상태가 되도록 맞춰달라고 결과만 선언한다. 같은 파일을 여러 번 apply 해도 안전하고(멱등), 바뀐 부분만 반영된다. 그래서 Git에 YAML을 두고 관리하는 GitOps 와 잘 맞는다.

바꾸기 전에 뭐가 달라지는지 미리 보려면 kubectl diff -f deployment.yaml 을 쓴다. 실제 적용 없이 현재 상태와 파일의 차이만 보여준다.


디버깅 툴킷

뭔가 안 뜰 때 실제로 손이 가는 명령들. 대부분 읽기 전용이라 마음 편히 써도 된다.

명령용도
kubectl get pods -o wide파드 목록 + 노드/IP까지
kubectl describe pod <name>상세 상태 + 하단 Events가 핵심
kubectl logs <pod> -f로그 실시간. 이전 크래시는 --previous
kubectl exec -it <pod> -- sh컨테이너 안에서 셸 열기
kubectl port-forward <pod> 8080:80로컬 포트를 파드로 터널링
kubectl explain deployment.spec필드 스키마를 문서처럼 조회

describe 맨 아래 Events 섹션을 먼저 보자. ImagePullBackOff, CrashLoopBackOff, FailedScheduling 같은 메시지가 원인의 90%를 말해준다. 로그는 앱이 뜬 다음의 문제를, Events는 앱이 뜨기 전의 문제를 알려준다.


실습 1 - 지금 내가 어디를 보고 있는지 확인하기

아무것도 바꾸지 않고 현재 상태만 확인하는 루틴.

bash
kubectl config current-context     # 지금 활성 컨텍스트는?
kubectl auth can-i delete pods      # 지울 권한이 있나? (실제로 안 지움)
kubectl get pods -n kube-system     # 어떤 파드가 도는지 구경
왜 중요한가

current-context → auth can-i → get 순서는 어디에 / 무슨 권한으로 / 무엇을 보고 있는지를 몇 초 만에 파악하는 루틴이다. prod/dev를 오갈 때 첫 명령으로 두면 실수를 크게 줄인다.


실습 2 - 적용하지 않고 매니페스트 만들어보기

명령형 명령에 --dry-run=client -o yaml 을 붙이면 클러스터를 건드리지 않고 YAML만 뽑아준다.

bash
# nginx Deployment YAML을 생성만 해보기 (클러스터 변경 없음)
kubectl create deployment web --image=nginx \
  --dry-run=client -o yaml > deployment.yaml
 
# 이 파일을 적용하면 무엇이 바뀔지 미리보기 (역시 적용 안 함)
kubectl diff -f deployment.yaml
생각해볼 점

--dry-run=client 는 로컬에서만 계산하고, --dry-run=server 는 API 서버까지 보내 검증만 하고 저장은 안 한다. 명령형으로 초안을 뽑아 선언형 파일로 저장하고, diff 로 확인한 뒤 apply 하는 흐름이 실무에서 가장 안전하다.


레퍼런스