← 모든 글

ConfigMap과 Secret으로 설정 관리하기 - 환경별 설정 분리

하드코딩된 설정값을 분리하고 민감한 정보를 안전하게 관리하는 ConfigMap과 Secret 사용법을 학습했습니다

CKA 준비 · 4 / 8편

이 글의 목차

학습 예시 안내: 본문의 상황과 출력은 개념 설명을 위한 예시로 정리했습니다. 당시 실행 여부는 확인되지 않았으며, 현재 환경에서의 클러스터 실습도 별도 검증이 필요합니다.

🎯 학습 목표

이 글을 통해 다음을 학습할 수 있습니다:

  • 하드코딩된 설정값의 문제점과 해결책
  • ConfigMap을 이용한 설정 데이터 외부화
  • Secret을 이용한 민감 정보 보안 관리
  • 환경변수와 볼륨 마운트를 통한 데이터 주입 방법
  • CKA 준비 과정에서 연습할 구성 관리 패턴

📝 앞선 개념과 이번 예제

앞선 글에서는 Pod, Deployment, Service의 구성을 다뤘습니다. 이번에는 같은 애플리케이션을 개발·스테이징·운영 환경에 배포할 때 설정을 분리하는 상황을 가정합니다.

예제에서 가정한 환경

# 기존 리소스들 확인
kubectl get deployments
kubectl get services
kubectl get pods

문제 상황 시뮬레이션

예를 들어, 웹 애플리케이션에 데이터베이스 연결 설정이 필요하다고 가정해봅시다:

# 기존 방식의 발췌: 환경변수가 매니페스트에 하드코딩됨 (selector 등 생략)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
spec:
  template:
    spec:
      containers:
      - name: web
        image: nginx
        env:
        - name: DB_HOST
          value: "mysql.prod.example.com"    # 하드코딩!
        - name: DB_PORT
          value: "3306"                      # 하드코딩!
        - name: DB_USER
          value: "admin"                     # 하드코딩!
        - name: DB_PASSWORD
          value: "super-secret-password"     # 보안 위험!

문제점들:

  1. 환경별 설정 어려움: 개발/스테이징/운영 환경마다 다른 값 필요
  2. 보안 위험: 비밀번호가 YAML 파일에 평문으로 노출
  3. 재사용성 부족: 환경별 매니페스트를 반복 수정해야 함 (위처럼 환경변수로 전달하면 이미지 재빌드는 필요 없음)
  4. 관리의 복잡성: 설정값이 여러 곳에 분산

이런 설정 데이터를 분리하는 ConfigMap과 Secret을 살펴보겠습니다.

🗃 ConfigMap 완전 이해하기

ConfigMap이란?

ConfigMap은 설정 데이터를 저장하는 쿠버네티스 오브젝트입니다. 컨테이너 이미지와 설정을 분리하여 관리할 수 있게 해줍니다.

첫 번째 ConfigMap 만들기

1. 명령어로 생성

# key-value 형태로 직접 생성
kubectl create configmap app-config \
  --from-literal=DB_HOST=mysql.dev.example.com \
  --from-literal=DB_PORT=3306 \
  --from-literal=APP_ENV=development

# 생성 확인
kubectl get configmaps
kubectl describe configmap app-config

2. 파일에서 생성

먼저 설정 파일들을 만들어봅시다:

# 설정 파일 생성
echo "mysql.dev.example.com" > db-host.txt
echo "3306" > db-port.txt

# 애플리케이션 설정 파일 생성
cat > app.properties << EOF
app.name=MyWebApp
app.version=1.0.0
app.debug=true
max.connections=100
EOF
# 파일에서 ConfigMap 생성
kubectl create configmap file-config --from-file=app.properties
kubectl create configmap host-config --from-file=db-host.txt

# 확인
kubectl get configmap file-config -o yaml

3. YAML 매니페스트로 생성

