6.2.2 오브젝트 명세 (Spec)와 상태 (Status)
앞서 우리는 쿠버네티스가 ‘의도된 상태(Desired State)’를 받아들여 ‘현재 상태(Current State)’를 그에 맞게 조정하려 노력한다고 배웠습니다. 그렇다면 이 ‘의도된 상태’는 과연 어떤 모습으로 쿠버네티스에게 전달될까요? 그리고 쿠버네티스는 ‘현재 상태’를 우리에게 어떻게 알려줄까요? 바로 이 질문에 대한 답이 쿠버네티스 오브젝트(Object) 의 명세(Specification, 줄여서 Spec) 와 상태(Status) 에 담겨 있습니다.
쿠버네티스에서 다루는 모든 것은 ‘오브젝트’라는 형태로 표현됩니다. 파드(Pod), 디플로이먼트(Deployment), 서비스(Service), 네임스페이스(Namespace) 등 우리가 앞으로 배우고 사용하게 될 모든 것들이 바로 이 오브젝트에 해당합니다. 그리고 각 오브젝트는 자신이 어떤 모습이어야 하는지(Spec)와 현재 어떤 모습인지(Status)에 대한 정보를 가지고 있습니다. 마치 우리가 제품을 주문할 때 원하는 사양(Spec)을 명시하고, 배송된 제품의 실제 상태(Status)를 확인하는 것과 비슷하다고 생각하시면 이해가 쉬울 겁니다.
6.2.2.1 YAML 형식의 오브젝트 정의: 쿠버네티스에게 말 거는 방법
우리가 쿠버네티스에게 “이런이런 애플리케이션을 이렇게 실행해줘” 라고 우리의 ‘의도된 상태’를 전달하려면, 일종의 ‘요청서’ 또는 ‘설계도’를 작성해야 합니다. 쿠버네티스에서는 이 요청서를 대부분 YAML(YAML Ain’t Markup Language) 이라는 특별한 형식의 텍스트 파일로 작성합니다. 때로는 JSON(JavaScript Object Notation) 형식도 사용될 수 있지만, YAML이 사람의 눈으로 읽고 쓰기에 더 편리하여 널리 사용됩니다.
YAML은 데이터를 구조적으로 표현하기 위한 언어입니다. 들여쓰기(indentation)와 특정 기호(예: -, :)를 사용하여 데이터의 계층 구조와 목록, 키-값 쌍 등을 나타냅니다. 처음에는 조금 낯설 수 있지만, 몇 가지 규칙만 익히면 금방 익숙해지실 수 있습니다. 마치 우리가 이력서를 쓸 때 정해진 양식에 맞춰 정보를 기입하는 것처럼, 쿠버네티스 오브젝트를 정의하는 YAML 파일도 특정한 구조와 필수 필드들을 가지고 있습니다.
모든 쿠버네티스 오브젝트 정의 파일(보통 매니페스트(Manifest) 파일이라고 부릅니다)은 일반적으로 다음과 같은 최상위 필드들을 공통적으로 포함합니다:
- apiVersion: 이 오브젝트 정의가 어떤 쿠버네티스 API 버전을 따르는지를 명시합니다. 쿠버네티스는 API를 기능 그룹별로 나누고 버전 관리를 하는데, 예를 들어 디플로이먼트는 apps/v1, 서비스나 파드는 v1 과 같이 지정됩니다. 이는 쿠버네티스가 해당 오브젝트를 올바르게 해석하고 처리하기 위한 필수 정보입니다.
- kind: 생성하려는 오브젝트의 종류를 나타냅니다. 예를 들어, Pod, Deployment, Service, Namespace, ConfigMap 등이 여기에 해당됩니다. kind를 통해 쿠버네티스는 이 매니페스트가 어떤 유형의 리소스를 위한 것인지 식별합니다.
- metadata: 이름, 레이블(label), 어노테이션(annotation) 등 해당 오브젝트를 식별하고 부가 정보를 제공하는 메타데이터를 담습니다.
- name: 해당 네임스페이스 내에서 오브젝트를 고유하게 식별하는 이름입니다.
- namespace: 오브젝트가 속할 논리적인 격리 공간을 지정합니다. 지정하지 않으면 기본적으로 default 네임스페이스에 생성됩니다.
- labels: 오브젝트를 분류하고 선택(selector)하는 데 사용되는 키-값 쌍입니다. 예를 들어 app: my-web-app, env: production 과 같이 붙여서 여러 오브젝트를 그룹핑하고 관리할 수 있습니다.
- annotations: 레이블과 비슷하지만, 주로 시스템 도구나 다른 사용자에게 추가적인 정보를 제공하기 위한 비식별 메타데이터입니다.
- spec: 바로 이 필드가 우리가 쿠버네티스에게 바라는 의도된 상태(Desired State) 를 상세하게 기술하는 부분입니다. 어떤 컨테이너 이미지를 사용할지, 몇 개의 복제본을 실행할지, 어떤 포트를 열어둘지 등 오브젝트의 구체적인 사양을 정의합니다. 이 spec 필드의 내용은 kind에 따라 완전히 달라집니다.
- status: 이 필드는 쿠버네티스 시스템에 의해 채워지며, 해당 오브젝트의 현재 상태(Current State) 를 나타냅니다. 사용자가 직접 이 필드를 수정하는 경우는 거의 없습니다. 컨트롤러들이 실제 시스템의 상태를 반영하여 업데이트합니다.
예를 들어, 가장 기본적인 실행 단위인 파드(Pod)를 정의하는 간단한 YAML 매니페스트는 다음과 같은 모습일 수 있습니다:
이 YAML 파일을 작성한 후, 우리는 kubectl apply -f <파일명.yaml> 과 같은 명령어를 사용하여 이 ‘요청서’를 쿠버네티스 API 서버에 제출합니다. 그러면 API 서버는 이 요청을 받아 유효성을 검사하고, etcd라는 데이터 저장소에 이 정보를 기록합니다. 이제 쿠버네티스는 이 YAML 파일에 정의된 내용을 바탕으로 실제 작업을 수행하게 됩니다.
6.2.2.2 spec 필드: 사용자가 원하는 상태
이제 조금 더 깊이 들어가서 spec 필드에 대해 자세히 알아보겠습니다. 앞서 언급했듯이, spec 필드는 해당 쿠버네티스 오브젝트가 어떤 모습이어야 하는지, 즉 우리가 바라는 의도된 상태(Desired State)를 구체적으로 명시하는 공간입니다. 우리가 쿠버네티스에게 “이렇게 만들어 주세요!”라고 요청하는 내용이 바로 이 spec 안에 담기는 것이죠.
spec 필드의 내용은 오브젝트의 kind에 따라 천차만별입니다. 각 오브젝트 종류마다 자신만의 고유한 spec 구조와 필드들을 가지고 있습니다. 몇 가지 예를 들어볼까요?
- Pod의 spec:
- containers: 이 파드에서 실행될 하나 이상의 컨테이너 목록을 정의합니다. 각 컨테이너마다 사용할 이미지(image), 열어둘 포트(ports), 필요한 CPU나 메모리 같은 자원 요구량/한계(resources), 환경 변수(env), 볼륨 마운트(volumeMounts) 등을 상세하게 설정할 수 있습니다.
- volumes: 컨테이너들이 사용할 수 있는 다양한 종류의 볼륨(예: 로컬 디스크, 네트워크 스토리지 등)을 정의합니다.
- restartPolicy: 파드 내의 컨테이너가 종료되었을 때 재시작 정책(예: Always, OnFailure, Never)을 지정합니다.
- nodeSelector, affinity, tolerations: 파드가 어떤 노드에 스케줄링될지를 결정하는 규칙들을 정의합니다.
- Deployment의 spec:
- replicas: 이 디플로이먼트가 관리할 파드의 복제본 수를 지정합니다. 예를 들어 replicas: 3으로 설정하면, 쿠버네티스는 항상 3개의 동일한 파드가 실행되도록 노력합니다.
- selector: 이 디플로이먼트가 어떤 파드들을 자신의 관리 대상으로 삼을지를 결정하는 레이블 셀렉터를 정의합니다. matchLabels 필드 등을 사용합니다.
- template: 디플로이먼트가 새로 생성할 파드의 ‘설계도’ 즉, 파드 템플릿을 정의합니다. 이 템플릿 안에는 위에서 설명한 파드의 metadata (주로 labels)와 spec 내용이 그대로 포함됩니다.
- strategy: 디플로이먼트가 파드를 업데이트할 때 사용할 전략(예: RollingUpdate, Recreate)과 세부 옵션을 정의합니다.
- Service의 spec:
- selector: 이 서비스가 어떤 파드들에게 네트워크 트래픽을 전달할지를 결정하는 레이블 셀렉터를 정의합니다. 이 셀렉터와 일치하는 레이블을 가진 파드들이 서비스의 대상이 됩니다.
- ports: 서비스가 노출할 포트와 대상 파드의 포트를 매핑합니다. (예: 서비스의 80번 포트로 들어온 요청을 대상 파드의 8080 포트로 전달)
- type: 서비스의 타입을 지정합니다. ClusterIP(내부용), NodePort(각 노드의 포트를 통해 외부 노출), LoadBalancer(클라우드 로드밸런서 연동), ExternalName 등이 있습니다.
이처럼 spec 필드는 우리가 원하는 시스템의 구체적인 모습을 쿠버네티스에게 전달하는 매우 중요한 역할을 합니다. 사용자는 이 spec을 신중하게 작성하여 쿠버네티스 API 서버에 제출하고, 그 이후에는 쿠버네티스 컨트롤러들이 이 spec을 기준으로 실제 시스템을 만들어 나갑니다. 중요한 점은, 일단 오브젝트가 생성된 후에도 이 spec 필드의 내용을 변경하여 kubectl apply 명령을 다시 실행하면, 쿠버네티스는 변경된 spec에 맞춰 시스템 상태를 업데이트하려고 시도한다는 것입니다. (물론, 모든 spec 필드가 변경 가능한 것은 아니며, 일부 필드는 생성 후 변경할 수 없는 경우도 있습니다.)
spec은 마치 우리가 건축가에게 건물의 설계도를 전달하는 것과 같습니다. 얼마나 튼튼해야 하고, 방은 몇 개여야 하며, 창문은 어느 방향으로 나야 하는지 등을 상세하게 명시하는 것이죠.
6.2.2.3 status 필드: 시스템이 관리하는 현재 상태
spec 필드가 우리가 바라는 ‘이상적인 모습’을 담고 있다면, status 필드는 그에 대한 ‘현실적인 모습’, 즉 시스템에 의해 관찰되고 관리되는 해당 오브젝트의 현재 상태(Current State)를 반영하는 공간입니다. 이 status 필드는 사용자가 직접 작성하거나 수정하는 것이 아니라, 쿠버네티스 클러스터 내의 다양한 컨트롤러들과 Kubelet 같은 컴포넌트들이 실제 시스템의 상황을 모니터링하고 그 결과를 API 서버를 통해 해당 오브젝트의 status 필드에 기록합니다.
status 필드의 내용 역시 오브젝트의 kind에 따라 다릅니다. spec에 대응하여 현재 시스템이 어떤 상태인지를 알려주는 정보를 담고 있죠.
- Pod의 status:
- phase: 파드의 현재 생명주기 단계를 나타냅니다. (예: Pending (생성 대기 중), Running (실행 중), Succeeded (성공적으로 완료), Failed (실패), Unknown (상태 알 수 없음))
- conditions: 파드의 현재 조건들을 배열 형태로 나타냅니다. (예: Initialized, Ready, ContainersReady, PodScheduled) 각 조건마다 type, status (True, False, Unknown), lastProbeTime, lastTransitionTime 등의 정보를 가집니다.
- hostIP: 파드가 스케줄링된 노드의 IP 주소입니다.
- podIP: 파드에 할당된 내부 IP 주소입니다.
- containerStatuses: 파드 내 각 컨테이너의 현재 상태(예: state (waiting, running, terminated), ready, restartCount, imageID 등)를 상세하게 보여줍니다.
- Deployment의 status:
- replicas: 현재 실제로 실행 중인 파드의 총 개수입니다.
- updatedReplicas: spec.template과 일치하는 최신 버전으로 업데이트된 파드의 수입니다.
- readyReplicas: 사용 가능한 상태(헬스 체크 통과 등)인 파드의 수입니다.
- availableReplicas: readyReplicas와 유사하지만, 디플로이먼트의 minReadySeconds 설정을 고려한, 실제 서비스 가능한 파드의 수입니다.
- conditions: 디플로이먼트의 현재 조건들을 나타냅니다. (예: Available, Progressing)
- Service의 status:
- loadBalancer: 서비스 타입이 LoadBalancer인 경우, 클라우드 프로바이더로부터 할당받은 로드밸런서의 정보(예: 외부 IP 주소나 호스트 이름)가 여기에 기록됩니다.
우리는 kubectl describe <오브젝트 종류> <오브젝트 이름> 명령어나 kubectl get <오브젝트 종류> <오브젝트 이름> -o yaml (또는 -o json) 명령어를 통해 이 status 필드의 내용을 확인할 수 있습니다. 이를 통해 현재 우리 애플리케이션이 정상적으로 동작하고 있는지, 어떤 문제가 발생했는지 등을 파악하고 디버깅하는 데 중요한 단서를 얻을 수 있습니다.
status는 건축 현장의 감리 보고서와 같습니다. 설계도(spec)대로 공사가 잘 진행되고 있는지, 현재까지의 공정률은 얼마인지, 혹시 문제는 없는지 등을 실제 현장 상황을 바탕으로 기록하는 것이죠.
결국, 쿠버네티스의 마법은 이 spec과 status 사이에서 일어납니다. 사용자는 spec을 통해 “나는 이런 상태를 원한다”고 선언하고, 쿠버네티스 컨트롤러들은 끊임없이 현재 시스템의 status를 감시하며 spec과 status가 일치하도록 필요한 조치를 취합니다. 이 끊임없는 ‘조정 루프(Reconciliation Loop)’가 바로 쿠버네티스를 강력하고 자동화된 시스템으로 만드는 핵심 원리입니다.
따라서 쿠버네티스를 제대로 이해하고 활용하려면, 내가 원하는 상태를 YAML 매니페스트의 spec에 정확하게 표현하는 방법과, 그렇게 정의된 오브젝트의 status를 통해 현재 시스템이 어떻게 돌아가고 있는지를 읽어내는 능력을 키우는 것이 매우 중요합니다. 이것이 바로 쿠버네티스와 효과적으로 소통하는 첫걸음이 될 것입니다.