LabHub
学习 学习路径 课程

CGOA — GitOps 认证助理

推送之后,什么也没有发生

在 LabHub 中继续学习

한국어 원문으로 표시합니다.

목표

Argo CD 가 Git 을 주기적으로 당겨 오는 경로와 push 웹훅으로 즉시 갱신하는 경로를 같은 커밋 흐름에서 잽니다. 폴링을 끄면 무엇이 멈추는지, 웹훅은 무엇을 확인한 뒤에만 받아들이는지, 운영에서 둘을 어떻게 겹쳐 쓰는지 봅니다.

왜 중요한가

GitOps 조정기는 원하는 상태를 스스로 당겨 옵니다. 당겨 오는 계기는 두 가지입니다. 정해진 주기마다 저장소를 다시 읽는 폴링과, 저장소가 "방금 push 가 있었다" 고 알려 주는 이벤트입니다. 폴링만 쓰면 커밋이 반영되기까지 주기만큼 기다리고, 저장소가 많아지면 그 주기가 곧 Git 서버의 부하가 됩니다. 이벤트만 쓰면 빠르지만 알림이 한 번 유실되면 커밋이 조용히 멈춥니다. 그리고 누구나 보낼 수 있는 웹훅은 조정기를 깨우는 공격 표면이 되므로 서명으로 보낸 쪽을 확인해야 합니다. 이벤트는 조정기에게 "지금 읽어라" 고 알릴 뿐 배포 명령이 아니라는 점도 이 실습에서 확인합니다. 무엇을 배포할지는 여전히 Git 이 정합니다.

