kubectl 로 Kubernetes 리소스 검사하기

KubernetesBeginner
지금 연습하기

소개

Kubernetes 입문 과정에서는 애플리케이션이 정상적으로 동작하는지 확인하기 위해 kubectl get, describe, 레이블을 사용했습니다. 하지만 실제 클러스터에는 많은 객체가 존재하므로, 다음 단계에서는 더 구체적인 질문을 던지고 필요한 근거만 추출하는 능력이 필요합니다.

이 실습에서는 작은 상점 애플리케이션 워크로드를 배포한 뒤 네임스페이스, 레이블, 구조화된 필드, 컨트롤러 소유권, 이벤트 등 여러 관점에서 이를 검사합니다. 마지막에는 Deployment 와 해당 Deployment 가 관리하는 Pod 를 연결하는 간결한 명령을 완성합니다.

모든 출력 형식을 암기할 필요는 없습니다. 대신 질문을 구체적인 조회로 바꾸는 연습을 하세요. “어떤 객체가 존재하는가?”, “이 Service 와 일치하는 Pod 는 무엇인가?”, “이 Pod 의 소유자는 누구인가?”, “가장 최근에 무슨 일이 발생했는가?”와 같은 질문을 던지는 것입니다. 각 단계에서 이 검사 워크플로에 도구 하나씩을 추가합니다.

집중 작업 공간 만들기

이 단계에서는 네임스페이스를 만들고 준비된 리소스를 해당 네임스페이스에 배포합니다. 네임스페이스를 사용하면 이후 조회 범위를 좁힐 수 있어 이름이 같은 객체를 혼동하는 일을 방지할 수 있습니다.

실습 작업 공간으로 이동한 다음, 클러스터 상태를 변경하기 전에 현재 컨텍스트를 확인합니다. cd는 셸의 현재 작업 디렉터리를 변경하므로 이후 storefront.yaml과 같은 상대 경로의 파일은 준비된 작업 공간을 기준으로 참조됩니다.

cd /home/labex/project/resource-inspection
kubectl config current-context

출력은 labex-v135여야 합니다. 네임스페이스를 만들고 준비된 여러 문서로 구성된 매니페스트를 적용합니다.

kubectl create namespace practice-observe
kubectl apply -f storefront.yaml
kubectl rollout status deployment/storefront -n practice-observe --timeout=60s

마지막 몇 줄은 다음과 비슷하게 표시됩니다. 두 개의 Pod 가 시작되는 동안 중간에 다른 롤아웃 메시지가 나타날 수 있습니다.

namespace/practice-observe created
deployment.apps/storefront created
service/storefront created
deployment "storefront" successfully rolled out

-n practice-observe는 명령이 해당 네임스페이스만 대상으로 하도록 제한합니다. 롤아웃 명령은 임의의 대기 시간에 의존하지 않고 Deployment 의 두 복제본을 사용할 수 있을 때까지 기다립니다.

앞으로 -n practice-observe를 반복해서 사용합니다. 별도로 지정하지 않으면 Kubernetes 명령은 현재 컨텍스트에 설정된 네임스페이스 (대개 default) 를 사용합니다. 따라서 실제로는 정상인 객체도 잘못된 범위에서 조회했다는 이유만으로“없음”으로 보일 수 있습니다.

리소스와 범위 확인하기

이 단계에서는 객체를 조회하기 전에 API 검색 기능을 사용합니다. kubectl api-resources는 현재 API 서버가 인식하는 리소스 이름과 약칭, 그리고 각 리소스가 네임스페이스 범위인지 여부를 보여 줍니다. head로 파이프를 연결하면 처음 열 줄만 표시되어 터미널에 너무 많은 내용이 출력되는 것을 막을 수 있습니다.

kubectl api-resources | head
kubectl api-resources --namespaced=true | grep -E 'pods|deployments|services'

파이프는 리소스 목록을 grep으로 전달합니다. -E는 |를 사용한 대체 조건을 활성화하므로 하나의 명령으로 여러 리소스 이름을 검색할 수 있습니다. Pod, Deployment, Service 가 모두 네임스페이스 범위의 리소스인지 확인하세요.

