목차
6 장

ConfigMap과 Secret

ConfigMap과 Secret으로 설정과 비밀번호를 매니페스트 본체에서 분리합니다. 12-factor의 "설정은 환경에 둔다"를 K8s에서 푸는 모양, env · envFrom · volume 세 가지 주입 방식, Secret의 base64가 암호화가 아니라는 한 줄, 그리고 설정 변경 시 Pod 재시작이 필요한 이유까지 다룹니다.

5장 Service까지 만든 매니페스트에는 한 가지가 어색하게 남아 있습니다 — 이미지 태그 · 포트 · 도메인 같은 값이 매니페스트에 직접 적힌 채라는 점입니다. 이번 챕터에서는 그 값을 매니페스트 본체에서 분리하는 두 객체인 ConfigMapSecret을 다룹니다. 설정은 어떻게 주입하는지, 비밀값은 어떻게 다른지, 그리고 값이 바뀌었을 때 Pod에 어떻게 다시 반영하는지까지 정리합니다.

이번 챕터의 끝에서는 같은 Deployment 매니페스트 한 장을 dev / staging / prod 어디에든 그대로 적용할 수 있는 모양이 손에 들어옵니다. 환경별로 달라지는 값은 ConfigMap · Secret 쪽에만 갈라져 있으면 되고, 워크로드 정의는 한 벌로 충분합니다.

12-factor의 한 줄 — 설정은 환경에 둔다 #

ConfigMap · Secret이 풀려고 하는 문제는 K8s가 처음 만들어 낸 게 아닙니다. 컨테이너가 보편화되기 한참 전부터 웹 앱 운영의 정설로 굳어진 패턴입니다. 가장 자주 인용되는 출처가 12-factor app의 III 번 항목, 한 줄로 표현하면 이렇습니다.

Store config in the environment.

설정은 환경에 둔다.

여기서 “설정 (config)“은 환경마다 달라지는 값들을 가리킵니다 — DB 호스트, 외부 API 키, 로그 레벨, 비밀번호 같은 값입니다. 컨테이너 시대 이전에는 이걸 환경변수, /etc/의 설정 파일, .env 파일 같은 형태로 풀었습니다. K8s 시대에 와서는 그 역할을 ConfigMap (평범한 설정값)과 Secret (비밀값)이 맡습니다.

왜 굳이 분리해야 하는지 한 줄씩 짚어 두면 다음 셋입니다.

  • 이미지 · 코드 변경 없이 설정만 바꾸기 — 같은 컨테이너 이미지를 그대로 두고 환경변수만 다르게 줘서 동작을 바꿀 수 있습니다. 새 빌드도, 새 이미지 태그도 필요 없습니다.
  • 환경별 다중 배포 — dev / staging / prod의 매니페스트가 거의 같고, 다른 부분은 ConfigMap · Secret으로 떨어져 있습니다. 워크로드 정의를 환경마다 통째로 복제하지 않아도 됩니다.
  • 비밀값을 git에 안 올리기 — DB 비밀번호, API 토큰 같은 값이 매니페스트 본체에 평문으로 적혀 있으면 그게 곧 git 저장소에 올라가게 됩니다. Secret 객체로 분리하면 매니페스트는 “그 Secret을 참조한다"라는 한 줄만 적고, 실제 값은 별도 경로로 클러스터에 들어갑니다.

이 셋이 운영의 거의 모든 결정을 좌우합니다. 그럼 ConfigMap부터 보겠습니다.

ConfigMap — 설정의 키-값 묶음 #

ConfigMap은 이름 그대로 설정값의 키-값 모음을 들고 있는 K8s 객체입니다. 매니페스트 한 장으로 만듭니다.

web-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: web-config
data:
  LOG_LEVEL: "info"
  APP_GREETING: "hello from k8s"
  app.conf: |
    server {
      listen 80;
      location / {
        return 200 "ok\n";
      }
    }

apiVersion은 Service와 마찬가지로 코어 그룹의 **v1**입니다. ConfigMap은 워크로드 컨트롤러가 아니라서 apps/v1이 아닙니다.

