AI DevOps

Kubernetes ConfigMap, Secret, Ingress 제대로 이해하기 — 앱이 뜨는 것과 운영 가능한 것은 다르다

thisnorm 2026. 4. 27. 18:00

Pod가 Running 상태라고 해서 운영 가능한 상태는 아닙니다. 진짜 운영 가능한 애플리케이션은 환경별 설정이 분리되어 있고, 민감 정보가 코드 밖에서 관리되며, 외부 트래픽 진입 경로가 설계되어 있어야 합니다. 이번 글에서는 그 세 가지를 담당하는 ConfigMap, Secret, Ingress를 정리합니다.

왜 하드코딩이 문제인가

코드에 설정값을 직접 박아 넣으면 두 가지 문제가 생깁니다.

보안 관점

  • Git 저장소에 비밀번호가 노출됩니다
  • Docker 이미지 레이어에 값이 남습니다
  • 팀원 누구나 접근할 수 있는 정보가 됩니다

운영 관점

  • 설정 하나 바꾸려면 전체 재빌드가 필요합니다
  • 개발/운영 환경 분기가 코드 레벨에서 생깁니다
  • 같은 이미지를 여러 환경에 재사용할 수 없습니다

12-Factor App 원칙: 설정은 환경에서 주입하고, 코드와 분리해야 합니다.

ConfigMap vs Secret — 무엇이 다른가

기준은 하나입니다. "이 값이 공개되면 보안 사고가 나는가?"

종류 용도 예시
ConfigMap 노출되어도 큰 문제 없는 비민감 설정 APP_ENV, LOG_LEVEL, PUBLIC_API_URL
Secret 절대 노출되면 안 되는 민감 설정 DB_PASSWORD, API_KEY, JWT_SECRET

ConfigMap 사용법

YAML 구조

apiVersion: v1
kind: ConfigMap
metadata:
  name: team-board-config
  namespace: default
data:
  APP_ENV: "production"
  LOG_LEVEL: "info"

핵심: data 필드에 키-값 쌍을 정의합니다. 모든 값은 문자열로 저장되므로 숫자도 따옴표로 감쌉니다.

주입 방식 — 환경변수 vs 파일 마운트

방식 언제 쓰나 특징
envFrom / env process.env, os.getenv()로 읽을 때 Pod 재시작 시에만 새 값 반영
volume 마운트 nginx.conf처럼 파일 형태가 필요할 때 ConfigMap 변경 시 파일 자동 갱신

적용 확인 방법

kubectl get configmap team-board-config
kubectl describe configmap team-board-config
kubectl exec -it [pod-name] -- env | grep APP_ENV
kubectl rollout restart deployment/team-board-app

자주 하는 실수

  • 키 이름 오타: configMapKeyRef.key가 data의 키와 정확히 일치해야 합니다
  • 이름 오타: Pod가 CreateContainerConfigError 상태가 되면 kubectl get configmap으로 확인합니다
  • 민감 정보를 ConfigMap에 저장: 비밀번호는 반드시 Secret으로 분리합니다

Secret — base64는 암호화가 아니다

Secret의 data 필드 값이 알 수 없는 문자로 보여서 암호화된 것으로 오해하기 쉽습니다.

cGFzc3dvcmQ= 이건 password를 base64로 인코딩한 것입니다. 누구나 즉시 복호화할 수 있습니다.

echo "cGFzc3dvcmQ=" | base64 -d  # → password

Secret이 안전한 이유는 base64가 아니라, etcd 암호화 설정, RBAC 접근 제어, 네트워크 격리 때문입니다.

YAML 구조 — stringData 방식 권장

apiVersion: v1
kind: Secret
metadata:
  name: team-board-db-secret
  namespace: default
type: Opaque
stringData:
  DB_HOST: "postgres-service"
  DB_USER: "boarduser"
  DB_PASSWORD: "s3cur3P@ss!"
  DB_NAME: "teamboard"

stringData는 평문으로 입력하면 Kubernetes가 자동으로 base64로 저장합니다. 단, 이 파일을 Git에 올리면 절대 안 됩니다.

Ingress — Service만으로 충분한가

Service만 쓰면 서비스마다 별도 포트(30001, 30002…)가 필요하고 도메인 기반 라우팅이 불가능합니다. Ingress를 추가하면 하나의 진입점(80/443)으로 통합할 수 있습니다.

host 기반 vs path 기반 라우팅

방식 사용 시점 예시
host 기반 서브도메인으로 서비스를 나눌 때 app.example.com → team-board-service
path 기반 하나의 도메인 아래 여러 서비스를 운영할 때 example.com/api → api-service

Ingress YAML 구조

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: team-board-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - host: team-board.local
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: web-service
            port:
              number: 80
      - path: /api
        pathType: Prefix
        backend:
          service:
            name: app-service
            port:
              number: 8080

Ingress Controller를 별도로 설치해야 합니다

Ingress 리소스는 라우팅 규칙을 선언하는 설계도일 뿐입니다. 실제로 동작하려면 Controller가 필요합니다.

# minikube 환경
minikube addons enable ingress

운영 환경에서는 nginx-ingress-controller, Traefik, AWS ALB Ingress Controller 중 환경에 맞는 것을 선택합니다.

정리

리소스 역할
ConfigMap 비민감 설정을 이미지 밖으로 분리, 재빌드 없이 설정 변경 가능
Secret 민감 정보 보호, base64 인코딩(암호화 아님) + RBAC으로 접근 제어
Ingress 외부 트래픽 단일 진입점, host/path 기반 라우팅, TLS 한 곳에서 관리

설정 분리는 단순한 코드 정리가 아닙니다. 동일한 이미지를 여러 환경에 재사용하고, 민감 정보를 코드 바깥에서 관리하고, 외부 트래픽 흐름을 선언적으로 설계하는 것이 운영 가능한 애플리케이션의 기준입니다.

반응형