ConfigMap 완벽 마스터

ConfigMap의 핵심 개념과 실전 활용법

TypeScript중급
8시간
4개 항목
학습 진행률0 / 4 (0%)

학습 항목

1. Python
ConfigMap|Secret|활용|가이드
퀴즈튜토리얼
2. TypeScript
초급
K8s|베스트|프랙티스|완벽|가이드
퀴즈튜토리얼
3. TypeScript
Kubernetes|기본|개념|완벽|가이드
퀴즈튜토리얼
4. TypeScript
초급
Kubernetes|실무|활용|팁
퀴즈튜토리얼
1 / 4

이미지 로딩 중...

ConfigMap Secret 활용 가이드 - 슬라이드 1/13

Kubernetes ConfigMap과 Secret 활용 완벽 가이드

Kubernetes 환경에서 설정 정보와 민감한 데이터를 안전하게 관리하는 방법을 배웁니다. ConfigMap과 Secret의 차이점, 실전 활용법, 그리고 보안 모범 사례까지 초급 개발자도 쉽게 이해할 수 있도록 안내합니다.


목차

  1. ConfigMap 기본 개념 - 설정 정보를 코드에서 분리하는 첫걸음
  2. ConfigMap을 환경변수로 사용하기 - 가장 간단한 주입 방법
  3. ConfigMap을 볼륨으로 마운트하기 - 설정 파일 주입의 고급 기법
  4. Secret 기본 개념 - 민감한 정보를 안전하게 저장하기
  5. Secret을 환경변수로 사용하기 - 안전한 자격증명 주입
  6. Secret을 볼륨으로 마운트하기 - 파일 기반 자격증명 관리
  7. kubectl로 ConfigMap과 Secret 생성하기 - 명령형 접근법
  8. ConfigMap과 Secret 업데이트 전략 - 무중단 배포 구현
  9. 보안 모범 사례 - Secret을 안전하게 관리하기
  10. 실전 활용 패턴 - 환경별 설정 관리하기

1. ConfigMap 기본 개념 - 설정 정보를 코드에서 분리하는 첫걸음

시작하며

여러분이 애플리케이션을 개발하다 보면 API 엔드포인트, 데이터베이스 이름, 기능 플래그 같은 설정 값들을 코드 곳곳에 하드코딩하게 되는 경우가 많습니다. 개발 환경에서는 localhost를 사용하다가 프로덕션에서는 실제 도메인으로 바꿔야 하는데, 매번 코드를 수정하고 다시 빌드하는 번거로움을 겪어본 적 있으신가요?

이런 문제는 실제 개발 현장에서 자주 발생합니다. 환경마다 다른 설정 값 때문에 같은 코드를 여러 버전으로 관리하게 되고, 배포 시마다 실수할 위험이 커집니다.

특히 마이크로서비스 아키텍처에서는 수십 개의 서비스가 각자 다른 설정을 가지고 있어서 관리가 더욱 복잡해집니다. 바로 이럴 때 필요한 것이 ConfigMap입니다.

ConfigMap을 사용하면 설정 정보를 컨테이너 이미지에서 완전히 분리할 수 있어서, 같은 이미지를 다양한 환경에서 재사용할 수 있습니다.

개요

간단히 말해서, ConfigMap은 Kubernetes에서 설정 데이터를 키-값 쌍으로 저장하는 오브젝트입니다. ConfigMap이 필요한 이유는 설정과 코드를 분리함으로써 12-Factor App 원칙을 따를 수 있기 때문입니다.

한 번 빌드한 컨테이너 이미지를 개발, 스테이징, 프로덕션 환경에서 동일하게 사용하면서도 각 환경에 맞는 설정만 주입할 수 있습니다. 예를 들어, 프론트엔드 애플리케이션의 API 서버 주소를 환경마다 다르게 설정하거나, 로그 레벨을 개발 환경에서는 DEBUG로, 프로덕션에서는 ERROR로 설정하는 경우에 매우 유용합니다.

기존에는 환경변수를 Dockerfile에 하드코딩하거나 별도의 설정 파일을 이미지에 포함시켰다면, 이제는 ConfigMap으로 설정을 외부화하여 런타임에 주입할 수 있습니다. ConfigMap의 핵심 특징은 첫째, 민감하지 않은 데이터를 평문으로 저장한다는 점, 둘째, 환경변수나 볼륨 마운트 등 다양한 방식으로 Pod에 주입할 수 있다는 점, 셋째, 애플리케이션 코드 변경 없이 설정만 업데이트할 수 있다는 점입니다.

이러한 특징들이 애플리케이션의 이식성을 높이고 배포 프로세스를 단순화하는 데 핵심적인 역할을 합니다.

코드 예제

# ConfigMap 생성
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  # 간단한 키-값 설정
  API_URL: "https://api.example.com"
  LOG_LEVEL: "info"
  MAX_CONNECTIONS: "100"
  # 멀티라인 설정 파일
  app.properties: |
    server.port=8080
    database.name=myapp
    feature.newUI=true

설명

이것이 하는 일: ConfigMap은 애플리케이션 설정 데이터를 Kubernetes 클러스터에 저장하고, 이를 Pod에서 실행되는 컨테이너에 주입하는 역할을 합니다. 첫 번째로, metadata.name에서 이 ConfigMap의 이름을 'app-config'로 지정합니다.

이 이름을 통해 Pod에서 이 ConfigMap을 참조하게 됩니다. 이름은 같은 네임스페이스 내에서 유일해야 하며, 여러 Pod가 동일한 ConfigMap을 공유할 수 있기 때문에 의미 있는 이름을 사용하는 것이 중요합니다.

그 다음으로, data 섹션에서 실제 설정 값들을 정의합니다. API_URL, LOG_LEVEL 같은 단순한 키-값 쌍은 환경변수로 직접 사용할 수 있고, app.properties처럼 파이프(|) 문자를 사용한 멀티라인 값은 설정 파일 전체를 저장할 수 있습니다.

이렇게 하면 application.properties, nginx.conf 같은 복잡한 설정 파일도 ConfigMap에 담을 수 있습니다. 마지막으로, 이렇게 생성된 ConfigMap은 Pod 정의에서 envFrom이나 volumeMounts를 통해 컨테이너에 주입됩니다.

환경변수로 주입하면 process.env.API_URL 형태로 접근할 수 있고, 볼륨으로 마운트하면 /etc/config/app.properties 같은 파일로 읽을 수 있습니다. 여러분이 이 코드를 사용하면 애플리케이션 코드나 Dockerfile을 수정하지 않고도 설정만 변경하여 배포할 수 있습니다.

개발 환경과 프로덕션 환경에서 서로 다른 ConfigMap을 사용하면 완전히 동일한 컨테이너 이미지로 다양한 환경을 운영할 수 있고, 설정 변경 이력을 Git으로 관리하여 감사(audit)와 롤백이 용이해집니다.

실전 팁

💡 ConfigMap은 1MB 크기 제한이 있으므로 큰 설정 파일은 외부 스토리지를 고려하세요

💡 ConfigMap 변경 시 Pod가 자동으로 재시작되지 않으니 Deployment의 롤링 업데이트를 활용하세요

💡 민감한 정보(비밀번호, API 키)는 절대 ConfigMap에 저장하지 말고 반드시 Secret을 사용하세요

💡 kubectl create configmap 명령어로 기존 파일이나 디렉토리에서 빠르게 ConfigMap을 생성할 수 있습니다

💡 네임스페이스별로 ConfigMap을 관리하면 환경 분리와 권한 관리가 용이합니다


