TL;DR
- 기능 플래그를 활용하면 코드베이스는 최신 상태로 배포하면서 기능 활성화는 별도 일정으로 관리해 고객별 속도와 안정성을 함께 제공함.
- 수요 기반 릴리스는 초기 속도가 빠르지만 고객별 변형이 누적되며 지원 부담과 유지 비용이 고객 수에 따라 커짐.
- 일정 기반 릴리스는 계획을 명확히 하지만 고객의 기능 검증과 업그레이드를 늦추며, 전용 릴리스가 전체 계획을 흔들 수 있음.
- kaniko는 명백한 버그 수정까지 모든 행동 변화를 기능 플래그 뒤에 두고, 플래그를 기본 활성화한 뒤 삭제하는 수명 주기를 운영함.
- FF_KANIKO_OCI_STAGES 사례는 기능 플래그가 새 동작을 선택적으로 시험하게 하고, 문제가 생겼을 때 다른 수정은 유지한 채 동작만 되돌리는 탈출구임을 보여줌.
수요 기반 릴리스
- 수요 기반 모델에서는 고객 요청이 있을 때 릴리스를 만듦. 현장에서 발견된 버그를 수정한 뒤 고객이 수정 사항이 포함된 업데이트를 요청하는 방식임.
- 소규모에서는 문제가 없고 초기 개발 속도도 높지만, 규모가 커지면서 부담이 드러남.
- 고객별 릴리스는 점차 고객별 제품으로 바뀜. 고객이 작은 차이를 요구하면 해당 고객 전용 릴리스에 설정을 포함하는 데 추가 노력이 거의 들지 않는다고 판단하게 됨.
- 그 결과 제품 카탈로그가 수천 개에 이를 수 있고, 지원팀은 어떤 버그가 여전히 중요하고 어떤 버그가 이미 사라진 문제인지 파악하기 어려워짐.
- 불만을 제기하지 않는 고객은 릴리스를 촉발하지 않으므로 대부분의 제품은 업데이트되지 않음. 하지만 고객이 문제를 제기하면 그날의 코드 상태에서 수정 릴리스를 만들게 됨.
- 수정 릴리스에는 요청하지 않은 변경 사항이 함께 들어오고, 의도치 않게 다음 릴리스를 촉발할 수 있음. 작업량이 기능 수가 아니라 고객 수에 비례하므로 소프트웨어의 비용이 상각되지 않음.
일정 기반 릴리스
- 달력 기반 모델에서는 릴리스 날짜와 유형을 미리 정함. 이 방식에서는 시맨틱 버전 관리(SemVer)의 작동 방향이 뒤집혀, 작업의 종류가 버전을 결정하는 대신 버전이 허용되는 작업의 종류를 결정함.
- 이론상 수요 기반 주기의 혼란을 통제하고 각 기능의 출시 시점을 명확히 계획할 수 있음.
- 그러나 고객은 새 기능을 시험하려면 다음 릴리스 주기까지 기다려야 함. 변경 사항 하나만 업그레이드 경로를 막아도 고객이 기존 버전을 수년간 유지할 수 있음.
- 장기 지원(LTS) 브랜치가 기존 버전의 사용을 가능하게 하지만, 릴리스 관리에 다시 큰 부담을 줌.
- 고객이 단일 제품으로 만족할 수 없더라도 전용 릴리스를 만들기 어려움. 만들면 전체 계획이 틀어질 위험이 있기 때문임.
- 첫 번째 모델은 모든 요청을 수용하다 변형에 파묻히는 반면, 이 모델은 거절해야만 유지될 수 있음.
두 개의 시계
- 릴리스 관리에는 근본적인 긴장이 내재함. 기다리는 고객에게 기능을 최대한 빠르게 제공하는 동시에 다른 모든 고객에게 안정성을 보장해야 함.
- 고객별 맞춤 솔루션을 만들고 싶으면서도 하나의 범용 제품을 유지해야 함.
- 자주 업데이트하는 고객은 실험적 기능이 빠르고 예측 불가능하게 활성화될 때 영향을 받음. SemVer는 변경 사항의 안전성에 따라 구분하려 하지만, 이는 거친 신호에 불과함.
- LTS 브랜치에 머무는 고객은 대개 변경 사항 하나가 처음 업그레이드를 막았기 때문이며, 기다리는 기간이 길어질수록 업그레이드 위험도 커짐.
- 두 문제의 해법은 릴리스를 코드가 아니라 동작의 관점에서 생각하는 것임.
“코드를 배포하고 기능을 출시하라”는 긴장을 코드베이스 안에서 해결한다는 뜻임.
- 코드베이스를 최신 상태와 LTS 상태로 동시에 유지함. 변경 사항을 가능한 한 빨리 병합하되, 활성화는 기능 플래그 뒤에 숨김.
- 이렇게 하면 아무것도 바뀌지 않았다는 확신을 갖고 코드베이스를 배포할 수 있음. 기능은 바이너리 안에서 잠복하며, 시험을 원하는 고객은 손쉽게 활성화하고 나머지 고객은 안정적인 경험을 유지함.
- 릴리스 주기는 더 이상 어떤 코드를 언제 병합할지 결정하지 않음. 대신 기능 플래그의 기본값을 바꾸고 플래그를 폐기하는 시점만 정함.
기능 플래그의 수명 주기
- kaniko는 기능 플래그를 적극적으로 채택함. 명백한 버그 수정까지 모든 동작 변화를 플래그 뒤에 두며, 변경이 누군가의 설정을 깨뜨릴 가능성이 항상 있기 때문임.
- FF_KANIKO_OCI_STAGES는 중간 단계의 저장 형식을 도커 타르볼에서 OCI 레이아웃으로 바꾸는 사례임. 최종 이미지의 미디어 유형이 달라질 수 있어 10월에 병합하고 나흘 뒤 비활성화한 상태로 출시함.
- 이후 5개월 동안 새 형식을 원하는 사용자는 직접 플래그를 켰음. 3월 마이너 릴리스에서 기본값을 활성화했고, 6월에는 플래그를 삭제해 OCI 레이아웃을 항상 사용하도록 전환함.
- 기능 활성화 자체는 특별한 절차가 아님. v1.28.0은 수개월 동안 비활성 상태였고 선택한 사용자의 프로덕션 환경에서 이미 실행 중이던 플래그 9개를 한꺼번에 활성화함. 이는 자체적으로 수행할 수 있는 어떤 테스트보다 나은 검증임.
- 탈출구는 계획하지 않은 문제에도 쓰임. 6월 v1.27.6의 의존성 업데이트로 인해
--cache와 메타데이터 전용 명령을 함께 사용하는 빌드가 교착 상태에 빠짐. - 버그는 kaniko가 아니라
go-containerregistry에 있었고, OCI 레이아웃을 쓰는 경로에서만 발생함. FF_KANIKO_OCI_STAGES 뒤에는 이전 구현인 도커 타르볼 작성기가 남아 있었으며, 이 작성기는 레이어를 순차적으로 기록해 제한기 누출이 발생하지 않는 방식임. - 플래그를 다시
false로 설정하면 해당 백엔드를 선택할 수 있었음. 실제 수정은 3주 뒤 v1.28.0에서 출시됐으며, 이 릴리스에서 플래그도 삭제됨. - 해당 이슈 스레드의 두 사용자는 서로 다른 경로를 택함. 한 명은 이전 릴리스에 고정한 채 기다렸고, 다른 한 명은 플래그를 설정해 릴리스의 다른 수정 사항은 모두 유지하며 작업을 계속함.
- 이 차이가 바로 거부할 수 있는 단위를 버전으로 삼는 것과 동작으로 삼는 것의 차이임.
- osscontainertools는 기능 플래그가 가득한 코드베이스의 복잡성을 전적으로 받아들임. 플래그 조합 중 하나만 테스트하며, 플래그에 명확한 만료일이 있기 때문에 이 방식이 가능함.
- 코드는 한 시계에 맞춰 배포하고 기능은 다른 시계에 맞춰 출시함. 어느 시계에 맞춰 운영할지는 각자의 선택임.
댓글 (0)
로그인하면 이 기사에 내 생각을 남길 수 있어요