# app-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: web-app-config
data:
  # key-value 형태
  DB_HOST: "mysql.dev.example.com"
  DB_PORT: "3306"
  DB_NAME: "webapp"
  
  # 파일 형태 (멀티라인)
  app.properties: |
    app.name=MyWebApp
    app.version=1.0.0
    app.debug=true
    logging.level=INFO
  
  nginx.conf: |
    server {
        listen 80;
        server_name localhost;
        root /usr/share/nginx/html;
    }
# ConfigMap 생성
kubectl apply -f app-configmap.yaml

# 상세 확인
kubectl describe configmap web-app-config

🔐 Secret 완전 이해하기

Secret이란?

Secret은 민감한 정보를 별도로 관리하는 쿠버네티스 오브젝트입니다. data 필드는 Base64로 표현되지만, 이는 암호화가 아니며 누구나 디코딩할 수 있습니다. 기본 설정의 etcd 저장 암호화는 보장되지 않으므로 저장 시 암호화와 RBAC 접근 제한을 별도로 구성해야 합니다. Secret 보안 권장 사항

이 글의 비밀번호·토큰은 설명용 값입니다. 실제 비밀값을 글이나 저장소에 넣지 않고 별도로 관리합니다.

첫 번째 Secret 만들기

1. 명령어로 생성

# Generic Secret 생성
kubectl create secret generic db-credentials \
  --from-literal=DB_USER=admin \
  --from-literal=DB_PASSWORD=super-secret-password

# 생성 확인
kubectl get secrets
kubectl describe secret db-credentials

2. 파일에서 생성

# 민감한 정보를 파일로 저장
echo -n "admin" > username.txt
echo -n "super-secret-password" > password.txt

# 파일에서 Secret 생성
kubectl create secret generic file-credentials \
  --from-file=username.txt \
  --from-file=password.txt

# 확인 (데이터가 Base64로 인코딩됨)
kubectl get secret file-credentials -o yaml

3. YAML 매니페스트로 생성

# Base64 인코딩 (수동)
echo -n "admin" | base64
# YWRtaW4=

echo -n "super-secret-password" | base64
# c3VwZXItc2VjcmV0LXBhc3N3b3Jk
# db-secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: database-secret
type: Opaque
data:
  DB_USER: YWRtaW4=                           # admin (Base64)
  DB_PASSWORD: c3VwZXItc2VjcmV0LXBhc3N3b3Jk     # super-secret-password (Base64)
# Secret 생성
kubectl apply -f db-secret.yaml

# 데이터 확인 (디코딩)
kubectl get secret database-secret -o jsonpath='{.data.DB_USER}' | base64 --decode

Secret 타입별 이해

Docker Registry Secret

# Docker Hub 로그인 정보 저장
kubectl create secret docker-registry my-registry-secret \
  --docker-server=docker.io \
  --docker-username=myusername \
  --docker-password=mypassword \
  --docker-email=my@email.com

TLS Secret

# TLS 인증서 Secret 생성
kubectl create secret tls tls-secret \
  --cert=path/to/cert.crt \
  --key=path/to/cert.key

🔄 Pod에서 ConfigMap과 Secret 사용하기

1. 환경변수로 주입

# pod-with-config.yaml
apiVersion: v1
kind: Pod
metadata:
  name: config-test-pod
spec:
  containers:
  - name: web
    image: nginx
    env:
    # ConfigMap에서 환경변수 가져오기
    - name: DB_HOST
      valueFrom:
        configMapKeyRef:
          name: web-app-config
          key: DB_HOST
    
    - name: DB_PORT
      valueFrom:
        configMapKeyRef:
          name: web-app-config
          key: DB_PORT
    
    # Secret에서 환경변수 가져오기
    - name: DB_USER
      valueFrom:
        secretKeyRef:
          name: database-secret
          key: DB_USER
    
    - name: DB_PASSWORD
      valueFrom:
        secretKeyRef:
          name: database-secret
          key: DB_PASSWORD
# Pod 생성 및 환경변수 확인
kubectl apply -f pod-with-config.yaml
kubectl exec config-test-pod -- env | grep DB_

2. 전체 ConfigMap/Secret을 환경변수로 주입