핵심 필드는 data 한 곳입니다. 그 안의 모양이 두 갈래입니다.

  • 짧은 키-값 (스칼라)LOG_LEVEL: "info", APP_GREETING: "hello from k8s"처럼 한 줄짜리 값입니다. 환경변수에 그대로 부어 쓰기 좋은 형태입니다.
  • 멀티라인 파일 — YAML의 블록 스칼라 (|)로 적은 긴 텍스트입니다. 위 예시의 app.conf처럼 nginx 설정 파일, 앱의 config.yaml, 작은 SQL 스크립트 같은 것들을 그대로 넣을 수 있습니다.

명령형 생성도 한 번 짚어 두면 #

매니페스트로 적는 길 외에 명령형으로 만드는 길도 있습니다.

명령형으로 ConfigMap 만들기
kubectl create configmap web-config \
  --from-literal=LOG_LEVEL=info \
  --from-literal=APP_GREETING="hello from k8s" \
  --from-file=app.conf

--from-literal은 인라인 키-값을, --from-file은 디스크의 파일을 그대로 가져와 키로 등록합니다. 빠르긴 하지만 분명한 단점이 있습니다 — 만들어진 ConfigMap이 매니페스트로 안 남습니다. 다음 사람이 클러스터의 ConfigMap을 봐도, 이 값이 어디서 왔는지 git 저장소에서 추적할 길이 없습니다. 운영에서는 거의 항상 kubectl apply -f 흐름으로 가고, 명령형은 디버깅 · 실험 중에만 잠깐 씁니다.

크기 한도 — 1 MiB #

ConfigMap이 들고 있는 값의 총합은 **1 MiB (메비바이트)**를 넘을 수 없습니다. 이건 K8s의 임의 결정이 아니라 그 아래의 etcd가 객체 한 개당 들 수 있는 크기 상한입니다. 작은 설정 파일 · 환경변수 묶음은 이 한도 안에 충분히 들어가지만, 큰 정적 자산 (예: 모델 가중치, 큰 SQL 스키마, 브라우저용 번들 파일)을 ConfigMap에 욱여넣는 것은 패턴에 맞지 않습니다. 그런 자산은 별도 스토리지 (S3 · GCS, PV)에 두고 컨테이너에서 받아 가는 모양이 정공법입니다.

만들어 두고 한 번 봅시다.

ConfigMap 적용
kubectl apply -f web-config.yaml
출력 예시
configmap/web-config created
ConfigMap 목록
kubectl get cm
출력 예시
NAME               DATA   AGE
kube-root-ca.crt   1      2d
web-config         3      10s

컬럼명을 한 줄로 짚어 두면 — NAME / DATA / AGE입니다. DATA 컬럼의 숫자는 data 아래의 키 개수입니다. 위 예시에서 3개 키 (LOG_LEVEL, APP_GREETING, app.conf)를 넣었으니 3으로 찍힙니다. kube-root-ca.crt는 K8s가 자체적으로 들고 있는 ConfigMap이라 신경 안 써도 됩니다.

ConfigMap을 Pod에 주입하는 세 가지 방법 #

ConfigMap을 만들기만 해서는 Pod가 그 값을 알아채지 못합니다. Pod가 그 값을 어떻게 받아 가는지를 매니페스트에 적어 줘야 합니다. 방법이 셋 있고, 이 셋의 차이를 잡아 두는 게 이번 챕터의 가장 실용적인 부분입니다.

1. 단일 키 → 환경변수 (env.valueFrom.configMapKeyRef) #

ConfigMap의 키 한 개를 컨테이너의 환경변수 하나로 매핑하는 가장 명시적인 모양입니다.

env.valueFrom.configMapKeyRef
spec:
  containers:
    - name: web
      image: nginx:1.27
      env:
        - name: LOG_LEVEL
          valueFrom:
            configMapKeyRef:
              name: web-config
              key: LOG_LEVEL

env3장 kubectl과 첫 Pod의 매니페스트에서 짧게 본 적이 있는 그 필드입니다. 보통 인라인으로 value: "info"처럼 적지만, valueFrom을 쓰면 다른 객체에서 값을 끌어다 쓴다는 뜻이 됩니다. configMapKeyRefname이 ConfigMap 이름, key가 그 안의 키 이름입니다.

