인프라 · 배포
릴리스·배포 파이프라인 자동화 — 9단계를 2단계로
매주 1~2회, 릴리스마다 9단계를 손으로 반복했다. 이걸 GitHub Actions 체인으로 묶어 2단계로 줄이기까지 20일이 걸렸다. 코드를 짠 시간보다 "왜 안 되지"를 파고든 시간이 훨씬 길었다.
릴리스 한 번에 9단계, 12~17분
릴리스 한 번에 이걸 손으로 했다.
# 릴리스 PR 만들기 (5단계)
git worktree add ../release-temp develop
git checkout -b release/v26.04.30 origin/develop
git merge origin/main -s ours # ← 빼먹으면 phantom conflict
git push -u origin release/v26.04.30
# GitHub 웹에서 PR 생성# 머지 후 (4단계)
6. PR 머지 → CodeBuild 자동 빌드 (몇 분 대기)
7. Slack에서 새 이미지 태그 복사
8. service-config 리포 yaml의 image 태그 손으로 교체 → main 직접 push
9. ArgoCD UI에서 SYNC 클릭그중에서도 사고가 잦던 지점들:
-s ours를 빼먹으면 develop과 main이 충돌도 없는데 git이 충돌이라 우기는 _phantom conflict_가 처음부터 다시 시작됐다- 같은 날 두 번째 배포면
v26.04.30.1같은 suffix를 머릿속으로 카운트했다 - yaml 태그를 손으로 갈아끼우다 다른 라인을 건드릴 위험
- service-config를 PR 없이 main에 직접 push — 리뷰 우회
- 브랜치명 오타·옵션 누락·태그 오타 — 휴먼 에러 지점이 5곳 이상
무엇이 좋아졌나 (정량)
| 지표 | Before | After |
|---|---|---|
| 사람 액션 단계 | 9 | 2 (78%↓) |
| 1회 소요 | 12~17분 | ~30초 + 버튼 클릭 (95%↓) |
| 외울 명령어 | 5개 + -s ours | 0개 |
| 휴먼 에러 지점 | 5곳+ | 0 |
연간 약 16시간(개인), 팀 5인 확산 시 약 80시간. 덤으로 service-config를 main에 직접 push하던 패턴이 사라져 감사 추적성도 올랐다.
결정의 흔적 — 막힐 때마다 갈아엎었다
자동화는 한 번에 안 됐다. 막힐 때마다 원인을 파고 결정을 바꿨다.
① "PR은 만들어지는데 CI가 안 도네" — GITHUB_TOKEN의 함정
| 차수 | 시도 | 결과 |
|---|---|---|
| 1차 | GITHUB_TOKEN으로 PR 자동 생성 | PR은 생성되나 CI가 트리거 안 됨 |
| 2차 | PR 자동 생성 포기, 생성 링크만 출력 → 사람이 클릭 | CI 정상 (반자동) |
| 3차 | RELEASE_PR_PAT(user-context) 발급 | CI 트리거 + 완전 자동 |
원인: GitHub는 GITHUB_TOKEN으로 만든 PR이 다른 워크플로를 트리거하지 않게 막는다(자동화 무한
루프 방지). 이걸 모르고 한참 헤맸다. 토큰 3종(GITHUB_TOKEN / PAT / GitHub App)의 트레이드오프를
따져 — 개인·소규모엔 PAT, 조직 단위엔 App — PAT로 갔다.
② "같은 버전이 두 번 만들어지네" — N차 카운트를 두 번 갈아엎다
| 차수 | 방식 | 문제 |
|---|---|---|
| 1차 | release 브랜치가 있으면 .1 붙이기 | 머지 후 브랜치 자동삭제 룰 때문에 이미 머지됐어도 브랜치가 없어 같은 버전 재생성 |
| 2차 | 브랜치가 아니라 PR 제목으로 카운트 (OPEN + MERGED 합산) | 해결 |
OPEN=$(gh pr list --state open --search "[RELEASE] v26.04.30 in:title" --jq length)
MERGED=$(gh pr list --state merged --search "[RELEASE] v26.04.30 in:title" --jq length)
COUNT=$((OPEN + MERGED)) # closed-without-merge는 제외 — 취소된 시도 이름 재사용 OK상태(브랜치 존재)가 아니라 PR 이력(기록)으로 카운트해야 삭제 룰에 안 깨진다는 걸 배웠다.
③ "둘이 동시에 누르면?" — concurrency
영희와 철수가 같은 순간 "Run workflow"를 누르면, 둘 다 "오늘 버전 없네" 카운트 → 같은 브랜치명으로
push 충돌. concurrency 그룹으로 한 번에 하나만 돌게 큐잉했다.
concurrency: { group: release-pr, cancel-in-progress: false }④ "전체를 자동 sync하면 위험" — 자원 한정 배포
ArgoCD의 deallo-prod는 web/api/ws가 한 묶음이라, 전체 자동 sync를 켜면 web 배포가 api·ws의
수동 변경(drift)까지 건드린다. 그래서 자동 sync 토글을 켜지 않고 web Deployment만 자원 한정 sync로
좁혔다.
argocd app sync deallo-prod --resource apps:Deployment:web-deallo절반은 레포 밖이었다 — 권한을 하나씩 얻어내며
이 자동화는 deallo 웹 레포 안에서 끝나는 일이 아니었다. 체인이 지나가는 시스템이 넷 — 앱 레포, 배포 설정 레포, AWS(CodeBuild·Secrets Manager·IAM), 그리고 ArgoCD/k8s 클러스터 — 인데, FE에겐 그 어디에도 기본 권한이 없었다. 그래서 코드만큼 권한을 짰다.
- 자동 PR 생성용 fine-grained PAT는 배포 설정 레포 한정 권한으로 조직 owner의 승인을 받아 발급
- 그 토큰을 AWS Secrets Manager에 등록하고, CodeBuild 서비스 롤에는 그 시크릿만 읽을 수 있는 IAM 인라인 정책을 추가
- 마지막 자동 sync는 백엔드 인프라 담당과 협의해 ArgoCD 계정·토큰을 새로 만들고, 빌드 환경에서 ArgoCD 서버로 네트워크가 열리는지부터 확인
- 검증하려고 프로덕션 클러스터를 kubectl로 직접 조회할 IAM 접근도 이때 얻었다
코드 한 줄보다 승인 하나가 오래 걸리는 날이 많았다. 대신 권한을 빌려 쓰지 않고 직접 얻어둔 덕에 — 몇 주 뒤 상용 OOM 사건이 터졌을 때, 이때 뚫어둔 kubectl·관측 도구 접근이 그대로 추적 무기가 됐다.
검증이 진짜 사투였다
코드를 다 짜고도 실제 릴리스에서 세 번 깨졌다. 자동화의 비용은 "짜기"가 아니라 "정말 도나"였다.
- argocd-server를 재시작 안 해 설정이 안 먹은 걸 첫 릴리스에서 발견 → 재시작
- RBAC 권한 객체 형식 오류 —
deallo-prod/*로 줬는데 실제 포맷은default/deallo-prod였다 - 권한 시뮬레이션
argocd admin settings rbac can ... → Yes로 사전 검증을 못 박은 뒤에야, 실제 release(2026-05-19)에서 자동 sync가 도는 걸 확인하고 종료했다
배운 점
- 자동화의 본체는 "짜기"가 아니라 "프로덕션에서 검증"이다. 권한·재시작·환경 같은 보이지 않는 전제가 실제 릴리스에서야 드러난다 → 이제는 권한 시뮬레이션으로 사전에 못 박는다
- 막힐 때마다 결정을 갈아엎은 게 자산이 됐다. GITHUB_TOKEN이 왜 CI를 안 깨우는지, 카운트를 왜 "기록" 기준으로 해야 하는지는 막혀봐야 안다
- "전체 자동화"가 늘 정답은 아니다. web·api·ws가 묶인 구조에선 자원 한정이 더 안전했다