2. ConfigMap을 환경변수로 사용하기 - 가장 간단한 주입 방법

시작하며

여러분의 Node.js 애플리케이션에서 process.env.DATABASE_HOST로 데이터베이스 주소를 읽어오는 코드를 작성했다고 가정해봅시다. 로컬에서는 .env 파일로 관리했지만, Kubernetes에 배포할 때는 어떻게 이 환경변수를 전달해야 할까요?

Docker 컨테이너라면 docker run의 -e 플래그를 사용하거나 docker-compose.yml에 environment 섹션을 작성하면 됩니다. 하지만 Kubernetes에서는 수십 개의 Pod가 동시에 실행되고, 각각 다른 설정이 필요할 수 있어서 더 체계적인 방법이 필요합니다.

바로 이럴 때 ConfigMap을 환경변수로 주입하는 방법을 사용합니다. 한 번 ConfigMap을 정의하면 모든 Pod에서 일관되게 환경변수를 사용할 수 있고, 설정 변경도 중앙에서 관리할 수 있습니다.

개요

간단히 말해서, ConfigMap의 데이터를 Pod의 환경변수로 주입하는 것은 가장 직관적이고 널리 사용되는 방법입니다. 이 방법이 필요한 이유는 대부분의 애플리케이션이 이미 환경변수를 통해 설정을 읽어오도록 설계되어 있기 때문입니다.

12-Factor App 방법론에서도 환경변수를 통한 설정 관리를 권장하고 있어서, 기존 애플리케이션 코드를 수정하지 않고도 Kubernetes에서 바로 사용할 수 있습니다. 예를 들어, Express.js 앱에서 PORT 환경변수로 서버 포트를 설정하거나, Spring Boot에서 SPRING_PROFILES_ACTIVE로 프로파일을 지정하는 경우에 매우 유용합니다.

기존에는 Deployment YAML에 env 섹션에 모든 환경변수를 하나하나 나열했다면, 이제는 envFrom으로 ConfigMap 전체를 한 번에 주입할 수 있습니다. 환경변수 주입 방식의 핵심 특징은 첫째, 애플리케이션 코드 변경이 전혀 필요 없다는 점, 둘째, ConfigMap의 모든 키가 자동으로 환경변수가 된다는 점, 셋째, 특정 키만 선택적으로 주입할 수도 있다는 점입니다.

이러한 특징들이 기존 애플리케이션의 Kubernetes 마이그레이션을 매우 쉽게 만들어줍니다.

코드 예제

apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
spec:
  containers:
  - name: myapp-container
    image: myapp:1.0
    # 방법 1: ConfigMap 전체를 환경변수로 주입
    envFrom:
    - configMapRef:
        name: app-config
    # 방법 2: 특정 키만 선택적으로 주입
    env:
    - name: DATABASE_URL  # 환경변수 이름
      valueFrom:
        configMapKeyRef:
          name: app-config  # ConfigMap 이름
          key: API_URL      # ConfigMap의 키

설명

이것이 하는 일: Pod 정의에서 ConfigMap의 데이터를 컨테이너의 환경변수로 매핑하여, 애플리케이션이 process.env나 os.environ으로 설정 값을 읽을 수 있게 합니다. 첫 번째로, envFrom 섹션은 ConfigMap의 모든 키-값 쌍을 자동으로 환경변수로 변환합니다.

app-config ConfigMap에 API_URL, LOG_LEVEL, MAX_CONNECTIONS가 있다면, 컨테이너 내부에서 세 개의 환경변수가 자동으로 생성됩니다. 이 방식은 설정이 많을 때 매우 편리하지만, ConfigMap의 모든 값이 노출되므로 주의가 필요합니다.

그 다음으로, env 섹션의 valueFrom.configMapKeyRef 방식은 더 세밀한 제어를 제공합니다. 환경변수 이름(DATABASE_URL)과 ConfigMap의 키(API_URL)를 다르게 매핑할 수 있어서, 애플리케이션이 기대하는 환경변수 이름으로 변환할 수 있습니다.

또한 여러 ConfigMap에서 필요한 값만 선택적으로 가져올 수 있습니다. 마지막으로, 두 방식을 함께 사용할 수도 있습니다.

대부분의 설정은 envFrom으로 한 번에 주입하고, 특별히 이름을 바꿔야 하거나 다른 ConfigMap에서 가져와야 하는 값만 env로 개별 지정하는 하이브리드 접근이 실무에서 자주 사용됩니다. 여러분이 이 코드를 사용하면 Node.js의 process.env.API_URL, Python의 os.environ['LOG_LEVEL'], Java의 System.getenv("MAX_CONNECTIONS") 등 언어별 표준 방식으로 설정 값에 접근할 수 있습니다.

기존 코드를 전혀 수정하지 않고도 Kubernetes 환경에서 동작하며, ConfigMap만 업데이트하면 다음 배포 시 새로운 설정이 자동으로 적용됩니다.

실전 팁

💡 envFrom 사용 시 ConfigMap 키 이름이 유효한 환경변수 이름 규칙(대문자, 숫자, 언더스코어)을 따르는지 확인하세요

💡 환경변수는 Pod 생성 시점에 주입되므로 ConfigMap 변경 후 Pod를 재시작해야 반영됩니다

💡 민감한 정보가 포함된 경우 envFrom보다는 env로 필요한 값만 선택적으로 주입하세요

💡 동일한 이름의 환경변수가 여러 곳에서 정의되면 env가 envFrom보다 우선순위가 높습니다

💡 kubectl exec로 Pod에 접속하여 printenv 명령으로 환경변수가 올바르게 주입되었는지 확인할 수 있습니다


3. ConfigMap을 볼륨으로 마운트하기 - 설정 파일 주입의 고급 기법

시작하며

여러분이 Nginx 웹 서버를 Kubernetes에서 실행한다고 가정해봅시다. Nginx는 nginx.conf라는 설정 파일을 /etc/nginx/ 디렉토리에서 읽어오는데, 이 파일에는 수십 줄의 복잡한 설정이 들어있습니다.

이런 멀티라인 설정 파일을 환경변수로 전달하기는 매우 어렵고 비효율적입니다. 이런 문제는 설정 파일 기반 애플리케이션에서 항상 발생합니다.

application.properties, appsettings.json, config.yaml 같은 구조화된 설정 파일들은 단순한 키-값 환경변수로 표현하기 어렵고, 파일 경로에 있어야 애플리케이션이 읽을 수 있습니다. 바로 이럴 때 필요한 것이 ConfigMap 볼륨 마운트입니다.

ConfigMap의 데이터를 파일 시스템의 특정 경로에 파일로 마운트하여, 애플리케이션이 일반 파일처럼 읽을 수 있게 만듭니다.

개요

간단히 말해서, ConfigMap 볼륨 마운트는 ConfigMap의 데이터를 컨테이너 파일 시스템의 실제 파일로 만들어주는 기능입니다. 볼륨 마운트가 필요한 이유는 많은 애플리케이션이 설정을 파일에서 읽어오도록 설계되어 있기 때문입니다.

특히 레거시 애플리케이션이나 서드파티 소프트웨어는 환경변수를 지원하지 않는 경우가 많아서, 설정 파일을 직접 제공해야 합니다. 예를 들어, Prometheus의 prometheus.yml, Elasticsearch의 elasticsearch.yml, 또는 커스텀 애플리케이션의 JSON/YAML 설정 파일을 주입하는 경우에 필수적입니다.

