사전 단계적 출시
패턴

단계적 출시

gabury1

새 판을 전부에게 한 번에 내보내지 않기로 하는 결정입니다. 처음에는 일부에게만 닿게 합니다. 그 비율을 시간을 두고 올립니다. 문제가 보이면 남은 사람들에게 가기 전에 멈춥니다.

상세

받는 쪽이 여럿일 때, 새 판을 그 전부에게 동시에 밀어 넣을 이유는 없습니다. 단계적 출시는 받는 쪽을 비율로 잘라 앞쪽부터 내보내고 그 비율을 올려 가는 방식입니다. 잘린 조각 하나가 문제 없이 지나가면 다음 조각을 엽니다. 문제가 보이면 남은 조각을 열지 않습니다.

flowchart TD
    A[새 판] --> B[일부에게 내보냄]
    B --> C{문제가 보이나}
    C -->|보임| D[중단]
    C -->|안 보임| E[비율 올림]
    E -->|100% 미만| C
    E -->|100%| F[전량 도달]

사용자 기기에 설치되는 소프트웨어에서 이 결정은 스토어의 기능으로 굳어 있습니다. 부르는 이름은 스토어마다 갈립니다. Google 쪽 이름은 staged rollout 이고 Apple 쪽 이름은 phased release 입니다. Google Play Console 도움말은 단계적 출시를 쓰면 갱신이 사용자의 일정 비율에만 닿는다고 적습니다. 그 비율은 시간을 두고 올릴 수 있습니다. 프로덕션 트랙과 테스트 트랙에 쓸 수 있습니다. 같은 문서는 단계적 출시를 앱 갱신에만 쓸 수 있다고 못 박습니다. 앱을 처음 게시할 때는 못 씁니다. 대상은 새 사용자와 기존 사용자 모두입니다. 릴리스마다 무작위로 뽑힙니다. Apple 의 App Store Connect 도움말은 같은 자리를 7일에 걸친 점진 배포로 정해 두었습니다. 자동 업데이트를 켠 기기의 무작위 표본이 갱신을 받습니다. 자기가 그 표본에 들었다는 알림은 사용자에게 가지 않습니다.

서버 쪽에도 같은 결정이 있습니다. Google 의 SRE(Site Reliability Engineering) 책은 롤아웃 과정이 필요한 만큼 단순하거나 복잡할 수 있다고 적습니다. 관련된 잡을 즉시 전부 갱신할 수도 있습니다. 새 바이너리를 여러 시간에 걸쳐 연속된 클러스터에 내보낼 수도 있습니다. 같은 문서는 목표가 배포 과정을 그 서비스의 위험 프로필에 맞추는 것이라고 적습니다. 사용자를 크게 마주하는 서비스라면 Google 은 한 클러스터에서 시작해 모든 클러스터가 갱신될 때까지 지수적으로 넓힐 수 있다고 적습니다. 민감한 인프라라면 롤아웃을 며칠에 걸쳐 늘릴 수 있다고 적습니다. 그때는 지리적으로 다른 지역의 인스턴스에 번갈아 끼워 넣는다고 적습니다.

쿠버네티스 디플로이먼트의 롤링 업데이트도 이 형태입니다. 디플로이먼트 컨트롤러는 새 판을 볼 때마다 레플리카셋을 만들어 원하는 파드를 띄웁니다. 옛 레플리카셋은 0까지 줄어듭니다. 두 레플리카셋이 함께 존재하는 구간이 곧 비율이 옮겨 가는 구간입니다.

대가

되돌리기가 반쪽입니다. Google Play Console 도움말은 문제를 발견하면 단계적 출시를 중단할 수 있다고 적습니다. 중단하면 추가 사용자는 그 버전을 받지 않습니다. 그러나 같은 문서는 이미 그 버전을 받은 사용자는 그 버전에 남는다고 적습니다. 중단은 앞으로 갈 것을 막는 손잡이지 이미 간 것을 거두는 손잡이가 아닙니다.

전량에 닿는 시점이 뒤로 밀립니다. Play 도움말은 갱신이 단계적 출시 비율만큼의 사용자에게 제공되지만 전체 집단이 갱신을 받기까지는 시간이 걸릴 수 있다고 적습니다. Apple 쪽은 그냥 두면 7일에 걸쳐 나갑니다. 그 표를 앞당기려면 Ready for Distribution 상태에서 전체 사용자에게 내보내는 선택을 따로 해야 합니다. 단계별 배포에 맡겨 둔 채로는 급히 고친 것도 같은 일정을 탑니다.

