Chapter 8

애플리케이션에서 파드 메타데이터와 그 외의 리소스 접근

  • 8.1 Downward API로 Pod 메타데이터 넘기기
  • 8.2 API 서버와 직접 대화하기
  • 8.3 Ambassador 컨테이너 패턴
  • 8.4 클라이언트 라이브러리

8장은 한 마디로 "Pod 안의 앱이 자기 자신이나 클러스터 정보를 어떻게 알아내나"에 대한 장이야. 두 가지 수단이 있어. 첫째는 Downward API — Pod 자신의 메타데이터를 주입받는 가벼운 방식. 둘째는 API 서버와 직접 통신 — 다른 Pod, 다른 리소스 정보까지 끌어오고 변경까지 할 수 있는 강력한 방식. 후자는 인증서와 토큰을 다뤄야 해서 약간 복잡한데, Ambassador 패턴으로 그 복잡도를 옆 컨테이너로 떠넘길 수 있어.

먼저 Downward API. ConfigMap은 내가 미리 알고 있는 값만 주입할 수 있어. 근데 Pod 이름, Pod IP, 스케줄된 노드 이름 같은 건 Pod가 만들어지기 전엔 모르거든. 그리고 라벨이나 어노테이션처럼 이미 Pod 스펙 어딘가에 쓰여있는 값을 컨테이너 안에서도 쓰고 싶을 때, 같은 걸 두 번 쓰는 건 DRY 위반이야. Downward API가 이걸 해결해. 이름이 좀 오해 소지가 있는데, REST 엔드포인트가 아니라 Pod 스펙의 값을 컨테이너 안 환경변수나 파일로 내려주는 메커니즘이야. 노출 가능한 메타데이터는 Pod 이름·네임스페이스·IP, 노드 이름, ServiceAccount 이름, 컨테이너의 CPU/메모리 requests·limits, Pod 라벨·어노테이션 같은 것들이야.

환경변수로 주입할 땐 env 항목에 valueFrom.fieldRefmetadata.name이나 status.podIP 같은 경로를 적어. 컨테이너 리소스 값은 valueFrom.resourceFieldRefrequests.cpu 같은 걸 가져오는데, 이 때 divisor로 단위를 지정해야 해. CPU는 1(core) 또는 1m(milli), 메모리는 1, 1k, 1Ki, 1M, 1Mi 같은 식. 환경변수로는 한계가 있는데, 라벨과 어노테이션을 못 줘. 이유가 재밌어 — 라벨·어노테이션은 Pod가 돌고 있는 중에도 바뀔 수 있거든. 환경변수는 한 번 꽂히면 못 바꾸니까 맞지 않아. 대신 downwardAPI 볼륨을 쓰면 파일로 노출되고, 라벨·어노테이션이 변경되면 파일 내용도 업데이트돼. 컨테이너의 리소스 필드를 볼륨으로 노출할 땐 resourceFieldRefcontainerName을 명시해야 해. 볼륨은 Pod 수준에 정의되니까 어느 컨테이너의 필드인지 지정해줘야 하거든. 언제 쓰냐면, "기존 앱이 특정 환경변수를 기대하는데 그 값이 Pod 메타데이터에 있을 때". 앱을 수정하거나 래퍼 스크립트를 쓰지 않고 메타데이터를 노출할 수 있어. 다만 노출되는 정보가 제한적이야. Pod 자체에 없는 정보(다른 Pod, Service 목록 같은 거)는 API 서버에 직접 물어봐야 해.

API 서버와 대화하기. API 서버는 HTTPS에 인증까지 필요해서 curl -k https://...만으로는 안 돼. 로컬 머신에서 API를 둘러보고 싶을 때는 **kubectl proxy**가 제일 쉬운 방법이야. 이 명령은 로컬에 프록시를 띄워서 인증·암호화·서버 검증을 알아서 처리해줘. 그 다음은 curl localhost:8001로 REST API를 자유롭게 탐색하면 돼. API 구조는, 루트 /를 찍으면 paths 목록이 나와. 크게 /api/v1(core API group — Pod, Service, ConfigMap 등 초창기부터 있던 리소스)과 /apis/<group>/<version>(그 외 API 그룹들 — apps, batch, extensions 같은 거)로 나뉘어. 각 그룹 URL에선 API 버전, 리소스 타입 목록, 지원 동사(get/list/create/update/patch/delete/watch)를 확인할 수 있고, 특정 리소스 조회는 namespaces/<ns>/<resource>/<name> 패턴이야.