# pod-env-from.yaml
apiVersion: v1
kind: Pod
metadata:
  name: env-from-pod
spec:
  containers:
  - name: web
    image: nginx
    envFrom:
    # ConfigMap의 모든 key-value를 환경변수로
    - configMapRef:
        name: web-app-config
    # Secret의 모든 key-value를 환경변수로
    - secretRef:
        name: database-secret

3. 볼륨 마운트로 파일 형태로 주입

# pod-volume-mount.yaml
apiVersion: v1
kind: Pod
metadata:
  name: volume-mount-pod
spec:
  containers:
  - name: web
    image: nginx
    volumeMounts:
    # ConfigMap을 파일로 마운트
    - name: config-volume
      mountPath: /etc/config
    # Secret을 파일로 마운트
    - name: secret-volume
      mountPath: /etc/secrets
      readOnly: true
  
  volumes:
  - name: config-volume
    configMap:
      name: web-app-config
  - name: secret-volume
    secret:
      secretName: database-secret
# Pod 생성 및 마운트된 파일 확인
kubectl apply -f pod-volume-mount.yaml

# ConfigMap 파일들 확인
kubectl exec volume-mount-pod -- ls -la /etc/config
kubectl exec volume-mount-pod -- cat /etc/config/app.properties

# Secret 파일들 확인
kubectl exec volume-mount-pod -- ls -la /etc/secrets
kubectl exec volume-mount-pod -- cat /etc/secrets/DB_USER

4. 특정 키만 선택적으로 마운트

# selective-mount.yaml
apiVersion: v1
kind: Pod
metadata:
  name: selective-mount-pod
spec:
  containers:
  - name: web
    image: nginx
    volumeMounts:
    - name: selective-config
      mountPath: /etc/app
  
  volumes:
  - name: selective-config
    configMap:
      name: web-app-config
      items:
      - key: app.properties
        path: application.properties
      - key: nginx.conf
        path: nginx/nginx.conf

💡 실전 예제: 환경별 설정 관리

환경별로 다른 설정값을 전달하는 학습용 시나리오입니다. 실제 운영 환경에 적용한 결과를 기록한 것은 아닙니다.

개발 환경 ConfigMap

# dev-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
  namespace: development
data:
  DB_HOST: "mysql.dev.local"
  DB_PORT: "3306"
  REDIS_HOST: "redis.dev.local"
  LOG_LEVEL: "DEBUG"
  API_ENDPOINT: "https://api-dev.example.com"

운영 환경 ConfigMap

# prod-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
  namespace: production
data:
  DB_HOST: "mysql.prod.local"
  DB_PORT: "3306"
  REDIS_HOST: "redis.prod.local"
  LOG_LEVEL: "WARN"
  API_ENDPOINT: "https://api.example.com"

환경마다 생성할 Secret 템플릿 (Base64 표현)

<base64-encoded-…>는 실제 값으로 바꿔야 하는 자리표시자입니다. Secret·ConfigMap·Deployment는 같은 네임스페이스에 생성해야 하며, 운영 비밀값을 개발 환경과 공유하지 않습니다.

# common-secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: app-secret
type: Opaque
data:
  DB_PASSWORD: <base64-encoded-password>
  API_KEY: <base64-encoded-api-key>
  JWT_SECRET: <base64-encoded-jwt-secret>

애플리케이션 Deployment

# web-app-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web-app
  template:
    metadata:
      labels:
        app: web-app
    spec:
      containers:
      - name: web
        image: myapp:latest
        ports:
        - containerPort: 8080
        
        # ConfigMap에서 환경변수 주입
        envFrom:
        - configMapRef:
            name: app-config
        - secretRef:
            name: app-secret
        
        # 설정 파일 마운트
        volumeMounts:
        - name: app-config-volume
          mountPath: /app/config
          readOnly: true
      
      volumes:
      - name: app-config-volume
        configMap:
          name: app-config

🔄 ConfigMap과 Secret 업데이트

데이터 업데이트 방법

# ConfigMap 데이터 업데이트
kubectl patch configmap web-app-config -p '{"data":{"DB_HOST":"new-mysql.example.com"}}'

