[Kubernetes] 어플리케이션 설정 - 4. Downward API
Kubernetes in Action 2nd Edition 8장의 학습 내용을 기반으로 합니다.
TL;DR
- Downward API는 파드의 메타데이터(
metadata,spec,status필드)를 환경 변수 또는 볼륨 파일로 컨테이너에 전달하는 메커니즘이다 - REST 엔드포인트가 아니라, Kubernetes API 서버의 Pod object 정보를 컨테이너 “아래로(down)” 내려주는 방식이다
fieldRef로 파드의 일반 메타데이터(이름, IP, 노드 등)를,resourceFieldRef로 컨테이너의 리소스 제약(CPU/메모리)을 참조한다- 어플리케이션이 Kubernetes API에 종속되지 않고도 파드 메타데이터를 활용할 수 있게 해준다
Downward API 소개
지금까지 어플리케이션에 설정 데이터를 전달하는 방법을 알아봤지만, 그 데이터는 항상 정적이었다. 값은 파드를 배포하기 전에 이미 알려져 있었고, 동일한 파드를 여러 개 배포해도 모두 같은 값을 사용했다.
하지만 아래와 같은 경우는 문제가 된다.
- 파드가 생성되고 클러스터 노드에 스케줄링될 때까지 알 수 없는 데이터(파드의 IP, 클러스터 노드의 이름, 파드 자체의 이름 등)는 어떻게 전달하는가?
- 파드 매니페스트의 다른 곳에 이미 지정된 데이터(컨테이너에 할당된 CPU와 메모리 양 등)를 중복해서 정의하는 것이 바람직한가?
이런 경우를 Downward API를 이용해 해결할 수 있다.
Downward API란
Downward API는 파드와 컨테이너의 메타데이터를 환경 변수 또는 파일을 통해 컨테이너에 주입할 수 있게 해주는 메커니즘이다. 파드 매니페스트에 설정할 수 있는 다양한 구성 옵션이 있고, 동일한 정보를 어플리케이션에 전달해야 하는 경우도 있는데, 이때 환경 변수에 값을 반복하는 대신 Downward API를 사용하는 것이 더 좋은 방법이다.
“Downward”의 의미
Downward API는 어플리케이션이 호출해야 하는 REST 엔드포인트가 아니다. 파드 매니페스트의 metadata, spec, status 필드의 값을 컨테이너 아래로(down) 내려주는 방식이다.
출처: Kubernetes in Action 2nd Edition
Kubernetes 아키텍처 관점에서 위(up)는 Kubernetes API 서버(컨트롤 플레인)이고, 아래(down)는 컨테이너(워크로드)다.
- Upward: 컨테이너 안의 어플리케이션이 Kubernetes API 서버에 위로 올라가서 정보를 직접 질의하는 방향이다. 예를 들어, 파드 안에서
curl https://kubernetes.default.svc/api/v1/pods로 API 서버를 호출하는 것이 이에 해당한다 - Downward: API 서버가 관리하는 Pod object의 메타데이터를 컨테이너 아래로 내려보내주는 방향이다. 환경 변수나 파일로 투영된다
파드 이름, IP 같은 기본 정보를 얻기 위해 매번 API 서버를 호출(upward)하는 건 과하고, 어플리케이션이 Kubernetes에 종속되게 만든다. Downward API는 이런 간단한 메타데이터를 환경 변수나 파일로 내려줘서 어플리케이션이 Kubernetes API를 전혀 모르고도 사용할 수 있게 해준다.
데이터 소스와 전달 방식
ConfigMap의 값을 가져올 때 configMapKeyRef, Secret의 값을 가져올 때 secretKeyRef를 사용했다. Downward API를 통해 값을 가져오려면, 주입하려는 정보에 따라 다음 두 가지를 사용한다.
fieldRef→ 파드의 일반 메타데이터 참조 (metadata.name,spec.nodeName,status.podIP등)resourceFieldRef→ 컨테이너의 컴퓨트 리소스 제약 참조 (requests.cpu,limits.memory등)
전달 방식도 ConfigMap/Secret과 동일한 패턴이다.
- env →
valueFrom.fieldRef또는valueFrom.resourceFieldRef로 환경 변수에 주입 - volume →
downwardAPI볼륨으로 파일 시스템에 파일로 투영
ConfigMap/Secret volume과 동일한 방식이지만, 데이터 소스가 외부 오브젝트가 아니라 Pod object 자체라는 점이 다르다.
분류 체계: 데이터 소스와 전달 방식
Downward API를 이해하려면 두 가지 축을 파악하면 된다.
- 데이터 소스: 어떤 정보를 가져오는가
fieldRef→ 파드의 일반 메타데이터resourceFieldRef→ 컨테이너의 컴퓨트 리소스 제약
- 전달 방식: 어떻게 컨테이너에 전달하는가
- env → 환경 변수에 주입
- volume → 파일 시스템에 파일로 투영
그런데 완전한 2x2 조합은 아니다. resourceFieldRef 필드들은 env/volume 모두 가능하지만, fieldRef 필드들은 필드마다 제한이 다르다.
fieldRef 필드별 지원 범위
| 필드 | env | volume |
|---|---|---|
metadata.name |
O | O |
metadata.namespace |
O | O |
metadata.uid |
O | O |
metadata.labels (전체) |
X | O |
metadata.annotations (전체) |
X | O |
metadata.labels['key'] (개별) |
O | O |
metadata.annotations['key'] (개별) |
O | O |
spec.nodeName |
O | X |
spec.serviceAccountName |
O | X |
status.podIP |
O | X |
status.hostIP |
O | X |
resourceFieldRef 필드별 지원 범위
| 필드 | env | volume |
|---|---|---|
requests.cpu |
O | O |
requests.memory |
O | O |
limits.cpu |
O | O |
limits.memory |
O | O |
requests.ephemeral-storage |
O | O |
limits.ephemeral-storage |
O | O |
fieldRef 주입 방식 제한 이유
왜 fieldRef는 필드마다 지원하는 전달 방식이 다를까?
- labels/annotations 전체는 여러 줄의 키-값 쌍이라 단일 환경 변수에 담기 부적합하다. 그래서 volume만 허용된다
spec.nodeName,status.podIP등은 파드가 스케줄링된 후에야 결정되는 런타임 값이다. 환경 변수 방식이 더 적합하므로 env만 허용된다
주입 가능한 메타데이터
fieldRef로 주입할 수 있는 필드
| 필드 | 설명 | env | volume |
|---|---|---|---|
metadata.name |
파드 이름 | O | O |
metadata.namespace |
파드 네임스페이스 | O | O |
metadata.uid |
파드 UID | O | O |
metadata.labels |
파드의 모든 레이블 (한 줄에 하나씩, key="value" 형식) |
X | O |
metadata.labels['key'] |
지정된 레이블의 값 | O | O |
metadata.annotations |
파드의 모든 어노테이션 (한 줄에 하나씩, key="value" 형식) |
X | O |
metadata.annotations['key'] |
지정된 어노테이션의 값 | O | O |
spec.nodeName |
파드가 실행되는 워커 노드 이름 | O | X |
spec.serviceAccountName |
파드의 서비스 어카운트 이름 | O | X |
status.podIP, status.podIPs |
파드의 IP 주소 | O | X |
status.hostIP, status.hostIPs |
워커 노드의 IP 주소 | O | X |
resourceFieldRef로 주입할 수 있는 필드
| 리소스 필드 | 설명 | env | volume |
|---|---|---|---|
requests.cpu |
컨테이너의 CPU 요청 | O | O |
requests.memory |
컨테이너의 메모리 요청 | O | O |
requests.ephemeral-storage |
컨테이너의 임시 스토리지 요청 | O | O |
requests.hugepages-* |
컨테이너의 hugepages 요청 | O | O |
limits.cpu |
컨테이너의 CPU 제한 | O | O |
limits.memory |
컨테이너의 메모리 제한 | O | O |
limits.ephemeral-storage |
컨테이너의 임시 스토리지 제한 | O | O |
limits.hugepages-* |
컨테이너의 hugepages 제한 | O | O |
환경 변수로 메타데이터 주입
파드 메타데이터 주입: fieldRef
Downward API를 사용하여 파드의 메타데이터를 환경 변수로 주입하는 예시를 보자. 다음은 파드와 노드의 이름, IP 주소를 환경 변수로 주입하는 파드 매니페스트다.
apiVersion: v1
kind: Pod
metadata:
name: kiada
spec:
containers:
- name: kiada
image: luksa/kiada:0.4
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name # 파드 이름
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP # 파드 IP
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName # 노드 이름
- name: NODE_IP
valueFrom:
fieldRef:
fieldPath: status.hostIP # 노드 IP
ports:
- name: http
containerPort: 8080
파드를 생성한 후 어플리케이션에 요청을 보내면 다음과 같은 응답을 받을 수 있다.
Request processed by Kiada 0.4 running in pod "kiada" on node "kind-worker".
Pod hostname: kiada; Pod IP: 10.244.2.15; Node IP: 172.18.0.4. Client IP:
::ffff:127.0.0.1.
This is the default status message
kubectl exec kiada -- env로 컨테이너의 환경 변수 전체를 확인하거나, kubectl get po kiada -o yaml로 파드 매니페스트의 값과 비교할 수도 있다.
컨테이너 리소스 주입: resourceFieldRef
컨테이너에 할당된 컴퓨트 리소스 제약을 어플리케이션에 전달할 수도 있다. 일부 어플리케이션은 주어진 제약 내에서 최적으로 동작하기 위해 할당된 리소스를 알아야 한다.
env:
- name: MAX_CPU_CORES
valueFrom:
resourceFieldRef:
resource: limits.cpu # CPU 리소스 제한
- name: MAX_MEMORY_KB
valueFrom:
resourceFieldRef:
resource: limits.memory # 메모리 리소스 제한
divisor: 1k # 킬로바이트 단위로 변환
valueFrom.resourceFieldRef를 사용하여 컨테이너 리소스 필드를 주입하고, resource 필드로 주입할 리소스 값을 지정한다.
divisor 필드는 값을 어떤 단위로 저장할지 지정한다.
- 메모리:
1(바이트),1k(킬로바이트),1Ki(키비바이트),1M(메가바이트),1Mi(메비바이트) 등 - CPU: 기본값
1(전체 코어),1m(밀리코어 = 1/1000 코어)으로 설정 가능 - divisor를 생략하면 기본값
1이 사용된다
containerName필드를 사용하면 다른 컨테이너의 리소스 제한도 참조할 수 있다. 기본적으로는 해당 환경 변수가 정의된 컨테이너의 리소스가 사용된다.
주요 유스케이스
Downward API가 실제로 어떤 상황에서 유용하게 쓰이는지 정리해 보자.
로깅/모니터링에서 파드 식별
로그에 파드 이름, 네임스페이스, 노드 이름을 포함시켜 어떤 파드에서 발생한 로그인지 추적할 수 있다. Prometheus, Datadog 등 메트릭 수집 시 라벨로 파드 메타데이터를 첨부하는 데에도 활용된다. MY_POD_NAME, MY_NODE_NAME을 환경 변수로 주입하여 로그 포맷에 포함하는 방식이다.
리소스 제한 인지
리소스 인지(resource-aware) 어플리케이션에서 유용하다. JVM의 -Xmx 설정을 컨테이너 requests.memory 기반으로 동적 계산하거나, 워커 스레드 수를 requests.cpu 기반으로 조정하는 식이다.
서비스 디스커버리/자기 참조
파드가 자신의 IP(status.podIP)를 알아야 다른 서비스에 자기 주소를 등록할 때 필요하다. Consul, etcd 클러스터 조인 같은 경우가 대표적이다. StatefulSet에서 자기 이름을 알아 리더/팔로워 역할을 분기하는 데에도 쓰인다.
멀티테넌트/RBAC 맥락 전달
metadata.namespace를 앱에 전달하여 네임스페이스별 분기 처리를 하는 경우에 활용된다.
정리
Downward API의 핵심 가치는 어플리케이션이 Kubernetes API에 종속되지 않고도 런타임 메타데이터를 활용할 수 있다는 점이다. 파드 이름, IP, 노드 정보 같은 런타임 데이터를 얻기 위해 API 서버를 직접 호출할 필요가 없다.
ConfigMap/Secret과 동일한 valueFrom 패턴을 사용하지만, 데이터 소스가 Pod object 자체라는 점이 다르다. fieldRef로 파드의 일반 메타데이터를, resourceFieldRef로 컨테이너의 리소스 제약을 참조한다.
전달 방식은 env와 volume 두 가지를 지원하며, fieldRef 필드마다 지원 범위가 다르다는 점을 기억해 두자. labels/annotations 전체는 volume만, spec.nodeName 등 런타임 결정 값은 env만 지원된다.
댓글남기기