LabHub
배우기 러닝패스 코스

CGOA — GitOps 인증 어소시에이트 · 저장소 구조와 환경 승격 · 실습

Git 은 받았는데 Argo 는 적용하지 못했다

LabHub 에서 이어서 보기

목표

깨진 설정이 상태 저장소에 들어갔을 때 조정기가 겪는 일을 실제로 보고, CI 의 검증을 저장소 입구로 옮겨 같은 실수가 main 에 닿지 못하게 합니다.
CI 는 Git 에만 쓰고 클러스터에는 조정기만 쓰는 분담을 RBAC 판정으로 확인합니다.

왜 중요한가

GitOps 는 CI/CD 를 없애지 않고 경계를 옮깁니다. CI 는 원하는 상태를 만들고 검증해 Git 에 기록하고,
배포는 클러스터 안의 조정기가 Git 을 당겨 와서 합니다. 그래서 CI 에게 운영 클러스터의 쓰기 자격 증명을 줄 이유가 사라집니다.
대신 Git 에 들어간 것은 곧 배포 명령이 됩니다. Git 은 YAML 의 의미를 모르므로 replicas: two 같은 커밋도 받아 주고,
조정기는 그것을 적용하려다 계속 실패합니다. 설정을 코드로 다룬다(Configuration as Code)는 것은 코드처럼 검증을 거친 뒤에만
원본에 들어오게 한다는 뜻이고, 이 실습은 그 검증을 검증 전용 신원과 서버 측 dry-run 으로 세웁니다.

단계

1. 베어 저장소 /srv/bare/ci.git 을 만들고 /root/cgoa-ci/repo 로 복제해 deploy/web.yaml 에 Deployment web(namespace 없음, replicas 2, 라벨 app: web, 컨테이너 web, 이미지 nginx:1.27-alpine)을 커밋하고 main 으로 push 하세요. /root/cgoa-ci/app.yaml 에 Application ci-web(argocd 네임스페이스, project default, 저장소 git://gitd.gitsrv.svc.cluster.local:9418/ci.gitmain·deploy 경로, 대상 네임스페이스 ci-web, 자동 동기화 prune·selfHeal, CreateNamespace=true)을 작성해 적용하고 Synced·Healthy 를 확인합니다.
2. deploy/web.yaml 의 replicas 를 문자열 two 로 바꿔 커밋·push 하고 ci-web 을 hard refresh 하세요. 20초쯤 뒤 Application 상태를 읽어 /root/cgoa-ci/broken.jsoncommit(깨진 커밋 SHA), sync_status(status.sync.status), operation_message(status.operationState.message), live_replicas(ci-web 네임스페이스 Deployment 의 실제 spec.replicas, 숫자)를 적습니다. 관찰이 끝나면 git revert 로 깨진 커밋을 되돌려 push 하세요. 되돌린 뒤에도 status.operationState 가 깨진 커밋으로 Running(재시도 중)이면 /root/cgoa-ci/kubeconfig(k3s kubeconfig 사본, 현재 네임스페이스 argocd)로 argocd app terminate-op ci-web --core 를 실행해 그 작업을 끝냅니다. ci-web 이 새 main 에 Synced·Healthy 이고 깨진 커밋을 재시도하는 작업이 남아 있지 않아야 합니다.
3. 네임스페이스 cici-dryrun 을 만들고, ci 에 ServiceAccount validator 를 만드세요. ci-dryrun 에 Role dryrun-deployments(apps 그룹 deployments 에 get·create·patch 만)와 그것을 ci:validator 에 묶는 RoleBinding validator-dryrun 을 둡니다. 그 계정의 토큰(8시간)으로 kubeconfig /root/cgoa-ci/ci-kubeconfig 를 만드세요(서버 주소는 k3s kubeconfig 와 같게, 현재 네임스페이스 ci-dryrun). 이 계정은 ci-dryrun 에서만 Deployment 를 만들 수 있고 ci-web 에는 아무것도 쓸 수 없어야 합니다.
4. /root/cgoa-ci/validate.sh <디렉터리> 를 실행 가능한 스크립트로 만드세요. 그 디렉터리의 매니페스트를 /root/cgoa-ci/ci-kubeconfigci-dryrun 네임스페이스에 서버 측 dry-run 으로 적용해 보고, 하나라도 거절되면 0 이 아닌 값으로 끝나야 합니다. 환경 변수 KUBECONFIG 에 기대지 말고 스크립트 안에서 그 kubeconfig 를 지정합니다. 실제 객체를 만들면 안 됩니다.
5. /srv/bare/ci.git/hooks/pre-receive 를 실행 가능한 훅으로 만드세요. refs/heads/main 을 갱신하는 push 마다 새 커밋의 deploy 디렉터리를 임시 디렉터리에 꺼내 /root/cgoa-ci/validate.sh 로 검증하고, 실패하면 push 를 거절합니다. main 삭제는 거절하고 다른 참조는 통과시킵니다.
6. /root/cgoa-ci/repo 에서 deploy/web.yamlreplicas 키를 오타 replica 로 바꾼 커밋을 만들어 push 를 시도하고, 거절된 출력 전체를 /root/cgoa-ci/blocked.txt 에 저장하세요. 그다음 로컬 main 을 원격 main 으로 되돌립니다(git reset --hard origin/main). 원격 main 은 그대로이고 ci-web 은 계속 Synced·Healthy 여야 합니다.
7. /root/cgoa-ci/bump.sh <태그> 를 실행 가능한 스크립트로 만드세요. /root/cgoa-ci/repo 를 원격 main 으로 맞춘 뒤 deploy/web.yaml 의 이미지를 nginx:<태그> 로 바꾸고 메시지 ci: web nginx:<태그> 로 커밋해 push 합니다. kubectl 은 쓰지 않습니다. bump.sh 1.28-alpine 을 실행하고 hard refresh 뒤 ci-web 이 그 커밋에 Synced·Healthy 가 되어 Deployment 이미지가 nginx:1.28-alpine 으로 바뀐 것을 확인하세요.
8. /root/cgoa-ci/report.jsonci_writes(git), cd_writes(cluster), ci_can_write_prod(ci:validatorci-web 에 deployments 를 patch 할 수 있는지, 불리언), gate(pre-receive), deployed_revision(지금 ci-web 의 status.sync.revision), broken_reached_git(2단계 깨진 커밋이 main 이력에 남아 있는지, 불리언)를 적으세요.

참고

단계 8개

  1. Git 에서 배포되는 기준선
  2. 저장소는 받았는데 Argo 가 적용에 실패했다
  3. 운영에는 못 쓰는 CI 신원
  4. 서버가 거절할 매니페스트를 CI 에서 먼저 거절
  5. 검증을 저장소 입구에 세운다
  6. 같은 실수는 이제 main 에 닿지 않는다
  7. CI 는 Git 에 쓰고 Argo 가 배포한다
  8. 누가 어디에 쓰는지 보고