# Secret 데이터 업데이트 (Base64 인코딩 필요)
kubectl patch secret database-secret -p '{"data":{"DB_PASSWORD":"'$(echo -n "new-password" | base64)'"}}'

# 또는 파일에서 업데이트
kubectl create configmap web-app-config --from-file=new-config.properties --dry-run=client -o yaml | kubectl replace -f -

Pod에서 업데이트 반영

주의할 점: ConfigMap이나 Secret을 업데이트해도 기존 Pod의 환경변수는 자동으로 업데이트되지 않습니다!

# Pod 재시작이 필요
kubectl rollout restart deployment web-app

# 또는 Pod를 직접 삭제 (Deployment가 자동으로 새 Pod 생성)
kubectl delete pods -l app=web-app

볼륨 마운트의 경우: 일반적인 ConfigMap·Secret 볼륨은 지연 후 갱신되며, 애플리케이션도 변경을 다시 읽어야 합니다. subPath로 마운트한 파일은 자동 갱신되지 않습니다. ConfigMap 공식 문서

🚨 문제 해결과 디버깅

ConfigMap/Secret이 Pod에 반영되지 않을 때

1. 존재 여부 확인

# ConfigMap/Secret 존재 확인
kubectl get configmap <name>
kubectl get secret <name>

# 네임스페이스 확인
kubectl get configmap <name> -n <namespace>

2. 키 이름 확인

# 정확한 키 이름 확인
kubectl describe configmap <name>
kubectl get configmap <name> -o yaml

3. Pod 이벤트 확인

# Pod 생성 실패 원인 확인
kubectl describe pod <pod-name>
kubectl get events --sort-by=.metadata.creationTimestamp

일반적인 실수들

1. Base64 인코딩 누락

# ❌ 잘못된 예 (Secret에서 평문 사용)
data:
  password: "my-password"

---
# ✅ 올바른 예
data:
  password: "bXktcGFzc3dvcmQ="  # Base64 인코딩된 값

2. 존재하지 않는 키 참조

# ❌ ConfigMap에 없는 키 참조
env:
- name: DB_HOST
  valueFrom:
    configMapKeyRef:
      name: my-config
      key: DATABASE_HOST  # 실제로는 DB_HOST

---
# ✅ 올바른 키 사용
env:
- name: DB_HOST
  valueFrom:
    configMapKeyRef:
      name: my-config
      key: DB_HOST

3. 네임스페이스 불일치

# Pod는 default 네임스페이스, ConfigMap은 다른 네임스페이스에 있는 경우
# ConfigMap을 같은 네임스페이스로 이동하거나 Pod 네임스페이스 변경 필요

🎯 CKA 대비 연습 패턴

연습할 문제 유형

1. ConfigMap 생성과 사용

# 문제: key-value로 ConfigMap 생성하고 Pod에서 환경변수로 사용
kubectl create configmap app-config --from-literal=APP_ENV=production

2. Secret 생성과 사용

# 문제: Secret 생성하고 Pod에서 볼륨으로 마운트
kubectl create secret generic db-secret --from-literal=password=secret123

3. 파일에서 ConfigMap/Secret 생성

# 문제: 기존 파일에서 ConfigMap 생성
kubectl create configmap nginx-config --from-file=nginx.conf

4. 기존 Pod에 ConfigMap 적용

  • 기존 Deployment를 수정해서 ConfigMap 사용하도록 변경
  • 환경변수와 볼륨 마운트 방식 선택

시간 단축 팁

# 빠른 생성
kubectl create cm <name> --from-literal=key=value  # cm = configmap
kubectl create secret generic <name> --from-literal=key=value

# 기존 리소스를 YAML로 출력 후 수정
kubectl get configmap <name> -o yaml > config.yaml

# 즉시 적용 및 확인
kubectl apply -f config.yaml && kubectl describe configmap <name>

📊 ConfigMap vs Secret 비교 정리

기능 비교표