단계

  1. 베어 저장소 /srv/bare/evt.git 을 만들고 /root/cgoa-evt/repo 로 복제해 app/signal.yaml 에 ConfigMap signal(namespace 없음, data rev: r1)을 커밋하고 main 으로 push 하세요. /root/cgoa-evt/app.yaml 에 Application evt(argocd 네임스페이스, project default, repoURL http://githttp.gitsrv.svc.cluster.local/cgi-bin/git/evt.git, targetRevision main, path app, 대상 네임스페이스 evt, 자동 동기화 prune·selfHeal, CreateNamespace=true)를 작성해 적용하고 Synced 를 확인합니다.
  2. ConfigMap argocd-cmtimeout.reconciliation: 30stimeout.reconciliation.jitter: 0s 를 넣고 argocd-application-controller StatefulSet 과 argocd-repo-server Deployment 를 둘 다 재시작하세요. 새 컨트롤러 파드의 로그에서 appResyncPeriod 가 들어 있는 줄을 찾아 그대로 /root/cgoa-evt/interval.txt 에 저장하고, 재시작이 끝나면 evt 를 한 번 hard refresh 해 둡니다.
  3. app/signal.yaml 의 rev 를 r2 로 바꿔 커밋·push 하고, refresh 도 웹훅도 쓰지 않은 채 evtstatus.sync.revision 이 그 커밋이 될 때까지 기다리세요. /root/cgoa-evt/poll.jsoncommit, pushed_at(push 직후 유닉스 초), synced_at(revision 이 바뀐 것을 본 유닉스 초)을 숫자로 적습니다.
  4. argocd-cmtimeout.reconciliation0s 로 바꾸고 컨트롤러와 repo-server 를 재시작하세요. 그다음 rev 를 r3 로 바꿔 push 하고 아무 요청 없이 40초 이상 기다린 뒤 /root/cgoa-evt/nopoll.jsoncommit(r3 커밋), pushed_at, checked_at(기다린 뒤 확인한 유닉스 초), revision_at_check(그때 evt 의 status.sync.revision)를 적습니다.
  5. argocd-server 서비스의 /api/webhook 에 GitHub push 이벤트를 보내세요. 헤더는 X-GitHub-Event: pushContent-Type: application/json, 본문은 ref refs/heads/main, after 에 r3 커밋, repository.html_urlhttp://githttp.gitsrv.svc.cluster.local/cgi-bin/git/evt 를 담은 JSON 입니다. evt 가 r3 커밋으로 바뀔 때까지 기다려 /root/cgoa-evt/webhook.jsonhttp_status(숫자), sent_at, synced_at 을 적으세요.
  6. openssl rand -hex 16 으로 만든 값을 /root/cgoa-evt/webhook-secret 에 저장하고 Secret argocd-secretwebhook.github.secret 키에 넣으세요. rev 를 r4 로 바꿔 push 한 뒤 서명 없는 push 이벤트를 먼저 보내고 15초 기다립니다. 이어서 같은 본문 파일을 그 비밀값으로 HMAC-SHA256 서명해 X-Hub-Signature-256: sha256=<hex> 헤더와 함께 보내고 동기화를 기다리세요. /root/cgoa-evt/secret.jsoncommit(r4), unsigned_status, signed_status(숫자), revision_after_unsigned(서명 없는 요청 15초 뒤 evt 의 revision), signed_synced_at 을 적습니다.
  7. 웹훅은 유실될 수 있으므로 폴링을 되살립니다. argocd-cmtimeout.reconciliation120s 로 바꾸고 timeout.reconciliation.jitter 키는 지운 뒤 컨트롤러와 repo-server 를 재시작하세요. 새 컨트롤러 로그의 appResyncPeriod 줄을 /root/cgoa-evt/restored.txt 에 저장하고, evt 를 한 번 hard refresh 해 둡니다. 웹훅 비밀값은 그대로 둡니다.
  8. /root/cgoa-evt/report.jsonpoll_seconds(3단계에서 잰 synced_at-pushed_at), nopoll_synced(4단계 확인 시점에 r3 가 배포됐는지, 불리언), webhook_seconds(5단계 synced_at-sent_at), unsigned_accepted(서명 없는 이벤트가 배포를 일으켰는지, 불리언), trigger(webhook), safety_net(timeout.reconciliation)을 적으세요.

참고

smart HTTP 저장소를 추적하는 앱

베어 저장소 /srv/bare/evt.git 을 만들고 /root/cgoa-evt/repo 로 복제해 app/signal.yaml 에 ConfigMap signal(namespace 없음, data rev: r1)을 커밋하고 main 으로 push 하세요. /root/cgoa-evt/app.yaml 에 Application evt(argocd 네임스페이스, project default, repoURL http://githttp.gitsrv.svc.cluster.local/cgi-bin/git/evt.git, targetRevision main, path app, 대상 네임스페이스 evt, 자동 동기화 prune·selfHeal, CreateNamespace=true)를 작성해 적용하고 Synced 를 확인합니다.

이 실습의 저장소 주소는 git:// 가 아니라 http:// 입니다. githttp 서버가 /srv/bare 아래 저장소를 git-http-backend 로 내보냅니다. git ls-remote 로 먼저 보이는지 확인하세요.

당겨 오는 주기를 30초로 줄인다

ConfigMap argocd-cmtimeout.reconciliation: 30stimeout.reconciliation.jitter: 0s 를 넣고 argocd-application-controller StatefulSet 과 argocd-repo-server Deployment 를 둘 다 재시작하세요. 새 컨트롤러 파드의 로그에서 appResyncPeriod 가 들어 있는 줄을 찾아 그대로 /root/cgoa-evt/interval.txt 에 저장하고, 재시작이 끝나면 evt 를 한 번 hard refresh 해 둡니다.

이 설정은 두 구성요소가 시작할 때 읽습니다. 컨트롤러에게는 앱을 다시 비교하는 주기이고, repo-server 에게는 브랜치가 가리키는 커밋을 캐시해 두는 기간입니다. 컨트롤러만 재시작하면 30초마다 비교해도 repo-server 가 옛 커밋을 계속 돌려줍니다. 캐시는 Redis 에 있어서 재시작 전에 옛 만료(기본 3분)로 저장된 항목이 남으므로, hard refresh 로 한 번 새로 읽혀야 새 만료가 적용됩니다. 로그의 그 줄은 기간을 Go 의 time.Duration 표기로 보여 줍니다.

아무것도 누르지 않고 기다린 시간

app/signal.yaml 의 rev 를 r2 로 바꿔 커밋·push 하고, refresh 도 웹훅도 쓰지 않은 채 evtstatus.sync.revision 이 그 커밋이 될 때까지 기다리세요. /root/cgoa-evt/poll.jsoncommit, pushed_at(push 직후 유닉스 초), synced_at(revision 이 바뀐 것을 본 유닉스 초)을 숫자로 적습니다.

date +%s 로 시각을 잽니다. 기다리는 동안 2초 간격으로 revision 만 읽으세요. annotate 로 refresh 를 요청하면 이 측정은 폴링이 아니게 됩니다.

폴링을 끄자 커밋이 멈춰 섰다

argocd-cmtimeout.reconciliation0s 로 바꾸고 컨트롤러와 repo-server 를 재시작하세요. 그다음 rev 를 r3 로 바꿔 push 하고 아무 요청 없이 40초 이상 기다린 뒤 /root/cgoa-evt/nopoll.jsoncommit(r3 커밋), pushed_at, checked_at(기다린 뒤 확인한 유닉스 초), revision_at_check(그때 evt 의 status.sync.revision)를 적습니다.

0 은 주기적 갱신을 끈다는 뜻입니다. 재시작한 컨트롤러 로그의 appResyncPeriod 가 어떻게 바뀌었는지 보세요. 관리 대상 객체가 바뀌는 클러스터 이벤트와 Git 을 다시 읽는 일은 별개입니다.

push 이벤트 한 번에 몇 초

argocd-server 서비스의 /api/webhook 에 GitHub push 이벤트를 보내세요. 헤더는 X-GitHub-Event: pushContent-Type: application/json, 본문은 ref refs/heads/main, after 에 r3 커밋, repository.html_urlhttp://githttp.gitsrv.svc.cluster.local/cgi-bin/git/evt 를 담은 JSON 입니다. evt 가 r3 커밋으로 바뀔 때까지 기다려 /root/cgoa-evt/webhook.jsonhttp_status(숫자), sent_at, synced_at 을 적으세요.

서비스의 ClusterIP 로 https 에 보내고 자체 서명 인증서라 curl -k 를 씁니다. Argo CD 는 이벤트의 저장소 주소를 각 Application 의 repoURL 과 대조해 일치하는 앱만 갱신합니다. commits[].modified 에 바뀐 파일 경로를 넣어 두면 경로 대조도 통과합니다.

서명 없는 이벤트는 문 앞에서 돌려보낸다

openssl rand -hex 16 으로 만든 값을 /root/cgoa-evt/webhook-secret 에 저장하고 Secret argocd-secretwebhook.github.secret 키에 넣으세요. rev 를 r4 로 바꿔 push 한 뒤 서명 없는 push 이벤트를 먼저 보내고 15초 기다립니다. 이어서 같은 본문 파일을 그 비밀값으로 HMAC-SHA256 서명해 X-Hub-Signature-256: sha256=<hex> 헤더와 함께 보내고 동기화를 기다리세요. /root/cgoa-evt/secret.jsoncommit(r4), unsigned_status, signed_status(숫자), revision_after_unsigned(서명 없는 요청 15초 뒤 evt 의 revision), signed_synced_at 을 적습니다.

서명은 보낸 바이트 그대로에 대해 계산해야 합니다. 본문을 파일로 만들어 curl --data-binary @파일 로 보내고 같은 파일을 openssl dgst -sha256 -hmac 에 넣으세요. argocd-server 는 Secret 변경을 스스로 다시 읽습니다.

웹훅을 믿되 폴링을 안전망으로 되돌린다

웹훅은 유실될 수 있으므로 폴링을 되살립니다. argocd-cmtimeout.reconciliation120s 로 바꾸고 timeout.reconciliation.jitter 키는 지운 뒤 컨트롤러와 repo-server 를 재시작하세요. 새 컨트롤러 로그의 appResyncPeriod 줄을 /root/cgoa-evt/restored.txt 에 저장하고, evt 를 한 번 hard refresh 해 둡니다. 웹훅 비밀값은 그대로 둡니다.

merge patch 에서 키를 지우려면 값을 null 로 보냅니다. jitter 를 지우면 기본값이 쓰입니다. 로그 줄에서 주기와 jitter 가 모두 어떻게 표시되는지 확인하세요. 주기가 0 이던 동안 repo-server 는 브랜치가 가리키는 커밋을 기본 캐시 만료(24시간)로 저장했습니다. 주기를 되돌려도 그 항목은 남으므로 hard refresh 로 한 번 새로 읽혀야 폴링이 다시 새 커밋을 봅니다.

당겨 오기와 이벤트의 역할 보고

/root/cgoa-evt/report.jsonpoll_seconds(3단계에서 잰 synced_at-pushed_at), nopoll_synced(4단계 확인 시점에 r3 가 배포됐는지, 불리언), webhook_seconds(5단계 synced_at-sent_at), unsigned_accepted(서명 없는 이벤트가 배포를 일으켰는지, 불리언), trigger(webhook), safety_net(timeout.reconciliation)을 적으세요.

앞 단계에서 남긴 JSON 파일의 숫자로 계산합니다. 채점기는 같은 파일과 Argo CD 동기화 기록을 다시 대조합니다.