사전 롤링 업데이트
패턴

롤링 업데이트

gabury1고친 사람 github-actions[bot]

롤링 업데이트는 돌고 있는 서버를 한꺼번에 멈추지 않고 몇 대씩 끊어서 새 버전으로 갈아 끼우기로 하는 결정입니다. 갈아 끼우는 동안에도 나머지 서버는 요청을 계속 받습니다. 그래서 서비스를 멈추지 않고 버전을 올릴 수 있습니다.

쉽고 빠른 이해

서버 아홉 대가 같은 프로그램을 돌리고 있다고 해 봅시다. 롤링 업데이트는 이 아홉 대를 세 대씩 끊어 차례로 새 버전으로 바꿉니다. 세 대를 바꾸는 동안 남은 여섯 대가 요청을 받습니다.

아홉 대를 한 번에 내렸다가 새 버전으로 올리면 그 사이에는 요청을 받을 서버가 없습니다. 새 서버 아홉 대를 따로 띄워 두고 한 번에 갈아타는 방법도 있지만, 그때는 서버를 두 벌 띄울 여유가 있어야 합니다. 롤링 업데이트는 서버를 조금만 더 쓰면서 중단도 없앱니다.

  1. 몇 대를 새 버전으로 띄웁니다
  2. 그 서버들이 요청을 받아도 되는 상태인지 확인합니다
  3. 확인이 끝나면 같은 수만큼 옛 서버를 뺍니다. 옛 서버가 없어질 때까지 되풀이합니다

대가는 갈아 끼우는 내내 옛 버전과 새 버전이 같이 요청을 받는다는 것입니다. 되돌릴 때도 한 걸음씩 거꾸로 밟아야 해서 시간이 걸립니다. 그래서 두 버전이 같이 돌면 안 되는 배포나 아주 빨리 되돌려야 하는 서비스에는 안 맞습니다.

상세

롤링 업데이트의 핵심은 옛 버전을 다 내린 다음 새 버전을 올리는 것이 아니라, 두 버전이 잠깐 겹치는 상태를 허용하는 것입니다. 겹치는 동안에도 요청을 받을 서버가 늘 남아 있어서 서비스가 안 끊깁니다.

먼저 정할 것이 둘입니다. 한 걸음에 몇 대를 옮길지, 그리고 옮긴 서버가 준비됐는지를 무엇으로 판단할지입니다. 그다음 두 버전이 겹치는 동안과 되돌릴 때를 보고, 마지막으로 이 방식을 고를 상황을 가립니다.

한 걸음에 바꾸는 대수

한 걸음에 몇 대를 바꾸느냐 하나가 배포에 걸리는 시간과 그동안 잃는 처리 용량을 같이 정합니다.

서버가 아홉 대인데 세 대씩 바꾸기로 했다면 걸음마다 이렇게 옮겨 갑니다.

flowchart TD
    subgraph G0["걸음 0 · 시작"]
        A0["옛 버전 9대 · 새 버전 0대 · 요청 받는 서버 9대"]
    end
    subgraph G1["걸음 1"]
        A1["옛 버전 6대 · 새 버전 3대 · 요청 받는 서버 6대"]
    end
    subgraph G2["걸음 2"]
        A2["옛 버전 3대 · 새 버전 6대 · 요청 받는 서버 6대"]
    end
    subgraph G3["걸음 3 · 끝"]
        A3["옛 버전 0대 · 새 버전 9대 · 요청 받는 서버 9대"]
    end
    A0 --> A1 --> A2 --> A3

걸음마다 옛 버전이 세 대씩 줄고 새 버전이 그만큼 늡니다. 걸음 안에서는 옛 서버 셋이 빠졌는데 새 서버 셋은 아직 준비 중이라 여섯 대만 요청을 받습니다. 한 대씩 바꾸기로 하면 같은 그림이 아홉 층이 됩니다.