전체 이름을 사용해 애플리케이션 객체를 조회한 다음, 일반적인 약칭으로 같은 조회를 다시 실행합니다.

kubectl get pods,deployments,services -n practice-observe
kubectl get po,deploy,svc -n practice-observe

두 명령은 동일한 API 리소스를 대상으로 합니다. 약칭을 사용하면 입력을 줄일 수 있지만, 스크립트와 문서에서는 전체 이름이 더 명확합니다.

Deployment 하나, 자동 생성된 Pod 두 개, Service 하나가 표시되는지 확인하세요. 쉼표로 구분된 리소스 목록은 조회를 편리하게 해 주는 문법일 뿐이며, 각 객체의 역할과 API 스키마는 서로 다릅니다.

출력의 리소스 유형 접두사는 여러 결과를 함께 해석하는 데 도움이 됩니다. 자동 생성된 접미사, 주소, 생성 후 경과 시간은 실행할 때마다 달라집니다.

NAME                              READY   STATUS    RESTARTS   AGE
pod/storefront-...                1/1     Running   0          ...
pod/storefront-...                1/1     Running   0          ...

NAME                         READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/storefront   2/2     2            2           ...

NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
service/storefront   ClusterIP   ...          <none>        80/TCP    ...

레이블과 셀렉터로 필터링하기

이 단계에서는 레이블을 조회 가능한 메타데이터로 활용합니다. --show-labels는 모든 레이블을 표시하고, -l은 표현식과 일치하는 레이블을 가진 객체만 선택합니다.

kubectl get pods -n practice-observe --show-labels
kubectl get pods -n practice-observe -l app=storefront
kubectl get pods -n practice-observe -l 'app=storefront,tier=web'

셀렉터 조건을 쉼표로 구분하면 논리적 AND 조건이 됩니다. 따라서 마지막 조회에서는 두 레이블을 모두 가진 Pod 만 반환됩니다.

Service 의 셀렉터와 선택된 Pod 의 레이블을 비교합니다.

kubectl get service storefront -n practice-observe -o jsonpath='Service selector: {.spec.selector.app}{"\n"}'
kubectl get pods -n practice-observe -l app=storefront -o custom-columns='NAME:.metadata.name,TIER:.metadata.labels.tier,IP:.status.podIP,READY:.status.containerStatuses[0].ready'
kubectl get endpointslices -n practice-observe -l kubernetes.io/service-name=storefront

JSONPath 는 Service 에서 특정 필드 하나를 추출합니다. -o custom-columns는 여러 Pod 필드를 간결한 표 형태로 출력합니다. 큰 YAML 문서를 눈으로 검색하는 것보다 훨씬 확실한 방법입니다.

마지막 명령은 자동으로 관리되는 kubernetes.io/service-name 레이블의 값이 storefront인 EndpointSlice 를 조회합니다. EndpointSlice 는 Service 가 선택한 네트워크 엔드포인트를 기록합니다. ENDPOINTS 열에 값이 표시되면 일치하는 레이블을 통해 실제로 연결 가능한 백엔드가 생성되었다는 뜻입니다.

Service 셀렉터는 storefront을 출력해야 하며, Pod 표의 두 행 모두 tier=web을 포함해야 합니다. 일치하는 app 레이블이 두 객체를 연결합니다. Service 는 Deployment 의 소유권을 통해서가 아니라 레이블을 통해 Pod 를 찾습니다.

Service selector: storefront
NAME             TIER   IP          READY
storefront-...   web    10.244...   true
storefront-...   web    10.244...   true

NAME             ADDRESSTYPE   PORTS   ENDPOINTS                  AGE
storefront-...   IPv4          80      10.244...,10.244...          ...

컨트롤러 소유권 추적하기

이 단계에서는 Deployment 에서 ReplicaSet 과 Pod 로 이어지는 Kubernetes 소유권 관계를 추적합니다. 컨트롤러는 ownerReferences를 설정하여 어떤 상위 객체가 각 종속 객체를 관리하는지 Kubernetes 에 알려 줍니다.

