CNPA — 클라우드 네이티브 플랫폼 엔지니어링 어소시에이트 · 설정까지 재현되는 배포와 복구 · 실습
dev 에 넣은 변경이 prod 에도 나타났다
목표
진짜 k3s 와 Argo CD 에서 ApplicationSet 하나로 dev·prod 두 환경을 Git 에서 배포합니다. 공통 base 한 줄이 두 환경을 함께 바꾸는 사고를 겪고,
prod 를 승격 브랜치에 묶어 환경 사이 승격을 Git 조작으로 바꾼 뒤, 드리프트 복구·승격·되돌리기가 각각 저장소와 클러스터에 어떻게 남는지 확인합니다.
왜 중요한가
GitOps 에서는 Git 이 각 환경의 원하는 상태이고, 클러스터 안의 조정기가 그 상태를 당겨 와 맞춥니다. 그래서 "무엇이 prod 에 있는가" 는
prod 가 추적하는 Git 참조가 가리키는 커밋으로 답해야 하고, 승격은 그 참조를 옮기는 일이 됩니다. 모든 환경이 같은 브랜치를 보면
승격 단계가 사라져 dev 의 실험이 곧바로 prod 가 됩니다. 반대로 환경마다 참조를 나누면 변경이 어디까지 갔는지가 저장소 이력으로 증명되고,
누군가 클러스터를 손으로 고쳐도 조정기가 Git 으로 되돌립니다. 플랫폼 팀이 여러 팀에 전달 경로를 제공할 때 이 구조가 기본 골격입니다.
단계
1. 베어 저장소 /srv/bare/shop-envs.git 을 만들고 /root/cnpa-env/repo 로 복제하세요. base/configmap.yaml 에 ConfigMap shop-config(namespace 없음, data FEATURE_CHECKOUT_V2: "off", LOG_LEVEL: "info")와 그것을 담는 base/kustomization.yaml 을, envs/dev/kustomization.yaml 과 envs/prod/kustomization.yaml 에는 ../../base 를 불러와 LOG_LEVEL 을 각각 debug·warn 으로 바꾸는 패치를 두세요. 커밋해 main 으로 push 합니다.
2. /root/cnpa-env/appset.yaml 에 ApplicationSet shop(argocd 네임스페이스)을 작성해 적용하세요. list 생성기의 원소는 {env: dev, revision: main} 과 {env: prod, revision: main}, 템플릿은 이름 shop-{{env}}, project default, repoURL git://gitd.gitsrv.svc.cluster.local:9418/shop-envs.git, targetRevision {{revision}}, path envs/{{env}}, 대상 네임스페이스 shop-{{env}}, 자동 동기화 prune·selfHeal, CreateNamespace=true 입니다. 두 Application 이 Synced 가 될 때까지 기다리세요.
3. dev 에서 새 결제 화면을 켜려고 base/configmap.yaml 의 FEATURE_CHECKOUT_V2 를 "on" 으로 바꿔 커밋·push 하세요. 두 앱이 그 커밋으로 동기화될 때까지 기다린 뒤 /root/cnpa-env/incident.json 에 commit(그 커밋 SHA), dev_value, prod_value(각 환경 ConfigMap 의 FEATURE_CHECKOUT_V2 실제 값)를 적으세요.
4. 사고 직전 커밋(3단계 커밋의 부모)에 브랜치 release/prod 를 만들어 push 하고, ApplicationSet 의 prod 원소 revision 을 release/prod 로 바꾸세요(dev 는 main 그대로). shop-prod 가 그 커밋으로 동기화되고 FEATURE_CHECKOUT_V2 가 다시 off 가 된 것을 확인한 뒤 /root/cnpa-env/pin.json 에 release_prod_initial(브랜치를 만든 커밋 SHA)과 prod_value_after_pin 을 적으세요.
5. shop-prod 네임스페이스의 ConfigMap shop-config 에서 LOG_LEVEL 을 kubectl patch 로 debug 로 바꾸고, Argo CD 가 Git 의 값으로 되돌릴 때까지 걸린 초를 재세요. /root/cnpa-env/drift.json 에 uid(그 ConfigMap 의 uid), edited(debug), restored(되돌아온 값), seconds(정수)를 적습니다.
6. dev 에서 새 결제 화면을 확인했다고 보고 prod 로 승격하세요. release/prod 를 3단계 커밋으로 fast-forward push 하고(강제 push 금지), shop-prod 가 그 커밋으로 동기화되어 FEATURE_CHECKOUT_V2 가 on 이 될 때까지 기다리세요. /root/cnpa-env/promote.json 에 from(옮기기 전 release/prod SHA), to(옮긴 뒤 SHA)를 적습니다.
7. envs/dev/kustomization.yaml 의 resources 에 없는 파일 missing.yaml 을 더해 커밋·push 하세요. shop-dev 에 비교 오류가 나타나면 그 메시지를, 같은 순간 shop-prod 의 status.sync.revision 을 읽어 두고, git revert 로 되돌려 push 한 뒤 shop-dev 가 다시 Synced 가 되기를 기다리세요. /root/cnpa-env/revert.json 에 bad_commit, revert_commit, dev_error(오류 메시지 일부), prod_revision_during 을 적습니다.
8. /root/cnpa-env/report.json 에 dev_tracks, prod_tracks(각 앱의 targetRevision), promoted_commit(6단계 to), drift_seconds(5단계), bad_commit_reached_prod(7단계 bad_commit 이 지금 release/prod 이력에 있는지, 불리언), rollback(revert 또는 reset 중 7단계에서 쓴 방식)을 적으세요.
참고
- VM 안에 k3s, Argo CD v3.5.2, git 데몬(
gitd.gitsrv)이 있습니다./srv/bare/<이름>.git이git://gitd.gitsrv.svc.cluster.local:9418/<이름>.git으로 보입니다. - 즉시 다시 읽기:
kubectl -n argocd annotate app <앱> argocd.argoproj.io/refresh=hard --overwrite. - 렌더 확인:
kubectl kustomize envs/<환경>. - 흔한 실수: ApplicationSet 이 만든 Application 을 직접 고치는 것. 컨트롤러가 템플릿대로 되돌립니다.
- 흔한 실수: 승격을
git push --force로 하는 것. 이력이 바뀌면 무엇이 언제 prod 에 갔는지 증명할 수 없습니다. - [ApplicationSet List Generator](https://argo-cd.readthedocs.io/en/stable/operator-manual/applicationset/Generators-List/) · [Automated Sync Policy(selfHeal)](https://argo-cd.readthedocs.io/en/stable/user-guide/auto_sync/) · [Kustomize](https://kubectl.docs.kubernetes.io/references/kustomize/kustomization/) · [OpenGitOps 원칙](https://opengitops.dev/) · [CNCF Platforms White Paper](https://tag-app-delivery.cncf.io/whitepapers/platforms/)
단계 8개
- 환경 두 개를 한 저장소에
- ApplicationSet 하나가 환경 둘을 만든다
- dev 에 넣은 변경이 prod 에도 나타났다
- prod 를 승격 브랜치에 묶는다
- prod 를 손으로 고치자 몇 초 뒤 되돌아갔다
- 승격은 브랜치를 옮기는 커밋 한 번
- dev 를 깨뜨린 커밋은 prod 에 닿지 않았다
- 환경과 승격을 값으로 남긴다