세 대씩 바꾸면 걸음이 세 번이라 금방 끝납니다. 대신 한 걸음마다 세 대만큼의 처리 용량이 빠집니다. 한 대씩 바꾸면 빠지는 용량은 한 대뿐이지만 걸음이 아홉 번이라 오래 걸립니다. 빨리 끝내는 것과 용량을 지키는 것이 서로 반대로 당깁니다.

걸음의 폭은 값 두 개로 잡습니다. 하나는 용량을 얼마나 덜어도 되느냐입니다. 다른 하나는 서버를 얼마나 더 띄워도 되느냐입니다.

값 정하는 것 0으로 두면
한 걸음에 내려도 되는 최대 대수 갈아 끼우는 동안 줄어도 되는 용량 용량이 한 대만큼도 안 줄어듭니다
원래 대수보다 더 띄워도 되는 최대 대수 걸음마다 잠깐 빌려 쓸 여윳분 서버를 한 대도 더 안 띄웁니다

둘을 동시에 0으로 두면 걸음을 아예 뗄 수 없습니다. 내릴 수도 없고 더 띄울 수도 없으니 바꿀 서버가 생기지 않습니다. 둘 중 하나는 0보다 커야 합니다.

한 걸음의 절차

한 걸음은 서버 몇 대를 옛 버전에서 새 버전으로 옮기는 최소 단위입니다. 그 안에서 네 가지 일이 정해진 순서로 일어납니다.

배포 도구가 새 버전 서버를 띄웁니다. 그 서버가 요청을 받아도 되는지 확인합니다. 로드 밸런서가 그쪽으로 요청을 보내기 시작합니다. 옛 서버에서 요청을 끊고 내립니다.

sequenceDiagram
    participant D as 배포 도구
    participant N as 새 서버
    participant LB as 로드 밸런서
    participant O as 옛 서버
    D->>N: 새 버전으로 띄움
    N-->>D: 요청 받을 준비 끝
    D->>LB: 새 서버를 대상에 넣음
    D->>LB: 옛 서버를 대상에서 뺌
    Note over O: 처리 중이던 요청을 마저 끝낸다
    D->>O: 종료

그림의 마지막 두 걸음이 특히 중요합니다. 옛 서버를 로드 밸런서의 대상에서 먼저 빼면 그 뒤로는 새 요청이 안 들어옵니다. 그다음 이미 처리 중이던 요청만 마저 끝내고 내려갑니다. 이렇게 마무리할 시간을 주는 것을 커넥션 드레이닝이라고 부릅니다.

순서를 바꾸면 사고가 납니다. 새 서버를 확인하기 전에 옛 서버를 먼저 내리면 그 사이 용량이 빕니다. 대상에서 빼지 않은 채 옛 서버를 바로 끄면 이미 보낸 요청이 갈 곳을 잃습니다.

준비됐다고 판단하는 기준

앞 절에서 새 서버가 「요청 받을 준비 끝」을 알렸습니다. 그 준비를 무엇으로 판단하느냐에 롤링 업데이트의 안전이 거의 다 달려 있습니다.

프로세스가 떴다는 것과 요청을 처리할 수 있다는 것은 다릅니다. 시작할 때 설정을 읽고 데이터베이스에 연결을 맺고 캐시를 채워야 하는 서버가 있습니다. 이런 서버는 뜬 직후에 요청을 받으면 그대로 실패합니다.

그래서 서버에 「지금 요청을 받아도 되나」를 따로 물어봅니다. 그 답이 온 뒤에만 로드 밸런서의 대상에 넣습니다. 이 물음을 주기적으로 던지는 검사를 헬스 체크라고 합니다.

새 서버 한 대가 걸음 안에서 지나는 상태는 이렇습니다.

stateDiagram-v2
    state "떴음" as A
    state "준비 확인 중" as B
    state "준비됨 · 대상에 넣음" as C
    state "다음 걸음" as D
    state "배포 멈춤 · 옛 버전이 남아 있다" as E
    [*] --> A
    A --> B: 요청을 받아도 되나 묻는다
    B --> C: 된다고 답한다
    B --> E: 정해진 시간 안에 답이 없다
    C --> D