장점은 명시성입니다 — 어떤 환경변수가 어느 ConfigMap의 어느 키에서 왔는지가 매니페스트에 그대로 드러납니다. 단점은 길이입니다. 환경변수가 여러 개라면 그만큼 줄이 길어집니다.

2. 전체 키 → 환경변수 한꺼번에 (envFrom.configMapRef) #

ConfigMap 안의 키 전부를 한 번에 환경변수로 부어 넣고 싶을 때 쓰는 모양입니다.

envFrom.configMapRef
spec:
  containers:
    - name: web
      image: nginx:1.27
      envFrom:
        - configMapRef:
            name: web-config

이렇게 적으면 web-config의 모든 키가 컨테이너의 환경변수로 자동 주입됩니다. LOG_LEVEL, APP_GREETING, app.conf 셋이 모두 같은 이름의 환경변수가 됩니다. 짧고 편하지만 한 가지 주의할 점이 있습니다 — ConfigMap의 키 이름이 그대로 환경변수명이 됩니다. 그래서 ConfigMap을 envFrom으로 쓸 의도라면 키 이름을 UPPER_SNAKE_CASE로 두는 게 무난합니다. app.conf처럼 점이 들어간 키는 환경변수로 쓰기에 적합하지 않습니다 (셸에서 점이 들어간 환경변수는 다루기 까다롭습니다). 그런 키는 다음 절의 볼륨 마운트로 가는 게 맞습니다.

3. 파일로 마운트 (volumes.configMap + volumeMounts) #

app.conf처럼 그 자체가 파일이어야 의미가 있는 값은 컨테이너 안에 파일로 넣어 줘야 합니다. ConfigMap을 볼륨처럼 마운트하면 키마다 한 개의 파일이 만들어집니다.

volumes + volumeMounts
spec:
  containers:
    - name: web
      image: nginx:1.27
      volumeMounts:
        - name: app-conf
          mountPath: /etc/myapp
  volumes:
    - name: app-conf
      configMap:
        name: web-config
        items:
          - key: app.conf
            path: app.conf

읽는 법은 두 단계입니다.

  • volumes — Pod 단위로 정의하는 볼륨입니다. 위에서는 app-conf라는 이름의 볼륨을 만들었고, 그 내용물이 web-config ConfigMap의 app.conf 키로 정해져 있습니다. items를 적으면 ConfigMap 안에서 일부 키만 골라 마운트할 수 있습니다. items를 생략하면 ConfigMap의 모든 키가 각각 파일로 마운트됩니다.
  • volumeMounts — 컨테이너 단위로, 위 볼륨을 컨테이너 파일시스템의 어느 경로에 끼워 넣을지 정합니다. mountPath: /etc/myapp 이면 컨테이너 안에서 /etc/myapp/app.conf 경로로 그 내용이 보입니다.

언제 어떤 걸 쓸까 #

세 갈래의 용도를 한 표로 정리해 두면 결정이 한결 빠릅니다.

주입 모양적합한 경우설명
env.valueFrom.configMapKeyRef환경변수 한두 개명시적이지만 길이가 깁니다
envFrom.configMapRef환경변수 묶음 전체짧지만 키 이름 규칙이 필요합니다
volumes.configMap설정 파일 전체를 파일로앱이 파일에서 읽도록 만들어졌을 때 적합합니다

머릿속 단순한 결정 규칙은 — **값이 한두 개면 env, 키-값 묶음이 통째로 환경변수가 돼야 하면 envFrom, 파일이어야 의미가 있으면 volume**입니다.

전체 합쳐 보기 — Deployment + ConfigMap 한 사이클 #

위 세 모양을 한 매니페스트에 모아 봅니다. 4장의 web Deployment를 가져와서, 환경변수 한 개는 env로, 나머지는 envFrom으로, 그리고 app.conf는 볼륨으로 마운트하는 구성입니다.

web.yaml — ConfigMap을 끌어다 쓰는 Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
  labels:
    app: web
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: web
          image: nginx:1.27
          ports:
            - containerPort: 80
          env:
            - name: LOG_LEVEL
              valueFrom:
                configMapKeyRef:
                  name: web-config
                  key: LOG_LEVEL
          envFrom:
            - configMapRef:
                name: web-config
          volumeMounts:
            - name: app-conf
              mountPath: /etc/myapp
      volumes:
        - name: app-conf
          configMap:
            name: web-config
            items:
              - key: app.conf
                path: app.conf

