요약: MySQL 같은 상태ful(데이터가 중요한) 워크로드는 Kubernetes에서 StatefulSet을 기본으로 두고, Secret 분리, 프로브 설계, 종료(graceful termination) 동작을 “명시적으로” 만들지 않으면 운영에서 쉽게 사고가 납니다. 이 글은 공식 문서에 근거해, 최소한의 안전장치(스토리지·아이덴티티·시크릿·프로브·종료)를 한 번에 점검할 수 있게 정리합니다.
1) 왜 Deployment가 아니라 StatefulSet인가
Kubernetes 공식 문서는 StatefulSet을 다음 요구가 있을 때 가치가 크다고 정리합니다: 안정적인(고정) 네트워크 식별자, 안정적인(지속) 스토리지, 순서 보장(배포/스케일/종료) 등입니다. 또한 Deployment와 달리, StatefulSet의 Pod는 “서로 교체 가능한 인스턴스”가 아니라 리스케줄링되더라도 지속되는 식별자를 갖습니다. (출처: Kubernetes StatefulSet 문서)
MySQL은 디스크에 상태(데이터)를 저장하는 워크로드입니다. 따라서 “Pod가 죽어도 동일한 볼륨을 다시 붙여야 하는” 요구가 일반적이고, 이는 StatefulSet + PVC 패턴으로 자연스럽게 풀립니다. StatefulSet 문서는 또한 StatefulSet을 삭제하거나 스케일 다운해도, 연관된 볼륨을 자동으로 삭제하지 않는다고 명시합니다(데이터 안전을 위해). 이 동작을 이해하지 못하면, 테스트/운영에서 ‘리소스 정리’가 안 된다고 오해하거나 반대로 데이터를 날릴 수 있습니다.
2) Headless Service는 “선택”이 아니라 “전제”에 가깝습니다
StatefulSet 문서는 “StatefulSet은 Pod 네트워크 아이덴티티를 위해 Headless Service가 필요하며, 사용자가 그 Service를 직접 만들어야 한다”고 적고 있습니다. 즉, StatefulSet만 만들고 Service를 생략하면 운영에서 이름 기반 접근이 흔들리거나, 클러스터 내부 DNS 설계가 꼬이기 쉽습니다. (출처: Kubernetes StatefulSet 문서)
3) Secret을 분리하는 이유(그리고 흔한 오해 2가지)
Kubernetes 공식 문서는 Secret을 “비밀번호/토큰/키 같은 소량의 민감정보를 담는 오브젝트”로 정의하며, Pod 스펙이나 이미지에 기밀을 넣지 않게 해 준다고 설명합니다. 또한 Secret은 Pod와 독립적으로 생성 가능하기 때문에, Pod를 만들고 편집하는 과정에서 민감정보 노출 위험이 줄어든다고도 말합니다. (출처: Kubernetes Secrets 문서)
- 오해 1: “Secret을 쓰면 자동으로 완벽히 안전하다”
공식 문서도 Secret 자체가 ‘모든 것을 해결한다’고 말하지 않습니다. 단지 기밀을 코드/이미지/Pod 편집 흐름에서 분리하고, 추가적인 보안 조치를 취할 수 있는 기반을 제공합니다. - 오해 2: “Terraform에서 secret 변수를 sensitive로 표시하면 state에 안 남는다”
Terraform 공식 문서는 sensitive는 CLI/UI 출력에서 값을 가려주지만, state/plan 파일에 값이 존재할 수 있다고 경고합니다. state는 민감정보가 포함될 수 있으니 별도 보안(원격 state, 암호화, 접근 제어)이 필요합니다. (출처: Terraform ‘Manage sensitive data’ 문서)
4) 프로브(readiness/liveness/startup)를 “DB 특성”에 맞게 분리하기
운영에서 가장 흔한 장애 패턴은 “기동이 느린데 livenessProbe가 먼저 죽여버리는” 것입니다. Kubernetes는 컨테이너 프로브(특히 liveness/readiness/startup)를 공식 문서에서 정의하고 있으며, 목적이 서로 다릅니다. DB 계열은 초기화/복구 시간이 길 수 있으므로, 시작 구간을 별도로 다루는 설계가 필요합니다. (출처: Kubernetes Pod Lifecycle 문서의 Container Probes 섹션)
아래 예시는 개념을 보여주기 위한 스니펫입니다(환경에 맞게 명령/포트는 조정 필요).
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
template:
spec:
terminationGracePeriodSeconds: 60
containers:
- name: mysql
image: mysql:8
ports:
- containerPort: 3306
startupProbe:
tcpSocket:
port: 3306
failureThreshold: 60
periodSeconds: 2
readinessProbe:
tcpSocket:
port: 3306
periodSeconds: 5
livenessProbe:
tcpSocket:
port: 3306
periodSeconds: 10
5) 종료(termination) 동작을 ‘명시’하지 않으면, “운영체제가 알아서”가 아닙니다
Pod 종료는 단순히 프로세스를 죽이는 문제가 아니라, 트래픽 차단 → 정리 → 종료의 흐름으로 이해해야 합니다. Kubernetes는 Pod lifecycle 문서에서 Pod termination을 설명하며, 종료 시나리오(삭제/업데이트/스케일다운 등)에서 컨트롤 플레인과 kubelet이 Pod를 종료 상태로 전환하는 메커니즘을 다룹니다. (출처: Kubernetes Pod Lifecycle 문서의 Pod Termination 섹션)
핵심은 “정리할 시간을 주는 것”입니다. 예를 들어 MySQL은 내부적으로 flush, 로그 정리, 파일 시스템 동기화 같은 작업이 필요할 수 있습니다. 최소한 다음 항목은 팀 표준으로 합의해두는 게 좋습니다.
- terminationGracePeriodSeconds를 워크로드에 맞게 충분히 크게 잡기
- 롤링 업데이트/스케일 다운 시, 앱/DB가 안전하게 내려가도록 순서와 대기를 설계하기(StatefulSet은 순서 기반 동작을 목표로 합니다)
6) Terraform을 같이 쓴다면: state/plan 파일을 ‘민감정보 저장소’로 취급하세요
Terraform 공식 문서는 state/plan 파일이 인프라의 상세 속성을 담고 있으며, 초기 DB 비밀번호나 API 토큰 같은 민감값을 포함할 수 있다고 명시합니다. 또한 로컬 state는 기본적으로 평문 파일로 저장될 수 있으므로, Git 제외 및 state 보안 권고를 따르라고 안내합니다. (출처: Terraform ‘Manage sensitive data’ 문서)
따라서 Kubernetes Secret을 Terraform으로 관리할 때도, 다음 원칙을 먼저 확인해야 합니다.
- state 파일을 Git에 커밋하지 않기(기본 중의 기본)
- 원격 state 사용 시: 백엔드 별로 “저장 시 암호화(encrypt at rest)”와 “전송 시 TLS”를 어떻게 보장하는지 확인하기
- Terraform 버전에 따라 ephemeral 같은 기능으로 state/plan에 값을 아예 남기지 않는 옵션을 검토하기(버전 조건은 공식 문서에 명시)
실무 체크리스트 (배포 전에 10분 점검)
- StatefulSet을 쓰는가? (고정 네트워크/스토리지 아이덴티티 요구가 있는가)
- Headless Service를 만들었는가? (StatefulSet의 네트워크 아이덴티티 전제)
- volumeClaimTemplates 또는 PVC 설계가 명확한가? (스토리지 클래스/용량/접근모드)
- Secret을 Pod 스펙/이미지/코드와 분리했는가?
- startupProbe vs readinessProbe vs livenessProbe를 구분했는가?
- terminationGracePeriodSeconds를 워크로드에 맞게 설정했는가?
- Terraform을 쓴다면 state/plan 보안(원격/암호화/접근제어/ephemeral)을 검토했는가?
트러블슈팅 빠른 표
| 증상 | 가능 원인 | 우선 확인 |
|---|---|---|
| DB가 기동 중인데 계속 재시작됨 | livenessProbe가 초기화 구간을 실패로 판단 | startupProbe 도입/조정, livenessProbe 지연/완화 |
| 스케일 다운/삭제 후에도 PV/PVC가 남음 | StatefulSet은 데이터 안전을 위해 볼륨을 자동 삭제하지 않을 수 있음 | StatefulSet 문서의 limitations(볼륨 삭제 정책) 확인 |
| Secret 값이 CI 로그/출력에 보임 | Terraform의 sensitive는 출력만 가림. state/plan/JSON 출력은 노출 가능 | Terraform 공식 문서의 sensitive/ephemeral/출력 주의사항 확인 |
FAQ
- Q. StatefulSet을 쓰면 데이터 안전이 자동으로 보장되나요?
- A. 아닙니다. StatefulSet은 “고정 아이덴티티/순서/스토리지 연결”을 위한 워크로드 컨트롤러입니다. 백업/복구/스토리지 클래스/퍼미션/리소스 한계 등 운영 설계는 별도로 필요합니다(공식 문서가 다루는 것은 StatefulSet이 제공하는 보장 범위입니다).
- Q. Kubernetes Secret은 암호화되나요?
- A. Secret은 기밀을 분리하기 위한 오브젝트이며, 공식 문서에서 정보보안 관점의 주의사항을 별도 섹션으로 다룹니다. (클러스터 설정/스토리지/접근 제어에 따라 실제 보안 수준이 달라질 수 있으니, ‘Secret을 쓴다’만으로 만족하면 위험합니다.)
- Q. Terraform에서 sensitive만 붙이면 state에 안 남나요?
- A. 아닙니다. Terraform 공식 문서는 sensitive 값이 state/plan에 저장될 수 있으며, 파일 접근자가 민감값을 볼 수 있음을 명시합니다. state 자체를 민감정보로 취급하고 보안 조치를 해야 합니다.
원문 근거(공식 문서)
- Kubernetes StatefulSet: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/
- Kubernetes Secrets: https://kubernetes.io/docs/concepts/configuration/secret/
- Kubernetes Pod Lifecycle (Container Probes): https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#container-probes
- Kubernetes Pod Lifecycle (Pod termination): https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-termination
- Terraform: Manage sensitive data: https://developer.hashicorp.com/terraform/language/manage-sensitive-data
다음 글 후보: (1) Terraform 1.10+ ephemeral을 실제로 “Kubernetes Secret”에 적용할 때의 함정, (2) StatefulSet 롤링 업데이트(OrderedReady)에서 깨지는 케이스와 복구 절차(공식 문서 기반)로 이어서 쓰면 좋습니다.