Pod 내부에서 API 서버에 접근하려면 세 가지를 챙겨야 해. API 서버 주소 찾기, API 서버 검증(man-in-the-middle 방지), 인증. 이 셋을 모두 default-token Secret이 해결해줘. 7장에서 봤던 그 Secret 말이야 — /var/run/secrets/kubernetes.io/serviceaccount/에 마운트되어 있는 세 파일. **ca.crt**는 API 서버 인증서를 검증할 CA 인증서, **token**은 API 서버 인증에 쓸 Bearer 토큰, **namespace**는 이 Pod가 속한 네임스페이스. 그리고 API 서버 주소는 클러스터가 기본으로 띄우는 kubernetes 서비스(kubernetes.default)로 찾으면 돼. 환경변수로도 KUBERNETES_SERVICE_HOST, KUBERNETES_SERVICE_PORT로 들어와 있고. curl로 흉내 내려면 CURL_CA_BUNDLEca.crt 경로를 넣고 Authorization: Bearer <token> 헤더를 붙여서 https://kubernetes/api/v1/namespaces/$NS/pods 같은 URL을 때리면 돼. 이걸 실제 앱에서 하려면 언어별 HTTP 클라이언트로 구현하면 되고. 다만 TLS 검증을 건너뛰지 마 — 놓치면 토큰이 중간자에게 노출돼. 저자가 "본인도 몇 번 그랬다"고 고백할 정도로 흔한 실수야. 그리고 RBAC이 켜진 클러스터에선 기본 ServiceAccount에 권한이 거의 없어. 그래서 위 요청도 Forbidden이 뜰 수 있고, 실전에선 Pod가 쓸 ServiceAccount에 필요한 Role/RoleBinding을 붙여줘야 해 (12장).

TLS, 토큰, CA 검증을 매 앱마다 구현하는 건 귀찮고 실수하기 쉬워. Ambassador 패턴은 이 복잡도를 같은 Pod 안의 사이드카 컨테이너로 떠넘기는 방법이야. 어떻게 하냐면, Pod에 kubectl proxy 이미지를 사이드카로 하나 띄우는 거야. 그러면 메인 컨테이너는 평문 HTTP로 localhost:8001에 요청을 보내고(같은 Pod니까 loopback 공유), Ambassador(kubectl-proxy) 컨테이너가 그 요청을 받아서 API 서버와 HTTPS로 통신하면서 인증과 검증을 처리해. 메인 앱은 TLS, 토큰, CA 같은 걸 하나도 몰라도 돼. 그냥 http://localhost:8001/api/v1/pods라고 때리면 끝. 언어·라이브러리에 관계없이 재사용 가능한 패턴이야. 단점은 Pod마다 프로세스가 하나 더 뜨니까 약간의 리소스 오버헤드가 있다는 것. 이 패턴은 API 서버 접근뿐 아니라 외부 시스템과의 통신을 단순화하는 일반 기법이야. 메인 컨테이너가 복잡한 프로토콜·인증을 모르고 사이드카가 대신 처리하는 구조.

제대로 된 앱을 쓸 거면 직접 HTTP를 때리기보다 공식 클라이언트 라이브러리를 쓰는 게 나아. SIG API Machinery가 공식 지원하는 건 Go(client-go)와 Python(client-python)이야. 커뮤니티 라이브러리는 Java(Fabric8), Node.js, Ruby, PHP 등 많고. Fabric8 Java 클라이언트는 fluent DSL을 제공해서 읽기 좋아 — client.pods().inNamespace("default").list() 같은 식. 언어에 맞는 라이브러리가 없으면 Swagger/OpenAPI 스펙으로 직접 생성할 수도 있어. API 서버는 /swaggerapi, /swagger.json으로 스펙을 노출하거든. REST API를 눈으로 탐색할 땐 Swagger UI가 편해. API 서버 옵션에 --enable-swagger-ui=true를 주면 /swagger-ui에서 열려. curl로 path 뒤지는 것보다 훨씬 빨라.


정리

8장 읽고 기억할 거 세 가지:

  1. Downward API는 Pod 자신의 스펙·상태를 컨테이너에 주입하는 경량 수단이다. Pod 이름·IP·노드명처럼 생성 시점에야 확정되는 값과, 라벨·어노테이션처럼 Pod 전체에 이미 쓰여있는 값을 DRY하게 노출할 수 있다. 라벨·어노테이션은 환경변수로 못 주고 downwardAPI 볼륨으로만 노출 가능하다 — 바뀔 수 있기 때문이다.
  2. **Pod 안에서 API 서버에 접근하는 데 필요한 세 가지(주소·검증·인증)**는 모두 default-token Secret에 들어있다. /var/run/secrets/kubernetes.io/serviceaccount/ca.crt, token, namespace 세 파일. TLS 검증을 생략하는 지름길은 가지 말 것 — 토큰 유출의 지름길이다.
  3. Ambassador 패턴으로 복잡도를 사이드카에 떠넘길 수 있다. 메인 컨테이너는 평문 HTTP로 localhost의 kubectl-proxy에 요청하고 ambassador가 HTTPS·인증·검증을 대신 처리. 앱 코드가 쿠버네티스 특화 로직에서 자유로워진다. 제대로 된 앱이면 클라이언트 라이브러리(Go client-go, Python client-python) 사용이 정석.