(예시상 envenvFrom을 둘 다 적었습니다 — 실무에서는 한쪽만 쓰는 게 보통입니다. 같은 ConfigMap에서 동일한 키를 두 번 끌어오면 마지막에 정의된 쪽이 우선합니다.)

적용하고 결과를 확인합니다.

apply
kubectl apply -f web-config.yaml -f web.yaml
출력 예시
configmap/web-config unchanged
deployment.apps/web created

Pod 안에 들어가서 환경변수와 파일이 실제로 들어왔는지 봅니다.

환경변수 확인
kubectl exec -it deploy/web -- env | grep -E "LOG_LEVEL|APP_GREETING"
출력 예시
LOG_LEVEL=info
APP_GREETING=hello from k8s
파일 마운트 확인
kubectl exec -it deploy/web -- cat /etc/myapp/app.conf
출력 예시
server {
  listen 80;
  location / {
    return 200 "ok\n";
  }
}

ConfigMap에 적은 그대로가 컨테이너 안에서 환경변수와 파일로 보이는 게 확인됩니다. 이게 한 사이클의 끝입니다. 매니페스트의 본체에는 값 자체가 적혀 있지 않고, “ConfigMap에서 가져온다"라는 참조만 적혀 있습니다.

Secret — 비밀값 분리 #

ConfigMap이 평범한 설정값을 위한 객체라면, Secret은 비밀번호 · 토큰 · 인증서처럼 매니페스트 본체에 평문으로 두면 안 되는 값을 위한 객체입니다. 매니페스트 모양은 ConfigMap과 거의 같습니다.

db-secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: db-secret
type: Opaque
stringData:
  DB_USER: "myapp"
  DB_PASSWORD: "s3cret-do-not-commit"

apiVersion은 동일하게 v1입니다. ConfigMap과 다른 부분이 둘 있습니다.

  • type — Secret은 종류가 여러 가지여서 type 필드가 있습니다. 평범한 키-값 묶음이면 Opaque (기본값)입니다.
  • stringData vs data — Secret 본체에서 값을 적는 두 길입니다.

data vs stringData — 그리고 base64라는 한 줄 #

이번 챕터의 가장 중요한 한 줄이 여기입니다.

Secret이라는 이름이 붙어 있지만, 기본 동작은 base64 인코딩일 뿐입니다. 암호화가 아닙니다.

kubectl get secret db-secret -o yaml로 보면 매니페스트의 stringData가 사라지고 data 아래에 base64 인코딩된 문자열만 보입니다.

apply와 확인
kubectl apply -f db-secret.yaml
kubectl get secret db-secret -o yaml
출력 예시 — 발췌
apiVersion: v1
kind: Secret
metadata:
  name: db-secret
type: Opaque
data:
  DB_USER: bXlhcHA=
  DB_PASSWORD: czNjcmV0LWRvLW5vdC1jb21taXQ=

bXlhcHA=czNjcmV0LWRvLW5vdC1jb21taXQ=는 어렵게 보이지만, base64는 보안 장치가 아니라 바이너리를 텍스트로 옮기기 위한 인코딩입니다. 한 줄로 풀어 볼 수 있습니다.

base64 디코딩
kubectl get secret db-secret -o jsonpath='{.data.DB_PASSWORD}' | base64 -d
출력 예시
s3cret-do-not-commit

원래 값이 그대로 나옵니다. 그러니 Secret 객체를 다루는 일은 본질적으로 평문 비밀값을 다루는 일과 같다고 봐야 합니다. 진짜 보호는 다른 층에서 와야 합니다 — 뒤에서 짧게 짚어 두겠습니다.

datastringData의 차이는 사람이 적기 편한지뿐입니다.

  • data — base64로 미리 인코딩한 값을 적습니다. 사람이 직접 적기는 번거롭습니다.
  • stringData — 평문을 적으면 K8s가 받아서 base64로 알아서 인코딩합니다. 사람이 매니페스트로 Secret을 만들 때 거의 항상 이쪽을 씁니다.

