Parity before cutover

Alex O'Callaghan

컷오버 전 패리티 맞추기

저희는 여러 팀과 서비스가 의존하는 서비스를 운영하고 있습니다. 몇 년 동안 이 서비스는 두 가지 형태로 존재했습니다. 온프레미스 서버에 배포된 Python 2 기반 프로덕션 버전과, 별도 브랜치에서 클라우드 마이그레이션을 통해 기존 버전을 대체할 날을 기다리고 있던 현대화된 Python 3 버전이 그것이었습니다.

많은 레거시 업그레이드가 이런 모습입니다. 최종 상태는 명확합니다. 새 언어 버전, 새 인프라, 그리고 사라질 구 시스템. 그래서 팀들은 곧장 최종 상태를 향해 달려갑니다. 작업은 오래 유지되는 브랜치나 “v2” 재작성으로 진행되고, 어려운 작업은 프로덕션과 떨어진 곳에서 해치운 뒤 마지막에 한 번에 전환하겠다는 계획입니다.

끝나지 않는 재작성

하지만 그 계획은 약속한 결과를 거의 가져오지 못합니다. 새 버전이 릴리스되지 않은 채로 있는 동안 리스크는 사라지지 않고 오히려 쌓입니다.

모든 긴급 수정은 여전히 구 버전에 먼저 적용되어야 하고, 그 다음 신 버전으로 포팅되어야 합니다. 그렇지 않으면 누락됩니다. 의도했든 아니든 두 버전 사이의 모든 동작 차이는 컷오버 이후 다운스트림 팀에 의해 발견되기를 기다리는 버그이며, 그때가 원인을 규명하기 가장 어려운 시점입니다.

오래 유지되는 브랜치는 모든 것을 하나의 이벤트로 묶기 때문에 효율적으로 느껴집니다. 하지만 실제로는 리스크를 한 시점에 집중시키고 문제가 있음을 알려줄 피드백을 지연시킵니다.

전진을 위한 후퇴

마이그레이션을 맡았을 때 본능적으로 최종 상태를 밀어붙이고 싶었습니다. 현대화된 버전을 클라우드에서 동작시키고 컨슈머들을 옮기기 시작하는 것이었습니다. 저는 먼저 한 걸음 물러설 것을 제안했습니다. 다른 어떤 것을 옮기기 전에 현대화된 버전을 기존 인프라에서 프로덕션으로 동작시켜, 구 버전과 신 버전이 동일한 코드가 되도록 하자는 것이었습니다.

레거시 호스트의 운영체제는 너무 오래되어 Python 지원이 3.6에서 끝났고, 현대화된 버전은 3.7을 타깃으로 하고 있었습니다. 우회할 수도 있었습니다. 소스에서 Python을 빌드하거나, 애초에 컨테이너를 염두에 두고 설계되지 않은 호스트에 컨테이너를 올리는 식으로요. 하지만 그런 우회책 하나하나가 이미 충분히 복잡한 마이그레이션의 범위를 더 넓힐 뿐이었습니다.

대신 저희는 서비스를 3.6으로 다운그레이드하고 온프레미스 배포 구성을 복원했으며, 설정은 런타임에 환경별로 선택되도록 했습니다. 하나의 코드베이스로 레거시 호스트와 Kubernetes 모두에 배포할 수 있게 된 것입니다.

버전 다운그레이드는 후퇴처럼 보입니다. 하지만 그것이 나머지 마이그레이션을 단순하게 만든 결정이었습니다.

한 번에 하나의 변수만

패리티를 확보하자 모든 릴리스가 두 환경에 모두 배포되었습니다. 구 버전과 신 버전 간의 동작 차이는 컷오버 시점에 한꺼번에 드러나는 대신, 작은 단위로 즉시 드러났습니다. 두 버전 사이에 수년간 쌓인 차이는 기존 인프라가 아직 폴백으로 남아 있는 동안 해소되었습니다.

클라우드로 컷오버할 때쯤에는 순수한 인프라 변경만이 남았습니다. “플랫폼 마이그레이션이 잘 됐는가?”와 “재작성이 잘 됐는가?”를 동시에 물을 필요가 없었습니다. 무언가 고장 나더라도 코드가 동일했기 때문에 원인이 플랫폼에 있다는 것을 알 수 있었습니다. 컷오버는 지루했고, 지루한 것이 바로 원하는 결과입니다. 이는 마이크로 프론트엔드에서 React를 점진적으로 업그레이드하는 것과 같은 원리입니다. 각 릴리스가 하나의 변수만 바꾸도록 순서를 정하는 것입니다.

codeinfrastructureversionbig bang: everything at oncePython 2on-premPython 3.6on-premPython 3.6KubernetesPython 3.10Kubernetes

최종 상태가 아니라 경로를 배포하기

마이그레이션이 끝나자 수년간 막혀 있던 Python 업그레이드는 일상적인 릴리스로 배포되었습니다. 기존 호스트의 버전 상한이 사라졌고 업그레이드할 코드베이스도 하나뿐이었기 때문입니다. 최종 상태에 곧장 도달하려 하지 않았기 때문에 오히려 더 빨리 도달할 수 있었습니다.

브랜치가 수개월째 열려 있거나 재작성이 다른 팀의 마이그레이션을 기다리고 있다면, 대개 한 번에 너무 많은 일을 하려는 신호입니다. 지금 바로 릴리스할 수 있는 중간 상태를 찾아보십시오. 설령 한 걸음 물러나는 것처럼 보이더라도 말입니다. 어려운 것은 최종 상태가 아니었습니다. 어려운 것은 경로이며, 가장 안전한 경로는 한 단계씩 릴리스할 수 있는 경로입니다.

원문은 Alex O'Callaghan님이 에 게재했습니다.

이 글은 muse-spark-1.2-contributor 모델을 사용해 번역했습니다.