머지하면 배포되게 만든다 — GitHub Actions + OIDC + SSM
무엇을
main 에 머지하면 이미지를 굽고 EC2 의 k3s 에 배포되게 한다.
지금은 손으로 굽고 손으로 kubectl apply 한다.
어떻게 — 설계가 갈린 지점 넷
① 버전은 저장소가 정한다. kustomization.yaml 의 images.newTag 를 읽어 그 태그로 굽는다.
CI 가 태그를 만들어 저장소에 되쓰지 않는다 — main 이 보호돼 있어 봇이 push 할 수 없고,
되쓰면 「저장소 = 클러스터」 불변식이 흔들린다. 버전 올리기는 PR 안에서 사람이 한다.
② 인스턴스에서 git 을 쓰지 않는다. 러너가 kubectl kustomize 로 렌더한 YAML 을 base64 로
SSM 에 실어 보낸다. 2026-09-09 확인 결과 인스턴스에 git 도 저장소 클론도 없었다.
만들 이유가 없다 — CI 가 렌더한 그 YAML 이 그대로 적용되는 편이 어긋날 여지가 적다.
렌더 결과는 /opt/callguard/current.yaml 에 남겨 부팅 시 자동 수렴이 재사용한다.
③ SSM 을 쓰는 이유는 포트를 열지 않기 위해서다. 러너 IP 는 매번 바뀌는데 6443·22 는 담당자 IP 하나로만 열려 있다. GitHub IP 대역을 여는 것은 6443 을 인터넷에 여는 것과 같다.
④ 자격증명을 저장하지 않는다. OIDC 로 callguard-deploy-role 을 빌린다(1시간 임시).
Root 단독 운영이라 대안이 Root 액세스 키뿐인데 그건 계정 전체를 GitHub 에 맡기는 것이다.
완료 조건
callguard-ec2-role생성·부착 — SSM 통로 (실측: IMDS 404 →HealthCheck reporting agent health)- OIDC 자격 증명 공급자
callguard-deploy-role— 신뢰 정책refs/heads/main한정 · 권한 3종(475개 서비스 중 2개).github/workflows/release.yml탄력적 IP 부착— 붙이지 않기로 했다(2026-09-09). IP 갱신을 매일 수동으로 한다- Docker Hub Access Token + GitHub Secrets 4개 (2026-09-09)
- 첫 배포 실검증 (2026-09-14) — PR #74 에서 네 잡(
plan·image·call-mediator-image·deploy)이 전부 통과했다(117초). 운영/health스포크 4종(masking·closure_gate·retrieval·trigger) 유지 확인 - 부팅 시 자동 수렴 systemd 유닛 —
infra/systemd/callguard-converge.service·converge.sh 자동 중지 cron (런북 21-1)— 2026-09-19 하지 않기로 했다(_project/decisions/116①)- 결정 기록 —
_project/decisions/117로 썼다 (2026-09-19). 설계가 갈린 넷(저장소가 태그를 정한다 · 인스턴스에서 git 을 안 쓴다 · SSM 통로 · OIDC 임시 자격증명)을 근거·되돌리는 법과 함께 남겼다. ⚠ 그 과정에서 본문의 「Root 단독 운영이라 대안이 Root 액세스 키뿐」이 사실이 아님을 확인해 정정했다 (루트 액세스 키 0개 · 장기 키는 IAM 사용자admin것 하나, 09-19 실측). 결론은 그대로다
탄력적 IP 를 붙이지 않는다 (2026-09-09 결정)
인스턴스를 껐다 켜면 공인 IP 가 바뀐다(16.184.8.48 → 16.184.33.38 실측).
매일 켤 때 Cloudflare server A 레코드를 손으로 갱신한다. 월 약 $3.6 을 아끼는 대신
그 손질을 받는다.
⚠ 파이프라인에 미치는 영향 하나 — release.yml 의 스모크 테스트가
https://server.solidbob.cloud/health 를 친다. DNS 가 낡은 상태에서 머지하면 배포는
성공해도 스모크 테스트가 실패한다. 인스턴스를 켠 뒤 DNS 를 먼저 고치고 머지한다.
머지 후 빨간불이 세 번 났다 (2026-09-14)
가드는 설계대로 작동했다 — PR #59·#61·#73 셋 다 「코드가 바뀌었는데 newTag 가 그대로」다.
문제는 판정 시점이다. 이 티켓이 일부러 필수 통과 검사에서 뺐기 때문에(아래) 머지가 먼저 되고
배포가 막힌다. 배포를 하지 않는 태그 검사만 떼어내면 머지 전에 걸 수 있다 →
w4-release-tag-gate(2026-09-14 완료, _project/decisions/111) — 아직 main 에 안 올라온 티켓이라 링크를 걸지 않았다.
안 하는 것
- 필수 통과 검사에 넣지 않는다. 넣으면 배포가 PR 머지를 막는다(
CLAUDE.md§7) - ES 인덱스 적재를 파이프라인에 넣지 않는다. 지식베이스가 바뀔 때만 필요하다 — 별도 Job