ConfigMap 은 단순한 키-값 저장소다. 값 안에 ${HOST} 같은 문자열을 써 두어도 Kubernetes 가 알아서 바꿔 주지 않는다. ConfigMap 을 읽는 쪽이 어떤 형태로 쓰느냐에 따라 치환 방법이 달라진다.
$(VAR) 로 조합하기env[].value · command · args 안에서는 kubelet 이 $(VAR) 형태를 앞서 정의된 환경 변수 값으로 치환한다. 컨테이너 안의 셸이 하는 일이 아니므로 셸이 없는 이미지에서도 동작한다. 문법도 ${VAR} 가 아니라 $(VAR) 이다.
apiVersion: v1
kind: ConfigMap
metadata:
name: my-config
data:
HOST: "myapp"
PORT: "8080"
spec:
containers:
- name: app
image: busybox
env:
- name: HOST
valueFrom:
configMapKeyRef: { name: my-config, key: HOST }
- name: PORT
valueFrom:
configMapKeyRef: { name: my-config, key: PORT }
- name: BASE_URL
value: "http://$(HOST):$(PORT)"
command: ["sh", "-c", "echo $BASE_URL && sleep 3600"]
지켜야 할 규칙이 둘 있다. 참조되는 변수(HOST · PORT)가 참조하는 변수보다 목록에서 앞에 있어야 한다. 그리고 정의되지 않은 이름을 $(FOO) 로 쓰면 치환되지 않고 문자 그대로 남는다. 리터럴 $( 를 넣어야 하면 $$( 로 escape 한다.
kubectl exec -it my-app-0 -- env | grep BASE_URL
ConfigMap 을 파일로 마운트하면 내용은 손대지 않고 그대로 들어간다. 파일 안의 플레이스홀더를 채우려면 방법을 골라야 한다.
초기화 컨테이너에서 치환하는 방법이 가장 단순하다. 템플릿을 담은 ConfigMap 을 읽어 envsubst 로 채운 뒤 emptyDir 에 쓰고, 본 컨테이너는 그 emptyDir 를 마운트한다.
initContainers:
- name: render-config
image: bhgedigital/envsubst
command: ["sh", "-c", "envsubst < /tpl/app.conf > /out/app.conf"]
env:
- name: HOST
valueFrom: { configMapKeyRef: { name: my-config, key: HOST } }
volumeMounts:
- { name: tpl, mountPath: /tpl }
- { name: out, mountPath: /out }
배포 시점에 치환하는 방법도 있다. Helm 의 {{ .Values.host }} 나 Kustomize 의 configMapGenerator+replacements 로 매니페스트를 만들 때 값을 박아 넣는다. 클러스터에는 이미 치환된 ConfigMap 이 들어가므로 런타임 의존이 없다.
helm template myrelease ./chart --set host=myapp | kubectl apply -f -
subPath 로 ConfigMap 의 한 키만 파일로 마운트하면 그 파일은 ConfigMap 을 갱신해도 자동으로 바뀌지 않는다. 디렉터리째 마운트해야 kubelet 이 갱신분을 반영한다. 반영 주기도 즉시가 아니라 kubelet 의 동기화 주기(기본 1분 남짓)에 달렸고, 애플리케이션이 파일을 다시 읽지 않으면 결국 재시작이 필요하다.
환경 변수로 주입한 값은 파드 생성 시점에 정해지므로 ConfigMap 을 고쳐도 절대 바뀌지 않는다. 반드시 파드를 다시 만든다.
secretKeyRef 로 민감한 값을 넣을 때.command · args 작성 규칙.