항목 ConfigMap Secret
용도 일반 설정 데이터 민감한 정보
데이터 표현 data 문자열 / binaryData Base64 data Base64 (암호화 아님)
크기 제한 1 MiB 1 MiB
환경변수 주입 ✅ ✅
볼륨 마운트 ✅ ✅
변경 반영 일반 볼륨은 지연 갱신, 환경변수·subPath는 자동 갱신 안 됨 일반 볼륨은 지연 갱신, 환경변수·subPath는 자동 갱신 안 됨

사용 가이드라인

ConfigMap 사용하는 경우:

  • 데이터베이스 호스트명, 포트 번호
  • API 엔드포인트 URL
  • 애플리케이션 설정 파일
  • 로그 레벨, 디버그 모드 설정

Secret 사용하는 경우:

  • 데이터베이스 비밀번호
  • API 키, 토큰
  • TLS 인증서와 개인키
  • OAuth 클라이언트 시크릿

📚 필수 명령어 정리

ConfigMap 관리

# 생성
kubectl create configmap <name> --from-literal=key=value
kubectl create configmap <name> --from-file=<file>
kubectl apply -f configmap.yaml

# 조회
kubectl get configmaps  # 또는 kubectl get cm
kubectl describe configmap <name>
kubectl get configmap <name> -o yaml

# 수정
kubectl edit configmap <name>
kubectl patch configmap <name> -p '{"data":{"key":"new-value"}}'

# 삭제
kubectl delete configmap <name>

Secret 관리

# 생성
kubectl create secret generic <name> --from-literal=key=value
kubectl create secret generic <name> --from-file=<file>
kubectl create secret docker-registry <name> --docker-server=<server>

# 조회
kubectl get secrets
kubectl describe secret <name>
kubectl get secret <name> -o yaml

# 데이터 확인 (Base64 디코딩)
kubectl get secret <name> -o jsonpath='{.data.key}' | base64 --decode

# 삭제
kubectl delete secret <name>

🚀 다음 학습 계획

다음에는 애플리케이션의 CPU·메모리 요청량과 제한을 설정하는 방법을 살펴볼 계획입니다.

다음 주제들

  • Pod 리소스 제한과 요청 설정 - 리소스 최적화와 안정성
  • Liveness와 Readiness Probe 구성 - 헬스체크와 자동 복구
  • 네임스페이스를 통한 리소스 격리
  • Ingress를 통한 외부 트래픽 관리
  • RBAC과 보안 설정

개인 학습 목표

  • 실제 운영 환경에서의 설정 관리 패턴 이해
  • 보안을 고려한 민감 정보 관리 방법 숙달
  • 다양한 주입 방식(환경변수 vs 볼륨)의 적절한 선택

ConfigMap과 Secret은 설정을 코드와 분리하는 수단입니다. 환경변수와 볼륨은 갱신 방식이 다르므로, 설정을 바꾼 뒤 애플리케이션이 실제로 읽는 값까지 확인해야 합니다.

다음 글에서는 Pod의 리소스 요청과 제한을 다룹니다.


🔗 참고 자료:


yellow.log · 2025.08.06에 작성 · 2026.09.29에 수정

CKA 준비 전체 8편

  1. 1.Pod 생성과 관리 - 쿠버네티스의 기본 단위 이해하기
  2. 2.ReplicaSet과 Deployment로 Pod 관리하기 - 확장성과 가용성 확보
  3. 3.Service를 통한 Pod 네트워킹 - 안정적인 접근 경로 만들기
  4. 4.ConfigMap과 Secret으로 설정 관리하기 - 환경별 설정 분리읽는 중
  5. 5.Pod 리소스 제한과 요청 설정 - 안정적인 클러스터 운영을 위한 리소스 관리
  6. 6.Liveness와 Readiness Probe 구성 - 헬스체크와 자동 복구
  7. 7.네임스페이스를 통한 리소스 격리와 다중 테넌시 - 팀별, 환경별 격리
  8. 8.Ingress를 통한 외부 트래픽 관리와 라우팅 - 클러스터의 똑똑한 관문 구축