본문 바로가기

Backend/Study

[DevOps] Kubernetes에서 ConfigMap과 Secret 관리하기

반응형

0. 개요

  1. 개념과 차이: ConfigMap vs Secret
    • 1-1. 목적·사용 시점
    • 1-2. 보안·용량·인코딩 제약
  2. 생성과 업데이트: kubectl & YAML
    • 2-1. ConfigMap 생성(from-literal/from-file/YAML)
    • 2-2. Secret 생성(generic/docker-registry/tls)
  3. 애플리케이션 주입 패턴
    • 3-1. 환경 변수(env/envFrom)
    • 3-2. 볼륨 마운트(파일 주입·subPath)
  4. 보안과 암호화
    • 4-1. etcd/리소스 암호화(KMS)
    • 4-2. RBAC·감사·네임스페이스 격리
  5. 배포 도구와 환경 분리
    • 5-1. Kustomize 오버레이(dev/stg/prod)
    • 5-2. Helm 템플릿/values로 관리
  6. 고급: Sealed Secrets & External Secrets
    • 6-1. Sealed Secrets 워크플로우
    • 6-2. External Secrets Operator(AWS/GCP/Azure)
  7. 로테이션과 무중단 전략
    • 7-1. DB/토큰 교체 롤링
    • 7-2. 애플리케이션 재시작 정책
  8. 네임스페이스/라벨링/거버넌스
    • 8-1. 네이밍·라벨·어노테이션 규칙
    • 8-2. Immutable ConfigMap/Secret 전략
  9. 관측·디버깅·트러블슈팅
    • 9-1. kubectl 필수 명령
    • 9-2. 흔한 오류와 해결
  10. 체크리스트 & 베스트 프랙티스
    • 10-1. 도입 전 점검
    • 10-2. 운영 중 가이드
  11. FAQ
    • base64는 암호화인가?
    • 언제 Secret/ConfigMap을 나눌까?
  12. 결론 요약

Kubernetes에서 ConfigMap과 Secret 관리하기

백엔드 애플리케이션을 쿠버네티스에 배포하면 가장 먼저 부딪히는 주제가 “설정과 비밀을 어떻게 안전하게 주입할 것인가”입니다. 이 글은 ConfigMap/Secret의 차이에서 시작해 생성·주입·보안·배포 도구·로테이션까지, 바로 실전에 복붙 가능한 예제로 정리했습니다.

1. 개념과 차이: ConfigMap vs Secret

1-1. 목적·사용 시점

  • ConfigMap: 코드와 분리된 비민감 설정값(예: 기능 플래그, 외부 API 엔드포인트, 포맷 문자열).
  • Secret: 민감 데이터(예: DB 비밀번호, API 키, TLS 키/인증서). 접근과 감사가 중요합니다.
  • 공통: 애플 이미지를 다시 빌드하지 않고도 설정을 바꿀 수 있게 해 주는 운영 레버입니다.

1-2. 보안·용량·인코딩 제약

  • Secret 데이터는 API 상 base64 인코딩으로 저장됩니다(암호화와 다름). etcd 암호화RBAC으로 보호해야 합니다.
  • 리소스 크기: 단일 오브젝트 수백 KB 수준을 권장(너무 큰 설정은 별도 스토리지/파일 서버 고려).

2. 생성과 업데이트: kubectl & YAML

2-1. ConfigMap 생성(from-literal/from-file/YAML)

# ① 즉석 키/값 kubectl create configmap app-config -n app \ --from-literal=APP_ENV=prod --from-literal=FEATURE_X=true
② 파일로부터

kubectl create configmap app-config -n app
--from-file=config.json=./config.prod.json

③ 선언형 YAML

apiVersion: v1
kind: ConfigMap
metadata: { name: app-config, namespace: app, labels: { app: demo } }
data:
APP_ENV: "prod"
API_BASE: "https://api.example.com
"

2-2. Secret 생성(generic/docker-registry/tls)

# ① generic: 문자열/파일 kubectl create secret generic db-secret -n app \ --from-literal=DB_USER=app --from-literal=DB_PASS='S3cr3t!'
② docker-registry: 프라이빗 레지스트리 인증

kubectl create secret docker-registry regcred -n app
--docker-server=registry.example.com --docker-username=alice
--docker-password='********' --docker-email=dev@example.com

③ tls: 인증서/키