기존에는 설정 파일을 컨테이너 이미지에 COPY 명령으로 포함시켜야 했다면, 이제는 ConfigMap으로 외부화하여 이미지 재빌드 없이 설정만 변경할 수 있습니다. 볼륨 마운트의 핵심 특징은 첫째, ConfigMap의 각 키가 파일 이름이 되고 값이 파일 내용이 된다는 점, 둘째, 디렉토리 전체를 덮어쓰거나 특정 파일만 추가할 수 있다는 점, 셋째, 파일 권한과 경로를 세밀하게 제어할 수 있다는 점입니다.

이러한 특징들이 복잡한 설정 파일 관리를 가능하게 합니다.

코드 예제

apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
spec:
  containers:
  - name: nginx
    image: nginx:latest
    volumeMounts:
    - name: config-volume      # 볼륨 이름
      mountPath: /etc/nginx/conf.d  # 마운트할 경로
      readOnly: true           # 읽기 전용으로 마운트
  volumes:
  - name: config-volume        # 위의 volumeMounts와 매칭
    configMap:
      name: nginx-config       # 사용할 ConfigMap
      items:                   # 선택적: 특정 키만 마운트
      - key: nginx.conf        # ConfigMap의 키
        path: default.conf     # 생성될 파일 이름

설명

이것이 하는 일: ConfigMap의 데이터를 Kubernetes 볼륨으로 변환하고, 이를 컨테이너의 특정 디렉토리에 파일로 마운트하여 애플리케이션이 파일 시스템을 통해 읽을 수 있게 합니다. 첫 번째로, volumes 섹션에서 ConfigMap 기반 볼륨을 정의합니다.

configMap.name으로 어떤 ConfigMap을 사용할지 지정하고, items 배열로 특정 키만 선택할 수 있습니다. items를 생략하면 ConfigMap의 모든 키가 파일로 생성됩니다.

key는 ConfigMap의 원본 키 이름이고, path는 실제로 생성될 파일 이름으로 변환할 수 있습니다. 그 다음으로, volumeMounts 섹션에서 앞서 정의한 볼륨을 컨테이너의 어느 경로에 마운트할지 지정합니다.

mountPath가 /etc/nginx/conf.d라면 해당 디렉토리 아래에 파일들이 생성됩니다. readOnly를 true로 설정하면 컨테이너가 실수로 파일을 수정하는 것을 방지할 수 있고, 보안상 권장됩니다.

마지막으로, 실제 파일 시스템에서는 /etc/nginx/conf.d/default.conf 파일이 생성되고, 그 내용은 nginx-config ConfigMap의 nginx.conf 키 값이 됩니다. Nginx가 시작되면 이 파일을 읽어서 설정을 로드하게 됩니다.

ConfigMap이 업데이트되면 마운트된 파일도 자동으로 업데이트되지만(최대 몇 분 소요), 애플리케이션이 설정을 다시 로드하려면 별도의 reload 메커니즘이 필요합니다. 여러분이 이 코드를 사용하면 복잡한 멀티라인 설정 파일을 깔끔하게 관리할 수 있습니다.

Git으로 ConfigMap YAML을 버전 관리하면 설정 변경 이력이 남고, 여러 Pod가 동일한 설정 파일을 공유하여 일관성을 유지할 수 있으며, 설정 파일만 업데이트하고 애플리케이션 재배포 없이 변경사항을 반영할 수 있습니다.

실전 팁

💡 mountPath가 기존 디렉토리를 덮어쓰므로 subPath를 사용하여 특정 파일만 마운트할 수 있습니다

💡 ConfigMap 업데이트 시 볼륨은 자동 갱신되지만 애플리케이션은 수동으로 reload해야 하므로 sidecar 패턴을 고려하세요

💡 defaultMode로 파일 권한을 설정할 수 있으며 기본값은 0644입니다 (예: defaultMode: 0400)

💡 대용량 설정 파일은 ConfigMap 1MB 제한에 주의하고, 필요시 Persistent Volume을 고려하세요

💡 kubectl describe pod로 볼륨 마운트 상태를 확인하고, exec로 접속하여 실제 파일 내용을 검증하세요


4. Secret 기본 개념 - 민감한 정보를 안전하게 저장하기

시작하며

여러분이 데이터베이스 연결 문자열을 설정할 때를 생각해봅시다. 이 문자열에는 사용자 이름과 비밀번호가 포함되어 있습니다.

만약 이 정보를 ConfigMap에 평문으로 저장한다면 어떻게 될까요? Kubernetes API에 접근할 수 있는 누구나 kubectl get configmap 명령으로 비밀번호를 볼 수 있게 됩니다.

이런 보안 문제는 실제 프로덕션 환경에서 치명적인 결과를 초래할 수 있습니다. API 키가 유출되면 외부 서비스 비용이 폭증하거나, 데이터베이스 자격증명이 노출되면 고객 데이터가 위험에 처하게 됩니다.

특히 Git 저장소에 ConfigMap YAML을 커밋하면 민감한 정보가 영구적으로 기록되어 더 큰 문제가 됩니다. 바로 이럴 때 필요한 것이 Secret입니다.

Secret은 ConfigMap과 비슷하지만 민감한 데이터를 다루도록 특별히 설계되어, base64 인코딩과 접근 제어 등 추가 보안 기능을 제공합니다.

개요

간단히 말해서, Secret은 비밀번호, OAuth 토큰, SSH 키 같은 민감한 정보를 저장하기 위한 Kubernetes 오브젝트입니다. Secret이 필요한 이유는 민감한 데이터를 일반 설정과 분리하여 더 엄격하게 관리하고, 접근을 제한할 수 있기 때문입니다.

Kubernetes는 Secret을 메모리에만 저장하고 디스크에 기록하지 않으며, RBAC로 특정 ServiceAccount만 접근하도록 제한할 수 있습니다. 예를 들어, 프로덕션 데이터베이스 비밀번호는 프로덕션 네임스페이스의 특정 Pod만 읽을 수 있도록 설정하여, 개발자가 실수로 접근하는 것을 방지할 수 있습니다.

기존에는 민감한 정보를 ConfigMap에 저장하거나 환경변수로 하드코딩했다면, 이제는 Secret으로 분리하여 암호화하고 접근을 제어할 수 있습니다. Secret의 핵심 특징은 첫째, 데이터가 base64로 인코딩되어 저장된다는 점(암호화는 별도 설정 필요), 둘째, etcd에 저장 시 암호화 옵션을 활성화할 수 있다는 점, 셋째, ConfigMap과 동일한 방식(환경변수, 볼륨)으로 사용할 수 있다는 점입니다.

이러한 특징들이 민감한 데이터의 안전한 관리를 가능하게 합니다.

코드 예제

# Secret 생성 (YAML)
apiVersion: v1
kind: Secret
metadata:
  name: db-secret
type: Opaque  # 일반적인 사용자 정의 데이터
data:
  # base64로 인코딩된 값 (echo -n 'mypassword' | base64)
  username: YWRtaW4=           # 'admin'
  password: bXlwYXNzd29yZA==   # 'mypassword'
# 또는 stringData 사용 (자동 인코딩)
stringData:
  database-url: "postgresql://admin:mypassword@db:5432/mydb"
  api-key: "sk-proj-abc123xyz"

설명

이것이 하는 일: Secret은 민감한 데이터를 Kubernetes 클러스터에 안전하게 저장하고, 필요한 Pod에만 선택적으로 제공하여 보안을 강화합니다. 첫 번째로, type 필드는 Secret의 종류를 지정합니다.

Opaque는 가장 일반적인 타입으로 사용자 정의 데이터를 저장하며, 다른 타입으로는 kubernetes.io/service-account-token(ServiceAccount 토큰), kubernetes.io/dockerconfigjson(Docker 레지스트리 인증) 등이 있습니다. 타입에 따라 Kubernetes가 데이터 구조를 검증하고 특별한 처리를 할 수 있습니다.

