7.1 ConfigMap
컨테이너 이미지는 빌드 시점에 내용이 고정된다. 반면 설정은 환경마다 다르다. 개발 환경과 프로덕션은 데이터베이스 주소가 다르고, 같은 프로덕션 안에서도 로그 레벨은 수시로 바뀐다. 설정을 이미지 안에 두면 값 하나를 바꾸기 위해 이미지를 다시 빌드하고 배포해야 한다. ConfigMap은 이 결합을 끊는 오브젝트다. 기밀이 아닌 설정 데이터를 key-value 쌍으로 담아 두면, Pod가 실행 시점에 env 변수나 파일로 받아서 쓴다. 같은 이미지 하나를 환경마다 다른 ConfigMap과 조합하는 구조가 된다.
생성 방법
kubectl create configmap은 세 가지 소스를 받는다. 리터럴 값, 파일 하나, 디렉토리다.
# 리터럴: key=value를 직접 지정
kubectl create configmap app-config \
--from-literal=LOG_LEVEL=info \
--from-literal=DB_HOST=db.example.internal
# 파일: 파일명이 key, 파일 내용이 value
kubectl create configmap nginx-conf --from-file=nginx.conf
# 디렉토리: 안에 있는 파일마다 key가 하나씩 생긴다
kubectl create configmap app-profiles --from-file=./conf.d/매니페스트로 선언할 때는 data 필드에 적는다. ConfigMap에는 spec이 없고 data가 바로 최상위 필드다. 값은 UTF-8 문자열이고, 바이너리는 binaryData 필드에 base64로 담는다.
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
LOG_LEVEL: "info"
DB_HOST: "db.example.internal"
app.properties: |
cache.ttl=300
feature.flags=search,exportLOG_LEVEL처럼 짧은 값과 app.properties처럼 파일 통째의 내용이 한 오브젝트에 공존한다. 어느 형태로 쓸지는 주입 방식이 결정한다.
env 주입과 volume 주입
env 주입은 두 형태다. configMapKeyRef로 key 하나를 골라 env 변수 하나에 연결하거나, envFrom으로 ConfigMap의 모든 key를 한 번에 가져온다. envFrom을 쓰면 key 이름이 그대로 env 변수명이 되므로, env 변수명으로 유효하지 않은 key는 건너뛴다.
volume 주입은 ConfigMap을 volume으로 선언하고 컨테이너에 마운트한다. 각 key가 마운트 디렉토리 아래 파일 하나가 되고, value가 파일 내용이 된다. volume 자체의 동작은 Part VI. 스토리지에서 다룬 것과 같다.
apiVersion: v1
kind: Pod
metadata:
name: configmap-demo
spec:
containers:
- name: app
image: busybox:1.36
command: ["sh", "-c", "env; ls /etc/app; sleep 3600"]
env:
- name: LOG_LEVEL
valueFrom:
configMapKeyRef:
name: app-config
key: LOG_LEVEL
volumeMounts:
- name: config
mountPath: /etc/app
readOnly: true
volumes:
- name: config
configMap:
name: app-config이 Pod에서 LOG_LEVEL은 env 변수로 들어오고, app.properties는 /etc/app/app.properties 파일로 나타난다. envFrom을 쓰면 env: 블록 전체가 다음 세 줄로 줄어든다.
envFrom:
- configMapRef:
name: app-config갱신 전파의 비대칭
ConfigMap을 수정했을 때 실행 중인 Pod에 반영되는지는 주입 방식에 따라 갈린다.
volume으로 마운트한 파일은 자동으로 갱신된다. kubelet이 주기적 동기화 때마다 마운트된 ConfigMap이 최신인지 확인하고 파일을 교체한다. 다만 즉시는 아니다. 공식 문서 기준으로 kubelet 동기화 주기에 캐시 전파 지연을 더한 만큼 늦을 수 있다. 그리고 파일이 바뀌어도 애플리케이션이 파일을 다시 읽지 않으면 의미가 없다. 시작할 때 한 번만 설정을 읽는 프로세스라면 결국 재시작이 필요하다.
env 변수는 갱신되지 않는다. Pod가 시작될 때 값이 프로세스 환경으로 고정되고, 이후 ConfigMap이 어떻게 바뀌든 재시작 전까지 이전 값을 유지한다. subPath로 마운트한 파일도 갱신 대상에서 제외된다. 공식 문서가 명시하는 예외다.
flowchart TD
U["ConfigMap 수정"] --> V["volume 마운트"]
U --> E["env 주입"]
U --> S["subPath 마운트"]
V --> V2["kubelet 주기로<br/>자동 갱신"]
E --> E2["Pod 재시작<br/>전까지 이전 값"]
S --> S2["갱신 안 됨"]
immutable ConfigMap과 1MiB 제한
immutable: true를 지정하면 그 ConfigMap의 data는 더 이상 수정되지 않는다. Kubernetes 1.21에서 GA가 된 기능이다.
효용은 두 가지다. 첫째, 실수나 의도치 않은 갱신으로 애플리케이션이 갑자기 다른 설정을 읽는 사고를 차단한다. 둘째, kube-apiserver 부하를 줄인다. ConfigMap을 volume으로 쓰는 Pod가 있으면 kubelet이 변경 감시용 watch를 유지하는데, immutable로 표시된 ConfigMap에 대해서는 kube-apiserver가 이 watch를 닫는다. ConfigMap을 참조하는 Pod가 수만 개인 대규모 클러스터에서는 이 감시 트래픽 감소가 상당한 성능 이득이 된다.
대신 수정하려면 삭제하고 다시 만드는 길뿐이다. 그래서 immutable을 쓰는 운영은 보통 내용 해시를 이름에 붙여(app-config-7f9c4b) 새 ConfigMap을 만들고, Deployment가 새 이름을 참조하도록 바꾼다. 이 변경이 rolling update를 일으키므로 env 주입의 갱신 문제도 함께 해소된다.
크기는 1MiB를 넘지 못한다. ConfigMap은 etcd에 저장되는 오브젝트라서 큰 데이터를 담는 용도가 아니다. 이 제한에 다다랐다면 volume 마운트로 옮길 파일인지, 아니면 별도 데이터베이스나 파일 서비스로 분리할 데이터인지 다시 판단한다.
정리
- ConfigMap은 기밀이 아닌 설정을 이미지에서 분리해 key-value로 담고, Pod는 env 변수나 volume 마운트로 받는다.
- 갱신 전파는 비대칭이다. volume 마운트는 kubelet 주기로 자동 갱신되지만 env와
subPath는 Pod를 재시작해야 한다. immutable: true는 실수 변경을 차단하고 대규모 클러스터의 watch 부하를 줄인다. 수정 대신 새 이름으로 교체한다.