K8s ConfigMap·Secret 운영

ConfigMap과 Secret이란?

Kubernetes에서 애플리케이션 설정을 컨테이너 이미지와 분리하는 두 가지 리소스입니다. ConfigMap은 일반 설정(환경변수, 설정 파일), Secret은 민감한 데이터(비밀번호, API 키, TLS 인증서)를 저장합니다. 설정과 코드를 분리하는 12-Factor App 원칙의 핵심 구현체입니다.

ConfigMap 생성과 사용

YAML 선언

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
  namespace: production
data:
  # 단일 키-값
  DATABASE_HOST: "db.internal.svc.cluster.local"
  DATABASE_PORT: "5432"
  LOG_LEVEL: "info"
  
  # 파일 형태 (멀티라인)
  nginx.conf: |
    server {
      listen 80;
      location / {
        proxy_pass http://localhost:3000;
        proxy_set_header Host $host;
      }
    }
  
  application.yml: |
    spring:
      datasource:
        url: jdbc:postgresql://${DATABASE_HOST}:${DATABASE_PORT}/mydb
      cache:
        type: redis

CLI로 빠르게 생성

# 리터럴 값으로
kubectl create configmap app-config 
  --from-literal=DATABASE_HOST=db.internal 
  --from-literal=LOG_LEVEL=info

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

# 디렉토리 전체
kubectl create configmap config-dir 
  --from-file=./config/

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

Pod에서 ConfigMap 사용: 3가지 방법

방법 1: 환경변수로 주입

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  template:
    spec:
      containers:
      - name: app
        image: my-app:latest
        env:
        # 개별 키 선택
        - name: DB_HOST
          valueFrom:
            configMapKeyRef:
              name: app-config
              key: DATABASE_HOST
        # 모든 키를 환경변수로
        envFrom:
        - configMapRef:
            name: app-config
          prefix: APP_    # APP_DATABASE_HOST, APP_LOG_LEVEL 등

방법 2: 볼륨 마운트 (파일로)

spec:
  containers:
  - name: nginx
    image: nginx:alpine
    volumeMounts:
    - name: config-volume
      mountPath: /etc/nginx/conf.d    # 디렉토리에 파일로 마운트
      readOnly: true
  volumes:
  - name: config-volume
    configMap:
      name: nginx-config
      items:                           # 특정 키만 선택
      - key: nginx.conf
        path: default.conf             # 파일명 변경 가능
      defaultMode: 0644

방법 3: 특정 파일만 subPath로

volumeMounts:
- name: config-volume
  mountPath: /app/config/application.yml   # 파일 하나만
  subPath: application.yml                  # 기존 디렉토리 내용 보존

주의: subPath를 사용하면 ConfigMap 업데이트 시 자동 갱신이 되지 않습니다. subPath 없이 디렉토리로 마운트해야 kubelet이 주기적으로 갱신합니다.

Secret 타입과 생성

타입 용도 자동 검증
Opaque 일반 시크릿 (기본값) 없음
kubernetes.io/tls TLS 인증서 tls.crt, tls.key 필수
kubernetes.io/dockerconfigjson 컨테이너 레지스트리 인증 JSON 형식 검증
kubernetes.io/basic-auth Basic 인증 username, password 필수
# Opaque Secret (base64 인코딩 필수)
apiVersion: v1
kind: Secret
metadata:
  name: db-credentials
  namespace: production
type: Opaque
data:
  username: cG9zdGdyZXM=          # echo -n "postgres" | base64
  password: c3VwZXJzZWNyZXQ=      # echo -n "supersecret" | base64

# stringData로 평문 입력 (적용 시 자동 base64 인코딩)
apiVersion: v1
kind: Secret
metadata:
  name: api-keys
type: Opaque
stringData:
  STRIPE_KEY: "sk_live_xxxxxxxxxxxx"
  JWT_SECRET: "my-jwt-secret-key"

# CLI로 생성
kubectl create secret generic db-credentials 
  --from-literal=username=postgres 
  --from-literal=password=supersecret

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

# Docker Registry Secret
kubectl create secret docker-registry regcred 
  --docker-server=ghcr.io 
  --docker-username=user 
  --docker-password=token

Secret 사용 패턴