그 다음으로, data 섹션에서는 값을 base64로 인코딩하여 저장합니다. 터미널에서 echo -n 'admin' | base64 명령으로 인코딩할 수 있으며, 디코딩은 echo 'YWRtaW4=' | base64 -d로 확인할 수 있습니다.

base64는 암호화가 아니라 인코딩이므로, 누구나 쉽게 디코딩할 수 있다는 점을 이해해야 합니다. 진짜 보안은 RBAC와 etcd 암호화에서 제공됩니다.

마지막으로, stringData 섹션은 평문 값을 입력하면 Kubernetes가 자동으로 base64 인코딩하여 data로 변환합니다. YAML 파일 작성 시 더 편리하지만, 이 파일을 Git에 커밋하면 평문이 그대로 노출되므로 주의해야 합니다.

실무에서는 stringData로 작성하되, Sealed Secrets나 External Secrets 같은 도구로 암호화한 후 Git에 저장하는 것이 권장됩니다. 여러분이 이 코드를 사용하면 민감한 정보를 코드나 ConfigMap에서 분리하여 보안을 강화할 수 있습니다.

RBAC 정책으로 특정 ServiceAccount만 Secret을 읽을 수 있도록 제한하고, 감사 로그로 누가 언제 Secret에 접근했는지 추적하며, Secret이 변경되면 자동으로 Pod를 재시작하는 오퍼레이터를 사용하여 보안 사고를 최소화할 수 있습니다.

실전 팁

💡 절대 Secret YAML을 평문으로 Git에 커밋하지 말고 Sealed Secrets, Vault, External Secrets를 사용하세요

💡 etcd 암호화를 활성화하여 Secret이 디스크에 암호화되어 저장되도록 설정하세요 (EncryptionConfiguration)

💡 kubectl create secret generic 명령으로 파일이나 리터럴 값에서 직접 Secret을 생성할 수 있습니다

💡 Secret 크기는 1MB로 제한되며, 너무 많은 Secret은 etcd 성능에 영향을 줄 수 있습니다

💡 Secret이 업데이트되어도 환경변수는 자동 갱신되지 않으므로 Pod를 재시작해야 합니다


5. Secret을 환경변수로 사용하기 - 안전한 자격증명 주입

시작하며

여러분의 애플리케이션이 외부 API를 호출할 때 API 키가 필요하다고 가정해봅시다. 이 키를 코드에 하드코딩하면 Git 히스토리에 영구적으로 남게 되고, ConfigMap에 저장하면 누구나 쉽게 볼 수 있습니다.

환경변수로 전달하고 싶지만, Deployment YAML에 직접 쓰는 것도 안전하지 않습니다. 이런 딜레마는 실무에서 매우 흔합니다.

클라우드 서비스 자격증명, OAuth 클라이언트 시크릿, 암호화 키 등 수많은 민감한 값들을 안전하게 전달해야 하는데, 각 환경마다 다른 값을 사용하므로 관리가 복잡합니다. 바로 이럴 때 Secret을 환경변수로 주입하는 방법을 사용합니다.

ConfigMap과 동일한 방식으로 사용할 수 있지만, 데이터는 암호화되어 저장되고 접근이 제한됩니다.

개요

간단히 말해서, Secret의 데이터를 환경변수로 주입하는 것은 애플리케이션 코드 변경 없이 민감한 정보를 안전하게 전달하는 표준 방법입니다. 이 방법이 필요한 이유는 대부분의 클라우드 SDK와 라이브러리가 환경변수를 통해 자격증명을 받도록 설계되어 있기 때문입니다.

AWS SDK는 AWS_ACCESS_KEY_ID와 AWS_SECRET_ACCESS_KEY를, Stripe API는 STRIPE_SECRET_KEY를 환경변수에서 읽습니다. 예를 들어, Node.js에서 Stripe를 초기화할 때 new Stripe(process.env.STRIPE_SECRET_KEY)처럼 사용하는 경우 Secret을 환경변수로 주입하면 완벽하게 동작합니다.

기존에는 .env 파일을 이미지에 포함시키거나 CI/CD 파이프라인에서 환경변수를 설정했다면, 이제는 Kubernetes Secret으로 중앙 관리하면서 RBAC로 접근을 제어할 수 있습니다. Secret 환경변수 주입의 핵심 특징은 첫째, ConfigMap과 완전히 동일한 문법을 사용한다는 점, 둘째, 여러 Secret을 조합할 수 있다는 점, 셋째, 컨테이너 내부에서는 일반 환경변수와 구별되지 않는다는 점입니다.

이러한 특징들이 기존 애플리케이션의 보안 강화를 매우 쉽게 만듭니다.

코드 예제

apiVersion: v1
kind: Pod
metadata:
  name: app-pod
spec:
  containers:
  - name: app
    image: myapp:1.0
    env:
    # 방법 1: Secret의 특정 키를 환경변수로
    - name: DATABASE_PASSWORD
      valueFrom:
        secretKeyRef:
          name: db-secret      # Secret 이름
          key: password        # Secret의 키
    # 방법 2: Secret 전체를 환경변수로 주입
    envFrom:
    - secretRef:
        name: api-secrets     # 모든 키가 환경변수로 변환됨

설명

이것이 하는 일: Secret의 데이터를 컨테이너 시작 시점에 환경변수로 주입하여, 애플리케이션이 민감한 정보를 안전하게 사용할 수 있도록 합니다. 첫 번째로, env 섹션의 secretKeyRef 방식은 특정 Secret의 특정 키만 선택하여 환경변수로 매핑합니다.

DATABASE_PASSWORD라는 환경변수 이름을 정의하고, db-secret Secret의 password 키 값을 가져옵니다. 이렇게 하면 환경변수 이름을 애플리케이션이 기대하는 형식으로 커스터마이즈할 수 있고, 여러 Secret에서 필요한 값만 조합할 수 있습니다.

그 다음으로, envFrom의 secretRef 방식은 Secret의 모든 키-값 쌍을 자동으로 환경변수로 변환합니다. api-secrets Secret에 STRIPE_KEY, SENDGRID_API_KEY, JWT_SECRET이 있다면, 세 개의 환경변수가 모두 생성됩니다.

이 방식은 관련된 여러 키를 한 번에 주입할 때 편리하지만, Secret의 모든 내용이 컨테이너에 노출되므로 최소 권한 원칙을 고려해야 합니다. 마지막으로, 두 방식을 함께 사용하여 여러 Secret을 조합할 수 있습니다.

예를 들어 공통 Secret은 envFrom으로 주입하고, 특정 서비스만 필요한 키는 env로 개별 지정할 수 있습니다. Kubernetes는 Pod 생성 시 모든 Secret을 읽어서 base64 디코딩한 후 환경변수로 설정하므로, 애플리케이션은 평문 값을 직접 사용할 수 있습니다.

여러분이 이 코드를 사용하면 애플리케이션 코드를 전혀 수정하지 않고도 자격증명을 안전하게 주입할 수 있습니다. 로컬 개발에서는 .env 파일을 사용하고, 프로덕션에서는 Kubernetes Secret을 사용하여 동일한 환경변수 이름으로 접근하므로 코드 호환성이 완벽합니다.

또한 Secret을 업데이트하고 Pod를 재시작하면 새로운 자격증명이 즉시 적용되어, 키 로테이션이 쉬워집니다.

실전 팁