이 기준이 느슨하면 롤링 업데이트가 장애를 퍼뜨리는 통로가 됩니다. 새 버전이 뜨자마자 죽는데도 배포 도구가 그것을 준비 완료로 읽으면 다음 걸음을 계속 뗍니다. 걸음이 다 끝나면 옛 버전은 한 대도 안 남습니다.

그래서 걸음과 걸음 사이에 확인을 끼웁니다. 정해진 시간 안에 준비가 안 끝나면 거기서 멈춥니다. 멈춘 상태에서는 아직 옛 버전 서버가 남아 있어서 서비스가 살아 있습니다.

두 버전이 겹치는 시간

롤링 업데이트가 도는 내내 옛 버전과 새 버전이 함께 요청을 받습니다. 어느 요청이 어느 버전에 닿을지는 정해져 있지 않습니다. 그래서 두 버전이 서로를 견뎌야 합니다.

새 버전은 옛 버전이 만들어 둔 데이터를 읽을 수 있어야 하고, 옛 버전도 새 버전이 만든 데이터를 읽을 수 있어야 합니다. 이렇게 옛것과 새것이 서로를 견디는 성질을 하위 호환이라고 부릅니다.

응답을 보면 알기 쉽습니다. 새 버전이 응답에 필드를 하나 더 붙이는 변경은 대개 무사합니다. 옛 버전이 읽던 필드를 빼거나 이름을 바꾸는 변경은 아직 옛 버전에 닿은 쪽을 곧바로 깨뜨립니다.

저장소의 스키마도 같습니다. 열을 새로 더하는 변경은 두 버전이 함께 견디지만, 열을 지우는 변경은 옛 버전이 다 내려간 다음이어야 합니다. 그래서 스키마 변경을 한 번에 하지 않고 여러 번에 나눠 합니다.

나눈 순서는 이렇습니다.

flowchart TD
    S1["열을 더한다"]
    S2["두 버전이 함께 돈다 · 옛 열과 새 열이 다 있다"]
    S3["옛 버전이 다 내려간다"]
    S4["옛 열을 지운다"]
    S1 --> S2 --> S3 --> S4

열을 지우는 일이 옛 버전 철수 뒤로 밀리는 것이 요점입니다. 열을 먼저 지우면 아직 돌고 있는 옛 버전이 그 열을 찾다가 깨집니다.

사람 눈에도 섞임이 보입니다. 화면을 새로 고칠 때마다 옛 버전과 새 버전을 번갈아 만날 수 있습니다. 생김새가 크게 바뀌는 배포라면 이 섞임이 사용자에게 그대로 드러납니다.

되돌리는 데 걸리는 시간

새 버전에 문제가 보이면 옛 버전으로 되돌립니다. 이것을 롤백이라고 합니다. 롤링 업데이트에서 되돌리기는 같은 절차를 거꾸로 밟는 일입니다. 앞에서 본 걸음 그림을 아래에서 위로 되짚는 셈입니다.

새 버전 서버를 몇 대씩 내리고 옛 버전 서버를 그만큼 띄웁니다. 방향을 한 번에 뒤집는 스위치가 없습니다. 그래서 되돌리는 데 걸리는 시간이 서버 대수를 따라 늘어납니다.

대신 걸음 몇 개만 나아간 상태에서 멈추면 옛 버전이 아직 많이 남아 있습니다. 새 버전이 닿은 범위가 그만큼 좁아서, 되돌릴 대상도 적습니다.

옛 서버를 지우지 않고 켜 둔 채 트래픽만 한 번에 돌리는 블루-그린 배포는 되돌리기가 한 번의 전환으로 끝납니다. 대신 서버를 두 벌 띄울 여유가 필요합니다. 롤링 업데이트는 그 여유를 덜 쓰고 되돌리는 시간을 내주는 선택입니다.

