학습 목표
ConfigMap과 Secret으로 설정·자격증명을 이미지·manifest와 분리할 수 있다.
환경변수·볼륨 마운트·envFrom 주입 방식 차이를 설명할 수 있다.
Secret의 저장·전송·RBAC·rotation 개요를 설명할 수 있다.
12-factor config 원칙과 K8s 설정 리소스 매핑을 설명할 수 있다.
문제 상황
- DB URL·API 키를 Dockerfile ENV에 박아두었다
- 이미지 재빌드 없이 dev/stage/prod 전환 불가
DeploymentYAML에 비밀번호가 평문으로 있다- Git에 올리면 유출, 감사·로테이션도 어렵다
- 설정 JSON 하나 바꿨는데 Pod 50개 이미지를 다시 빌드했다
- 설정 변경 = 재배포만 하면 되는데 빌드 파이프라인을 탔다
- ConfigMap을 바꿨는데 실행 중 Pod에 반영이 안 된다
- 주입 방식·재시작 필요 여부를 모른다
- Secret을
kubectl get -o yaml했더니 base64 — 암호화가 아니다- etcd encryption at rest·RBAC·외부 vault 연동이 별도다
앞서 Pod·Deployment·Service를 봤다. ConfigMap·Secret은 워크로드에 설정을 붙이는 K8s 네이티브 방법이다.
1. 설정 분리 — 12-factor config
III. Config — 설정은 환경마다 다르고, 코드·이미지와 분리한다.

| 단계 | 설정 위치 | K8s |
|---|---|---|
| Build | 없음 (이미지에 env별 값 금지) | Dockerfile — ARG만, prod URL 금지 |
| Release | release마다 config 묶음 | ConfigMap·Secret apply |
| Run | 프로세스 환경으로 주입 | Pod env·volumeMount |
- immutable image — 같은 이미지를 dev·prod에, 다른 ConfigMap으로
- GitOps — manifest + ConfigMap/Secret을 repo·환경별 overlay로 관리
- Helm values·Kustomize — 환경별 config 패치 (14편 GitOps에서 심화)
2. ConfigMap
ConfigMap — 비민감 설정(key-value 또는 파일)을 K8s API 객체로 저장.

apiVersion: v1
kind: ConfigMap
metadata:
name: api-config
data:
LOG_LEVEL: info
APP_ENV: production
app.properties: |
server.port=8080
cache.ttl=300
| 주입 방식 | 동작 | config 변경 시 |
|---|---|---|
| env | 키 하나 → 환경변수 | Pod 재시작 필요 |
| envFrom | ConfigMap 전체 → env | Pod 재시작 필요 |
| volume | 파일로 마운트 | kubelet 주기적 sync (앱이 reload해야 반영) |
| command args | $() 치환 (구버전) |
거의 env/volume 사용 |
# env + envFrom
env:
- name: LOG_LEVEL
valueFrom:
configMapKeyRef:
name: api-config
key: LOG_LEVEL
envFrom:
- configMapRef:
name: api-config
optional: false
# volume — file as config
volumes:
- name: config
configMap:
name: api-config
items:
- key: app.properties
path: app.properties
volumeMounts:
- name: config
mountPath: /etc/config
readOnly: true
- subPath — 단일 파일만 마운트 (디렉터리 덮어쓰기 방지)
- defaultMode — volume 마운트 파일 권한 (Secret은 0400 등)
- immutable ConfigMap — 생성 후 수정 불가, 실수·드리프트 방지
3. Secret
Secret — 민감 데이터(password, token, TLS cert). API상 base64 인코딩일 뿐 암호화 아님.