💡 환경변수는 프로세스 목록(ps aux)에서 볼 수 있으므로 매우 민감한 정보는 볼륨 마운트를 고려하세요

💡 Secret이 없으면 Pod가 시작하지 않으므로 optional: true 플래그로 선택적으로 만들 수 있습니다

💡 같은 이름의 환경변수가 중복 정의되면 env가 envFrom보다 우선합니다

💡 Secret 업데이트 시 환경변수는 자동 갱신되지 않으므로 Deployment의 롤링 업데이트를 트리거하세요

💡 컨테이너 로그에 환경변수를 출력하지 않도록 주의하세요 (console.log(process.env)는 금지!)


6. Secret을 볼륨으로 마운트하기 - 파일 기반 자격증명 관리

시작하며

여러분이 TLS 인증서와 개인 키를 사용하여 HTTPS 서버를 구성한다고 가정해봅시다. 인증서 파일(cert.pem)과 키 파일(key.pem)은 각각 수십 줄의 멀티라인 데이터인데, 이를 환경변수로 전달하는 것은 매우 불편하고 일부 애플리케이션은 아예 지원하지 않습니다.

이런 문제는 파일 기반 자격증명에서 항상 발생합니다. SSH 키, Kubernetes ServiceAccount 토큰, Google Cloud 서비스 계정 JSON 파일, 또는 .dockerconfigjson 같은 경우 반드시 파일 형태로 제공되어야 합니다.

파일 경로를 읽는 라이브러리도 많아서 환경변수로는 해결할 수 없습니다. 바로 이럴 때 필요한 것이 Secret 볼륨 마운트입니다.

Secret의 데이터를 파일 시스템에 파일로 생성하여, 애플리케이션이 파일 경로로 접근할 수 있게 만듭니다.

개요

간단히 말해서, Secret 볼륨 마운트는 민감한 데이터를 컨테이너 파일 시스템의 파일로 제공하여, 파일 기반 인증과 설정을 지원합니다. 볼륨 마운트가 필요한 이유는 많은 보안 메커니즘이 파일을 요구하기 때문입니다.

TLS/SSL 라이브러리는 인증서 파일 경로를 파라미터로 받고, SSH 클라이언트는 ~/.ssh/id_rsa 파일을 읽으며, Kubernetes의 서비스 간 통신은 /var/run/secrets/kubernetes.io/serviceaccount/ 디렉토리의 토큰 파일을 사용합니다. 예를 들어, Node.js의 https.createServer()는 {key: fs.readFileSync('key.pem'), cert: fs.readFileSync('cert.pem')} 형태로 파일 경로를 요구합니다.

기존에는 인증서 파일을 컨테이너 이미지에 포함시켜야 했다면, 이제는 Secret으로 외부화하여 이미지 재빌드 없이 인증서를 갱신할 수 있습니다. Secret 볼륨의 핵심 특징은 첫째, 파일이 tmpfs(메모리 파일 시스템)에 생성되어 디스크에 기록되지 않는다는 점, 둘째, 파일 권한을 0400(읽기 전용)으로 설정하여 보안을 강화할 수 있다는 점, 셋째, Secret 업데이트 시 파일이 자동으로 갱신된다는 점입니다.

이러한 특징들이 민감한 파일의 안전한 관리를 가능하게 합니다.

코드 예제

apiVersion: v1
kind: Pod
metadata:
  name: tls-app
spec:
  containers:
  - name: app
    image: myapp:1.0
    volumeMounts:
    - name: tls-certs
      mountPath: /etc/tls          # 마운트할 디렉토리
      readOnly: true               # 읽기 전용
  volumes:
  - name: tls-certs
    secret:
      secretName: tls-secret       # Secret 이름
      defaultMode: 0400            # 파일 권한 (소유자만 읽기)
      items:                       # 선택적: 특정 키만 마운트
      - key: tls.crt
        path: cert.pem             # 파일 이름 커스터마이즈
      - key: tls.key
        path: key.pem

설명

이것이 하는 일: Secret의 데이터를 메모리 기반 파일 시스템에 파일로 생성하고, 컨테이너의 특정 경로에 마운트하여 애플리케이션이 파일 I/O로 읽을 수 있게 합니다. 첫 번째로, volumes 섹션에서 Secret 기반 볼륨을 정의합니다.

secretName으로 사용할 Secret을 지정하고, defaultMode로 파일 권한을 8진수로 설정합니다. 0400은 소유자만 읽을 수 있는 권한으로, 보안상 권장됩니다.

items 배열을 사용하면 Secret의 특정 키만 선택하고, path로 파일 이름을 변경할 수 있습니다. 예를 들어 tls.crt 키를 cert.pem 파일로 생성할 수 있습니다.

그 다음으로, volumeMounts 섹션에서 볼륨을 컨테이너의 /etc/tls 디렉토리에 마운트합니다. readOnly를 true로 설정하여 컨테이너가 파일을 수정하지 못하게 보호합니다.

마운트 후에는 /etc/tls/cert.pem과 /etc/tls/key.pem 파일이 생성되며, 애플리케이션은 일반 파일처럼 읽을 수 있습니다. 마지막으로, 이 파일들은 tmpfs에 저장되어 메모리에만 존재하고 디스크에 기록되지 않습니다.

Pod가 종료되면 파일도 사라지므로, 민감한 데이터가 디스크에 남지 않아 보안이 강화됩니다. Secret이 업데이트되면 Kubernetes가 자동으로 파일 내용을 갱신하므로(kubelet의 sync 주기마다), 인증서 갱신 시 Pod를 재시작하지 않아도 될 수 있습니다(단, 애플리케이션이 파일 변경을 감지하고 reload하는 기능이 있어야 함).

여러분이 이 코드를 사용하면 TLS 인증서, SSH 키, 서비스 계정 JSON 같은 파일 기반 자격증명을 안전하게 관리할 수 있습니다. 인증서 갱신 시 Secret만 업데이트하면 되고, 파일이 메모리에만 존재하여 디스크 유출 위험이 없으며, 파일 권한으로 추가 보안 계층을 제공하여 컨테이너 내부의 악의적인 프로세스로부터도 보호할 수 있습니다.

실전 팁

💡 TLS Secret은 type: kubernetes.io/tls을 사용하면 tls.crt와 tls.key 구조를 자동으로 검증합니다

💡 subPath를 사용하면 디렉토리 전체를 덮어쓰지 않고 특정 파일만 추가할 수 있습니다

💡 Secret 업데이트 시 파일은 원자적으로 갱신되므로(symlink 변경) 읽기 중 손상 위험이 없습니다

💡 여러 Secret을 동일 경로에 마운트할 수 없으므로 projected volume을 사용하여 결합하세요

💡 파일 권한 변경이 필요하면 initContainer로 chown/chmod를 실행하거나 securityContext를 설정하세요


7. kubectl로 ConfigMap과 Secret 생성하기 - 명령형 접근법

시작하며

여러분이 빠르게 ConfigMap이나 Secret을 생성해야 하는 상황을 생각해봅시다. YAML 파일을 작성하고, 값을 base64로 인코딩하고, kubectl apply를 실행하는 전체 과정은 시간이 많이 걸립니다.

특히 기존 설정 파일이나 디렉토리를 그대로 ConfigMap으로 변환하고 싶을 때는 더욱 번거롭습니다. 이런 문제는 개발 중이나 프로토타이핑 단계에서 자주 발생합니다.

빠른 테스트를 위해 간단한 설정을 주입하거나, 로컬 설정 파일을 클러스터에 배포하거나, 일회성 Secret을 생성하는 경우 YAML을 수동으로 작성하는 것은 비효율적입니다. 바로 이럴 때 필요한 것이 kubectl create 명령입니다.

