2023. 6. 1. 09:00ㆍDev
브랜치 쓰지 마세요. 여러 GitOps 환경에 배포할 때요
Kostis Kapelonis 님이 쓴 https://codefresh.io/blog/stop-using-branches-deploying-different-gitops-environments/ 블로그 포스트를 원작자의 허락을 받고 번역한 글입니다.
GitOps 문제에 대한 대표 가이드에서 현재 GitOps 도구가 서로 다른 환경 간의 승격 사례나 다중 클러스터 설정을 모델링하는 방법을 제대로 다루지 못하는 이유를 간략하게 설명했습니다(포인트 3과 4 참조).
"다음 환경으로 릴리스를 승격하려면 어떻게 해야 하나요?"라는 질문은 GitOps를 도입하려는 조직들 사이에서 점점 더 인기를 얻고 있습니다. 여러 가지 답이 있을 수 있지만, 이 글에서는 하지 말아야 할 것에 초점을 맞추고자 합니다.
서로 다른 환경을 모델링하기 위해 Git 브랜치를 사용해서는 안 됩니다. 구성(매니페스트/템플릿)을 보관하는 Git 리포지토리에 "스테이징", "QA", "프로덕션" 등의 브랜치가 있는 경우 함정에 빠진 것입니다.
다시 한 번 말씀드리겠습니다. 다른 환경을 모델링하기 위해 Git 브랜치를 사용하는 것은 안티패턴입니다. 하지 마세요!
이런 방식이 왜 안티패턴인지 다음 사항으로 살펴보겠습니다:
- 배포 환경에 서로 다른 Git 브랜치를 사용하는 것은 과거의 유물입니다.
- 서로 다른 브랜치 간의 풀 리퀘스트 및 병합은 문제가 있습니다.
- 사람들은 환경 별 코드를 포함 시키고 구성 드리프트를 만들고 싶은 유혹을 받습니다.
- 환경 수가 많아지면 모든 환경에 대한 유지 관리가 금방 손에서 벗어날 수 있습니다.
- 환경 별 브랜치 모델은 기존 쿠버네티스 에코시스템에 위배됩니다.
다른 환경에 대한 브랜치 사용은 레거시 애플리케이션에만 적용해야 합니다.
사람들에게 다양한 환경을 모델링 할 때 Git 브랜치를 사용하는 이유를 물어보면 거의 항상 "항상 그렇게 해왔기 때문에", "자연스러워서", "개발자들이 알고 있는 방식이기 때문에" 등의 다양한 대답이 돌아옵니다.
사실입니다. 대부분의 사람들은 다양한 환경에서 브랜치를 사용하는 데 익숙합니다. 이 관행은 유서 깊은 Git-Flow 모델에 의해 대중화되었습니다. 하지만 상황이 많이 바뀌었습니다. Git-Flow를 처음 쓴 분도 그 영향에 대한 이해 없이 이 모델을 채택하지 말 것을 권고하는 큰 경고를 맨 위에 배치했습니다.
사실 Git 흐름 모델은:
- 애플리케이션 소스 코드에 중점을 둡니다. 환경 구성이나 Kubernetes manifests가 아니라요.
- 프로덕션 환경에서 애플리케이션의 여러 버전을 지원해야 할 때 가장 적합합니다. 이런 경우가 발생하긴 하지만 아닐 때가 더 많습니다.
이 글은 애플리케이션 소스 코드가 아닌 GitOps 환경에 대한 것이기 때문에 Git-Flow와 그 단점에 대해 너무 많이 이야기하지는 않겠지만, 요약하자면 트렁크 기반 개발을 따르고 환경마다 다른 기능을 지원해야 하는 경우 기능 플래그를 사용해야 합니다.
GitOps의 맥락에서 애플리케이션 소스 코드와 구성은 서로 다른 Git 리포지토리(애플리케이션 코드만 있는 리포지토리와 Kubernetes 매니페스트/템플릿이 있는 리포지토리)에 있어야 합니다. 즉, 애플리케이션 소스 코드에 대한 브랜치 선택이 환경을 정의하는 환경 리포지토리에서 브랜치가 사용되는 방식에 영향을 미치지 않아야 한다.
다음 프로젝트에 GitOps를 도입할 때는 깨끗한 슬레이트에서 시작해야 합니다. 애플리케이션 개발자는 애플리케이션 소스 코드에 대해 원하는 브랜치 전략을 선택할 수 있지만(심지어 Git-flow를 사용할 수도 있음), 환경 별 브랜치 모델을 따르지 않아야 하며, 구성 Git 리포지토리(모든 Kubernetes 매니페스트/템플릿이 있는)는 환경 별 브랜치 모델을 따라야 합니다.
승격은 결코 단순한 Git 병합이 아닙니다.
이제 배포에 환경 별 브랜치 접근 방식을 사용한 역사를 알았으니, 실제 단점에 대해 이야기할 수 있다.
이 접근 방식의 가장 큰 장점은 "승격은 간단한 git 병합"이라는 주장입니다. 이론상 릴리스를 QA에서 스테이징으로 승격하려면 QA 브랜치를 스테이징 브랜치에 병합하기만 하면 됩니다. 그리고 프로덕션 준비가 되면 다시 스테이징 브랜치를 프로덕션 브랜치에 병합하면 스테이징의 모든 변경 사항이 프로덕션에 적용된다는 것을 확신할 수 있다.
프로덕션과 스테이징의 차이점을 알고 싶으신가요? 두 브랜치 간에 표준 git diff를 수행하면 됩니다. 스테이징에서 QA로 구성 변경 사항을 backport하고 싶으신가요? 다시 말하지만, 스테이징 브랜치에서 QA로 간단한 Git 병합을 수행하면 됩니다.
프로모션에 추가 제한을 두려면 풀 리퀘스트를 사용하면 됩니다. 따라서 누구나 qa에서 스테이징 브랜치로 병합할 수 있지만 프로덕션 브랜치에서 무언가를 병합하려면 풀 리퀘스트를 사용하여 모든 중요한 이해 관계자에게 수동 승인을 요청할 수 있습니다.
이론적으로는 모두 훌륭하게 들리며, 일부 사소한 시나리오는 실제로 이렇게 작동할 수 있습니다. 하지만 실제로는 그렇지 않습니다. Git 병합을 통해 릴리스를 홍보하면 병합 충돌, 원치 않는 변경, 심지어 잘못된 변경 순서로 인해 어려움을 겪을 수 있습니다.
간단하게 현재 스테이징 브랜치에 있는 이 Kubernetes Deployment Manifest를 살펴봅시다:
apiVersion: apps/v1
kind: Deployment
metadata:
name: example-deployment
spec:
replicas: 15
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: backend
image: my-app:2.2
ports:
- containerPort: 80
QA 팀에서 2.3 버전(QA 브랜치에 있음)이 상태가 양호하며 스테이징으로 이동할 준비가 되었다고 알려왔습니다. QA 브랜치를 스테이징 브랜치에 병합하여 애플리케이션을 승격하고 모든 것이 정상이라고 생각합니다.
하지만 리소스 제한으로 인해 누군가 QA 브랜치의 replicas를 2로 변경했다는 사실을 몰랐을 수도 있습니다. Git 병합을 통해 2.3을 스테이징에 배포했을 뿐만 아니라 replicas도 15개가 아닌 2개로 확장했는데, 이는 원치 않는 결과일 수 있습니다.
병합하기 전에 replicas 값을 확인하는 것이 쉽다고 주장할 수도 있지만, 실제 시나리오에서는 거의 항상 (Helm 또는 Kustomize를 통해) 템플릿화된 매니페스트가 많은 애플리케이션이 많다는 점을 기억하세요. 따라서 어떤 변경 사항을 가져오고 어떤 변경 사항을 남겨둘지 이해하는 것은 사소한 작업이 아닙니다.
그리고 추진하지 말아야 할 변경 사항을 찾았다고 하더라도, 원래의 "간단한" Git 병합과는 거리가 먼 Git cherry-pick또는 기타 비표준 방법을 사용하여 "좋은" 부분을 수동으로 선택해야 합니다.
하지만 승격 할 수 있는 모든 변경 사항을 알고 있더라도 승격 순서가 커밋 순서와 다른 경우가 몇 가지 있습니다. 다음과 같은 4가지 변경 사항이 QA 환경에서 발생한다고 해봅시다.
- 애플리케이션의 인그레스가 추가 hostname으로 업데이트 됩니다.
- 릴리스 2.5가 QA 브랜치로 승격되고 모든 QA 담당자가 테스트를 시작합니다.
- 2.5에서 문제가 발견되어 Kubernetes ConfigMap이 수정됩니다.
- Resource limits이 미세 조정되어 QA에 투입됩니다.
그런 다음 ingress 설정과 resource limits을 다음 환경(스테이징)으로 이동해야 한다고 결정합니다. 하지만 QA 팀은 아직 2.5 릴리스에 대한 테스트를 완료하지 않았습니다.
무턱대고 QA 브랜치를 스테이징 브랜치에 병합하면 2.5 승격을 포함하여 4가지 변경 사항을 한 번에 모두 적용 받게 됩니다.
이를 해결하려면 다시 git cherry-pick 또는 기타 수동 방법을 사용해야 합니다.
커밋 간에 종속성이 있는 더 복잡한 경우에는 cherry-pick도 작동하지 않을 수 있습니다.
위의 예에서는 릴리스 1.24를 프로덕션으로 승격 해야 합니다. 문제는 커밋 중 하나(핫픽스)에 여러 변경 사항이 포함되어 있고, 그 중 일부는 스테이징에만 적용되므로 프로덕션으로 이동할 수 없는 다른 커밋(인그레스 구성 변경)에 종속되어 있다는 것입니다. 따라서 체리픽을 사용하더라도 필요한 변경 사항만 스테이징에서 프로덕션으로 가져오는 것은 불가능합니다.
결국 승격은 결코 단순한 Git 병합이 아닙니다. 대부분의 조직에는 많은 수의 매니페스트로 구성된 많은 수의 클러스터에 있는 많은 수의 애플리케이션이 있을 것입니다. 커밋을 수동으로 선택하는 것은 지는 싸움입니다.
환경 별 변경으로 컨피그레이션 드리프트가 쉽게 생성될 수 있습니다.
컨피그레이션 드리프트란? Configuration Drift
이론적으로 컨피그레이션 드리프트는 Git 병합에서 문제되지 않아야 합니다. 스테이징에서 변경한 후 해당 브랜치를 프로덕션에 병합하면 모든 변경 사항이 새 환경으로 이전 되어야 합니다.
하지만 대부분의 조직은 한 방향으로만 병합하고 팀원들은 업스트림 환경만 변경하고 다운스트림 환경으로 변경 내용을 back-port하기 귀찮아 하기 때문에 실제상황은 다릅니다.
QA, 스테이징, 프로덕션의 세 가지 환경이 있는 전형적인 예에서 Git 병합의 방향은 한 방향으로만 진행됩니다. 사람들은 QA 브랜치를 스테이징 브랜치로, 스테이징 브랜치를 프로덕션 브랜치로 병합합니다. 이는 변경 사항이 위쪽으로만 흐른다는 것을 의미합니다.
QA -> 스테이징 -> 프로덕션.
일반적인 시나리오는 프로덕션에서 빠른 구성 변경(핫픽스)이 필요하고 누군가 거기에 수정 사항을 적용하는 것입니다. 쿠버네티스의 경우, 이 핫픽스는 기존 매니페스트의 변경 또는 완전히 새로운 매니페스트와 같은 모든 것이 될 수 있습니다.
이제 프로덕션은 스테이징과는 완전히 다른 구성을 갖게 되었습니다. 다음에 릴리스가 스테이징에서 프로덕션으로 승격될 때, Git은 스테이징에서 가져올 내용에 대해서만 알려줍니다. 프로덕션에 대한 임시 변경 사항은 풀 리퀘스트의 어느 곳에도 나타나지 않습니다.
One direction only
즉, 프로덕션에 문서화 되지 않은 변경 사항이 있어 이후의 모든 배포가 실패할 수 있으며, 이는 이후의 어떤 프로모션에서도 감지되지 않을 것입니다.
이론적으로는 이러한 변경 사항을 back-port하고 프로덕션에서 스테이징으로(그리고 스테이징에서 QA로) 주기적으로 모든 커밋을 병합할 수 있습니다. 실제로는 앞서 설명한 이유로 인해 이런 일이 발생하지 않습니다.
환경이 3개가 아니라 많으면 문제가 더 커진다는 것을 상상할 수 있습니다.
요약하면, Git 병합으로 릴리스를 승격 해도 구성 드리프트가 해결되지 않으며, 오히려 팀이 순차적으로 승격되지 않는 임시 변경을 하고 싶은 유혹을 받기 때문에 더욱 문제가 됩니다.
수많은 환경에 대해 서로 다른 Git 브랜치를 관리하기란 쉽지 않습니다.
앞의 모든 예에서는 브랜치 기반 환경 승격의 단점을 설명하기 위해 3개의 환경(QA-> 스테이징-> 프로덕션)만 사용했습니다.
조직의 규모에 따라 더 많은 환경이 있을 수 있습니다. 지리적 위치와 같은 다른 차원을 고려하면 환경의 수는 빠르게 급증할 수 있습니다.
예를 들어 환경이 5개인 회사를 가정해 보겠습니다:
- 부하 테스트
- 통합 테스트
- QA
- 스테이징
- 프로덕션
이제 마지막 3개의 환경도 유럽, 미국, 아시아에 배포되고 앞의 2개의 환경도 GPU 및 비 GPU 버전이 있다고 가정해 봅시다. 즉, 회사에는 총 13개의 환경이 있습니다. 그리고 이것은 단일 애플리케이션을 위한 것입니다.
환경에 대한 지점 기반 접근 방식을 따르는 경우:
- 항상 13개의 장기적인 Git 브랜치를 보유해야 합니다.
- 모든 환경에서 하나의 변경 사항을 승격하려면 13개의 풀 리퀘스트가 필요합니다.
- 위로는 5단계, 아래로는 2~3단계의 2차원적인 승격 매트릭스를 가지고 있다.
- 이제 모든 환경 조합에서 잘못된 병합, 컨피그레이션 드리프트 및 임시 변경이 발생할 가능성은 사소하지 않습니다.
이 예시 조직의 맥락에서는 이전의 모든 문제가 이제 더 널리 퍼져 있습니다.
환경 별 브랜치 모델은 Helm/Kustomize에 위배된다.
애플리케이션을 설명하는 데 가장 많이 사용되는 쿠버네티스 도구 중 두 가지는 헬름과 커스터마이즈입니다. 이 두 도구가 서로 다른 환경을 모델링하는 데 어떻게 권장되는지 살펴보겠습니다.
헬름의 경우, 자체적으로 values.yaml 파일 형식의 매개변수를 허용하는 일반 차트를 생성해야 한다. 다른 환경을 사용하려면 여러 개의 값 파일이 필요합니다.
사용자 지정의 경우 base 구성을 만든 다음 각 환경을 자체 폴더가 있는 오버레이로 모델링해야 합니다:
두 경우 모두 서로 다른 폴더/파일로 서로 다른 환경을 모델링합니다. Helm과 Kustomize는 Git 브랜치나 Git 병합 또는 풀 리퀘스트에 대해 전혀 알지 못합니다. 그냥 일반 파일만 사용합니다.
다시 한 번 반복합니다: 헬름과 커스텀마이즈 모두 Git 브랜치가 아닌 다른 환경에서 일반 파일을 사용합니다. 이는 yaml 관리 도구 중 하나를 사용하여 다양한 쿠버네티스 구성을 모델링하는 방법에 대한 좋은 힌트가 될 것입니다.
이 상황에 Git 브랜치를 도입하면 복잡도가 높아질 뿐만 아니라 자체 도구와도 상충됩니다.
GitOps 환경에서 릴리스를 홍보하는 권장 방법
서로 다른 Kubernetes 환경을 모델링하고 그 사이에서 릴리스를 승격하는 것은 GitOps를 채택하는 모든 팀에게 매우 일반적인 문제이다. 매우 널리 사용되는 방법은 각 환경에 대해 Git 브랜치를 사용하고 승격이 "간단한" Git 병합이라고 가정하는 것이지만, 이 글에서 이것이 잘못된 패턴이라는 것을 확인했습니다.
다음 글에서는 서로 다른 환경을 모델링하고 쿠버네티스 클러스터 간에 릴리즈를 승격하는 더 나은 접근 방식을 살펴봅니다. 이 글의 마지막 요점(헬름/커스터마이즈 관련)에서 이 접근 방식이 어떻게 작동하는 지에 대한 힌트를 이미 얻었을 테니까요.
계속 지켜봐 주세요!
(번역자 추천 볼만한 추가 링크)
https://codefresh.io/blog/pains-gitops-1-0/
https://codefresh.io/blog/how-to-model-your-gitops-environments-and-promote-releases-between-them/
https://codefresh.io/blog/argo-cd-best-practices/
https://github.com/keptn/enhancement-proposals/pull/81
https://github.com/argoproj/argo-cd/discussions/5667
https://codefresh.io/blog/kubernetes-antipatterns-1/
https://github.com/kostis-codefresh/gitops-environment-promotion
'Dev' 카테고리의 다른 글
데브옵스를 위한 리눅스 (0) | 2023.06.08 |
---|---|
데브옵스란 무엇인가 (0) | 2023.06.08 |
턴키(Turnkey) 방식과 Out of the box (0) | 2023.05.31 |
Karmada란? (0) | 2023.05.27 |
컨피그레이션 드리프트란? Configuration Drift (0) | 2023.04.25 |