kubectl get replicasets -n practice-observe -l app=storefront
kubectl get replicasets -n practice-observe -l app=storefront -o custom-columns='REPLICASET:.metadata.name,OWNER_KIND:.metadata.ownerReferences[0].kind,OWNER_NAME:.metadata.ownerReferences[0].name'
kubectl get pods -n practice-observe -l app=storefront -o custom-columns='POD:.metadata.name,OWNER_KIND:.metadata.ownerReferences[0].kind,OWNER_NAME:.metadata.ownerReferences[0].name'

ReplicaSet 의 소유자는 storefront Deployment 여야 합니다. 각 Pod 의 소유자는 Deployment 가 아니라 자동 생성된 ReplicaSet 입니다.

Deployment/storefront -> ReplicaSet/storefront-... -> Pod/storefront-...

이 관계를 조건과 최근 이벤트와 함께 확인해야 할 때는 kubectl describe를 사용합니다.

kubectl describe deployment storefront -n practice-observe

매번 describe 출력을 처음부터 끝까지 읽을 필요는 없습니다. 이 질문에서는 Selector, 복제본 수, 조건, 이벤트에 집중하세요. 가장 좋은 검사 명령은 불필요한 출력은 최소화하면서 현재 질문에 답해 주는 명령입니다.

이벤트를 타임라인으로 읽기

이 단계에서는 이벤트를 시간 순서에 따른 근거로 확인합니다. 이벤트는 스케줄링, 이미지, 컨테이너, 컨트롤러의 동작을 설명하지만 영구적인 감사 기록이 아니라 일시적인 진단 정보입니다.

kubectl get events -n practice-observe --sort-by=.metadata.creationTimestamp

--sort-by는 필드 경로를 기준으로 객체를 정렬합니다. 먼저 Deployment 에 대한 타임라인으로 범위를 좁힌 다음, 선택한 Pod 하나를 확인합니다.

kubectl events -n practice-observe --for deployment/storefront
POD_NAME=$(kubectl get pods -n practice-observe -l app=storefront -o jsonpath='{.items[0].metadata.name}')
echo "$POD_NAME"
kubectl events -n practice-observe --for "pod/${POD_NAME}"

$(...)는 명령 치환 문법입니다. 셸이 내부 명령을 실행하고 그 결과를 POD_NAME에 저장합니다. 이제 반복해서 사용할 수 있는 검사 경로가 완성되었습니다. 네임스페이스로 범위를 좁히고, 레이블로 선택하고, 필드를 추출하고, 소유자를 추적한 뒤, 관련 이벤트 타임라인으로 마무리하는 방식입니다.

컨트롤러가 생성한 Pod 이름은 Pod 가 교체될 때마다 바뀌므로, 예전에 복사해 둔 이름을 사용하는 것보다 레이블로 현재 이름을 선택하는 편이 안전합니다. 이 패턴은 이후 문제 해결 실습에서도 계속 등장합니다.

정상적으로 관찰되는 수명 주기에는 보통 Scheduled, Pulled, Created, Started 이벤트가 포함됩니다. 막 시작된 로컬 클러스터에서는 Calico 가 준비되는 동안 FailedCreatePodSandBox와 같은 이전 경고가 남아 있을 수도 있습니다. 오래된 경고 하나만 보고 애플리케이션이 여전히 고장 났다고 판단하지 마세요. 해당 경고의 타임스탬프를 이후 정상 이벤트와 비교한 다음, Pod 의 현재 READY 상태를 확인해야 합니다. 타임스탬프와 자동 생성된 Pod 이름은 실행할 때마다 달라지므로 의도적으로 생략했습니다.

요약

관련 없는 객체를 모두 훑지 않고도 Kubernetes 애플리케이션을 검사했습니다. 네임스페이스, 레이블, JSONPath, 사용자 지정 열, 소유자 참조, 이벤트를 함께 사용하여 광범위한 인벤토리에서 정밀한 운영 근거로 범위를 좁혔습니다. 다음 실습에서는 이 검사 워크플로를 장애가 발생한 Pod 와 컨테이너에 적용합니다.