spec:
  containers:
  - name: app
    env:
    # 개별 키
    - name: DB_PASSWORD
      valueFrom:
        secretKeyRef:
          name: db-credentials
          key: password
    # 전체 키
    envFrom:
    - secretRef:
        name: api-keys
    
    # 파일로 마운트 (TLS 인증서 등)
    volumeMounts:
    - name: tls-certs
      mountPath: /etc/tls
      readOnly: true
  
  # Docker Registry 인증
  imagePullSecrets:
  - name: regcred
  
  volumes:
  - name: tls-certs
    secret:
      secretName: my-tls
      defaultMode: 0400    # 읽기 전용, owner만

Immutable ConfigMap/Secret

Kubernetes 1.21+에서는 immutable: true를 설정할 수 있습니다.

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config-v3
immutable: true    # 한 번 생성 후 수정 불가
data:
  DATABASE_HOST: "db.internal"

장점:

  • 실수로 운영 설정을 변경하는 사고 방지
  • kubelet의 watch 부하 감소 (대규모 클러스터에서 성능 향상)
  • 변경 시 새 ConfigMap 생성 + Deployment 롤링 업데이트 → 변경 이력 추적 가능

설정 변경 시 Pod 재시작 전략

ConfigMap/Secret을 변경해도 기존 Pod는 자동으로 재시작되지 않습니다 (볼륨 마운트 갱신은 되지만 앱이 다시 읽지 않으면 무의미). 3가지 해결책이 있습니다.

전략 1: Reloader (자동 롤링 업데이트)

# Stakater Reloader 설치
helm install reloader stakater/reloader -n kube-system

# Deployment에 어노테이션 추가
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
  annotations:
    reloader.stakater.com/auto: "true"    # 연관 ConfigMap/Secret 변경 시 자동 재시작
    # 또는 특정 ConfigMap만
    configmap.reloader.stakater.com/reload: "app-config"
    secret.reloader.stakater.com/reload: "db-credentials"

전략 2: 해시 어노테이션 (Helm/Kustomize)

# Helm 템플릿에서
spec:
  template:
    metadata:
      annotations:
        checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
        checksum/secret: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }}

전략 3: Immutable + 버전 관리

# 설정 변경 시 새 ConfigMap 생성
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config-v4    # 버전 업
data:
  LOG_LEVEL: "debug"

# Deployment에서 참조 변경
spec:
  template:
    spec:
      containers:
      - envFrom:
        - configMapRef:
            name: app-config-v4    # v3 → v4

보안 베스트 프랙티스

Secret은 기본적으로 안전하지 않습니다. etcd에 base64로 저장될 뿐, 암호화되지 않습니다.

# 1. etcd 암호화 활성화 (EncryptionConfiguration)
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
    - secrets
    providers:
    - aescbc:
        keys:
        - name: key1
          secret: <base64-encoded-32-byte-key>
    - identity: {}    # fallback: 암호화 없이 읽기 (마이그레이션용)

# 2. RBAC로 Secret 접근 제한
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: secret-reader
  namespace: production
rules:
- apiGroups: [""]
  resources: ["secrets"]
  resourceNames: ["db-credentials"]    # 특정 Secret만
  verbs: ["get"]                        # list 불가 → 다른 Secret 열람 방지

# 3. Pod가 불필요한 Secret 접근 차단
spec:
  automountServiceAccountToken: false    # SA 토큰 자동 마운트 비활성

운영 환경에서는 Sealed Secrets이나 External Secrets를 사용하여 Git에 암호화된 Secret을 저장하고, K8s RBAC로 접근 권한을 최소화하세요.

Kustomize로 ConfigMap 자동 생성

# kustomization.yaml
configMapGenerator:
- name: app-config
  literals:
  - DATABASE_HOST=db.internal
  - LOG_LEVEL=info
  files:
  - configs/nginx.conf

secretGenerator:
- name: db-credentials
  literals:
  - username=postgres
  - password=supersecret
  options:
    disableNameSuffixHash: false    # 해시 접미사 → 변경 시 자동 롤링 업데이트

Kustomize의 configMapGenerator는 내용이 변경되면 자동으로 해시 접미사가 바뀌어(app-config-abc123), Deployment가 자동으로 롤링 업데이트됩니다.

정리

ConfigMap과 Secret 관리의 핵심은 “설정과 코드를 분리하고, 민감 데이터를 보호하라”입니다. 환경변수와 볼륨 마운트로 유연하게 주입하되, subPath의 갱신 제한을 이해하고, Reloader나 Immutable + 버전 관리로 설정 변경 시 Pod 재시작을 보장하세요. Secret은 etcd 암호화, RBAC 제한, External Secrets 연동까지 반드시 보안 계층을 추가해야 합니다.

위로 스크롤
WordPress Appliance - Powered by TurnKey Linux