같은 시점에 두 판이 필드에 함께 있습니다. Play 도움말은 새 사용자와 기존 사용자가 모두 대상이라고 적습니다. 릴리스마다 무작위로 뽑힌다고도 적습니다. 쿠버네티스 문서도 롤링 업데이트 동안 옛 레플리카셋과 새 레플리카셋이 함께 존재한다고 적습니다. 어느 쪽이든 두 판이 같은 데이터와 같은 상대를 마주하는 구간이 생깁니다. 다만 문서가 적는 것은 두 판이 공존한다는 사실까지입니다. 그 구간이 하위 호환을 요구한다는 문장은 없습니다.

부수 작업이 뒤로 밀립니다. Play 도움말은 앱 갱신이 스토어 등재정보 변경을 필요로 하면 릴리스가 100% 사용자에게 나간 다음에 등재정보를 갱신하기를 권합니다.

노출을 완전히 통제하지는 못합니다. Apple 도움말은 단계적 배포 중인 앱과 앱 갱신을 앱스토어에서 누구나 언제든 손으로 내려받을 수 있다고 적습니다. Play 쪽은 단계적 출시분을 받은 사용자가 Google Play 에 공개 리뷰를 남길 수 있다고 적습니다. 문제를 만난 사람이 소수여도 그 기록은 공개된 자리에 남습니다.

기기에 설치되는 소프트웨어에서는 통제가 더 얇아집니다. martinfowler.com 에 실린 Danilo Sato 의 「Canary Release」는 이 계통의 기법이 어려워지는 자리로 사용자의 컴퓨터나 모바일 기기에 설치되는 소프트웨어를 듭니다. 새 판으로의 업그레이드가 언제 일어나는지에 대한 통제가 그만큼 덜하다는 것입니다. 같은 글은 이 기법의 단점으로 여러 판의 소프트웨어를 한꺼번에 관리해야 하는 것을 듭니다.

예시

App Store Connect

Apple 의 단계별 배포는 날짜별 비율이 문서에 표로 박혀 있습니다.

단계별 배포 일차 사용자 비율
1 1%
2 2%
3 5%
4 10%
5 20%
6 50%
7 100%

이 표는 선택지가 아니라 일정입니다. 문서는 단계별 배포를 고르면 갱신이 7일에 걸쳐 점진적으로 나간다고 적습니다. 다만 Ready for Distribution 상태가 되면 언제든 전체 사용자에게 내보내는 선택을 따로 할 수 있습니다.

Argo Rollouts

서버 쪽에서는 같은 결정을 배포 명세 안에 단계 목록으로 적습니다. Argo Rollouts 의 Canary 전략 문서가 보여주는 최소 형태입니다.

YAML
- setWeight: 10
- pause:
    duration: 1h # 1 hour
- setWeight: 20
- pause: {}

setWeight 는 카나리로 보낼 트래픽의 비율을 정합니다. pause 는 롤아웃을 멈추라는 지시입니다. duration 이 붙으면 그 시간만큼 멈추고, pause: {} 는 기한 없이 멈춥니다. 문서는 레플리카가 10개인 롤아웃에서 첫 setWeight 가 10% 면 컨트롤러가 새 레플리카셋을 1개로 키운다고 적습니다.

쿠버네티스 디플로이먼트

디플로이먼트의 롤링 업데이트는 비율을 두 손잡이로 잡습니다.

YAML
.spec.strategy.rollingUpdate.maxUnavailable: 25%
.spec.strategy.rollingUpdate.maxSurge: 25%

maxUnavailable 은 갱신 중에 쓸 수 없어도 되는 파드의 최대치입니다. maxSurge 는 원하는 파드 수를 넘겨 더 만들 수 있는 최대치입니다. 둘 다 절대 수나 비율로 적을 수 있습니다. 기본값은 25% 입니다. maxUnavailable 은 비율에서 절대 수를 낼 때 내림합니다. maxSurge 는 올림합니다. 문서는 maxUnavailable 이 30% 면 롤링 업데이트가 시작될 때 옛 레플리카셋을 원하는 파드의 70% 까지 즉시 줄일 수 있다고 적습니다. 갱신 내내 쓸 수 있는 파드의 총수는 원하는 수의 70% 이상으로 유지됩니다.

운영