명령행에서 직접 값을 지정하거나 파일에서 읽어서 ConfigMap과 Secret을 즉시 생성할 수 있습니다.

개요

간단히 말해서, kubectl create configmap과 kubectl create secret 명령은 YAML 없이 명령행에서 바로 리소스를 생성하는 명령형 접근법입니다. 이 방법이 필요한 이유는 빠른 반복 개발과 임시 리소스 생성에 매우 효율적이기 때문입니다.

특히 기존 파일이나 디렉토리를 그대로 Kubernetes로 가져올 때, 또는 스크립트에서 동적으로 ConfigMap을 생성할 때 유용합니다. 예를 들어, nginx.conf 파일을 ConfigMap으로 만들거나, .env 파일을 Secret으로 변환하는 경우 한 줄의 명령으로 즉시 완료됩니다.

기존에는 YAML 파일을 작성하고 kubectl apply를 사용했다면, 이제는 kubectl create로 직접 생성하거나 --dry-run=client -o yaml로 YAML을 자동 생성할 수 있습니다. 명령형 생성의 핵심 특징은 첫째, 리터럴 값, 파일, 디렉토리 등 다양한 소스에서 데이터를 가져올 수 있다는 점, 둘째, base64 인코딩을 자동으로 처리한다는 점, 셋째, --dry-run으로 YAML을 미리 확인하고 파일로 저장할 수 있다는 점입니다.

이러한 특징들이 빠른 개발과 자동화를 가능하게 합니다.

코드 예제

# ConfigMap 생성 - 리터럴 값
kubectl create configmap app-config \
  --from-literal=API_URL=https://api.example.com \
  --from-literal=LOG_LEVEL=debug

# ConfigMap 생성 - 파일에서
kubectl create configmap nginx-config \
  --from-file=nginx.conf \
  --from-file=configs/

# Secret 생성 - 리터럴 값 (자동 base64 인코딩)
kubectl create secret generic db-secret \
  --from-literal=username=admin \
  --from-literal=password=mypassword

# Secret 생성 - 파일에서
kubectl create secret generic tls-secret \
  --from-file=tls.crt=./cert.pem \
  --from-file=tls.key=./key.pem

# YAML 생성 (실제 적용 안 함)
kubectl create configmap app-config \
  --from-literal=API_URL=https://api.example.com \
  --dry-run=client -o yaml > configmap.yaml

설명

이것이 하는 일: kubectl create 명령은 다양한 소스에서 데이터를 읽어서 ConfigMap이나 Secret을 즉시 클러스터에 생성하거나 YAML 파일로 출력합니다. 첫 번째로, --from-literal 플래그는 명령행에서 직접 키-값 쌍을 지정합니다.

여러 개의 --from-literal을 사용하여 다양한 설정을 추가할 수 있으며, Secret의 경우 자동으로 base64 인코딩됩니다. 이 방식은 간단한 설정을 빠르게 테스트할 때나, CI/CD 스크립트에서 환경별 값을 주입할 때 유용합니다.

그 다음으로, --from-file 플래그는 파일이나 디렉토리에서 데이터를 읽어옵니다. 단일 파일을 지정하면 파일 이름이 키가 되고 내용이 값이 되며, --from-file=custom-key=./file.txt 형식으로 키 이름을 커스터마이즈할 수 있습니다.

디렉토리를 지정하면 모든 파일이 자동으로 키-값 쌍으로 변환되어, 설정 디렉토리를 통째로 ConfigMap으로 만들 수 있습니다. 마지막으로, --dry-run=client -o yaml 옵션은 실제로 리소스를 생성하지 않고 YAML을 출력만 합니다.

이를 파일로 리디렉션하면 자동으로 YAML 파일이 생성되어, 수동으로 작성할 필요가 없습니다. 생성된 YAML을 검토하고 수정한 후 Git에 커밋하면, 선언형 관리의 이점을 그대로 누릴 수 있습니다.

여러분이 이 코드를 사용하면 개발 속도가 크게 향상됩니다. 설정 파일 변경 시 한 줄의 명령으로 즉시 ConfigMap을 업데이트하고, 로컬 .env 파일을 Secret으로 변환하여 개발 환경과 프로덕션을 동일하게 유지하며, --dry-run으로 YAML을 자동 생성하여 타이핑 오류를 줄이고 일관된 포맷을 유지할 수 있습니다.

실전 팁

💡 --from-env-file 플래그로 .env 형식 파일(KEY=VALUE)을 한 번에 로드할 수 있습니다

💡 기존 리소스를 업데이트하려면 kubectl create 대신 kubectl create --dry-run + kubectl apply를 사용하세요

💡 Secret 타입 지정은 --type 플래그로 가능합니다 (예: --type=kubernetes.io/tls)

💡 --append-hash 플래그로 ConfigMap 이름에 해시를 추가하여 immutable 패턴을 구현할 수 있습니다

💡 --from-file과 --from-literal을 함께 사용하여 파일과 리터럴 값을 결합할 수 있습니다


8. ConfigMap과 Secret 업데이트 전략 - 무중단 배포 구현

시작하며

여러분이 프로덕션 환경에서 API 엔드포인트를 변경해야 하는 상황을 생각해봅시다. ConfigMap을 업데이트했지만, 실행 중인 Pod는 여전히 이전 설정을 사용하고 있습니다.

환경변수는 Pod 시작 시점에 주입되므로 자동으로 갱신되지 않기 때문입니다. 이런 문제는 실무에서 매우 중요한 이슈입니다.

설정 변경 시 모든 Pod를 수동으로 재시작하는 것은 다운타임을 유발하고, 일부 Pod만 재시작되면 서로 다른 설정을 사용하는 Pod가 공존하여 일관성 문제가 발생합니다. 또한 Secret 업데이트 시 롤백 전략도 고려해야 합니다.

바로 이럴 때 필요한 것이 체계적인 업데이트 전략입니다. Deployment의 롤링 업데이트를 트리거하거나, ConfigMap을 immutable하게 관리하여 안전하고 일관된 설정 변경을 보장할 수 있습니다.

개요

간단히 말해서, ConfigMap과 Secret 업데이트 전략은 설정 변경 시 모든 Pod가 일관되게 새로운 설정을 사용하도록 보장하는 방법론입니다. 업데이트 전략이 필요한 이유는 Kubernetes가 ConfigMap/Secret 변경을 자동으로 Pod에 반영하지 않기 때문입니다.

환경변수는 Pod 생성 시점에 고정되고, 볼륨 마운트는 최대 몇 분 후 갱신되지만 애플리케이션이 파일 변경을 감지해야 합니다. 예를 들어, 데이터베이스 연결 문자열을 변경했는데 일부 Pod는 이전 DB에, 일부는 새 DB에 연결되면 데이터 불일치가 발생할 수 있습니다.

기존에는 kubectl delete pod로 수동 재시작하거나 Deployment를 삭제 후 재생성했다면, 이제는 롤링 업데이트나 immutable ConfigMap 패턴으로 무중단 배포를 구현할 수 있습니다. 업데이트 전략의 핵심 특징은 첫째, Deployment의 template.spec에 변경을 트리거하여 자동 롤링 업데이트를 유도한다는 점, 둘째, ConfigMap을 immutable로 설정하고 새 이름으로 생성하여 원자적 업데이트를 구현한다는 점, 셋째, 버전 관리와 롤백이 쉬워진다는 점입니다.

이러한 특징들이 안전하고 예측 가능한 설정 관리를 가능하게 합니다.

코드 예제