카나리 배포와 갈라지는 대목

카나리 배포는 새 버전을 요청의 아주 적은 몫에만 먼저 내보냅니다. 그 몫이 겪는 것을 지켜본 다음에 넓힐지 말지를 정합니다. 지켜보는 절차가 방식 안에 들어 있습니다.

롤링 업데이트는 그 절차를 요구하지 않습니다. 걸음을 뗄 때 보는 것은 새 서버가 준비됐느냐 하나뿐입니다. 준비만 됐으면 다음 걸음으로 나아갑니다. 새 버전이 뜨기는 잘 뜨는데 응답이 틀린 경우는 그냥 통과합니다.

두 방식이 갈리는 곳은 걸음을 뗀 뒤의 판단 하나입니다.

flowchart TD
    P["한 걸음을 뗀다"]
    Q{"새 서버가 준비됐나"}
    R{"지켜본 결과가 괜찮나"}
    S["멈추고 되돌린다"]
    P --> Q
    Q -->|"롤링 업데이트는 바로 다음 걸음으로"| P
    Q -->|"카나리 배포는 한 번 더 묻는다"| R
    R -->|"괜찮다"| P
    R -->|"아니다"| S

둘은 겹쳐 쓸 수도 있습니다. 첫 걸음을 아주 좁게 잡고 그 걸음에서 오래 지켜본 뒤 나머지를 넓히면, 롤링 업데이트의 첫 걸음이 카나리 배포의 몫을 겸합니다.

고를 만한 상황과 미룰 상황

이 결정이 맞는 상황은 서버가 여러 대일 때입니다. 그중 몇 대가 빠져도 나머지가 요청을 받아 낼 수 있어야 합니다. 서버가 자기 안에 상태를 안 갖고 있어서 아무 대나 요청을 받아도 되는 서비스일수록 잘 맞습니다.

서버를 두 벌 띄울 여유는 없는데 서비스를 잠깐도 멈출 수 없을 때 남는 선택이 대개 이 방식입니다. 그렇게 멈추지 않고 버전을 올리는 배포를 무중단 배포라고 부릅니다.

미룰 상황은 두 버전이 같이 돌면 안 되는 배포입니다. 하위 호환을 지킬 수 없는 저장 형식 변경이라면, 두 버전이 겹치는 시간 자체가 사고가 됩니다. 그럴 때는 잠깐 멈추고 전부 한 번에 바꾸거나, 앞에서 본 블루-그린 배포처럼 새 버전을 한 벌 따로 띄워 한 순간에 돌리는 쪽을 고릅니다.

되돌리는 시간이 아주 짧아야 하는 서비스도 미룰 상황입니다. 걸음이 많을수록 되돌리기도 길어지기 때문입니다.

관련 항목

같은 계보의 다른 배포 전략

블루-그린 배포 · 카나리 배포 · 단계적 출시 · 재생성 · A/B 테스트 · 기능 플래그

갈아 끼우는 동안 요청을 나눠 보내는 장치

로드 밸런서 · 리버스 프록시 · 서비스 메시 · API 게이트웨이 · 인그레스

새 서버가 준비됐는지 확인하는 검사

헬스 체크 · 준비성 프로브 · 활성 프로브 · 그레이스풀 셧다운 · 커넥션 드레이닝

두 버전이 함께 도는 동안 지켜야 하는 성질

하위 호환 · 상위 호환 · 버전 혼재 · 스키마 마이그레이션 · 무상태

롤링 업데이트를 대신 굴려 주는 제품과 리소스

Kubernetes · 디플로이먼트 · 레플리카셋 · Argo Rollouts · 오토스케일링

이 결정이 속하는 상위 흐름

배포 · 롤아웃 · 무중단 배포 · 지속적 배포 · 롤백 · 데브옵스

다른 이름: rolling update · rolling deployment · rolling upgrade · 롤링 배포 · 롤링 업그레이드