Theme:

데이터베이스 비밀번호를 코드에 하드코딩하면 안 된다는 건 알겠는데, Kubernetes에서는 어떻게 분리해서 관리할까요?

설정값과 민감 정보를 코드와 분리하는 것은 12-Factor App의 핵심 원칙 중 하나입니다. Kubernetes는 이를 위해 ConfigMap과 Secret이라는 두 가지 오브젝트를 제공합니다. 비슷해 보이지만 용도와 보안 수준이 다르고, 변경 시 반영 방식도 다릅니다.

ConfigMap — 일반 설정 관리

ConfigMap은 키-값 쌍으로 설정 데이터를 저장합니다.

YAML
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  # 단순 키-값
  APP_ENV: production
  LOG_LEVEL: info
  MAX_CONNECTIONS: "100"

  # 파일 형태의 설정
  application.yml: |
    server:
      port: 8080
    spring:
      datasource:
        url: jdbc:mysql://db-service:3306/mydb
SHELL
# 명령어로 생성
kubectl create configmap app-config \
  --from-literal=APP_ENV=production \
  --from-file=application.yml

환경변수로 주입

YAML
apiVersion: v1
kind: Pod
metadata:
  name: app
spec:
  containers:
    - name: app
      image: my-app:1.0
      env:
        # 개별 키 주입
        - name: APP_ENV
          valueFrom:
            configMapKeyRef:
              name: app-config
              key: APP_ENV
      envFrom:
        # 전체 키를 한꺼번에 환경변수로 주입
        - configMapRef:
            name: app-config

볼륨으로 마운트

YAML
spec:
  containers:
    - name: app
      image: my-app:1.0
      volumeMounts:
        - name: config-volume
          mountPath: /etc/config
          readOnly: true
  volumes:
    - name: config-volume
      configMap:
        name: app-config
        items:              # 특정 키만 선택 가능
          - key: application.yml
            path: application.yml

마운트 후 /etc/config/application.yml 파일로 접근할 수 있습니다.

Secret — 민감 정보 관리

Secret은 비밀번호, API 키, 인증서 같은 민감한 데이터를 저장합니다.

YAML
apiVersion: v1
kind: Secret
metadata:
  name: db-credentials
type: Opaque
data:
  # base64 인코딩된 값
  username: YWRtaW4=          # echo -n "admin" | base64
  password: cEBzc3cwcmQ=      # echo -n "p@ssw0rd" | base64

stringData를 사용하면 평문으로 작성할 수 있습니다.

YAML
apiVersion: v1
kind: Secret
metadata:
  name: db-credentials
type: Opaque
stringData:
  username: admin
  password: p@ssw0rd

Kubernetes가 자동으로 base64 인코딩하여 data 필드에 저장합니다. 주의할 점은 base64는 암호화가 아닙니다. 누구나 디코딩할 수 있으므로 etcd 암호화나 External Secrets 같은 추가 보안이 필요합니다.

Secret 타입

타입용도
Opaque일반 비밀 데이터 (기본값)
kubernetes.io/tlsTLS 인증서와 키
kubernetes.io/dockerconfigjson컨테이너 레지스트리 인증
kubernetes.io/basic-auth기본 인증 (username/password)
kubernetes.io/ssh-authSSH 키
YAML
# TLS Secret 예제
apiVersion: v1
kind: Secret
metadata:
  name: tls-secret
type: kubernetes.io/tls
data:
  tls.crt: <base64 인코딩된 인증서>
  tls.key: <base64 인코딩된 키>
SHELL
# 명령어로 TLS Secret 생성
kubectl create secret tls tls-secret \
  --cert=path/to/cert.pem \
  --key=path/to/key.pem

Secret을 Pod에 주입

YAML
spec:
  containers:
    - name: app
      image: my-app:1.0
      env:
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: db-credentials
              key: password
      volumeMounts:
        - name: secret-volume
          mountPath: /etc/secrets
          readOnly: true
  volumes:
    - name: secret-volume
      secret:
        secretName: db-credentials

환경변수 vs 볼륨 마운트

두 방식에는 중요한 차이가 있습니다.

특성환경변수볼륨 마운트
업데이트 반영Pod 재시작 필요자동 반영 (1분 내외)
접근 방식process.env.KEY파일 읽기
보안kubectl exec env로 노출 가능파일 권한으로 제어 가능
적합한 경우단순 키-값설정 파일, 인증서

공부하다 보니 이 차이를 놓쳐서 "설정을 바꿨는데 왜 반영이 안 되지?" 하는 경우가 꽤 있더라고요. 환경변수 방식은 Pod 재시작 전까지 이전 값이 유지됩니다.

immutable ConfigMap/Secret

YAML
apiVersion: v1
kind: ConfigMap
metadata:
  name: static-config
immutable: true  # 수정 불가
data:
  VERSION: "1.0"

immutable: true를 설정하면 두 가지 이점이 있습니다.

  1. 실수 방지: 운영 중인 설정이 의도치 않게 변경되는 것을 막습니다
  2. 성능 최적화: kubelet이 watch를 멈추므로 API Server 부하가 줄어듭니다

수정이 필요하면 삭제 후 새로 생성해야 합니다.

변경 시 재시작 전략

ConfigMap이나 Secret을 수정했을 때 Pod에 반영하는 방법입니다.

방법 1: 어노테이션 변경으로 롤링 업데이트

SHELL
# Deployment의 어노테이션을 변경하면 Pod이 재생성됨
kubectl patch deployment my-app -p \
  '{"spec":{"template":{"metadata":{"annotations":{"config-hash":"20260319"}}}}}'

방법 2: ConfigMap 이름에 해시 포함 (Helm 패턴)

YAML
# values.yaml이 바뀌면 ConfigMap 이름도 바뀜
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config-abc123  # 내용의 해시값
---
apiVersion: apps/v1
kind: Deployment
spec:
  template:
    spec:
      volumes:
        - name: config
          configMap:
            name: app-config-abc123  # 이름이 바뀌므로 자동 업데이트

방법 3: Reloader 사용

Stakater Reloader를 설치하면 ConfigMap/Secret 변경을 자동으로 감지하여 관련 Deployment를 롤링 업데이트합니다.

YAML
metadata:
  annotations:
    reloader.stakater.com/auto: "true"  # 자동 재시작 활성화

실무 팁

  • Secret은 환경변수보다 볼륨 마운트를 권장합니다: 환경변수는 로그에 노출될 위험이 있습니다
  • ConfigMap의 크기 제한은 1MB입니다: 큰 설정은 분할하세요
  • 네임스페이스 간 공유 불가: 같은 네임스페이스의 Pod만 사용할 수 있습니다
  • optional 플래그: 참조하는 ConfigMap/Secret이 없어도 Pod이 시작되게 할 수 있습니다
YAML
env:
  - name: OPTIONAL_CONFIG
    valueFrom:
      configMapKeyRef:
        name: maybe-exists
        key: some-key
        optional: true  # 없어도 Pod 시작 가능

정리

ConfigMap은 일반 설정, Secret은 민감 정보를 코드와 분리하는 Kubernetes 오브젝트입니다. 환경변수와 볼륨 마운트 두 가지 주입 방식의 차이(특히 자동 업데이트 여부)를 이해하고, immutable 옵션과 재시작 전략을 적절히 활용하면 설정 관리가 훨씬 안정적이 됩니다. Secret의 base64 인코딩은 암호화가 아니라는 점은 꼭 기억해 두세요.

댓글 로딩 중...