| ConfigMap | Secret | |
|---|---|---|
| 용도 | 로그 레벨, feature flag, URL(공개) | DB password, API key, TLS key |
| 타입 | Opaque(기본) |
Opaque, kubernetes.io/tls, docker-registry |
| 저장 | etcd (평문) | etcd — encryption at rest 권장 |
| 표시 | kubectl get 값 노출 |
data 필드 base64, describe 제한 |
| RBAC | configmaps |
secrets — 더 좁게 |
apiVersion: v1
kind: Secret
metadata:
name: api-db
type: Opaque
stringData: # apply 시 base64 자동 (권장)
DB_USER: app
DB_PASSWORD: s3cr3t
# Pod — Secret as env
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: api-db
key: DB_PASSWORD
# TLS Secret for Ingress (앞서 shop-tls)
apiVersion: v1
kind: Secret
metadata:
name: shop-tls
type: kubernetes.io/tls
data:
tls.crt: <base64>
tls.key: <base64>
stringData— YAML에 평문 작성, API 저장 시 인코딩- Git에 Secret 평문 금지 — Sealed Secrets, External Secrets, SOPS, vault (Security 13편)
- ServiceAccount token Secret — legacy; bound token·projected volume 권장
4. Pod에 함께 쓰기
실무 API Pod는 ConfigMap(설정) + Secret(자격증명) 조합.
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
spec:
replicas: 2
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
containers:
- name: app
image: my/api:1.2.0
ports:
- containerPort: 8080
envFrom:
- configMapRef:
name: api-config
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: api-db
key: DB_PASSWORD
volumeMounts:
- name: config
mountPath: /etc/config
readOnly: true
volumes:
- name: config
configMap:
name: api-config
| 규칙 | 이유 |
|---|---|
| Secret은 env 또는 readOnly volume | 파일 권한·최소 노출 |
| ConfigMap 크기 1MiB 제한 | 큰 설정은 별도 스토리지·init container |
| optional: true | config 없어도 기동 (로컬·테스트) |
| probe·Service는 변경 없음 | 설정만 바뀌고 포트·label 동일 |
전체 manifest는 code/configmap-secret.yaml 참고.
5. 변경 반영과 reload
| 방식 | ConfigMap 수정 후 |
|---|---|
| env / envFrom | 값은 Pod 생성 시 고정 → rollout restart |
| volume | kubelet이 파일 업데이트 (수 분) → 앱 SIGHUP/reload 필요 |
| immutable | 새 ConfigMap 이름 + Deployment patch |
kubectl apply -f code/configmap-secret.yaml
kubectl rollout restart deployment/api
kubectl exec deploy/api -- cat /etc/config/app.properties
# Stakater Reloader 등 — ConfigMap/Secret 변경 시 자동 rollout
# annotation: configmap.reloader.stakater.com/reload: "api-config"
- downward API — Pod metadata·resource limit을 env로 (별도 주제)
- projected volume — ConfigMap + Secret + SA token 한 volume에
6. Secret rotation 개요
| 단계 | 내용 |
|---|---|
| 듀얼 credential | DB user 두 개, 번갈아 rotate |
| 새 Secret 버전 | api-db-v2 apply → Deployment env ref 변경 → rollout |
| External Secrets Operator | Vault/AWS SM → K8s Secret 동기화 |
| cert-manager | TLS Certificate → Secret 자동 갱신 (앞서 Ingress) |
- rotation 중 구·신 credential 동시 유효 기간 확보
- zero-downtime — readiness 통과 후 old credential 폐기
- 플랫폼 팀은 주입 경로 표준화, 앱 팀은 reload 계약
7. 보안·운영 체크리스트
| 항목 | 권장 |
|---|---|
| Git | Secret 평문 커밋 금지, Sealed/External Secrets |
| RBAC | Secret get/list 최소 권한, namespace 분리 |
| etcd | EncryptionConfiguration at rest |
| audit | Secret 접근 audit log |
| ConfigMap | 민감 정보 넣지 않기 — Secret으로 |
| 네임스페이스 | dev/prod ConfigMap 이름·overlay 분리 |
kubectl get configmap,secret
kubectl describe configmap api-config
kubectl get secret api-db -o jsonpath='{.data}' # still encoded — not for logs
8. 정리
- ConfigMap — 비민감 설정, Secret — 자격증명·TLS
- 12-factor — 설정은 release/run에, 이미지와 분리
- env vs volume — 재시작 vs 파일 reload 트레이드오프
- Secret base64 ≠ 암호화 — RBAC·etcd encryption·외부 vault
- rollout restart·Reloader로 config 변경 반영
다음에 다룰 것
- PV·PVC·StorageClass
- StatefulSet과 stable identity
해당 내용은 Kubernetes Documentation (kubernetes.io) 의 내용을 기반으로 합니다.
'플랫폼, 데브옵스와 클라우드' 카테고리의 다른 글
| 스케줄링과 리소스 (0) | 2026.06.27 |
|---|---|
| 스토리지와 StatefulSet (0) | 2026.06.26 |
| Service와 Ingress (0) | 2026.06.24 |
| Pod와 Deployment (0) | 2026.06.23 |
| Kubernetes 개요 (0) | 2026.06.22 |