[책 : 쿠버네티스 패턴 ] 선언적 배포
선언적 배포
선언적 배포 패턴의 핵심은 쿠버네티스의 디플로이먼트 자원이다. 디플로이먼트는 컨테이너 그룹의 업그레이드 및 롤백 프로세스를 캡슐화하며 컨테이너 그룹을 반복적이고 자동화된 동작으로 실행할 수 있다.
문제
마이크로서비스 수가 증가하면 새로운 버전을 지속적으로 업데이트하고 교체하는 것도 부담이 커지게됩니다. 업그레이드는 새로운 버전의 파드를 시작하기, 이전 버전의 파드를 안전하게 중지하기, 새로운 파드가 성공적으로 시작되었는지 대기 및 확인하기, 실패할 경우 이전 버전으로 롤백하기 등의 동작을 포함합니다. 이런 단계를 수동으로 진행하다보면 작업자에 의한 오류가 발생할 수 있고, 적합한 스크립트를 만드는 것도 많은 노력이 필요합니다. 두 방법 모두 릴리스 프로세스에 병목현상을 야기합니다.
해결
디플로이먼트 자원을 이용하면 애플리케이션 업데이트 방법, 개별 전략 활용, 다양한 업데이트 프로세스 조정 등을 기술할 수 있습니다. 디플로이먼트의 핵심은 예측 범위 안에서 파드 세트를 시작하고 중지하는 기능입니다. 예상대로 작동하기 위해선 컨테이너가 수명주기 이벤트를 잘 수신해야하고, 파드가 성공적으로 실행되었는지를 알려주는 정상상태 확인 종단점을 제공해야합니다. 두 영역을 정확하게 커버한다면 컨테이너를 깨끗하게 종료하고 업데이트된 인스턴스로 교체할 수 있습니다. 그런 다음 업데이트 프로세스의 나머지 부분을 선언적 방법으로 정의해서, 미리 정의된 단계와 예상된 결과를 하나의 원자적 작업으로 실행할 수 있습니다.
- 롤링배포
내부적으로 디플로이먼트는 세트 기반 레이블 셀렉터를 지원하는 레플리카세트를 생성합니다. 추상화를 통해 RollingUpdate(기본값)와 Recreate같은 전략을 사용해 업데이트 프로세스 동작을 구체화 할 수 있습니다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: random-generator
spec:
replicas: 3 #1
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 #2
maxUnavailable: 1 #3
selector:
matchLabels:
app: random-generator
template:
metadata:
labels:
app: random-generator
spec:
containers:
- image: k8spatterns/random-generator:1.0
name: random-generator
readinessProbe: #4
exec:
command: [ "stat", "/random-generator-ready" ]
#1: 3개의 레플리카 선언, 롤링 업데이트를 위해서는 2개 이상의 레플리카가 필요합니다.
#2: 업데이트 동안 일시적으로 지정된 레플리카 수에 더해, 실행될 수 있는 파드의 구,이 예제에서는 최대 총 4개의 레플리카가 될 수 있습니다.
#3: 업데이트 동안 사용불가능하게 될 수 있는 파드의 수, 여기서는 업데이트 하는 동안 단 2개 파드만 동시에 사용할 수 있습니다.
#4: 레디니스 점검은 무중단을 제공하기 위한 롤링 배포에 매우중요함을 잊어서는안됩니다.
- 롤링업데이트 전략 행동은 업데이트 프로세스 동안 중단이 없음을 보장합니다.
- 내부적으로 새로운 레플리카세트를 생성하고 새로운 컨테이너로 이전 컨테이너를 교체함으로써 비슷한 동작을 수행합니다.
- 디플로이먼트 객체를 사용하면 maxSurge 및 maxUnavailable 필드를 통해 초과 파드와 사용 가능한 파드 범위를 제어할 수 있습니다.
선언적 업데이트 작동을 위한 옵션(예제 : https://github.com/k8spatterns/examples/tree/main/foundational/DeclarativeDeployment)
- kubectl replace로 새로운 버전의 디플로이먼트로 전체 디플로이먼트를 교체합니다.
- 디플로이먼트를 패치나 대화식으로 편집해서 새로운 버전의 새로운 컨테이너 이미지를 넣습니다.
- kubectl set image 명령으로 디플로이먼트에 새로운 이미지를 넣습니다.
디플로이먼트 이점
- 전체 업데이트 프로세스는 클라이언트와 상호작용 없이 서버측에서 실행됩니다.
- 선언적 특성을 통해, 배포에 필요한 단계보다는 배포된 상태가 어떻게 보여야 하는지를 알 수 있습니다.
- 디플로이먼트 정의는 운영 환경에 배포되기 전에 다양한 환경에서 테스트된 실행 가능한 객체입니다.
- 업데이트 프로세스는 모두 기록되며, 일시 중지 및 계속을 위한 옵션, 이전 버전으로 롤백을 위한 옵션으로 버전이 지정됩니다.
롤링배포 부작용
- 업데이트 프로세스 동안 두 버전의 컨테이너가 동시 실행되어 서비스 컨슈머에 문제가 발생 할 수 있습니다.
- 업데이트 프로세스가 이전 버전과 호환되지 않는 변경 사항을 서비스 API에 도입하고 클라이언트가 이를 처리할 수 없을때 문제가 발생합니다.
- 고정 배포
롤링 배포에 문제를 해결하기 위해 Recreate 전략이 있습니다. maxUnavailable 개수를 레플리카와 똑같은 개수로 설정하는 효과가 있습니다.
- 우선적으로 현재 버전의 모든 컨테이너를 종료시킵니다.
- 이전 버전의 컨테이너가 축출될 때 모든 신규 컨테이너를 동시에 시작합니다.
이점 및 단점
- 두 버전의 컨테이너는 동시에 실행되지 않고 서비스 컨슈머가 한번에 오직 하나의 버전만 처리 가능하도록 단순화됩니다.
- 순차적 작업으로 이전 버전의 모든 컨테이너가 중지된 상태에서는 다운 타임이 발생하며, 들어오는 요청을 처리할 신규 컨테이너는 없습니다.
- 블루-그린 릴리스
다운타임을 최소화하고 위험성을 줄여서 운영 환경에 소프트웨어를 배포하기 위해 사용되는 릴리스 전략입니다.
- 서비스 메시나 케이네이티브 같은 확장 서비스를 사용하지 안흔ㄴ다면 블루-그린 배포는 수동으로 수행되어야합니다.
- 기술적으로는 아직 어떤 요청도 처리하지 않은 최신 버전의 컨테이너(그린으로 칭함)로 두번째 디플로이먼트를 생성해 실행합니다. 이 단계에서 원래 디플로이먼트의 이전 파드 레플리카가 여전히 실행되며 실제 요청을 처리합니다.
- 새로운 버전 파드가 정상적인 것이 확인되면 이전 파드 레플리카에서 새로운 레플리카로 트래픽을 전환합니다.
- 위 동작은 서비스 셀렉터를 새로운 컨테이너로 일치시키는 업데이트에 의해 수행됩니다.
- 위 이미지에서 그린 컨테이너가 모든 트래픽을 처리하면, 블루 컨테이너는 삭제될 수 있고 향후 또 다른 블루-그린 배포를 위해 블루 컨테이너 자원이 해재됩니다.
블루-그린 접근 방식의 이점 및 단점
- 이점은 애플리케이션 버전이 하나뿐이므로 서비스 컨슈머에 의해 동시에 여러 버전이 처리되는 복잡성을 줄여주었다는 점이 되겠습니다.
- 단점은 블루와 그린 컨테이너가 모두 실행되는 동안 애플리케이션 용량이 2배가 필요하다는 점이 되겠습니다.
- 또한 전환하는 동안 장기 실행 프로세스 및 데이터베이스 상태 변화로 인해 심각한 문제가 발생할 수 있습니다.
- 카나리아 릴리스
인스턴스의 작은 하위집합만 새로운 인스턴스로 교체함으로 새로운 버전의 애플리케이션을 운영에 유연하게 배포하는 방식입니다. 일부 컨슈머만 업데이트된 버전을 사용하게 함으로써 운영에 새버전을 도입할 때의 위험을 줄여줍니다.새 버전의 서비스와 소수의 사용자 시험군에게 적용된 방식이 만족스럽다면 비로소 이전 인스턴스를 모두 새로운 버전으로 교체합니다.
- 쿠버네티스에서 카나리아 인스턴스로 쓰이는 작은 레플리카 수를 갖는 신규 컨테이너 버전(주로 디플로이먼트 사용)에 대해 새로운 레플리카 세트를 생성함으로써 구현할 수 있습니다.
- 서비스는 컨슈머 중 일부를 업데이트 된 파드 인스턴스로 바로 연결 해야합니다.
- 신규 레플리카 세트와 함께 모든 것이 예상대로 작동한다면 신규 레플리카 세트를 늘리고 이전 레플리카 세트를 0으로 줄입니다
정리
디플로이먼트 기본 요소는 수동으로 업데이트하는 프로세스를 반복적이고 자동화된 선언적 동작으로 바꿔주는 하나의 사례입니다. 바로 사용 가능한 배포전략(롤링 및 리크리에이트)은 기존 컨테이너를 새로운 컨테이너로 교체하는 것을 제어하고, 릴리스 전략(블루-그린, 카나리아)은 서비스 컨슈머에게 새로운 버전이 제공되는 방식을 제어합니다. 각 배포방식의 장단점을 이해하고 상황과 환경에 맞게 걸맞는 전략을 선택하면 되겠습니다.