Markdown Preview
Kubernetes Secrets로 안전하게 민감 정보를 관리하는 방법과 실습 가이드
Frontmatter
Kubernetes Secret은 비밀번호, API 키 등 민감 정보를 Base64 인코딩으로 저장하고, 환경 변수 주입, 파일 마운트 등으로 안전하게 활용할 수 있다. Secret 생성은 명령어, YAML 파일, 직접 Base64 인코딩 방식으로 가능하며, MySQL 배포 예시를 통해 Secret을 사용해 데이터베이스 비밀번호 등을 주입한다. 보안을 강화하려면 Secret을 immutable로 설정하고 RBAC로 접근을 제한하며, 파일 마운트 방식이 더 안전하다. 또한 Secret 디버깅 방법과 삭제 절차를 제공하고, 생산 환경에서는 Vault, AWS Secrets Manager, Sealed Secrets와 같은 외부 솔루션 사용을 권장한다.
Secret이란?
비밀번호, API 키, 인증서와 같은 민감한 정보를 안전하게 저장하는 Kubernetes 객체입니다.
ConfigMap vs Secret
구분 | ConfigMap | Secret |
용도 | 일반 설정 (포트, 경로 등) | 민감 정보 (비밀번호, 토큰) |
저장 방식 | 평문 | Base64 인코딩 |
보안 | 낮음 | 높음 (암호화 가능) |
1. Secret 생성
방법 1: 명령어로 생성
# MySQL root 비밀번호 생성 (06-volume 네임스페이스)
kubectl create secret generic mysql-secret \
--from-literal=MYSQL_ROOT_PASSWORD=mySecurePassword123! \
--namespace volume
# 확인 (값은 Base64로 인코딩되어 보임)
kubectl get secret mysql-secret -n volume -o yaml방법 2: YAML 파일로 생성
secret.yaml:
apiVersion: v1
kind: Secret
metadata:
name: mysql-secret
namespace: volume
type: Opaque
stringData:
MYSQL_ROOT_PASSWORD: mySecurePassword123!
MYSQL_DATABASE: flask_db
MYSQL_USER: flask_user
MYSQL_PASSWORD: flaskPassword456!kubectl apply -f secret.yaml주의:
stringData는 평문으로 작성 가능, data는 Base64 인코딩 필요방법 3: Base64로 직접 인코딩
# Base64 인코딩
echo -n 'mySecurePassword123!' | base64
# 출력: bXlTZWN1cmVQYXNzd29yZDEyMyE=
# YAML에 직접 사용
cat <<EOF | kubectl apply -n 06-volume -f -
apiVersion: v1
kind: Secret
metadata:
name: mysql-secret
type: Opaque
data:
MYSQL_ROOT_PASSWORD: bXlTZWN1cmVQYXNzd29yZDEyMyE=
EOF2. MySQL with Secret
secret.yaml:
apiVersion: v1
kind: Secret
metadata:
name: mysql-secret
namespace: volume
type: Opaque
stringData:
MYSQL_ROOT_PASSWORD: rootPassword123!
MYSQL_DATABASE: flask_db
MYSQL_USER: flask_user
MYSQL_PASSWORD: flaskPassword456!
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql
namespace: volume
spec:
replicas: 1
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8.0
ports:
- containerPort: 3306
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: MYSQL_ROOT_PASSWORD
- name: MYSQL_DATABASE
valueFrom:
secretKeyRef:
name: mysql-secret
key: MYSQL_DATABASE
- name: MYSQL_USER
valueFrom:
secretKeyRef:
name: mysql-secret
key: MYSQL_USER
- name: MYSQL_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: MYSQL_PASSWORD
volumeMounts:
- name: mysql-storage
mountPath: /var/lib/mysql
volumes:
- name: mysql-storage
hostPath:
path: /tmp/mysql-data
type: DirectoryOrCreate
---
apiVersion: v1
kind: Service
metadata:
name: mysql
namespace: volume
spec:
selector:
app: mysql
ports:
- port: 3306
targetPort: 3306
type: ClusterIP3. 실습: MySQL 배포 및 접속
# 1. Secret과 MySQL 배포
kubectl apply -f secret.yaml
# 2. Pod 상태 확인 (Ready 될 때까지 대기)
kubectl get pods -n volume -l app=mysql -w
# 3. MySQL Service 확인
kubectl get svc -n volume mysql
# 4. MySQL 접속 테스트
POD_NAME=$(kubectl get pod -n 06-volume -l app=mysql -o jsonpath='{.items[0].metadata.name}')
kubectl exec -n volume -it $POD_NAME -- mysql -u root -p
# 비밀번호 입력: rootPassword123!
# MySQL 프롬프트에서:
# mysql> SHOW DATABASES;
# mysql> USE flask_db;
# mysql> CREATE TABLE users (id INT, name VARCHAR(50));
# mysql> INSERT INTO users VALUES (1, 'Alice');
# mysql> SELECT * FROM users;
# mysql> exit4. 환경 변수로 Secret 확인
# Secret 값 확인 (Base64 인코딩된 상태)
kubectl get secret mysql-secret -n volume -o yaml
# Secret 디코딩
kubectl get secret mysql-secret -n volume -o jsonpath='{.data.MYSQL_ROOT_PASSWORD}' | base64 -d
echo ""
# Pod 내부에서 환경 변수 확인
kubectl exec -n volume $POD_NAME -- env | grep MYSQL주의: 프로덕션에서는 Secret 값을 직접 출력하지 마세요!
5. Flask 애플리케이션에서 MySQL 연결
app-mysql.py:
from flask import Flask
import os
import mysql.connector
app = Flask(__name__)
def get_db_connection():
return mysql.connector.connect(
host=os.getenv('MYSQL_HOST', 'mysql'),
user=os.getenv('MYSQL_USER', 'flask_user'),
password=os.getenv('MYSQL_PASSWORD'),
database=os.getenv('MYSQL_DATABASE', 'flask_db')
)
@app.route('/')
def home():
return "<h1>Flask + MySQL on Kubernetes</h1>"
@app.route('/db-test')
def db_test():
try:
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute("SELECT VERSION()")
version = cursor.fetchone()
cursor.close()
conn.close()
return f"<h2>MySQL Version: {version[0]}</h2>"
except Exception as e:
return f"<h2>Database Error: {e}</h2>"
if __name__ == '__main__':
app.run(host='0.0.0.0', port=3000)flask-deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: flask-app
spec:
replicas: 1
selector:
matchLabels:
app: flask
template:
metadata:
labels:
app: flask
spec:
containers:
- name: flask
image: pieroot/flask-mysql-app:latest
ports:
- containerPort: 3000
env:
- name: MYSQL_HOST
value: mysql
- name: MYSQL_USER
valueFrom:
secretKeyRef:
name: mysql-secret
key: MYSQL_USER
- name: MYSQL_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: MYSQL_PASSWORD
- name: MYSQL_DATABASE
valueFrom:
secretKeyRef:
name: mysql-secret
key: MYSQL_DATABASE6. Secret 보안 강화
1) Secret을 불변(immutable)으로 설정
apiVersion: v1
kind: Secret
metadata:
name: mysql-secret
immutable: true # 수정 불가능
type: Opaque
stringData:
MYSQL_ROOT_PASSWORD: mySecurePassword123!2) RBAC으로 접근 제어
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: secret-reader
rules:
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["mysql-secret"]
verbs: ["get"]3) 파일로 마운트 (환경 변수보다 안전)
volumeMounts:
- name: mysql-secret
mountPath: /etc/mysql-secret
readOnly: true
volumes:
- name: mysql-secret
secret:
secretName: mysql-secret# 파일로 읽기
kubectl exec $POD_NAME -- cat /etc/mysql-secret/MYSQL_ROOT_PASSWORD7. Secret 디버깅
# Secret 목록
kubectl get secrets -n volume
# Secret 상세 정보 (값은 보이지 않음)
kubectl describe secret mysql-secret -n volume
# Secret 값 확인 (Base64 인코딩)
kubectl get secret mysql-secret -n volume -o yaml
# 특정 키 디코딩
kubectl get secret mysql-secret -n volume -o jsonpath='{.data.MYSQL_ROOT_PASSWORD}' | base64 -d
# Pod에서 환경 변수 확인
kubectl exec -n volume $POD_NAME -- printenv MYSQL_ROOT_PASSWORD8. 정리
kubectl delete -f secret.yaml
# 데이터 삭제
sudo rm -rf /tmp/mysql-data핵심 정리
✅ Secret: 민감한 정보를 Base64로 인코딩하여 저장
✅ 환경 변수 주입:
valueFrom.secretKeyRef 사용✅ 볼륨 마운트: 파일로 마운트하여 더 안전하게 사용
✅ 보안: immutable 설정, RBAC 사용, Git에 커밋 금지
주의사항
⚠️ Git에 커밋하지 마세요: Secret YAML 파일을 Git에 올리지 마세요
⚠️ Base64는 암호화가 아님: 쉽게 디코딩 가능
⚠️ 로그에 출력 금지: Secret 값을 로그에 출력하지 마세요
⚠️ 접근 제한: RBAC으로 Secret 접근을 제한하세요
대안
프로덕션 환경에서는 다음을 고려하세요:
- HashiCorp Vault: 중앙 집중식 Secret 관리
- AWS Secrets Manager: AWS에서 제공하는 Secret 관리 서비스
- Sealed Secrets: Git에 안전하게 저장 가능한 암호화된 Secret