7.3.1 디플로이먼트의 필요성 및 기능

앞서 레플리카셋이 파드의 개수를 안정적으로 유지하고 장애 시 자가 치유하는 훌륭한 기능을 제공한다는 것을 배웠습니다. 하지만 실제 우리가 애플리케이션을 운영하다 보면 단순히 파드의 개수를 관리하는 것 이상의 정교한 작업들이 필요합니다. 예를 들어, 애플리케이션의 버그를 수정하거나 새로운 기능을 추가했을 때, 이 변경 사항을 실제 운영 환경에 안전하게 반영해야 합니다. 또한, 만약 새로 배포한 버전에 예상치 못한 심각한 문제가 발생한다면, 최대한 빨리 이전의 안정적인 버전으로 되돌릴 수 있어야 합니다.

레플리카셋만으로는 이러한 ‘애플리케이션 업데이트’와 ‘안전한 버전 관리’라는 과제를 우아하게 해결하기 어렵습니다. 물론, 수동으로 새로운 버전의 이미지를 사용하는 새 레플리카셋을 만들고, 기존 레플리카셋의 파드 수를 점차 줄이면서 새 레플리카셋의 파드 수를 늘리는 복잡한 절차를 거칠 수도 있습니다. 하지만 이 과정은 번거롭고, 사람의 실수로 인해 서비스 중단이 발생할 위험도 큽니다.

바로 이러한 지점에서 디플로이먼트(Deployment)의 필요성이 대두됩니다. 디플로이먼트는 레플리카셋의 상위 개념으로, 파드와 레플리카셋에 대한 선언적 업데이트를 제공하여 애플리케이션의 배포 및 업데이트 과정을 훨씬 더 정교하고 자동화된 방식으로 관리할 수 있게 해줍니다. 마치 레플리카셋이 개별 배우(파드)들의 수를 관리하는 무대 감독이라면, 디플로이먼트는 전체 공연(애플리케이션 버전)의 시작, 변경, 그리고 필요시 이전 장면으로의 전환까지 총괄하는 연출가와 같습니다.

이제 디플로이먼트가 왜 필요하며, 구체적으로 어떤 핵심 기능들을 제공하는지 자세히 살펴보겠습니다.

7.3.1.1 레플리카셋과 파드의 선언적 관리

디플로이먼트의 가장 기본적인 역할은 애플리케이션의 원하는 상태(desired state)를 선언적으로 관리하는 것입니다. 여기서 ‘선언적’이라는 말은 “어떻게(how)”가 아니라 “무엇을(what)”에 집중한다는 의미입니다. 예를 들어, “애플리케이션 A의 버전 2를 3개의 복제본으로 실행하고 싶다”라고 디플로이먼트에 선언하면, 디플로이먼트는 이 목표 상태를 달성하기 위해 필요한 모든 작업을 내부적으로 수행합니다.

이때 중요한 점은, 디플로이먼트는 파드를 직접 관리하지 않는다는 것입니다. 대신, 디플로이먼트는 레플리카셋을 생성하고 관리하며, 이 레플리카셋이 결과적으로 파드를 생성하고 관리하도록 합니다. 이러한 한 단계 추상화된 접근 방식이 디플로이먼트의 강력한 업데이트 및 롤백 기능을 가능하게 하는 핵심입니다.

구체적인 흐름을 살펴보겠습니다.

  1. 디플로이먼트 생성: 사용자가 디플로이먼트 명세(YAML 파일)에 원하는 파드 템플릿(예: 사용할 컨테이너 이미지 버전, 필요한 복제본 수 등)을 정의하여 쿠버네티스에 제출합니다.
  2. 레플리카셋 생성: 디플로이먼트 컨트롤러는 이 명세를 바탕으로 해당 파드 템플릿을 가지는 새로운 레플리카셋을 생성합니다. 이때 레플리카셋의 이름은 보통 디플로이먼트 이름에 해시값을 붙여 고유하게 만들어집니다.
  3. 파드 생성: 생성된 레플리카셋은 자신의 명세에 따라 지정된 수의 파드를 생성하고 관리합니다.

만약 사용자가 디플로이먼트의 파드 템플릿을 변경하면 (예: 컨테이너 이미지 버전을 v1에서 v2로 업데이트), 디플로이먼트는 다음과 같이 동작합니다.

  1. 새로운 레플리카셋 생성: 디플로이먼트는 변경된 파드 템플릿(v2 이미지 사용)을 가지는 새로운 레플리카셋을 생성합니다.
  2. 점진적 전환: 기존 레플리카셋(v1 이미지 사용)의 파드 수를 점차 줄이는 동시에, 새로운 레플리카셋의 파드 수를 점차 늘립니다. 이 전환 방식은 아래에서 설명할 ‘롤링 업데이트’ 전략에 따라 수행됩니다.
  3. 상태 관리: 디플로이먼트는 이 모든 과정을 조율하며, 최종적으로 원하는 상태(v2 이미지를 사용하는 파드들이 지정된 복제본 수만큼 실행되는 상태)에 도달하도록 합니다.

