
디플로이먼트 선택 하나로 서비스 안정성이 결정되는 이유
한창 트래픽이 몰리는 피크 타임에 갑자기 서비스가 중단되는 경험을 해보셨나요? 업데이트를 위해 디플로이먼트(Deployment)를 사용했는데, 롤링 업데이트 과정에서 데이터가 유실되거나 파드(Pod)들이 서로 엉켜버리는 상황은 SRE(Site Reliability Engineer)에게 가장 끔찍한 시나리오 중 하나예요. 단순히 컨테이너를 띄우는 것을 넘어, 우리가 어떤 컨트롤러를 선택하느냐에 따라 시스템의 회복 탄력성과 데이터 일관성이 완전히 달라지기 때문이에요.
많은 팀이 관성적으로 모든 애플리케이션에 디플로이먼트를 적용하곤 해요. 하지만 데이터베이스처럼 상태를 유지해야 하는 워크로드나, 모든 노드에 반드시 실행되어야 하는 모니터링 에이전트에 디플로이먼트를 사용하는 것은 운영 리스크를 키우는 지름길이에요. 잘못된 선택은 결국 불필요한 장애 대응 시간과 인프라 비용 증가로 이어지곤 해요.
이 글에서는 단순히 개념을 나열하는 것을 넘어, 실제 운영 환경에서 마주하는 기술적 한계와 이를 해결하기 위한 디플로이먼트 비교 분석을 진행해요. 어떤 상황에서 어떤 대안을 선택해야 하는지, 그리고 그 선택이 운영 난이도에 어떤 영향을 미치는지 상세히 다룰 예정이에요.
이 글을 통해 다음과 같은 내용을 확실히 얻어갈 수 있어요.
- 워크로드 특성에 따른 4가지 핵심 컨트롤러의 기술적 차이점
- 상태 유지 여부에 따른 디플로이먼트 대안 선택 기준
- 실무에서 자주 발생하는 컨트롤러 설정 실수와 해결 방법
- 대규모 트래픽 환경을 위한 최적의 워크로드 조합 전략
워크로드 선택 전 반드시 점검해야 할 기준
컨트롤러를 결정하기 전에 우리 서비스의 워크로드가 어떤 성격을 가졌는지 냉정하게 평가해야 해요. 무턱대고 기능을 살펴보기보다는, 애플리케이션이 요구하는 최소한의 조건을 먼저 정의하는 과정이 필요해요. 이 과정이 생략되면 결국 운영 단계에서 데이터 유실이나 무한 재시작 루프와 같은 문제에 직면하게 돼요.
가장 먼저 고려해야 할 것은 상태성(Statefulness)이에요. 애플리케이션이 종료되었다가 다시 뜰 때 이전의 데이터를 그대로 가져와야 하나요? 아니면 아무런 데이터 없이 깨끗한 상태로 시작해도 상관없나요? 두 번째는 배치 전략이에요. 모든 노드에 하나씩 떠 있어야 하나요, 아니면 트래픽에 따라 유연하게 개수를 늘려야 하나요? 마지막으로는 실행 생명주기를 따져봐야 해요. 지속적으로 떠 있는 서비스인가요, 아니면 특정 작업만 수행하고 사라지는 형태인가요?
쿠버네티스의 컨트롤러는 사용자가 정의한 ‘원하는 상태(Desired State)’를 유지하기 위해 끊임없이 루프를 돌며 작동해요. 따라서 각 컨트롤러가 상태를 어떻게 정의하고 유지하는지를 이해하는 것이 핵심이에요.
아래 표를 통해 주요 컨트롤러들의 핵심 차이점을 한눈에 비교해 보세요. 이 표는 디플로이먼트 대안을 고민할 때 가장 기초적인 판단 근거가 될 거예요.
| 컨트롤러 유형 | 상태 관리 여부 | 파드 식별자 | 주요 활용 사례 |
|---|---|---|---|
| Deployment | 무상태 (Stateless) | 랜덤 생성 | 웹 서버, API 서버 |
| StatefulSet | 상태 유지 (Stateful) | 고유 번호 부여 | DB, 메시지 큐 (Kafka 등) |
| DaemonSet | 무상태/상태 혼합 | 노드 종속적 | 로그 수집기, 모니터링 에이전트 |
| Job / CronJob | 일회성/스케줄링 | 수행 후 소멸 | 데이터 백업, 배치 작업 |
상황별 최적의 컨트롤러 활용 전략
이제 각 컨트롤러가 실무에서 어떻게 쓰이는지, 그리고 어떤 메커니즘으로 동작하는지 깊이 있게 살펴볼게요. 단순히 이름만 아는 것이 아니라, 내부 동작 원리를 알아야 장애 상황에서 올바른 판단을 내릴 수 있어요.
STEP 1. 무상태 서비스의 정석, 디플로이먼트(Deployment)
우리가 가장 흔하게 사용하는 디플로이먼트는 애플리케이션이 어떤 데이터를 로컬에 저장하지 않는 ‘무상태(Stateless)’일 때 가장 빛을 발해요. 웹 서버나 마이크로서비스의 API 서버가 대표적인 예시죠. 디플로이먼트의 핵심은 ReplicaSet을 관리하며 원하는 파드 개수를 유지하는 데 있어요.
디플로이먼트의 강력한 기능 중 하나는 롤링 업데이트(Rolling Update)예요. 새로운 버전의 이미지를 배포할 때, 기존 파드를 하나씩 제거하고 새 파드를 하나씩 생성하며 서비스 중단을 최소화하죠. 이때 `maxSurge`와 `maxUnavailable` 설정을 통해 업데이트 중에 가용 가능한 리소스와 파드 개수를 아주 정밀하게 제어할 수 있어요. 예를 들어, 트래픽이 매우 높은 서비스라면 `maxSurge`를 높여서 업데이트 중에도 충분한 파드 수를 확보하는 전략이 필요해요.
하지만 디플로이먼트는 파드의 식별자가 매번 무작위로 생성된다는 점을 기억해야 해요. 파드가 재시작되면 이전 파드와는 완전히 다른 이름과 IP를 갖게 되죠. 따라서 특정 파드를 지칭해서 데이터를 주거나 연결을 유지해야 하는 작업에는 절대 사용해서는 안 돼요.
STEP 2. 데이터 일관성을 보장하는 스테이트풀셋(StatefulSet)
데이터베이스나 분산 시스템처럼 각 파드가 고유한 정체성을 가져야 한다면, 디플로이먼트의 대안으로 스테이트풀셋(StatefulSet)을 선택해야 해요. 스테이트풀셋은 파드에 `pod-0`, `pod-1`과 같이 순차적이고 고유한 이름을 부여해요. 이는 파드가 죽었다가 다시 살아나더라도 동일한 네트워크 식별자와 스토리지를 가질 수 있음을 의미해요.
스테이트풀셋의 가장 중요한 특징은 개별적인 영구 볼륨(PVC) 관리예요. 디플로이먼트는 모든 파드가 하나의 볼륨을 공유하도록 설정하기 쉽지만, 스테이트풀셋은 각 파드 번호에 매칭되는 전용 볼륨을 자동으로 생성하고 연결해 줘요. 덕분에 `pod-0`이 사용하는 데이터 볼륨이 `pod-0`이라는 이름표를 달고 계속 따라다니는 구조를 만들 수 있어요.
주의할 점은 배포와 확장 속도예요. 스테이트풀셋은 안정성을 위해 파드를 하나씩 순서대로 생성하거나 삭제해요. 즉, 10개의 파드를 늘릴 때 1번이 완전히 준비될 때까지 2번이 뜨지 않아요. 이는 데이터 동기화가 중요한 분산 시스템에서는 안정성을 주지만, 급격한 트래픽 증가에 대응하기에는 느릴 수 있다는 단점이 있어요.
STEP 3. 인프라 관리를 위한 데몬셋(DaemonSet) 전략
클러스터의 모든 노드에 빠짐없이 실행되어야 하는 프로세스가 있다면 무엇을 써야 할까요? 정답은 데몬셋(DaemonSet)이에요. 로그 수집기(Fluentd, Logstash)나 모니터링 에이전트(Prometheus Node Exporter), 혹은 네트워크 플러그인(CNI) 같은 시스템 수준의 서비스가 여기에 해당해요.
데몬셋은 사용자가 노드 개수를 신경 쓸 필요가 없게 해 줘요. 새로운 노드가 클러스터에 추가되면 쿠버네티스가 자동으로 해당 노드에 데몬셋 파드를 하나 띄워 주거든요. 반대로 노드가 삭제되면 파드도 함께 사라지죠. 이는 인프라 관리를 자동화하는 데 매우 강력한 도구예요.
데몬셋 파드가 너무 많은 리소스를 점유하도록 설정하면, 실제 비즈니스 로직을 수행해야 할 애플리케이션 파드들이 실행될 공간이 부족해지는 ‘자원 고갈’ 현상이 발생할 수 있어요. 반드시 리소스 제한(Limit)을 엄격하게 설정해야 해요.
STEP 4. 효율적인 배치 작업을 위한 잡(Job)과 크론잡(CronJob)
24시간 내내 떠 있을 필요 없이, 특정 작업을 수행하고 깔끔하게 종료되어야 하는 워크로드가 있어요. 데이터 백업, 대량의 이미지 리사이징, 정기적인 리포트 생성 등이 그 예시죠. 이런 경우에는 디플로이먼트 대신 잡(Job)을 사용해야 해요.
잡은 설정한 작업이 성공적으로 완료될 때까지 파드를 유지하며, 만약 실패하면 재시도 정책(backoffLimit)에 따라 다시 실행해요. 여기서 한 단계 더 나아가 특정 시간마다 주기적으로 실행해야 한다면 크론잡(CronJob)이 정답이에요. 리눅스의 crontab과 유사한 문법을 사용하여 매일 새벽 2시, 혹은 매주 월요일 아침처럼 정교한 스케줄링이 가능해요.
STEP 5. 복잡한 요구사항을 해결하는 오퍼레이터(Operator) 패턴
만약 앞서 언급한 4가지 컨트롤러만으로 해결되지 않는 복잡한 애플리케이션이라면 어떨까요? 예를 들어, 데이터베이스의 백업, 복구, 샤딩, 업그레이드 과정을 사람이 직접 개입하지 않고 자동화하고 싶다면 오퍼레이터(Operator) 패턴을 고려해야 해요.
오퍼레이터는 쿠버네티스의 커스텀 리소스 정의(CRD)와 기존 컨트롤러의 메커니즘을 결합한 형태예요. 애플리케이션의 운영 지식(Domain Knowledge)을 코드로 구현하여, 마치 숙련된 관리자가 옆에서 지켜보는 것처럼 복잡한 운영 시나리오를 수행하죠. 이는 단순한 비교를 넘어, 진정한 클라우드 네이티브 운영을 완성하는 단계라고 할 수 있어요.
실무에서는 단일 컨트롤러만 쓰기보다 조합해서 쓰는 경우가 많아요. 예를 들어, 핵심 데이터는 StatefulSet으로 관리하고, 그 데이터를 처리하는 API 서버는 Deployment로 구성하는 식이죠.
자주 하는 실수와 해결법
실무 운영 과정에서 컨트롤러를 잘못 설정하여 발생하는 문제는 대부분 정해진 패턴이 있어요. 이를 미리 알고 있다면 장애 발생 시 훨씬 빠르게 대응할 수 있어요.
❌ 디플로이먼트로 데이터베이스 운영하기
왜 발생하는가: 파드가 재시작될 때마다 새로운 식별자와 새로운 볼륨이 할당되어 데이터 연결이 끊겨요.
✅ 해결법: 반드시 스테이트풀셋(StatefulSet)을 사용하고, 각 파드에 고유한 PVC가 바인딩되도록 설정하세요.
❌ Liveness/Readiness Probe 설정 누락
왜 발생하는가: 애플리케이션이 준비되지 않았는데 트래픽이 유입되어 사용자에게 에러를 노출하거나, 죽어있는 파드를 계속 서비스에 포함시켜요.
✅ 해결법: 모든 디플로이먼트에는 애플리케이션의 상태를 체크할 수 있는 헬스 체크 프로브를 반드시 포함하세요.
❌ 데몬셋에 과도한 리소스 할당
왜 발생하는가: 노드 전체에 파드가 뜨기 때문에, 모든 노드의 자원을 조금씩 갉아먹어 결국 전체 클러스터의 가용성을 떨어뜨려요.
✅ 해결법: 데몬셋 파드에는 반드시 `resources.limits`를 설정하여 자원 점유율을 통제하세요.
❌ 잡(Job)의 재시도 횟수(backoffLimit) 미설정
왜 발생하는가: 일시적인 네트워크 오류로 작업이 실패했을 때, 무한히 재시도하거나 혹은 한 번 실패하자마자 종료되어 데이터 누락이 생겨요.
✅ 해결법: 작업의 성격에 따라 적절한 `backoffLimit` 값을 설정하여 복구 기회를 부여하세요.
자주 묻는 질문
Q. 스테이트풀셋은 왜 디플로이먼트보다 배포 속도가 느린가요?
스테이트풀셋은 안정성을 최우선으로 하기 때문이에요. 파드 간의 순서(Ordinal index)를 지키고, 각 파드가 가진 데이터 볼륨이 안전하게 연결되었는지 확인하는 과정을 거치기 때문에, 순차적인 배포 방식을 택하게 되어 속도가 느려질 수밖에 없어요.
Q. 서비스가 중단되지 않으려면 디플로이먼트 설정에서 무엇이 제일 중요한가요?
가장 중요한 것은 `strategy` 설정이에요. `RollingUpdate` 전략을 사용할 때 `maxUnavailable`을 0으로 설정하면, 새로운 파드가 완전히 준비될 때까지 기존 파드를 지우지 않으므로 서비스 가용성을 극대화할 수 있어요.
Q. 데몬셋을 사용하면 노드 수가 늘어날 때 자동으로 파드가 생성되나요?
네, 맞아요. 데몬셋의 핵심 기능이에요. 새로운 노드가 클러스터에 합류하면 쿠버네티스 컨트롤러가 이를 감지하고 즉시 해당 노드에 지정된 데몬셋 파드를 배치해요.
Q. 크론잡이 실행되지 않을 때는 어디를 먼저 봐야 하나요?
우선 `schedule` 문법이 정확한지 확인하세요. 그 다음으로는 해당 작업을 실행할 충분한 노드 자원이 있는지, 그리고 `concurrencyPolicy` 설정 때문에 이전 작업이 끝나지 않아 새 작업이 대기 중인 것은 아닌지 확인해 보는 것이 좋아요.
Q. 무상태 앱인데 왜 디플로이먼트를 권장하나요?
디플로이먼트는 선언적 업데이트와 롤백 기능을 기본적으로 제공하기 때문이에요. 갑작스러운 배포 실패 시 이전 버전으로 빠르게 되돌릴 수 있는 기능은 운영 안정성에 필수적이에요.
완벽한 워크로드 선택을 위한 마지막 체크리스트
지금까지 살펴본 내용을 바탕으로, 여러분의 서비스를 위한 최적의 선택을 내릴 준비가 되셨나요? 복잡한 상황일수록 기본으로 돌아가 워크로드의 본질을 질문해 보세요.
- 데이터 저장이 필요 없는 API/웹 서버 $\rightarrow$ Deployment
- DB, 메시지 큐 등 고유 식별자와 스토리지가 필수인 경우 $\rightarrow$ StatefulSet
- 모든 노드에 모니터링/로그 수집기가 필요한 경우 $\rightarrow$ DaemonSet
- 일회성 작업이나 주기적인 배치 작업 $\case$ $\rightarrow$ Job / CronJob
- 복잡한 운영 자동화가 필요한 경우 $\rightarrow$ Operator 패턴
- 모든 컨트롤러에는 반드시 Liveness/Readiness Probe를 설정할 것
오늘 당장 해야 할 일은 현재 운영 중인 서비스의 컨트롤러 설정 파일을 열어보는 것이에요. 혹시 데이터베이스를 디플로이먼트로 돌리고 있지는 않은지, 데몬셋에 리소스 제한이 빠져 있지는 않은지 확인해 보세요. 이번 주에는 각 컨트롤러의 `strategy` 설정을 하나씩 변경하며 실제 롤링 업데이트가 어떻게 동작하는지 테스트 환경에서 검증해 보는 것을 추천해요.
실무 환경을 구축하다가 특정 컨트롤러의 설정이 꼬이거나, 예상치 못한 동작을 발견한다면 주저하지 말고 댓글로 질문을 남겨 주세요. 함께 고민하면 더 나은 해답을 찾을 수 있어요.
관련하여 더 깊은 내용이 궁금하다면 아래 글들도 함께 읽어보시면 큰 도움이 될 거예요.