# 방법 1: Deployment에 annotation 추가로 롤링 업데이트 트리거
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
spec:
  template:
    metadata:
      annotations:
        # ConfigMap 변경 시 이 값을 업데이트하면 Pod 재생성
        configmap-version: "v2"
    spec:
      containers:
      - name: app
        envFrom:
        - configMapRef:
            name: app-config

# 방법 2: Immutable ConfigMap 패턴
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config-v2  # 버전을 이름에 포함
immutable: true        # 변경 불가능하게 설정
data:
  API_URL: "https://api.example.com"

설명

이것이 하는 일: ConfigMap/Secret 변경 시 Deployment의 Pod template을 수정하여 Kubernetes가 자동으로 롤링 업데이트를 수행하도록 만들거나, 새로운 ConfigMap을 생성하여 원자적 전환을 구현합니다. 첫 번째로, annotation 기반 방법은 Deployment의 template.metadata.annotations에 버전 정보를 추가합니다.

ConfigMap을 업데이트한 후 이 annotation 값을 변경하면(예: v1 → v2), Kubernetes는 Pod template이 변경되었다고 판단하여 롤링 업데이트를 시작합니다. 이렇게 하면 새 Pod는 최신 ConfigMap을 읽어서 시작되고, 이전 Pod는 점진적으로 종료되어 무중단 배포가 가능합니다.

kubectl patch deployment myapp -p '{"spec":{"template":{"metadata":{"annotations":{"configmap-version":"v3"}}}}}' 명령으로 자동화할 수 있습니다. 그 다음으로, immutable ConfigMap 패턴은 ConfigMap을 수정하지 않고 새로운 ConfigMap을 생성합니다.

immutable: true로 설정하면 ConfigMap이 절대 변경되지 않아서 etcd 성능이 향상되고, 실수로 수정하는 것을 방지할 수 있습니다. 설정 변경 시 app-config-v2, app-config-v3처럼 새 이름으로 ConfigMap을 생성하고, Deployment의 configMapRef.name을 업데이트하면 자동으로 롤링 업데이트가 트리거됩니다.

마지막으로, 두 방법 모두 롤백이 쉽습니다. annotation 방법은 kubectl rollout undo deployment myapp으로 이전 버전으로 되돌릴 수 있고, immutable 방법은 Deployment를 이전 ConfigMap 이름으로 업데이트하면 됩니다.

또한 Kustomize나 Helm의 configMapGenerator를 사용하면 자동으로 해시가 추가된 ConfigMap 이름을 생성하여 이 패턴을 자동화할 수 있습니다. 여러분이 이 코드를 사용하면 설정 변경 시 안전하고 예측 가능한 배포를 구현할 수 있습니다.

롤링 업데이트로 무중단 배포를 보장하고, immutable 패턴으로 설정 히스토리를 자동으로 관리하며, 문제 발생 시 빠르게 롤백하여 서비스 안정성을 유지할 수 있습니다.

실전 팁

💡 Kustomize의 configMapGenerator는 자동으로 해시를 추가하여 immutable 패턴을 구현합니다

💡 Reloader 같은 오퍼레이터를 사용하면 ConfigMap 변경 시 자동으로 Deployment를 업데이트할 수 있습니다

💡 볼륨 마운트 방식은 kubelet 동기화 주기(기본 1분)마다 자동 갱신되지만 애플리케이션 reload가 필요합니다

💡 Blue-Green 배포 전략과 결합하면 더 안전한 설정 변경이 가능합니다

💡 GitOps 도구(ArgoCD, Flux)는 ConfigMap 변경을 Git 커밋으로 추적하여 감사 로그를 제공합니다


9. 보안 모범 사례 - Secret을 안전하게 관리하기

시작하며

여러분이 Kubernetes Secret에 데이터베이스 비밀번호를 저장했다고 가정해봅시다. 하지만 Secret의 base64 인코딩은 암호화가 아니라는 사실을 알고 계신가요?

누군가 Kubernetes API에 접근할 수 있다면 kubectl get secret -o yaml로 쉽게 디코딩할 수 있습니다. 이런 보안 취약점은 실제 프로덕션 환경에서 치명적인 결과를 초래할 수 있습니다.

Secret YAML을 Git에 커밋하면 영구적으로 노출되고, etcd가 암호화되지 않으면 디스크에서 평문이 유출되며, 과도한 RBAC 권한은 불필요한 접근을 허용합니다. 최근 보안 사고의 많은 부분이 부적절한 Secret 관리에서 비롯됩니다.

바로 이럴 때 필요한 것이 Secret 보안 모범 사례입니다. etcd 암호화, RBAC 최소 권한, 외부 Secret 관리 시스템 등을 결합하여 다층 보안을 구현할 수 있습니다.

개요

간단히 말해서, Secret 보안 모범 사례는 민감한 데이터를 다층적으로 보호하고, 접근을 제한하며, 유출 위험을 최소화하는 종합적인 전략입니다. 보안 모범 사례가 필요한 이유는 Kubernetes Secret의 기본 설정만으로는 충분한 보안을 제공하지 않기 때문입니다.

base64는 단순 인코딩일 뿐이고, etcd에 평문으로 저장될 수 있으며, 모든 ServiceAccount가 기본적으로 Secret을 읽을 수 있는 환경도 있습니다. 예를 들어, 개발자가 디버깅을 위해 과도한 권한을 가지면 프로덕션 데이터베이스 자격증명에 접근할 수 있어 위험합니다.

기존에는 Secret을 단순히 생성하고 사용했다면, 이제는 암호화, 접근 제어, 감사, 외부 시스템 통합 등 다층 보안을 구현해야 합니다. 보안 모범 사례의 핵심 특징은 첫째, etcd 암호화로 저장 시 암호화(encryption at rest)를 구현한다는 점, 둘째, RBAC로 최소 권한 원칙을 적용한다는 점, 셋째, Sealed Secrets나 External Secrets로 Git 저장과 외부 관리를 분리한다는 점입니다.

이러한 특징들이 종합적인 보안 태세를 구축합니다.

코드 예제

# 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
rules:
- apiGroups: [""]
  resources: ["secrets"]
  resourceNames: ["db-secret"]  # 특정 Secret만 허용
  verbs: ["get"]

# 3. Sealed Secret (Git 안전 저장)
# kubeseal로 암호화된 Secret
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
  name: db-secret
spec:
  encryptedData:
    password: AgBY8... # 암호화된 값

설명

이것이 하는 일: 다양한 보안 메커니즘을 조합하여 Secret이 저장, 전송, 접근되는 모든 단계에서 보호받도록 하고, 유출 시 영향을 최소화합니다. 첫 번째로, EncryptionConfiguration은 Kubernetes API 서버 수준에서 etcd에 저장되는 Secret을 암호화합니다.

aescbc 프로바이더는 AES-CBC 알고리즘으로 암호화하며, 32바이트 키를 안전하게 관리해야 합니다(KMS 사용 권장). 이 설정을 활성화하면 etcd 데이터베이스가 유출되어도 암호화 키 없이는 Secret을 읽을 수 없습니다.

API 서버의 --encryption-provider-config 플래그로 적용하며, 기존 Secret은 kubectl get secrets --all-namespaces -o json | kubectl replace -f -로 재암호화해야 합니다. 그 다음으로, RBAC Role은 특정 ServiceAccount가 특정 Secret만 읽을 수 있도록 제한합니다.

resourceNames 필드로 db-secret만 허용하고, verbs를 get으로 제한하여 list나 watch를 막습니다. 이렇게 하면 애플리케이션 Pod는 필요한 Secret만 접근할 수 있고, 다른 Secret은 볼 수 없어 최소 권한 원칙을 준수합니다.

