설정과 민감정보를 이미지에서 떼어내기
이미지 안에 설정값을 하드코딩하면, 개발 환경과 운영 환경마다 이미지를 다시 빌드해야 합니다. 이건 컨테이너를 쓰는 의미 자체를 반쯤 무너뜨립니다 — 같은 이미지를 어디서나 그대로 돌릴 수 있다는 게 컨테이너의 핵심 장점인데, 환경마다 이미지가 달라지면 그 장점이 사라집니다. ConfigMap과 Secret은 이 설정값을 이미지 밖으로 꺼내는 도구입니다.
ConfigMap — 민감하지 않은 설정
# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: my-api-config
data:
LOG_LEVEL: "info"
FEATURE_FLAG_NEW_UI: "true"이렇게 만든 ConfigMap을 Pod에 넣는 방법은 두 가지입니다. 환경 변수로 주입하거나, 파일로 마운트하는 것.
# deployment.yaml (일부)
spec:
containers:
- name: my-api
image: my-api:1.0.0
envFrom:
- configMapRef:
name: my-api-config값이 몇 개 안 되고 애플리케이션이 이미 환경 변수 기반으로 설정을 읽게 짜여 있다면 envFrom이 간단합니다. 반면 설정이 nginx.conf처럼 통째로 파일 형태여야 한다면 볼륨으로 마운트합니다.
volumeMounts:
- name: config-volume
mountPath: /etc/config
volumes:
- name: config-volume
configMap:
name: my-api-config파일로 마운트하면 흥미로운 특성이 하나 생깁니다 — ConfigMap 내용을 수정하면(kubectl apply로 다시 적용) 마운트된 파일 내용도 자동으로 갱신됩니다(단, 반영까지 최대 1분 정도 걸릴 수 있습니다). 다만 애플리케이션이 그 파일을 최초 실행 시 한 번만 읽고 메모리에 캐시해둔다면, 파일이 바뀌어도 애플리케이션 동작은 그대로입니다. 환경 변수는 애초에 컨테이너 시작 시점에 한 번 주입되고 끝이라 이 문제 자체가 없습니다.
Secret — 그런데 이름만큼 안전하지는 않다
Secret은 API 키, DB 비밀번호 같은 민감한 값을 담는 용도로 별도 리소스로 분리되어 있습니다. 겉보기엔 ConfigMap과 거의 똑같이 생겼습니다.
apiVersion: v1
kind: Secret
metadata:
name: my-api-secret
type: Opaque
stringData:
DB_PASSWORD: "hunter2"kubectl apply -f secret.yaml
kubectl get secret my-api-secret -o yaml여기서 짚어야 할 게 있습니다. Secret에 저장된 값은 base64로 인코딩될 뿐, 암호화되는 게 아닙니다. kubectl get secret my-api-secret -o jsonpath='{.data.DB_PASSWORD}' | base64 -d 한 줄이면 누구나 원래 값을 그대로 복원할 수 있습니다. 그러니까 Secret 리소스가 여러분을 지켜주는 건 "이 값을 실수로 로그나 코드에 찍는 걸 막아주는" 정도이지, 이 리소스에 접근할 수 있는 사람으로부터 값을 숨겨주는 게 아닙니다. etcd에 저장된 Secret도 기본 설정에서는 암호화되어 있지 않습니다 — 이게 필요하다면 etcd 저장 시 암호화(encryption at rest)를 별도로 켜야 하는데, 관리형 클러스터라면 대부분 기본으로 켜져 있지만 직접 구축한 클러스터라면 확인이 필요합니다.
실무에서 진짜 민감한 값(운영 DB 비밀번호, 결제 API 키)을 다룰 때는 Kubernetes Secret을 최종 저장소로 쓰지 않고, HashiCorp Vault나 클라우드의 Secret Manager 같은 외부 시스템에 두고 클러스터에는 그 시스템에서 값을 가져오는 방식(External Secrets Operator 등)을 씁니다. 이 책에서 다루는 기본 Secret 리소스는 그 전 단계, 그러니까 "적어도 Git 저장소에 평문으로 커밋하는 것보다는 나은" 수준으로 이해하는 게 정확합니다.
RBAC과의 관계
Secret이 base64일 뿐이라는 사실이 특히 중요해지는 지점은 권한 관리입니다. 어떤 사용자나 서비스 계정이 secrets 리소스에 대한 get 권한을 가지고 있다면, 그 사람은 클러스터의 모든 비밀번호를 평문으로 읽을 수 있다는 뜻입니다. RBAC(Role-Based Access Control)으로 이 권한을 세밀하게 나누는 방법은 이 책의 범위를 넘어서지만, "누가 Secret을 읽을 수 있는가"는 컨테이너 이미지 보안 못지않게 신경 써야 할 문제라는 점만은 짚고 넘어갑니다.