kubectl create secret tls site-tls -n app
--cert=./tls.crt --key=./tls.key

④ 선언형 YAML (값은 base64로 인코딩)

apiVersion: v1
kind: Secret
metadata: { name: db-secret, namespace: app }
type: Opaque
data:
DB_USER: YXBw
DB_PASS: UzNjcjN0IQ==

3. 애플리케이션 주입 패턴

3-1. 환경 변수(env/envFrom)

# Deployment 발췌 env: - name: APP_ENV valueFrom: { configMapKeyRef: { name: app-config, key: APP_ENV } } - name: DB_PASS valueFrom: { secretKeyRef: { name: db-secret, key: DB_PASS } }
여러 키를 한 번에 주입

envFrom:

configMapRef: { name: app-config }

secretRef: { name: db-secret }
  • 장점: 간단·프레임워크 친화적. 단점: 긴 구조/파일형 설정엔 불리.

3-2. 볼륨 마운트(파일 주입·subPath)

# Deployment 발췌 - 파일로 마운트 volumes: - name: cm configMap: { name: app-config, items: [ { key: "config.json", path: "config.json" } ] } - name: sec secret: { secretName: db-secret } containers: - name: api volumeMounts: - { name: cm, mountPath: /app/config, readOnly: true } - { name: sec, mountPath: /app/secret, readOnly: true } 
  • subPath로 파일 한 개만 특정 경로에 마운트 가능. 애플 재시작 없이 파일 갱신을 폴링해 반영하는 전략과 궁합이 좋습니다.

4. 보안과 암호화

4-1. etcd/리소스 암호화(KMS)

# 컨트롤플레인(encryptionConfiguration) 예시 (관리형 K8s는 콘솔/플래그로 설정) apiVersion: apiserver.config.k8s.io/v1 kind: EncryptionConfiguration resources: - resources: [ "secrets" ] providers: - kms: name: kms-provider endpoint: unix:///var/run/kmsplugin/socket.sock - identity: {}
  • Secret은 전송/저장 모두 암호화가 기본값이 되도록 합니다(클라우드 매니지드 K8s는 옵션 제공).

4-2. RBAC·감사·네임스페이스 격리

# 읽기 전용 Role (특정 네임스페이스) kind: Role apiVersion: rbac.authorization.k8s.io/v1 metadata: { name: secret-reader, namespace: app } rules: - apiGroups: [""] resources: ["secrets"] verbs: ["get","list"] --- kind: RoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: { name: alice-secret-rb, namespace: app } subjects: [ { kind: User, name: alice } ] roleRef: { kind: Role, name: secret-reader, apiGroup: rbac.authorization.k8s.io }
  • 감사(오딧) 로그로 누가 언제 어떤 Secret을 읽었는지 추적 가능하게 합니다.

5. 배포 도구와 환경 분리

5-1. Kustomize 오버레이(dev/stg/prod)

# base/kustomization.yaml resources: [ deployment.yaml, service.yaml ] configMapGenerator: - name: app-config literals: [ APP_ENV=dev, API_BASE=https://dev.api.example.com ] generatorOptions: { disableNameSuffixHash: true }
overlays/prod/kustomization.yaml

bases: [ ../../base ]
configMapGenerator:

name: app-config
behavior: replace
literals: [ APP_ENV=prod, API_BASE=https://api.example.com
 ]

오버레이로 환경별 차이를 선언적으로 유지합니다. disableNameSuffixHash는 이름 해시를 끄고, 대신 롤아웃 트리거는 애노테이션으로 관리하는 패턴을 권장합니다.

5-2. Helm 템플릿/values로 관리

# templates/configmap.yaml apiVersion: v1 kind: ConfigMap metadata: { name: {{ include "app.fullname" . }}-config } data: APP_ENV: "{{ .Values.app.env }}" API_BASE: "{{ .Values.app.apiBase }}"
# values-prod.yaml app: env: prod apiBase: https://api.example.com

6. 고급: Sealed Secrets & External Secrets

6-1. Sealed Secrets 워크플로우

  • 컨트롤러의 공개키로 Secret을 암호화해 Git에 커밋 → 클러스터에서만 복호화되어 일반 Secret으로 생성.
# 암호화된 시크릿 예시(발췌) apiVersion: bitnami.com/v1alpha1 kind: SealedSecret metadata: { name: db-secret, namespace: app } spec: encryptedData: DB_PASS: AgBv...==