네임스페이스별로 Role을 분리하면 환경 간 격리도 강화됩니다. 마지막으로, Sealed Secrets는 Secret을 공개키로 암호화하여 Git에 안전하게 저장할 수 있게 합니다.

kubeseal 명령으로 일반 Secret을 SealedSecret으로 변환하면, 클러스터의 컨트롤러만 가진 개인키로만 복호화할 수 있습니다. 이렇게 하면 Git 히스토리에 암호화된 값만 남고, CI/CD 파이프라인에서도 평문을 다룰 필요가 없어 유출 위험이 크게 줄어듭니다.

External Secrets Operator를 사용하면 AWS Secrets Manager, HashiCorp Vault 같은 전문 시스템에서 Secret을 가져와 더 강력한 보안을 구현할 수 있습니다. 여러분이 이 코드를 사용하면 엔터프라이즈급 보안을 구현할 수 있습니다.

etcd 유출 시에도 암호화로 보호되고, RBAC로 내부자 위협을 줄이며, Sealed Secrets로 Git 기반 워크플로우를 안전하게 유지하고, 감사 로그로 모든 Secret 접근을 추적하여 컴플라이언스 요구사항을 충족할 수 있습니다.

실전 팁

💡 AWS/GCP/Azure KMS를 사용하면 암호화 키를 클라우드에서 안전하게 관리할 수 있습니다

💡 Vault나 AWS Secrets Manager로 Secret을 중앙 관리하고 자동 로테이션을 구현하세요

💡 Pod Security Standards로 hostPath 볼륨을 차단하여 노드의 Secret 파일 접근을 막으세요

💡 Audit logging을 활성화하여 누가 언제 어떤 Secret에 접근했는지 추적하세요

💡 Secret을 환경변수로 주입 시 컨테이너 로그나 crash dump에 노출될 수 있으니 주의하세요


10. 실전 활용 패턴 - 환경별 설정 관리하기

시작하며

여러분이 개발, 스테이징, 프로덕션 세 가지 환경을 운영한다고 가정해봅시다. 각 환경은 서로 다른 데이터베이스, API 엔드포인트, 로그 레벨을 사용하지만, 애플리케이션 코드는 동일합니다.

어떻게 환경마다 다른 설정을 깔끔하게 관리할 수 있을까요? 이런 문제는 모든 프로덕션 시스템에서 발생합니다.

환경별로 다른 ConfigMap을 수동으로 관리하면 실수가 잦고, 설정 드리프트가 발생하여 환경 간 일관성이 깨집니다. 또한 새로운 환경을 추가할 때마다 모든 설정을 복사하고 수정하는 번거로움이 있습니다.

바로 이럴 때 필요한 것이 체계적인 환경별 설정 관리 패턴입니다. Kustomize나 Helm을 사용하여 기본 설정과 환경별 오버라이드를 분리하고, 자동화된 배포 파이프라인을 구축할 수 있습니다.

개요

간단히 말해서, 환경별 설정 관리 패턴은 공통 설정과 환경별 차이를 구조적으로 분리하여, 일관성을 유지하면서도 환경별 커스터마이징을 가능하게 하는 방법론입니다. 환경별 관리가 필요한 이유는 코드와 설정의 분리만으로는 부족하고, 설정 내에서도 공통 부분과 환경별 부분을 분리해야 하기 때문입니다.

모든 환경에서 공통으로 사용하는 설정(예: 기능 플래그)과 환경마다 다른 설정(예: DB 연결 문자열)을 따로 관리하면 유지보수가 쉬워집니다. 예를 들어, 새로운 설정 키를 추가할 때 기본값을 한 곳에 정의하고 필요한 환경만 오버라이드하면 됩니다.

기존에는 dev-configmap.yaml, staging-configmap.yaml, prod-configmap.yaml을 각각 수동으로 관리했다면, 이제는 베이스와 오버레이 구조로 DRY 원칙을 적용할 수 있습니다. 환경별 관리의 핵심 특징은 첫째, Kustomize의 베이스와 오버레이로 계층적 설정을 구현한다는 점, 둘째, 네임스페이스로 환경을 격리한다는 점, 셋째, GitOps로 환경별 설정 변경을 추적하고 자동 배포한다는 점입니다.

이러한 특징들이 확장 가능하고 유지보수하기 쉬운 설정 관리를 가능하게 합니다.

코드 예제

# base/configmap.yaml - 공통 설정
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  LOG_FORMAT: "json"
  CACHE_TTL: "3600"
  FEATURE_NEW_UI: "true"

# overlays/production/configmap-patch.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  LOG_LEVEL: "error"        # 프로덕션만 error
  API_URL: "https://api.prod.example.com"

# overlays/production/kustomization.yaml
resources:
- ../../base
patches:
- path: configmap-patch.yaml
namespace: production

설명

이것이 하는 일: 공통 설정을 베이스로 정의하고, 환경별 차이만 오버레이로 패치하여, 코드 중복 없이 여러 환경의 설정을 체계적으로 관리합니다. 첫 번째로, base 디렉토리에는 모든 환경에서 공통으로 사용하는 설정을 정의합니다.

LOG_FORMAT, CACHE_TTL, FEATURE_NEW_UI처럼 환경에 관계없이 동일한 값들을 한 곳에 모아서, 수정 시 모든 환경에 일괄 적용됩니다. 이 베이스는 어떤 환경에도 직접 배포되지 않고, 오버레이의 기반으로만 사용됩니다.

그 다음으로, overlays/production 디렉토리에는 프로덕션 환경만의 차이를 정의합니다. configmap-patch.yaml에서 LOG_LEVEL과 API_URL처럼 프로덕션 전용 값을 지정하면, Kustomize가 베이스와 병합하여 최종 ConfigMap을 생성합니다.

동일한 방식으로 overlays/development, overlays/staging을 만들어 각 환경별 설정을 관리할 수 있습니다. 마지막으로, kustomization.yaml은 베이스를 참조하고, 패치를 적용하며, 네임스페이스를 지정합니다.

kubectl apply -k overlays/production 명령으로 프로덕션 전용 설정이 적용된 리소스가 배포됩니다. ArgoCD나 Flux 같은 GitOps 도구와 결합하면, Git 커밋만으로 자동 배포가 트리거되어 완전한 Infrastructure as Code를 구현할 수 있습니다.

여러분이 이 코드를 사용하면 환경 관리가 극적으로 단순해집니다. 공통 설정 변경 시 베이스만 수정하면 모든 환경에 자동 반영되고, 새 환경 추가 시 오버레이만 작성하면 되며, Git 히스토리로 환경별 설정 변경을 추적하여 언제든 롤백할 수 있고, 코드 리뷰로 설정 변경을 검증하여 실수를 줄일 수 있습니다.

실전 팁

💡 Helm의 values.yaml과 values-production.yaml로 비슷한 패턴을 구현할 수 있습니다

💡 ConfigMap 이름에 환경을 포함시키면 (app-config-prod) 같은 클러스터에 여러 환경을 배포할 수 있습니다

💡 Secret은 베이스에 두지 말고 환경별 오버레이에만 정의하여 Git 노출을 방지하세요

💡 Kustomize의 configMapGenerator를 사용하면 자동으로 해시가 추가되어 immutable 패턴이 적용됩니다

💡 CI/CD 파이프라인에서 환경별로 다른 브랜치나 태그를 사용하여 배포를 자동화하세요


#Kubernetes#ConfigMap#Secret#Configuration#DevOps#Python