본문으로 건너뛰기
김신건의 로그

[K8s] ConfigMap & Secret: 설정과 비밀의 분리

· 수정 · 📖 약 3분 · 1,019자/단어 #kubernetes #configmap #secret #configuration #k8s #security #gitops
ConfigMap, Secret, env from, volume mount config, Sealed Secret, External Secrets, SOPS, immutable configmap, ESO

정의

ConfigMapSecret 은 컨테이너 이미지에서 설정/비밀 값을 분리하기 위한 Kubernetes 리소스.

항목ConfigMapSecret
데이터평문 (UTF-8)base64 인코딩
크기 제한1 MiB1 MiB
용도비민감 설정비밀 (비밀번호, 토큰, 인증서)
etcd 저장평문평문 (EncryptionConfiguration 설정 시 암호화)
RBAC 권고일반엄격한 제한 권고
기본 마운트일반 volumetmpfs (in-memory)

IMPORTANT

Secret = base64 는 암호화가 아니다. echo "password" | base64 한 줄로 복호화됨. 진짜 보안은 etcd encryption at rest + RBAC + 외부 시크릿 관리(ESO, Vault) 로 달성.

ConfigMap

ConfigMap 생성

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
  namespace: production
data:
  # 단순 키-값
  log_level: "info"
  db_host: "db.production.svc.cluster.local"
  max_connections: "100"

  # 여러 줄 (파일 내용)
  feature_flags: |
    new_ui: true
    beta_search: false
    dark_mode: true

  # nginx.conf 파일 통째로
  nginx.conf: |
    worker_processes auto;
    events { worker_connections 1024; }
    http {
      server {
        listen 80;
        location / { proxy_pass http://backend:8080; }
      }
    }

CLI 생성

# 파일에서
kubectl create configmap nginx-config --from-file=nginx.conf

# 리터럴에서
kubectl create configmap app-env \
  --from-literal=log_level=info \
  --from-literal=db_host=db.prod

# 여러 파일 디렉토리에서
kubectl create configmap configs --from-file=./configs/

Secret

Secret 생성 (YAML)

apiVersion: v1
kind: Secret
metadata:
  name: db-creds
  namespace: production
type: Opaque
data:
  # base64 인코딩 값 (echo -n "value" | base64)
  username: a29h                # base64("koa")
  password: cGFzc3dvcmQ=        # base64("password")

# stringData: 평문으로 작성 (쿠버네티스가 자동 base64)
stringData:
  api_key: "sk-prod-xxxxxxx"    # 평문 OK (stringData 필드)

Secret 타입

type용도
Opaque (기본)임의 비밀 데이터
kubernetes.io/dockerconfigjson프라이빗 레지스트리 인증
kubernetes.io/tlsTLS 인증서 (tls.crt, tls.key)
kubernetes.io/service-account-token서비스 어카운트 토큰
kubernetes.io/ssh-authSSH 인증 키
# 프라이빗 레지스트리 secret
kubectl create secret docker-registry regcred \
  --docker-server=registry.example.com \
  --docker-username=admin \
  --docker-password=secret \
  --docker-email=admin@example.com

# TLS secret
kubectl create secret tls tls-secret \
  --cert=server.crt \
  --key=server.key

Pod 에서 사용

1. 환경 변수 (개별)

spec:
  containers:
    - name: app
      image: app:v1
      env:
        - name: LOG_LEVEL
          valueFrom:
            configMapKeyRef:
              name: app-config
              key: log_level
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: db-creds
              key: password
        - name: DB_HOST
          valueFrom:
            configMapKeyRef:
              name: app-config
              key: db_host

2. 환경 변수 (전체 import)

spec:
  containers:
    - name: app
      envFrom:
        - configMapRef:
            name: app-config     # ConfigMap 의 모든 키가 env 로
        - secretRef:
            name: db-creds       # Secret 의 모든 키가 env 로

3. Volume mount (파일로 마운트)

spec:
  containers:
    - name: app
      volumeMounts:
        - name: config-vol
          mountPath: /etc/config
          readOnly: true
        - name: secret-vol
          mountPath: /etc/secrets
          readOnly: true
        - name: nginx-conf
          mountPath: /etc/nginx/nginx.conf
          subPath: nginx.conf      # 특정 키만 단일 파일로
  volumes:
    - name: config-vol
      configMap:
        name: app-config
        items:                    # 특정 키만 선택 (선택적)
          - key: feature_flags
            path: feature_flags.yaml
    - name: secret-vol
      secret:
        secretName: db-creds
        defaultMode: 0400         # read-only (소유자만)
    - name: nginx-conf
      configMap:
        name: app-config

env vs volume mount 비교

flowchart LR
    subgraph "환경 변수 방식"
        CM1["ConfigMap"] -->|"envFrom / valueFrom"| ENV["Pod 환경 변수\n(start 시 로드)"]
        ENV --> App1["앱"]
        App1 -->|"변경 시"| Restart["Pod 재시작 필요"]
    end
    subgraph "Volume Mount 방식"
        CM2["ConfigMap"] -->|"volume mount"| File["파일 시스템\n(/etc/config)"]
        File --> App2["앱"]
        App2 -->|"변경 시"| Auto["자동 반영\n(수십 초 후)"]
    end
항목환경 변수Volume Mount
로드 시점Pod 시작 시실행 중 갱신 가능
ConfigMap 변경 반영Pod 재시작 필요자동 반영 (kubelet sync)
접근 방식os.getenv()파일 읽기
대용량 데이터비권장적합
민감 데이터프로세스 환경에 노출파일 권한으로 제어

TIP

설정 Hot Reload 가 필요하면 Volume Mount 를 사용하고, 앱이 파일 변경을 감지(inotify)하도록 구현. 환경 변수는 Pod 재시작 없이 변경 불가.

Immutable ConfigMap / Secret

쿠버네티스 1.21+ 에서 immutable: true 지원.

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config-v1
immutable: true    # 이 ConfigMap 은 변경 불가
data:
  version: "1.0.0"
  config: "..."

장점:

  • etcd 의 변경 감시(watch) 부하 제거 (수천 개 ConfigMap 이 있는 클러스터에서 유의미)
  • 실수로 설정 변경 방지
  • 변경이 필요하면 새 이름으로 새 ConfigMap 생성 후 Pod 업데이트 (GitOps 친화적)
# immutable ConfigMap 변경 시도 (실패)
kubectl patch configmap app-config-v1 --patch '{"data":{"new_key":"value"}}'
# Error: configmap "app-config-v1" is immutable

Secret 관리: git 에 평문 절대 금지

flowchart TD
    Q{"Secret 관리 방식"}
    Q --> S1["Sealed Secrets\n(Bitnami)"]
    Q --> S2["External Secrets Operator\n(ESO)"]
    Q --> S3["SOPS + age/KMS"]
    Q --> S4["Vault Agent / CSI Driver"]
    S1 -->|"GitOps 친화"| GitOps["git에 암호화 commit 가능"]
    S2 -->|"클라우드 통합"| Cloud["AWS/GCP/Azure 시크릿 동기화"]
    S3 -->|"파일 단위 암호화"| Git2["git에 SOPS 파일 commit"]
    S4 -->|"중앙집중식"| Vault["HashiCorp Vault 연동"]

1. Sealed Secrets (Bitnami)

클러스터의 공개키로 Secret 을 암호화. 복호화는 클러스터 안에서만 가능.

# Sealed Secrets 컨트롤러 설치
helm install sealed-secrets \
  oci://registry-1.docker.io/bitnamicharts/sealed-secrets \
  -n kube-system

# 클러스터 공개키 가져오기
kubeseal --fetch-cert > pub-cert.pem

# Secret 생성 (임시)
kubectl create secret generic db-creds \
  --from-literal=password=MySecret123 \
  --dry-run=client -o yaml > secret.yaml

# SealedSecret 으로 암호화
kubeseal --cert pub-cert.pem -f secret.yaml -w sealed-secret.yaml

# git 에 안전하게 커밋
git add sealed-secret.yaml && git commit -m "add sealed db-creds"

SealedSecret 을 git 에 올리면 ArgoCD/Flux 가 자동으로 클러스터에 복호화 적용.

2. External Secrets Operator (ESO)

클라우드 시크릿 관리 서비스(AWS Secrets Manager, GCP Secret Manager, Vault 등)와 K8s Secret 을 동기화.

# ClusterSecretStore: 시크릿 소스 설정
apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
  name: aws-secrets-manager
spec:
  provider:
    aws:
      service: SecretsManager
      region: ap-northeast-2
      auth:
        jwt:
          serviceAccountRef:
            name: external-secrets-sa
            namespace: external-secrets

---
# ExternalSecret: 동기화 규칙
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: db-creds
  namespace: production
spec:
  refreshInterval: 1h                # 1시간마다 동기화
  secretStoreRef:
    name: aws-secrets-manager
    kind: ClusterSecretStore
  target:
    name: db-creds                   # 생성할 K8s Secret 이름
    creationPolicy: Owner
  data:
    - secretKey: password            # K8s Secret 의 키
      remoteRef:
        key: prod/myapp/db           # AWS Secrets Manager 경로
        property: password           # JSON 필드
    - secretKey: username
      remoteRef:
        key: prod/myapp/db
        property: username

3. SOPS (Secrets OPerationS)

파일 자체를 암호화해서 git 에 커밋. age 또는 AWS KMS 키로 복호화.

# Secret YAML 암호화 (age 키 사용)
sops -e -i secrets/db-creds.yaml
# 복호화 후 적용 (CI/CD)
sops -d secrets/db-creds.yaml | kubectl apply -f -

4. 관리 방식 비교

방식GitOps 친화성클라우드 의존학습 비용시크릿 로테이션
Sealed Secrets높음없음낮음수동 재암호화
ESO중간높음중간자동 (refreshInterval)
SOPS높음KMS 선택적중간수동
Vault Agent낮음없음높음자동

etcd Encryption at Rest

Secret 이 etcd 에 암호화 저장되도록 kube-apiserver 옵션 설정:

# kube-apiserver flag
--encryption-provider-config=/etc/kubernetes/encryption-config.yaml

# 기존 Secret 재암호화 (적용 후)
kubectl get secrets --all-namespaces -o json | kubectl replace -f -

흔한 함정

WARNING

  1. base64 = 암호화 오해: base64 는 인코딩. echo "dGVzdA==" | base64 -d 한 줄이면 복호화. 실제 보안은 etcd encryption + RBAC.
  2. Secret 을 git 에 평문 커밋: 한 번 노출된 비밀은 히스토리에 남음. git filter-repo 로 지워야. Sealed Secrets/ESO/SOPS 사용.
  3. env var 갱신 안 됨: ConfigMap 변경 후 환경변수로 주입된 Pod 는 재시작 필요. Volume Mount 로 hot-reload 구현.
  4. 너무 큰 ConfigMap (1MiB+): 거절됨. 큰 데이터(ML 모델, 바이너리)는 PVC, S3 mount, init container 로 처리.
  5. etcd encryption 미설정: 클러스터 관리자가 etcd 직접 접근 시 모든 Secret 평문 노출. 프로덕션에서는 반드시 설정.
  6. Secret RBAC 미설정: default ServiceAccount 에 Secret 읽기 권한 부여되면 모든 Pod 가 Secret 접근 가능. 최소 권한 원칙 적용.
  7. immutable 사용 시 변경 방법 혼동: immutable ConfigMap 수정 불가. 새 이름(v2, hash suffix)으로 생성 후 Pod 롤링 업데이트.

관련 위키

이 글의 용어 (7개)
[AWS] KMS: 암호화 키 관리, envelope encryptioncloud
정의 KMS (Key Management Service) = 암호화 키 중앙 관리. envelope encryption + IAM 통합 + 감사 로그. 키 종류 | 종류 | 의미…
[AWS] Secrets Manager + Parameter Storecloud
정의 AWS Secrets Manager = 회전이 필요한 비밀 (DB 패스워드, API key) 을 안전하게 저장/관리하는 서비스. Lambda 기반 자동 회전과 암호화 내장.…
[K8s] Deployment: ReplicaSet, rolling update, rollbackkubernetes
정의 Deployment = stateless 워크로드를 위한 컨트롤러. 내부적으로 ReplicaSet 관리 + rolling update / rollback. 사용 시나리오 |…
[K8s] Pod: 컨테이너의 최소 단위, sidecar, lifecyclekubernetes
정의 Pod = K8s 의 가장 작은 배포 단위. 1개 이상의 컨테이너 + 공유 네트워크 + 공유 스토리지. [!IMPORTANT] Pod 는 컨테이너의 wrapping 이 아니…
[K8s] RBAC: Role, ClusterRole, ServiceAccount, RoleBindingkubernetes
정의 RBAC (Role-Based Access Control) = K8s 의 권한 부여 모델. 주체 + 권한 + 바인딩. 사용 시나리오 | 상황 | RBAC 역할 | |---|…
[Kubernetes] Admission Controllerskubernetes
정의 Admission Controller 는 kube-apiserver 가 인증/인가 후, etcd 저장 전에 요청을 가로채 검증 (validate) 하거나 수정 (mutate…
[Kubernetes] Kustomizekubernetes
정의 Kustomize 는 Kubernetes manifests 를 템플릿 없이 overlay 방식으로 커스터마이제이션하는 도구입니다. v1.14+ 에 내장 ( ). YAML 을…

💬 댓글

사이트 검색 / 명령어

검색

스크롤 = 확대/축소 · 드래그 = 이동 · 0 = 원래 크기 · ESC = 닫기