ConfigMap과 Secret이란
Kubernetes에서 애플리케이션 설정과 민감 정보를 컨테이너 이미지와 분리하는 두 가지 핵심 리소스다. ConfigMap은 환경 변수, 설정 파일, 커맨드라인 인자 등 비밀이 아닌 설정 데이터를 저장하고, Secret은 비밀번호, API 키, TLS 인증서 등 민감 데이터를 Base64 인코딩하여 저장한다.
이 두 리소스를 올바르게 설계하면 하나의 컨테이너 이미지로 dev/staging/prod 환경을 모두 운영할 수 있으며, 설정 변경 시 이미지 재빌드 없이 배포가 가능하다.
ConfigMap 생성 방법
# 1. YAML 선언형
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: production
data:
# 단순 키-값
DATABASE_HOST: "postgres-primary.db.svc.cluster.local"
DATABASE_PORT: "5432"
DATABASE_NAME: "myapp"
LOG_LEVEL: "info"
CACHE_TTL: "300"
# 설정 파일 통째로 저장
nginx.conf: |
server {
listen 80;
server_name _;
location / {
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
application.yml: |
spring:
datasource:
url: jdbc:postgresql://${DATABASE_HOST}:${DATABASE_PORT}/${DATABASE_NAME}
cache:
type: caffeine
# 2. kubectl 명령형
# 리터럴 값
kubectl create configmap app-config
--from-literal=DATABASE_HOST=postgres.db.svc
--from-literal=LOG_LEVEL=info
-n production
# 파일에서 생성
kubectl create configmap nginx-config
--from-file=nginx.conf=./config/nginx.conf
-n production
# 디렉토리에서 생성 (파일명 = 키, 내용 = 값)
kubectl create configmap app-configs
--from-file=./config/
-n production
# .env 파일에서 생성
kubectl create configmap env-config
--from-env-file=.env.production
-n production
Secret 생성과 타입
| 타입 | 용도 | 필수 키 |
|---|---|---|
| Opaque | 범용 (기본) | 없음 |
| kubernetes.io/tls | TLS 인증서 | tls.crt, tls.key |
| kubernetes.io/dockerconfigjson | Docker Registry 인증 | .dockerconfigjson |
| kubernetes.io/basic-auth | Basic 인증 | username, password |
| kubernetes.io/ssh-auth | SSH 키 | ssh-privatekey |
# Opaque Secret (Base64 인코딩)
apiVersion: v1
kind: Secret
metadata:
name: app-secrets
namespace: production
type: Opaque
data:
DATABASE_PASSWORD: cGFzc3dvcmQxMjM= # echo -n 'password123' | base64
JWT_SECRET: c3VwZXItc2VjcmV0LWtleQ==
REDIS_PASSWORD: cmVkaXNwYXNz
# stringData는 Base64 인코딩 불필요 (자동 변환)
---
apiVersion: v1
kind: Secret
metadata:
name: app-secrets-plain
type: Opaque
stringData:
DATABASE_PASSWORD: "password123"
JWT_SECRET: "super-secret-key"
# kubectl로 Secret 생성
kubectl create secret generic app-secrets
--from-literal=DATABASE_PASSWORD=password123
--from-literal=JWT_SECRET=super-secret-key
-n production
# TLS Secret
kubectl create secret tls api-tls
--cert=./certs/tls.crt
--key=./certs/tls.key
-n production
# Docker Registry Secret
kubectl create secret docker-registry ghcr-pull
--docker-server=ghcr.io
--docker-username=myuser
--docker-password=ghp_xxxxx
-n production
Pod에 주입하기: 환경 변수
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-server
spec:
template:
spec:
containers:
- name: api
image: myapp:latest
env:
# 개별 키 참조
- name: DB_HOST
valueFrom:
configMapKeyRef:
name: app-config
key: DATABASE_HOST
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: app-secrets
key: DATABASE_PASSWORD
# 전체 키를 한 번에 주입
envFrom:
- configMapRef:
name: app-config
- secretRef:
name: app-secrets
optional: true # Secret 없어도 Pod 시작 가능
Pod에 주입하기: 볼륨 마운트
설정 파일을 볼륨으로 마운트하면 파일 형태로 컨테이너에 제공된다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-proxy
spec:
template:
spec:
containers:
- name: nginx
image: nginx:alpine
volumeMounts:
# ConfigMap → 파일로 마운트
- name: nginx-config
mountPath: /etc/nginx/conf.d/default.conf
subPath: nginx.conf # 디렉토리가 아닌 단일 파일만 마운트
readOnly: true
# Secret → 파일로 마운트 (퍼미션 자동 설정)
- name: tls-certs
mountPath: /etc/nginx/ssl
readOnly: true
# 특정 키만 선택적 마운트
- name: app-config
mountPath: /app/config
readOnly: true
volumes:
- name: nginx-config
configMap:
name: nginx-config
- name: tls-certs
secret:
secretName: api-tls
defaultMode: 0400 # 소유자 읽기 전용
- name: app-config
configMap:
name: app-config
items: # 특정 키만 선택
- key: application.yml
path: application.yml
- key: LOG_LEVEL
path: log-level.txt
설정 변경 시 자동 롤링 업데이트
ConfigMap/Secret을 변경해도 기존 Pod는 자동으로 재시작되지 않는다. 두 가지 방법으로 해결한다.
# 방법 1: checksum 어노테이션 (Helm에서 많이 사용)
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-server
spec:
template:
metadata:
annotations:
# ConfigMap/Secret 내용이 바뀌면 해시가 바뀌어 롤링 업데이트 트리거
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
checksum/secret: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }}
# 방법 2: kubectl rollout restart
kubectl rollout restart deployment/api-server -n production
# 방법 3: Reloader (오픈소스 컨트롤러)
# ConfigMap/Secret 변경 감지 → 자동 롤링 업데이트
# https://github.com/stakater/Reloader
metadata:
annotations:
reloader.stakater.com/auto: "true"
Immutable ConfigMap/Secret
Kubernetes 1.21+에서 지원하는 Immutable 리소스는 변경을 금지하여 실수로 인한 장애를 방지한다.
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config-v3
immutable: true # 한번 생성 후 수정 불가
data:
DATABASE_HOST: "postgres.db.svc"
LOG_LEVEL: "info"
# 변경이 필요하면: 새 이름(v4)으로 생성 → Deployment 참조 변경
# 장점:
# - API 서버 부하 감소 (watch 불필요)
# - 실수로 인한 설정 변경 방지
# - 변경 이력이 리소스 이름에 남음
Secret 보안 강화
# 1. RBAC로 Secret 접근 제한
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: secret-reader
namespace: production
rules:
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["app-secrets"] # 특정 Secret만 허용
verbs: ["get"]
# 2. etcd 암호화 (kube-apiserver 설정)
# EncryptionConfiguration으로 etcd에 저장되는 Secret을 AES로 암호화
# 3. External Secrets Operator — 외부 비밀 저장소 연동
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: app-secrets
namespace: production
spec:
refreshInterval: 1h
secretStoreRef:
name: aws-secrets-manager
kind: ClusterSecretStore
target:
name: app-secrets
creationPolicy: Owner
data:
- secretKey: DATABASE_PASSWORD
remoteRef:
key: production/myapp
property: db_password
- secretKey: JWT_SECRET
remoteRef:
key: production/myapp
property: jwt_secret
K8s RBAC 권한 설계 가이드에서 다룬 최소 권한 원칙을 Secret 접근에도 적용해야 한다. resourceNames로 필요한 Secret만 허용하고, 불필요한 ServiceAccount의 Secret 읽기 권한을 차단한다.
Sealed Secrets: GitOps 친화적 암호화
# Sealed Secrets: Secret을 암호화하여 Git에 안전하게 커밋
# 1. kubeseal CLI로 암호화
kubectl create secret generic app-secrets
--from-literal=DB_PASSWORD=mypassword
--dry-run=client -o yaml |
kubeseal --format yaml > sealed-secret.yaml
# 2. 암호화된 SealedSecret (Git에 커밋 가능)
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
name: app-secrets
namespace: production
spec:
encryptedData:
DB_PASSWORD: AgBy3i4OJSWK+PiTySYZZA9rO... # 암호화된 값
JWT_SECRET: AgCtr8SLKJDF+werWERsdf324...
# 3. Sealed Secrets Controller가 복호화하여 일반 Secret 생성
디버깅 명령어
# ConfigMap 조회
kubectl get configmap app-config -n production -o yaml
kubectl describe configmap app-config -n production
# Secret 값 확인 (Base64 디코딩)
kubectl get secret app-secrets -n production -o jsonpath='{.data.DATABASE_PASSWORD}' | base64 -d
# Pod에 주입된 환경 변수 확인
kubectl exec -n production deploy/api-server -- env | grep DATABASE
# 마운트된 설정 파일 확인
kubectl exec -n production deploy/api-server -- cat /app/config/application.yml
# ConfigMap/Secret을 참조하는 Pod 찾기
kubectl get pods -n production -o json |
jq '.items[] | select(.spec.containers[].envFrom[]?.configMapRef.name == "app-config") | .metadata.name'
Helm Chart 실전 운영 가이드에서 다룬 values.yaml과 ConfigMap/Secret을 연동하면 환경별 설정을 Helm values로 일원화하여 관리할 수 있다.
정리: 설정 관리 체크리스트
- 비밀 분리: 민감 데이터는 반드시 Secret, 일반 설정은 ConfigMap
- envFrom 활용: 키-값이 많으면 개별 env 대신 envFrom으로 전체 주입
- subPath 마운트: 설정 파일 하나만 마운트할 때 디렉토리 덮어쓰기 방지
- Immutable: 운영 환경에서는 immutable: true로 실수 방지
- 자동 롤링: Reloader 또는 checksum 어노테이션으로 설정 변경 반영
- Git 커밋 금지: 평문 Secret을 Git에 커밋하지 않는다 → Sealed Secrets 또는 External Secrets 사용
- RBAC 제한: Secret 접근은 resourceNames로 최소 범위 지정