명령형 생성도 ConfigMap과 같은 방향입니다.

명령형으로 Secret 만들기
kubectl create secret generic db-secret \
  --from-literal=DB_USER=myapp \
  --from-literal=DB_PASSWORD=s3cret-do-not-commit

Secret type 한 줄씩 #

Secret은 용도에 따라 몇 가지 정해진 type이 있습니다. 자주 만나는 넷을 한 줄씩 짚어 둡니다.

type용도
Opaque기본. 임의의 키-값 묶음
kubernetes.io/dockerconfigjson사설 컨테이너 레지스트리 자격증명. imagePullSecrets에서 참조
kubernetes.io/tlsTLS 인증서 · 키 쌍. Ingress의 HTTPS 종단점에서 참조
kubernetes.io/service-account-tokenServiceAccount 토큰. RBAC와 함께 등장

이 중 사람이 직접 매니페스트로 만지는 건 보통 Opaquekubernetes.io/tls 정도입니다. dockerconfigjsonkubectl create secret docker-registry라는 전용 명령으로 만드는 게 보편적이고, service-account-token은 K8s가 거의 알아서 다룹니다. RBAC와 ServiceAccount의 관계는 14장 RBAC / NetworkPolicy / ResourceQuota16장 RBAC / ServiceAccount 깊이에서 본격적으로 다룹니다.

get secret 출력 컬럼 #

Secret 목록
kubectl get secret
출력 예시
NAME        TYPE     DATA   AGE
db-secret   Opaque   2      1m

ConfigMap과 다른 점은 TYPE 컬럼이 추가됐다는 것뿐입니다 — NAME / TYPE / DATA / AGE. DATA의 숫자는 키 개수입니다.

Secret을 Pod에 주입 #

Secret을 Pod에 주입하는 모양은 ConfigMap과 동일합니다 — 키 이름만 살짝 다를 뿐입니다. 셋을 한 번에 정리합니다.

1. 단일 키 → 환경변수 (env.valueFrom.secretKeyRef) #

env.valueFrom.secretKeyRef
env:
  - name: DB_PASSWORD
    valueFrom:
      secretKeyRef:
        name: db-secret
        key: DB_PASSWORD

ConfigMap의 configMapKeyRefsecretKeyRef로 바뀌는 게 전부입니다.

2. 전체 키 → 환경변수 한꺼번에 (envFrom.secretRef) #

envFrom.secretRef
envFrom:
  - secretRef:
      name: db-secret

configMapRefsecretRef로 바뀝니다. 같은 마음가짐으로 키 이름은 UPPER_SNAKE_CASE로 두는 게 무난합니다.

3. 파일로 마운트 (volume.secret) #

volumes + volumeMounts (Secret)
volumes:
  - name: db-creds
    secret:
      secretName: db-secret
volumeMounts:
  - name: db-creds
    mountPath: /etc/db
    readOnly: true

ConfigMap 볼륨은 configMap 키 아래 name을 적었지만, Secret 볼륨은 secret 키 아래 **secretName**으로 이름이 살짝 다릅니다. 또 한 가지 차이는 디스크 위치입니다 — 볼륨 마운트로 풀린 Secret 파일은 노드의 디스크에 평문으로 떨어지지 않습니다. K8s가 tmpfs (메모리 기반 파일시스템)에 둬서 노드를 재부팅하면 사라지도록 만들어 뒀습니다. ConfigMap에는 이런 보호가 없습니다.

진짜 비밀값을 안전하게 다루려면 #

Secret이 base64일 뿐이라는 한 줄을 짚었으니, 실제 운영에서 비밀값을 어떻게 다루는지도 짧게 정리해 둡니다. 깊은 설치는 본 챕터 범위 밖이고, 이름만 알아두는 게 목적입니다.

  • etcd 단계의 암호화 — apiserver의 EncryptionConfiguration을 설정하고 KMS (AWS KMS, GCP KMS 등)와 연동하면 etcd에 들어가는 Secret 값이 암호화된 채 저장됩니다. 클러스터를 직접 운영할 때 가장 먼저 켜는 항목입니다.
  • Sealed Secrets (Bitnami) — Secret 매니페스트를 클러스터의 공개키로 암호화한 SealedSecret이라는 객체로 변환해 둡니다. 이 암호화된 매니페스트는 git에 안전하게 올려도 되고, 클러스터에 들어가면 컨트롤러가 풀어서 일반 Secret으로 바꿔 줍니다.
  • External Secrets Operator — Vault, AWS Secrets Manager, GCP Secret Manager, Azure Key Vault 같은 외부 비밀 저장소가 진짜 비밀값을 들고 있고, 이 오퍼레이터가 그 값을 K8s Secret으로 동기화해 줍니다. 운영의 일반적인 답입니다.

