0. Contents
1. Overview
앱이 모르는 세 종류의 정보를 Downward API가 전달한다
자기 정체성이 필요한 순간
- 클라우드 네이티브 애플리케이션은 대부분 stateless하고, 자기 정체성이 필요 없다
- 하지만 가끔은 앱 스스로가 자신이 어디서, 어떤 조건으로 돌고 있는지 알아야 하는 경우가 생긴다
해법: Kubernetes의 Downward API
- 이 문제를 코드 변경 없이, 외부 API 호출 없이, YAML 선언만으로 해결한다
- Pod 메타데이터를 환경변수 또는 파일 형태로 앱에 직접 주입하는 메커니즘이다
2. Problem
2.1 앱이 자기 자신을 모르는 이유
Pod가 생성될 때 정해지는 값
| 항목 | 예시 | 정해지는 방식 |
|---|---|---|
| Pod 이름 | my-app-7f9d-xkp2q | 뜰 때마다 달라짐 |
| Pod IP | 10.244.1.23 | 배정받아야 알 수 있음 |
| 어느 노드 | node-3 | 스케줄러가 결정 |
뜨기 전에는 알 수 없는 정보
- 이 정보들은 Pod가 실제로 뜨기 전까지는 아무도 모른다
- 코드에 하드코딩할 수 없고, 배포 시점에 미리 알 수도 없다
- 하지만 앱이 아래 일을 하려면 이 정보가 반드시 필요하다
- 로그를 찍거나, 클러스터를 구성하거나, 리소스에 맞게 튜닝하기
2.2 필요한 정보의 3가지 유형
런타임에만 알 수 있는 정보
- Pod가 생성되고 배치되는 과정에서 결정되는 값들이다
- 개발자가 코드를 작성하거나 이미지를 빌드하는 시점에는 알 수 없다
- Pod 이름 →
metadata.name - Pod IP →
status.podIP - 호스트 노드 →
spec.nodeName
Pod 정의 시 고정된 정보
- YAML에 우리가 선언했지만, 앱 코드는 직접 알 방법이 없다
- CPU 요청량 →
requests.cpu: 500m - 메모리 한계 →
limits.memory: 256Mi
런타임에 바뀔 수 있는 정보
- Pod가 실행 중인 상태에서 사람이 수정할 수 있는 값들이다
- labels →
environment: "production"→"staging"으로 변경 가능 - annotations →
version: "v1.0"→"v1.1"으로 변경 가능
2.3 다른 클라우드 환경과의 비교
동적 환경의 공통 문제
- 이 문제는 Kubernetes만의 것이 아니다
- 동적 환경이라면 어디서나 생기는 문제고, 각자의 방식으로 해결하고 있다
| 환경 | 방식 | 특징 |
|---|---|---|
| Kubernetes | Downward API | 환경변수/파일로 주입 (Push) |
| AWS EC2 | Instance Metadata Service | HTTP API 직접 호출 (Pull) |
| AWS ECS | Task Metadata Endpoint | HTTP API 직접 호출 (Pull) |
Pull은 앱이 묻고, Push는 Kubernetes가 미리 넣어 둔다
Push 방식의 장점: 단순함
- Kubernetes 방식이 더 단순하다
- 앱 코드에 HTTP 호출 로직을 넣을 필요가 없다
3. Solution — Downward API
왼쪽은 Downward API(주입·마운트), 오른쪽은 API Server 직접 쿼리
두 가지 경로
- Container A: 환경변수와 Volume으로 값을 주입(Inject)/마운트(Mount) 받는 쪽
- Container B: Kubernetes 클라이언트로 API Server에 직접 쿼리(Query) 하는 쪽
- 지금부터 다룰 Downward API가 왼쪽 경로
- 이번 장 마지막(5장)에 다룰 API Server 직접 쿼리가 오른쪽 경로
3.1 ConfigMap / Secret과의 차이
같은 전달 메커니즘, 차이는 하나
- Downward API는 ConfigMap, Secret과 똑같은 전달 메커니즘(환경변수, 볼륨)을 사용한다
- 차이는 딱 하나다
같은 전달 방식, 다른 점은 값을 누가 쓰느냐
핵심: 선언만 하면 값은 Kubernetes가 채움
- “어떤 정보가 필요한지만 선언하면, Kubernetes가 알아서 값을 채워준다"는 것이 핵심이다
3.2 방식 1 — 환경변수
# Example 14-1. Environment variables from downward API
apiVersion: v1
kind: Pod
metadata:
name: random-generator
spec:
containers:
- image: k8spatterns/random-generator:1.0
name: random-generator
env:
- name: POD_IP # ① 앱에서 사용할 환경변수 이름 (우리가 자유롭게 지음)
valueFrom:
fieldRef: # ② Pod 필드에서 값을 가져올 것임을 선언
fieldPath: status.podIP # ③ Pod 정보 중 이 경로의 값을 사용
- name: MEMORY_LIMIT
valueFrom:
resourceFieldRef: # ④ 컨테이너 리소스 정보에서 값을 가져올 것임을 선언
containerName: random-generator # ⑤ 어느 컨테이너의 리소스인지 지정
resource: limits.memory # ⑥ 메모리 한계값 사용
fieldPath의 의미
fieldPath: status.podIP는 “Pod 전체 정보 중status안의podIP값을 써줘"라는 뜻이다- Pod가 뜨면 Kubernetes가 자동으로
status필드를 채워넣는다 - Downward API는 그 값을 읽어 환경변수에 꽂는다
# 실제로 kubectl로 Pod 정보를 보면 이렇게 생겼다
kubectl get pod random-generator -o yaml
status:
podIP: 10.244.1.23 # ← Downward API가 읽어오는 바로 그 값
hostIP: 192.168.1.5
phase: Running
앱 코드: 환경변수 읽기
- 앱 코드에서는 그냥 환경변수를 읽으면 된다
import os
pod_ip = os.environ["POD_IP"] # "10.244.1.23"
mem_limit = os.environ["MEMORY_LIMIT"] # "268435456" (256Mi를 bytes로)
값이 채워지는 경로
선언한 경로가 런타임 값이 되어 환경변수로 들어간다
resourceFieldRef의 containerName 생략
- 환경변수 방식에서
containerName은 생략 가능하다 - 생략하면 YAML 구조상 해당
env블록이 속한 컨테이너를 자동으로 참조한다
containers:
- name: random-generator # ← 이 컨테이너 블록 안에 선언했으므로
env:
- name: MEMORY_LIMIT
valueFrom:
resourceFieldRef:
# containerName 생략 → random-generator 자동 참조
resource: limits.memory
3.3 방식 2 — Volume 파일
# Example 14-2. Downward API through volumes
apiVersion: v1
kind: Pod
metadata:
name: random-generator
spec:
containers:
- image: k8spatterns/random-generator:1.0
name: random-generator
volumeMounts:
- name: pod-info
mountPath: /pod-info # ① 컨테이너 안에서 이 경로로 파일에 접근
volumes:
- name: pod-info
downwardAPI: # ② Downward API 볼륨 선언
items:
- path: labels # ③ /pod-info/labels 파일로 생성
fieldRef:
fieldPath: metadata.labels
- path: annotations # ④ /pod-info/annotations 파일로 생성
fieldRef:
fieldPath: metadata.annotations
볼륨과 컨테이너의 관계
volumes는 containers와 같은 레벨, volumeMounts가 둘을 잇는다
파일 내용
- 컨테이너 안에서 파일을 읽으면 이런 내용이 들어있다
cat /pod-info/labels
app="random-generator"
version="1.0"
environment="production"
저장 형식과 자동 업데이트
- 레이블과 어노테이션은
name=value형식으로 한 줄씩 저장된다 - Pod가 실행 중에 레이블이 바뀌면, Kubernetes가 자동으로 파일 내용을 업데이트한다
3.4 환경변수 vs Volume 비교
결정적 차이: 실시간 반영 여부
- 두 방식 모두 Downward API를 사용한다
- 하지만 실시간 반영 여부에서 결정적인 차이가 있다
레이블이 바뀐 뒤, 환경변수는 그대로이고 파일은 갱신된다
| 환경변수 | Volume 파일 | |
|---|---|---|
| 접근 방법 | os.environ["POD_IP"] | cat /pod-info/labels |
| 고정 값 (IP, 이름) | ✅ 적합 | ⚠️ 이름만 가능 (IP 불가) |
| 동적 값 (레이블, 어노테이션) | ❌ 재시작 필요 | ✅ 실시간 반영 |
| 전체 레이블/어노테이션 | ❌ 불가 | ✅ 가능 |
Volume 방식의 한계: 완전 자동은 아님
- 단, Volume 방식도 완전 자동은 아니다
| 단계 | 담당 |
|---|---|
| Kubernetes가 파일 업데이트 | Kubernetes 역할 (자동) |
| 앱이 파일 변경 감지 후 재읽기 | 앱 개발자 역할 (직접 구현 필요) |
감지 로직이 없을 때
- 파일 변경을 감지하는 로직이 앱에 없다면, 파일이 업데이트되어도 앱은 옛날 값을 계속 쓴다
- 이 경우엔 결국 Pod 재시작이 필요하다
4. 참조 가능한 키 목록
4.1 fieldRef — Pod 메타데이터
fieldRef.fieldPath 키
fieldRef.fieldPath로 접근 가능한 값들이다- 환경변수에서는 아래 키를 모두 사용할 수 있다
- Volume에서는
metadata.*키만 사용할 수 있고, IP·노드·서비스 계정 키는 환경변수 전용이다
| 키 | 설명 | 사용 가능 |
|---|---|---|
metadata.name | Pod 이름 | 환경변수, Volume |
metadata.namespace | Pod가 속한 네임스페이스 | 환경변수, Volume |
metadata.uid | Pod 고유 ID | 환경변수, Volume |
metadata.labels['key'] | 특정 레이블 값 | 환경변수, Volume |
metadata.annotations['key'] | 특정 어노테이션 값 | 환경변수, Volume |
status.podIP | Pod IP 주소 | 환경변수만 |
status.hostIP | Pod가 올라간 노드의 IP | 환경변수만 |
spec.nodeName | Pod가 올라간 노드 이름 | 환경변수만 |
spec.serviceAccountName | Pod가 사용하는 서비스 계정 | 환경변수만 |
fieldRef 키 분류: 노드 vs Pod
- 노드 관련 vs Pod 관련으로 구분하면 이해하기 쉽다
노드 정보 두 개, 나머지는 Pod 자신의 정보
4.2 resourceFieldRef — 컨테이너 리소스
resourceFieldRef.resource 키
resourceFieldRef.resource로 접근 가능한 값들이다- requests는 “최소 보장”, limits는 “최대 허용"이다
| 키 | 설명 |
|---|---|
requests.cpu | CPU 요청량 |
limits.cpu | CPU 한계 |
requests.memory | 메모리 요청량 |
limits.memory | 메모리 한계 |
requests.ephemeral-storage | 임시 저장공간 요청량 |
limits.ephemeral-storage | 임시 저장공간 한계 |
requests.hugepages-<size> | 대용량 메모리 페이지 요청량 |
limits.hugepages-<size> | 대용량 메모리 페이지 한계 |
5. Discussion
실제 활용 사례
리소스에 맞게 앱 자동 튜닝
limits.memory읽기 → JVM Heap 크기 자동 설정limits.cpu읽기 → 스레드풀 크기, GC 알고리즘 자동 선택- 코드에 하드코딩하지 않아도 환경에 맞게 자동으로 조정된다
로깅/메트릭에 Pod 정보 태깅
❌ 하드코딩 전:
[ERROR] 결제 실패
✅ Downward API 후:
[ERROR][Pod: payment-7f9d-xkp2q][Node: node-3] 결제 실패
효과: 에러 위치 바로 추적
- 수백 개 Pod 중 어디서 난 에러인지 바로 추적할 수 있다
- 이 로그는 앱이 stdout으로 출력한다
- kubectl이나 ELK/Loki 같은 로그 플랫폼에서 확인한다
Pod 자동 발견 및 클러스터 구성
- “같은 네임스페이스에서 label:
app=elasticsearch달린 Pod 누구야?” - 찾은 Pod들끼리 자동으로 클러스터 Join
Downward API의 한계와 그 다음
제공 키는 정해져 있음
- Downward API는 제공하는 키가 정해져 있다
- Table 14-1, 14-2에 있는 항목만 참조할 수 있다
| 구분 | 범위 |
|---|---|
| Downward API로 가능 | Pod 이름, IP, 레이블, 리소스 한계 등 (정해진 키) |
| Downward API로 불가능 | 다른 Pod 목록, 클러스터 전체 상태, 커스텀 리소스 정보 등 |
그 다음: API Server 직접 쿼리
- 더 많은 정보가 필요하다면 Kubernetes API Server에 직접 쿼리해야 한다
- 이를 위한 클라이언트 라이브러리가 다양한 언어로 제공된다
| 언어 | 라이브러리 |
|---|---|
| Python | kubernetes |
| Go | client-go (공식) |
| Java | io.kubernetes:client-java |
| Node.js | @kubernetes/client-node |
from kubernetes import client, config
config.load_incluster_config()
v1 = client.CoreV1Api()
# 같은 네임스페이스에서 특정 레이블 가진 Pod 찾기
pods = v1.list_namespaced_pod(
namespace="production",
label_selector="app=payment"
)
# → 찾은 Pod들끼리 클러스터 구성, 또는 모니터링 시작
전체 정리
정보 범위별 선택
- 간단한 Pod 자체 정보 → Downward API (환경변수 or Volume)
- 더 많은 클러스터 정보 → API Server 직접 쿼리 (클라이언트 라이브러리)
| 환경변수 | Volume | API Server | |
|---|---|---|---|
| 코드 변경 | 불필요 | 불필요 | 필요 |
| 실시간 반영 | ❌ | ✅ (감지 로직 필요) | ✅ |
| 참조 범위 | 정해진 키 | 정해진 키 | 클러스터 전체 |
| 복잡도 | 낮음 | 낮음 | 높음 |
Downward API는 “앱 코드 변경 없이, YAML 선언만으로 Pod가 자기 자신을 인식하게 하는” 우아한 메커니즘이다. 제공 범위가 한정적이지만, 대부분의 자기 인식 요구사항은 이것으로 충분히 해결된다.
