ConfigMap과 Secret — 설정과 민감 정보를 코드와 분리하는 전략
데이터베이스 비밀번호를 코드에 하드코딩하면 안 된다는 건 알겠는데, Kubernetes에서는 어떻게 분리해서 관리할까요?
설정값과 민감 정보를 코드와 분리하는 것은 12-Factor App의 핵심 원칙 중 하나입니다. Kubernetes는 이를 위해 ConfigMap과 Secret이라는 두 가지 오브젝트를 제공합니다. 비슷해 보이지만 용도와 보안 수준이 다르고, 변경 시 반영 방식도 다릅니다.
ConfigMap — 일반 설정 관리
ConfigMap은 키-값 쌍으로 설정 데이터를 저장합니다.
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
# 단순 키-값
APP_ENV: production
LOG_LEVEL: info
MAX_CONNECTIONS: "100"
# 파일 형태의 설정
application.yml: |
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://db-service:3306/mydb
# 명령어로 생성
kubectl create configmap app-config \
--from-literal=APP_ENV=production \
--from-file=application.yml
환경변수로 주입
apiVersion: v1
kind: Pod
metadata:
name: app
spec:
containers:
- name: app
image: my-app:1.0
env:
# 개별 키 주입
- name: APP_ENV
valueFrom:
configMapKeyRef:
name: app-config
key: APP_ENV
envFrom:
# 전체 키를 한꺼번에 환경변수로 주입
- configMapRef:
name: app-config
볼륨으로 마운트
spec:
containers:
- name: app
image: my-app:1.0
volumeMounts:
- name: config-volume
mountPath: /etc/config
readOnly: true
volumes:
- name: config-volume
configMap:
name: app-config
items: # 특정 키만 선택 가능
- key: application.yml
path: application.yml
마운트 후 /etc/config/application.yml 파일로 접근할 수 있습니다.
Secret — 민감 정보 관리
Secret은 비밀번호, API 키, 인증서 같은 민감한 데이터를 저장합니다.
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
type: Opaque
data:
# base64 인코딩된 값
username: YWRtaW4= # echo -n "admin" | base64
password: cEBzc3cwcmQ= # echo -n "p@ssw0rd" | base64
stringData를 사용하면 평문으로 작성할 수 있습니다.
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
type: Opaque
stringData:
username: admin
password: p@ssw0rd
Kubernetes가 자동으로 base64 인코딩하여 data 필드에 저장합니다. 주의할 점은 base64는 암호화가 아닙니다. 누구나 디코딩할 수 있으므로 etcd 암호화나 External Secrets 같은 추가 보안이 필요합니다.
Secret 타입
| 타입 | 용도 |
|---|---|
Opaque | 일반 비밀 데이터 (기본값) |
kubernetes.io/tls | TLS 인증서와 키 |
kubernetes.io/dockerconfigjson | 컨테이너 레지스트리 인증 |
kubernetes.io/basic-auth | 기본 인증 (username/password) |
kubernetes.io/ssh-auth | SSH 키 |
# TLS Secret 예제
apiVersion: v1
kind: Secret
metadata:
name: tls-secret
type: kubernetes.io/tls
data:
tls.crt: <base64 인코딩된 인증서>
tls.key: <base64 인코딩된 키>
# 명령어로 TLS Secret 생성
kubectl create secret tls tls-secret \
--cert=path/to/cert.pem \
--key=path/to/key.pem
Secret을 Pod에 주입
spec:
containers:
- name: app
image: my-app:1.0
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: password
volumeMounts:
- name: secret-volume
mountPath: /etc/secrets
readOnly: true
volumes:
- name: secret-volume
secret:
secretName: db-credentials
환경변수 vs 볼륨 마운트
두 방식에는 중요한 차이가 있습니다.
| 특성 | 환경변수 | 볼륨 마운트 |
|---|---|---|
| 업데이트 반영 | Pod 재시작 필요 | 자동 반영 (1분 내외) |
| 접근 방식 | process.env.KEY | 파일 읽기 |
| 보안 | kubectl exec env로 노출 가능 | 파일 권한으로 제어 가능 |
| 적합한 경우 | 단순 키-값 | 설정 파일, 인증서 |
공부하다 보니 이 차이를 놓쳐서 "설정을 바꿨는데 왜 반영이 안 되지?" 하는 경우가 꽤 있더라고요. 환경변수 방식은 Pod 재시작 전까지 이전 값이 유지됩니다.
immutable ConfigMap/Secret
apiVersion: v1
kind: ConfigMap
metadata:
name: static-config
immutable: true # 수정 불가
data:
VERSION: "1.0"
immutable: true를 설정하면 두 가지 이점이 있습니다.
- 실수 방지: 운영 중인 설정이 의도치 않게 변경되는 것을 막습니다
- 성능 최적화: kubelet이 watch를 멈추므로 API Server 부하가 줄어듭니다
수정이 필요하면 삭제 후 새로 생성해야 합니다.
변경 시 재시작 전략
ConfigMap이나 Secret을 수정했을 때 Pod에 반영하는 방법입니다.
방법 1: 어노테이션 변경으로 롤링 업데이트
# Deployment의 어노테이션을 변경하면 Pod이 재생성됨
kubectl patch deployment my-app -p \
'{"spec":{"template":{"metadata":{"annotations":{"config-hash":"20260319"}}}}}'
방법 2: ConfigMap 이름에 해시 포함 (Helm 패턴)
# values.yaml이 바뀌면 ConfigMap 이름도 바뀜
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config-abc123 # 내용의 해시값
---
apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
volumes:
- name: config
configMap:
name: app-config-abc123 # 이름이 바뀌므로 자동 업데이트
방법 3: Reloader 사용
Stakater Reloader를 설치하면 ConfigMap/Secret 변경을 자동으로 감지하여 관련 Deployment를 롤링 업데이트합니다.
metadata:
annotations:
reloader.stakater.com/auto: "true" # 자동 재시작 활성화
실무 팁
- Secret은 환경변수보다 볼륨 마운트를 권장합니다: 환경변수는 로그에 노출될 위험이 있습니다
- ConfigMap의 크기 제한은 1MB입니다: 큰 설정은 분할하세요
- 네임스페이스 간 공유 불가: 같은 네임스페이스의 Pod만 사용할 수 있습니다
- optional 플래그: 참조하는 ConfigMap/Secret이 없어도 Pod이 시작되게 할 수 있습니다
env:
- name: OPTIONAL_CONFIG
valueFrom:
configMapKeyRef:
name: maybe-exists
key: some-key
optional: true # 없어도 Pod 시작 가능
정리
ConfigMap은 일반 설정, Secret은 민감 정보를 코드와 분리하는 Kubernetes 오브젝트입니다. 환경변수와 볼륨 마운트 두 가지 주입 방식의 차이(특히 자동 업데이트 여부)를 이해하고, immutable 옵션과 재시작 전략을 적절히 활용하면 설정 관리가 훨씬 안정적이 됩니다. Secret의 base64 인코딩은 암호화가 아니라는 점은 꼭 기억해 두세요.