운영 클러스터에서는 위 셋 중 하나 이상을 거의 항상 함께 씁니다. 본 챕터에서는 매니페스트의 모양과 주입 방식까지만 다루고, 비밀값 운영의 깊은 부분 (sealed-secrets vs external-secrets vs SOPS 비교, IRSA와 결합한 비밀번호 0 운영)은 29장 시크릿 운영에서 본격적으로 정리합니다.

설정이 바뀌면 어떻게 적용되나 #

ConfigMap · Secret을 만들었으니 자연스럽게 다음 질문이 떠오릅니다 — 값을 바꿨을 때 그 변경이 Pod에 자동으로 반영되는가? 답은 주입 방식에 따라 갈라집니다. 운영에서 자주 헷갈리는 부분이라 한 번 정리해 둘 가치가 있습니다.

환경변수로 주입한 경우 — 시작 시점에 한 번 #

envenvFrom으로 환경변수에 부어 넣은 값은 Pod가 시작할 때 한 번만 채워지고 끝입니다. 그 뒤에 ConfigMap을 수정해도 이미 떠 있는 Pod의 환경변수는 변하지 않습니다. 프로세스의 환경 블록은 시작 시점에 한 번 만들어지면 OS 차원에서 그 모양 그대로 굳기 때문입니다 — K8s 만의 한계가 아니라 프로세스 모델의 본성입니다.

새 값으로 환경변수를 다시 채우려면 Pod가 새로 떠야 합니다. 의도된 점진 교체를 강제하는 표준 명령이 다음 한 줄입니다.

설정 반영을 위한 Pod 점진 교체
kubectl rollout restart deployment/web

이 명령은 4장의 롤링 업데이트와 같은 메커니즘으로 동작합니다 — 다만 spec은 그대로 두고 Pod만 점진적으로 교체합니다. 새로 떠오른 Pod가 ConfigMap의 최신 값을 환경변수로 가지고 시작합니다.

(kubectl rollout restart는 1.15+ 부터 도입된 표준 명령입니다. 그 이전에는 Pod 라벨에 임의의 어노테이션을 더해 spec을 살짝 바꾸는 우회 방법을 썼지만, 이제는 거의 안 만집니다.)

볼륨으로 마운트한 경우 — 자동 갱신 #

볼륨으로 마운트한 ConfigMap · Secret은 K8s가 주기적으로 동기화해서 파일이 자동으로 갱신 됩니다. ConfigMap을 수정하면 보통 분 단위 지연 안에 컨테이너 안의 파일 내용이 바뀌어 있습니다. 이 지연은 kubelet의 동기화 주기 (기본 1분)와 맞물려 있어서, 즉시는 아닙니다.

다만 한 가지 단서가 붙습니다 — 앱이 그 파일을 다시 읽는 코드를 가지고 있어야 변경이 의미가 있습니다. nginx처럼 SIGHUP을 받으면 설정을 다시 읽는 프로세스라면 동작하지만, 시작할 때 한 번만 설정을 읽고 끝인 앱은 파일이 바뀐들 행동이 달라지지 않습니다. 설정 파일 변경을 감지해 자동 reload를 거는 사이드카 (예: configmap-reload)를 두는 패턴도 흔하게 보입니다.

한 표로 정리 #

주입 모양변경 반영강제 갱신
env / envFrom자동 반영 안 됨 (시작 시 1회)kubectl rollout restart
volume자동 반영 (분 단위 지연)앱 자체의 reload 또는 kubectl rollout restart

운영의 단순한 기본기는 — 설정을 바꿨으면 일단 kubectl rollout restart로 의도된 점진 교체를 한 번 돌린다입니다. 환경변수든 볼륨이든 그 시점에는 확실히 반영됩니다.