이처럼 디플로이먼트는 레플리카셋이라는 ‘버전 관리 단위’를 통해 파드를 간접적으로 제어함으로써, 단순히 파드의 개수를 유지하는 것을 넘어 애플리케이션의 버전까지도 선언적으로 관리할 수 있게 해줍니다. 사용자는 복잡한 단계별 명령을 내릴 필요 없이, 최종적으로 원하는 애플리케이션의 상태만 정의하면 나머지는 디플로이먼트가 알아서 처리해주니 정말 편리하겠죠? 이는 쿠버네티스의 핵심 철학인 ‘선언적 API’가 잘 반영된 부분입니다.

7.3.1.2 롤링 업데이트 (Rolling Update) 전략

애플리케이션을 업데이트할 때 가장 우려되는 부분 중 하나는 바로 서비스 중단입니다. 기존 버전을 모두 내리고 새로운 버전을 한 번에 올리는 방식(Recreate 전략)은 간단하지만, 업데이트 중간에 서비스가 완전히 멈추는 다운타임(downtime)이 발생합니다. 사용자가 많은 중요한 서비스에서 이러한 다운타임은 치명적일 수 있습니다.

디플로이먼트는 이러한 문제를 해결하기 위해 기본적으로 롤링 업데이트(Rolling Update) 전략을 사용합니다. 롤링 업데이트는 애플리케이션의 무중단 업데이트(zero-downtime update)를 목표로, 기존 버전의 파드를 새로운 버전의 파드로 점진적으로 교체하는 방식입니다. 마치 달리는 자동차의 바퀴를 하나씩 교체하는 것처럼, 서비스는 계속 제공하면서 업데이트를 진행할 수 있습니다.

롤링 업데이트의 동작 방식은 다음과 같습니다.

  1. 업데이트 요청: 사용자가 디플로이먼트의 파드 템플릿을 변경(예: 컨테이너 이미지 버전 업데이트)하면 롤링 업데이트 프로세스가 시작됩니다.
  2. 새 버전 레플리카셋 생성: 디플로이먼트는 새로운 파드 템플릿을 기반으로 하는 새 레플리카셋을 생성합니다.
  3. 점진적 파드 교체:
    • 먼저, 새로운 레플리카셋에 의해 새 버전의 파드가 하나 이상 생성됩니다. (이때, 동시에 생성할 수 있는 새 파드의 최대 개수(maxSurge)를 설정할 수 있습니다.)
    • 새 버전의 파드가 정상적으로 실행되고 준비 상태(Ready)가 되면, 기존 버전의 레플리카셋에 의해 관리되던 구 버전의 파드 중 일부가 종료됩니다. (이때, 업데이트 중에도 사용 불가능한 상태가 될 수 있는 파드의 최대 개수(maxUnavailable)를 설정할 수 있습니다.)
  4. 반복: 이 과정(새 파드 추가 후 구 파드 제거)을 반복하여 모든 구 버전 파드가 새 버전 파드로 점진적으로 교체될 때까지 진행합니다.
  5. 완료: 모든 파드가 새 버전으로 성공적으로 업데이트되면, 구 버전의 레플리카셋은 파드 수가 0이 된 상태로 남아있게 됩니다 (롤백을 위해).

이러한 롤링 업데이트 전략 덕분에 다음과 같은 장점을 얻을 수 있습니다.

  • 무중단 서비스: 업데이트 중에도 항상 일정 수 이상의 파드가 실행 중이므로 서비스 중단 없이 업데이트를 진행할 수 있습니다.
  • 위험 감소: 만약 새로운 버전에 문제가 있더라도, 모든 파드가 한 번에 새 버전으로 바뀌는 것이 아니므로 문제를 조기에 발견하고 대응하기 용이합니다. 문제가 감지되면 업데이트를 중단하고 즉시 롤백할 수 있습니다.
  • 유연한 제어: maxSurge와 maxUnavailable 같은 파라미터를 통해 업데이트 속도와 안정성 사이의 균형을 조절할 수 있습니다. 예를 들어, maxSurge=1, maxUnavailable=0으로 설정하면 한 번에 하나의 새 파드만 추가하고, 새 파드가 준비된 후에만 구 파드를 제거하여 매우 안전하게 업데이트를 진행할 수 있습니다. 반대로, maxSurge 값을 크게 잡으면 더 빠르게 업데이트를 완료할 수 있습니다. (이 파라미터들은 디플로이먼트 명세 분석에서 더 자세히 다룰 것입니다.)