6-2. External Secrets Operator(AWS/GCP/Azure)

  • 클라우드 시크릿 매니저(예: AWS Secrets Manager)에서 값을 동기화해 Kubernetes Secret으로 투영.
# AWS 예시 apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: { name: db-secret, namespace: app } spec: refreshInterval: 1h secretStoreRef: { name: aws-sm, kind: ClusterSecretStore } target: { name: db-secret, creationPolicy: Owner } data: - secretKey: DB_PASS remoteRef: { key: prod/db, property: password }

7. 로테이션과 무중단 전략

7-1. DB/토큰 교체 롤링

  • Secret을 갱신하면 파드가 자동 재시작되진 않습니다. 애노테이션 해시를 파드 스펙에 포함해 롤아웃을 트리거하세요.
# Deployment 템플릿에 해시 주입(Helm 예) annotations: secret-hash: "{{ include (print $.Template.BasePath \"/secret.yaml\") . | sha256sum }}"

7-2. 애플리케이션 재시작 정책

  • 구(RollingUpdate) 전략으로 파드를 순차 교체해 연결 중단을 최소화.
  • DB 비밀번호 변경은 양쪽(구/신) 모두 허용되는 그레이스 기간을 두면 안전합니다.

8. 네임스페이스/라벨링/거버넌스

8-1. 네이밍·라벨·어노테이션 규칙

  • 네이밍: <팀>-<서비스>-<용도> (예: checkout-api-config).
  • 라벨: app=checkout, tier=backend, managed-by=helm.
  • 어노테이션: 소유자/만료일/티켓 참조를 기록해 가시성↑.

8-2. Immutable ConfigMap/Secret 전략

  • 기본 오브젝트는 불변 속성 제공이 제한적입니다. 해시가 붙은 이름/어노테이션 해시로 불변처럼 다루고, 변경 시 새 오브젝트를 생성해 롤아웃합니다.

9. 관측·디버깅·트러블슈팅

9-1. kubectl 필수 명령

kubectl -n app get cm,secret kubectl -n app describe cm app-config kubectl -n app get secret db-secret -o jsonpath='{.data.DB_PASS}' | base64 -d kubectl -n app rollout restart deploy/api kubectl -n app get events --sort-by=.lastTimestamp

9-2. 흔한 오류와 해결

  • CrashLoopBackOff: 환경 변수 키 오타 → describe 이벤트로 키 누락 확인.
  • PermissionDenied: RBAC 정책 부족 → Role/RoleBinding 점검.
  • 갱신 미반영: Secret 업데이트 후 파드 미재시작 → rollout restart 또는 해시 주입 전략 적용.

10. 체크리스트 & 베스트 프랙티스

10-1. 도입 전 점검

  • 민감/비민감 경계 정의, 키 네이밍 규칙 수립.
  • etcd 암호화/KMS·RBAC·감사 로깅 활성화.
  • 환경 분리(Kustomize/Helm)와 Git 정책(승인/리뷰) 정의.

10-2. 운영 중 가이드

  • Secret 로테이션 주기/소유자 명시, 만료 알림.
  • 해시 기반 롤아웃, 릴리즈마다 설정 드리프트 검출.
  • 가능하면 Sealed/External Secrets로 Git에 평문 무커밋.

11. FAQ

- base64는 암호화인가요?

아니요. 단순 인코딩입니다. 저장 시 etcd 암호화와 접근 제어(RBAC), 전송 시 TLS가 필수입니다.

- 언제 Secret·ConfigMap을 나누나요?

누출 시 피해가 있는 값(크리덴셜·토큰·키)은 Secret, 그 외 비민감 설정은 ConfigMap으로 분리하세요. 애매하면 일단 Secret로 시작해도 무방합니다.

12. 결론 요약

ConfigMap은 비밀을 제외한 설정을, Secret은 민감 정보를 안전하게 전달하는 도구입니다. 선언형(YAML)·도구(Kustomize/Helm)·보안(RBAC/KMS)·로테이션(해시/재시작)까지 표준을 정해두면 팀 전체의 배포와 운영 신뢰도가 급상승합니다. 처음엔 작은 서비스에 적용해 보고, Sealed/External Secrets로 Git 친화적·클라우드 네이티브한 비밀 관리를 완성해 보세요.

반응형