이 글에서 다루는 것

Kubernetes Secret과 ConfigMap으로 S3/PostgreSQL 인증 정보를 안전하게 주입하는 전략을 다룹니다.

선수지식


이 단계에서 해결하려는 문제

모든 구성 요소(MLflow, Airflow, FastAPI)가 외부 DB, GitHub, S3 등 민감한 리소스와 연동됩니다. API 키와 비밀번호를 YAML이나 코드에 직접 넣으면 보안 위험이 발생합니다. 이 단계에서는 Kubernetes Secret과 ConfigMap을 활용하여 인증 정보를 안전하게 분리하고 주입하는 구조를 설계합니다.


아키텍처 구성도

02


🔐 왜 Secret 처리가 중요한가?

항목이유
AWS Access Key노출 시 데이터 삭제/탈취 위험 있음
PostgreSQL 접속 URI내부망 DB 구조 노출 + Credential 유출 위험
MLflow Tracking URI내부 시스템 구조 노출 가능

실무에서는 인증정보를 Git에 노출하지 않고, 환경에 안전하게 주입해야 합니다. 부득이하게 Git에 올려야 할 경우 SealedSecret 또는 SOPS로 암호화 처리합니다.


📦 S3 인증 정보 처리 전략

실습 환경 조건

  • MLflow, FastAPI, Airflow에서 모두 boto3로 S3에 접근해야 함
  • FastAPI는 env 방식으로 처리, Airflow는 volumeMount 방식이 안정적

Secret 생성 (env 방식 - MLflow & FastAPI)

kubectl create secret generic aws-credentials-secret \
  --from-literal=AWS_ACCESS_KEY_ID=your-access-key \
  --from-literal=AWS_SECRET_ACCESS_KEY=your-secret-key \
  --from-literal=MLFLOW_S3_ENDPOINT_URL=https://s3.ap-northeast-2.amazonaws.com \
  -n mlflow

envFrom.secretRef로 사용하면 환경변수로 바로 주입되어 boto3가 자동 인식합니다.

envFrom:
  - secretRef:
      name: aws-credentials-secret

Secret 생성 (volumeMount 방식 - Airflow 전용)

# .aws/credentials 파일 예시
[default]
aws_access_key_id = your-access-key
aws_secret_access_key = your-secret-key
region = ap-northeast-2
kubectl create secret generic aws-credentials-secret \
  --from-file=credentials=.aws/credentials \
  -n airflow

Airflow는 Task마다 새 Pod가 생성되고 MLflow + S3 연동 시 boto3를 사용합니다. AWS EKS가 아닌 로컬 Kubernetes 환경에서는 .aws 경로 마운트 방식이 안정적입니다.

extraVolumes:
  - name: aws-credentials
    secret:
      secretName: aws-credentials-secret
extraVolumeMounts:
  - name: aws-credentials
    mountPath: /home/airflow/.aws
    readOnly: true

🗄 PostgreSQL 접속 정보 관리 전략

목표

  • Helm 차트 내부에서 backend-store-uri 혹은 sql_alchemy_conn 값을 직접 넣지 않음
  • 대신 Secret이나 ConfigMap으로 구성 요소 분리

🔐 MLflow 접속 정보 구성 (Secret + ConfigMap 분리 방식)

목표: 실험 metadata 저장소(PostgreSQL)에 대한 안전한 연결 설정

# Secret: 인증 정보 (username/password)
kubectl create secret generic mlflow-db-secret \
  --from-literal=username=mlflow_user \
  --from-literal=password='<YOUR_PASSWORD>' \   # 실제 환경: SealedSecrets 사용
  -n mlflow

# ConfigMap: 일반 정보 (host/port/dbname)
kubectl create configmap mlflow-db-config \
  --from-literal=host=192.168.18.142 \
  --from-literal=port=5432 \
  --from-literal=dbname=mlflow_db \
  -n mlflow

Helm chart에서 env.valueFrom으로 조합하여 주입합니다.

- name: DB_USER
  valueFrom:
    secretKeyRef:
      name: mlflow-db-secret
      key: username
- name: DB_PASSWORD
  valueFrom:
    secretKeyRef:
      name: mlflow-db-secret
      key: password
- name: DB_HOST
  valueFrom:
    configMapKeyRef:
      name: mlflow-db-config
      key: host
- name: DB_PORT
  valueFrom:
    configMapKeyRef:
      name: mlflow-db-config
      key: port
- name: DB_NAME
  valueFrom:
    configMapKeyRef:
      name: mlflow-db-config
      key: dbname

🔐 Airflow 접속 정보 구성 (통합 URI 방식)

목표: Airflow 메타 DB(PostgreSQL) URI를 단일 문자열로 관리

kubectl create secret generic airflow-db-secret \
  --from-literal=connection="postgresql://airflow_user:<AIRFLOW_DB_PASSWORD>@192.168.18.142:5432/airflow_db" \  # 실제 환경: SealedSecrets 사용
  -n airflow

Helm values.yaml에서 metadataSecretName으로 참조합니다.

data:
  metadataSecretName: airflow-db-secret

내부적으로 AIRFLOW__DATABASE__SQL_ALCHEMY_CONN 환경변수로 설정됩니다.


🔄 Secret 방식 정리 비교

방식대상장점단점
envFrom.secretRefMLflow, FastAPI깔끔하고 가독성 좋음Airflow에서는 실패 가능성 존재
.aws 마운트AirflowTask Pod에서도 안정적경로 구조 명확히 맞춰야 함 (boto3 대비)
base64 SecretDB URI, 민감한 키Helm에서 깔끔히 관리Git에 올리면 암호화 없이 노출됨 (주의)

🚨 트러블슈팅

TS_01: Airflow DAG Task에서 boto3 인증 실패

증상원인해결
botocore.exceptions.NoCredentialsErrorenv 방식으로 주입했지만 Task Pod에는 적용되지 않음.aws/credentials 경로로 마운트 방식 사용해야 함

설계 판단 (Why This Way?)

Airflow KubernetesExecutor의 Task Pod에는 envFrom이 전파되지 않을 수 있으므로 volumeMount 방식으로 AWS 인증을 주입하고, Secret과 ConfigMap은 컴포넌트별 주입 구조에 맞게 분리 또는 통합했습니다. Git에 커밋되는 Secret은 반드시 SealedSecrets 등으로 암호화해야 하며, base64 인코딩은 보안이 아닙니다.


다음에 읽을 글

MLOps 플랫폼 구축 3단계: MLflow Helm 구성 — PostgreSQL + S3 연동 기반 MLflow Helm 배포