정리·치우기 #

오늘 만든 객체를 정리합니다.

모두 정리
kubectl delete -f web.yaml
kubectl delete -f web-config.yaml
kubectl delete -f db-secret.yaml
출력 예시
deployment.apps "web" deleted
configmap "web-config" deleted
secret "db-secret" deleted

kubectl get deploy,cm,secret으로 비어 있는지 확인하면 출발점으로 돌아옵니다. kube-root-ca.crt ConfigMap과 default-token-... Secret은 K8s가 자체적으로 들고 있는 객체라 한 줄씩 남아 있어도 정상입니다.

연습문제 #

  1. 위 본문대로 web-config.yamlLOG_LEVEL 값을 "info"에서 "debug"로 바꾼 뒤 kubectl apply 해 보세요. 이미 떠 있는 Pod의 LOG_LEVEL 환경변수가 어떻게 보이는지 (kubectl exec deploy/web -- env | grep LOG_LEVEL)와, 같은 시점의 /etc/myapp/app.conf 파일이 어떻게 보이는지를 따로 기록합니다. 그런 다음 kubectl rollout restart deployment/web을 돌리고 두 출력이 어떻게 바뀌는지를 §“설정이 바뀌면 어떻게 적용되나"의 표와 맞춰 메모합니다.
  2. db-secret.yamlstringData로 적은 평문 값이 kubectl get secret db-secret -o yaml 출력에서 어떻게 표기되는지 확인하고, kubectl get secret db-secret -o jsonpath='{.data.DB_PASSWORD}' \| base64 -d로 원래 값을 복원해 보세요. “base64는 암호화가 아니다"의 의미를 자신의 표현으로 한 단락으로 정리하고, 운영에서 진짜 보호를 위해 어떤 옵션이 있는지 §“진짜 비밀값을 안전하게 다루려면"의 세 옵션을 짧게 비교합니다.
  3. web-config.yamlapp.conf 키 이름을 app_conf처럼 점 없는 이름으로 바꾼 뒤, Deployment에서 envFrom: [configMapRef: ...] 한 줄만 적어 모든 키가 환경변수가 되도록 해 보세요. kubectl exec deploy/web -- env 출력에 app_conf가 어떻게 들어왔는지 (들어왔는데 값이 멀티라인 텍스트라 어떻게 보이는지) 확인하고, §“언제 어떤 걸 쓸까"의 결정 규칙을 자신의 손에 맞춰 다시 적어 둡니다.

한 줄 요약: ConfigMap과 Secret은 12-factor의 “설정은 환경에 둔다"를 K8s에서 푸는 두 객체이고, 매니페스트 본체에서 환경별 값과 비밀값을 분리하는 표준 모양이다. Pod에 주입하는 길은 env (단일 키) · envFrom (전체) · volume (파일) 셋이고, 각각의 변경 반영 모델이 다르다. Secret의 기본 동작은 base64 인코딩일 뿐 암호화가 아니다 — 진짜 보호는 etcd 암호화, Sealed Secrets, External Secrets Operator 같은 별도 층에서 온다.

다음 챕터 #

여기까지 와도 한 가지가 여전히 어색한 채 남아 있습니다 — 지금까지 만든 모든 객체 (Pod, Deployment, Service, ConfigMap, Secret)가 전부 default 네임스페이스에 들어갔다는 점입니다. 한 클러스터 안에 여러 환경 (개발 / 스테이징)이나 여러 팀의 워크로드가 같이 떠 있어야 한다면 이 단일 공간이 곧 좁아집니다. 그리고 4장 selector부터 계속 만나 온 라벨도, 이쯤에서 한 번 정리해 둘 만한 양이 쌓였습니다.

7장 Namespace와 라벨에서는 네임스페이스가 클러스터를 어떻게 논리적으로 갈라 주는지, 라벨 · 셀렉터의 문법과 자주 쓰는 라벨 컨벤션, kubectl을 네임스페이스 단위로 다루는 운영 팁까지 따라가면서, 1부에서 다룬 매니페스트 7종을 한 클러스터 안에서 깨끗이 갈라 두는 모양으로 1부를 마무리합니다.

X