비율은 저절로 오르지 않습니다. Play 도움말은 앱의 단계적 출시 비율이 자동으로 증가하지 않는다고 적습니다. 더 큰 비율을 열려면 사람이 다시 들어가 올려야 합니다. 릴리스를 내보낼 때 받을 사용자 비율을 고르는 것도 사람입니다.

중단과 재개에는 사용자 집단이 따라붙습니다. Play 도움말은 롤아웃을 중단했다가 재개하면 같은 사용자 집합에 영향을 준다고 적습니다. 앞선 릴리스의 롤아웃을 끝내기 전에 새 릴리스를 단계적으로 내보내면, 새 릴리스는 비율에 따라 앞선 릴리스와 같은 사용자 집단을 씁니다. 국가를 지정하면 그 국가의 Google Play 계정을 가진 사용자로 업그레이드가 제한됩니다.

Apple 쪽의 손잡이는 일시정지입니다. 단계별 배포 중에 최대 30일까지 멈출 수 있습니다. 멈추는 횟수에는 제한이 없습니다. 10일을 멈추고 재개하면 다시 멈출 수 있는 날은 20일이 남습니다. 재개하면 멈춘 그날부터 이어집니다.

stateDiagram-v2
    state "단계적 배포 중" as 배포중
    state "일시정지" as 정지
    state "전원 공개" as 전원
    state "배포 중지" as 중지
    [*] --> 배포중
    배포중 --> 정지: 누적 최대 30일
    정지 --> 배포중: 멈춘 날부터 이어감
    배포중 --> 전원: 전체 배포 선택
    배포중 --> 중지: 판매 중단
    중지 --> 전원: 판매 재개 시 즉시

판매를 내리면 단계별 배포가 멈춥니다. Apple 도움말은 앱을 판매 중단하면 단계별 배포가 멈추고 그 버전에는 다시 쓸 수 없다고 적습니다. Apple Developer Program 멤버십이 만료된 경우도 같습니다. 앱이 복구되면 중단 전에 도달한 비율과 상관없이 즉시 모든 사용자가 받게 됩니다. 통제된 롤아웃을 다시 하려면 그 판을 내려받을 수 없게 만들어야 합니다. 그런 다음 단계별 배포를 켠 새 판을 다시 제출합니다.

볼 자리는 지표입니다. Android vitals 문서는 Google Play 에서 타이틀의 노출을 극대화하려면 아래 값들 아래로 유지하라고 적고, 이 값들을 bad behavior threshold 라고 부릅니다.

지표 전체 평균 폰 모델별 워치 모델별
사용자 인지 크래시율 1.09% 8% 4%
사용자 인지 ANR(Application Not Responding, 앱이 응답하지 않는 상태)율 0.47% 8% 5%
과도한 배터리 사용 1% - 1%
과도한 부분 웨이크 락 5% - -

같은 문서는 앱이나 게임이 이 값을 넘으면 Play 가 타이틀의 노출을 줄일 수 있다고 적습니다. 스토어 등재정보에 경고를 띄울 수도 있습니다. Play 도움말은 단계적 출시 중에 크래시 보고와 사용자 피드백을 가까이 지켜보기를 권합니다.

관련 항목

이것이 속하는 상위 분류

배포 · SRE · 롤아웃 · 릴리스 엔지니어링 · 지속적 배포 · progressive delivery

이름이 겹치는 이웃 기법

카나리 배포 · 블루-그린 배포 · 롤링 업데이트

progressive delivery 가 묶어 부르는 기법

기능 스위치 · A/B 테스트 · blast radius

이 이름을 붙이거나 쓰는 조직

Apple · Google · Microsoft · RedMonk · LaunchDarkly

이것이 걸리는 앱스토어 쪽 요소

Google Play Console · App Store Connect · 프로덕션 트랙 · 테스트 트랙 · 자동 업데이트 · 스토어 등재정보 · 알림

서버 쪽에서 이것을 실제로 굴리는 도구·설정값

바이너리 · 쿠버네티스 · 디플로이먼트 · 레플리카셋 · 파드 · 클러스터 · Argo Rollouts · maxSurge · maxUnavailable · setWeight · pause

이것의 속도를 정하는 기준

위험 프로필 · Android vitals · 사용자 인지 크래시율 · 사용자 인지 ANR율 · 크래시 보고

이것을 쓰면 떠안는 되돌리기·호환 부담

롤백 · 하위 호환 · 버전 혼재

다른 이름: staged rollout · phased release · phased rollout · incremental rollout · 단계별 배포