롤링 업데이트는 클라우드 네이티브 환경에서 애플리케이션을 배포하고 관리하는 데 있어 가장 널리 사용되는 표준적인 방식 중 하나입니다. 디플로이먼트는 이 강력한 기능을 기본으로 제공함으로써 개발자와 운영팀이 안심하고 애플리케이션을 지속적으로 개선해 나갈 수 있도록 돕습니다.

7.3.1.3 롤백 (Rollback) 기능

소프트웨어 개발에서 “실수는 언제나 발생할 수 있다”는 것을 인정하는 것은 매우 중요합니다. 아무리 철저히 테스트하더라도, 새로운 버전을 배포했을 때 예상치 못한 버그나 성능 문제가 발생할 수 있습니다. 이때 가장 중요한 것은 문제를 신속하게 인지하고, 사용자에게 미치는 영향을 최소화하면서 이전의 안정적인 상태로 빠르게 되돌아가는 것입니다.

디플로이먼트는 바로 이러한 상황에 대비한 강력한 롤백(Rollback) 기능을 제공합니다. 롤백은 현재 배포된 버전에서 문제가 발생했을 때, 이전에 배포되었던 특정 버전으로 애플리케이션의 상태를 되돌리는 것을 의미합니다. 마치 문서 편집기의 ‘실행 취소(Undo)’ 기능처럼, 디플로이먼트의 변경 이력을 사용하여 이전 상태로 복원할 수 있습니다.

디플로이먼트가 롤백을 지원할 수 있는 이유는 배포 기록(Revision History)을 유지하기 때문입니다. 사용자가 디플로이먼트의 파드 템플릿을 변경할 때마다, 디플로이먼트는 새로운 ‘리비전(revision)’을 생성하고 이전 리비전의 정보(주로 이전 레플리카셋의 템플릿)를 저장합니다. 기본적으로 최근 몇 개의 리비전이 저장되며, 이 개수는 설정(revisionHistoryLimit)을 통해 조절할 수 있습니다.

롤백이 필요한 상황이 발생하면, 다음과 같은 방식으로 롤백을 수행할 수 있습니다.

  1. 배포 기록 확인: 먼저 kubectl rollout history deployment/<디플로이먼트-이름> 명령어를 사용하여 현재까지의 배포 기록(리비전 목록)을 확인합니다. 각 리비전은 고유한 번호를 가지며, 언제 어떤 변경이 있었는지 간략한 정보를 포함합니다.
  2. 롤백 수행: kubectl rollout undo deployment/<디플로이먼트-이름> 명령어를 사용하면 가장 최근의 리비전에서 그 이전 리비전으로 롤백됩니다. 만약 특정 리비전으로 돌아가고 싶다면 –to-revision=<리비전-번호> 옵션을 추가하여 지정할 수 있습니다.
  3. 롤백 진행: 롤백 명령이 실행되면, 디플로이먼트는 지정된 이전 리비전의 파드 템플릿을 사용하여 롤링 업데이트와 유사한 방식으로 파드를 교체합니다. 즉, 현재 버전의 파드를 점진적으로 줄이고, 롤백 대상 버전의 파드를 점진적으로 늘려나갑니다.

이러한 롤백 기능은 다음과 같은 큰 이점을 제공합니다.

  • 신속한 장애 복구: 문제가 발생했을 때 복잡한 절차 없이 간단한 명령어로 이전 안정 버전으로 빠르게 돌아갈 수 있어, 서비스 장애 시간을 최소화(MTTR, Mean Time To Recovery 단축)할 수 있습니다.
  • 배포 안정성 향상: 롤백 기능이 있다는 사실 자체가 새로운 버전을 배포하는 데 대한 심리적 부담감을 줄여줍니다. “문제가 생겨도 쉽게 되돌릴 수 있다”는 안정감은 더 빠르고 잦은 배포를 가능하게 하여 애자일(Agile) 개발 문화를 촉진합니다.
  • 버전 관리의 용이성: 디플로이먼트가 배포 기록을 자동으로 관리해주므로, 어떤 버전들이 배포되었었는지 추적하고 특정 버전으로 돌아가는 작업이 매우 간편해집니다.

물론, 롤백 기능이 만능은 아닙니다. 예를 들어, 데이터베이스 스키마 변경과 같이 애플리케이션 코드 외적인 부분까지 되돌려주지는 못합니다. 따라서 롤백 전략을 수립할 때는 이러한 애플리케이션 의존성도 함께 고려해야 합니다.

결론적으로, 디플로이먼트가 제공하는 레플리카셋과 파드의 선언적 관리, 롤링 업데이트, 그리고 롤백 기능은 현대적인 클라우드 네이티브 애플리케이션 배포 및 운영에 있어 필수적인 요소들입니다. 이러한 기능들을 통해 개발자와 운영자는 복잡한 배포 과정에서 벗어나 서비스의 안정성과 신뢰성을 높이는 데 더욱 집중할 수 있게 됩니다.