GitOps 자동화 배포 - ArgoCD + n8n + GitHub Actions로 Slack 승인 배포 파이프라인 구축하기

ECS에서 EKS로 마이그레이션하면서 구축한 GitOps 기반 배포 자동화 시스템을 소개합니다. ArgoCD, n8n, GitHub Actions를 조합하여 환경별로 차등화된 배포 전략을 설계한 경험을 공유합니다.

배경

기존에는 AWS ECS 위에서 서비스를 운영하고 있었다. GitHub Actions에서 Docker 이미지 빌드하고, ECS 서비스를 직접 업데이트하는 단순한 구조였는데, 쓰다 보니 불편한 점이 꽤 있었다. 배포 상태를 바로 알기 어려웠다 - ECS 배포가 실패해도 확인하려면 콘솔을 직접 들여다봐야 했다 롤백이 귀찮았다 - 이전 Task Definition 리비전을 찾아서 수동으로 되돌리는 게 꽤 번거로운 작업이었다 환경별 설정이 여기저기 흩어져 있었다 - dev, stage, prod마다 별도 스크립트를 관리하다 보니 점점 복잡해졌다

EKS로 마이그레이션하면서 이 문제들을 한 번에 정리하고 싶었다. 방향은 GitOps - Git 저장소를 Single Source of Truth로 두고 클러스터 상태를 선언적으로 관리하는 것.

전체 아키텍처

환경별로 배포 전략을 다르게 가져갔다. dev/stage는 빠르게, prod은 안전하게. Screenshot 제안: ArgoCD 대시보드에서 Application 목록이 보이는 화면 (dev/stage/prod 환경이 함께 보이면 좋음)

dev / stage - 완전 자동

개발 환경은 속도가 중요하다. 브랜치에 push하면 알아서 배포된다. 별도 승인 같은 건 없다.

prod - Slack 승인 후 배포

프로덕션은 다르다. 코드가 main에 머지되면 immutable tag로 이미지를 빌드하고, manifest를 업데이트한 다음, n8n을 통해 Slack으로 승인 요청이 간다. 담당자가 버튼 누르면 그때 배포.

핵심 컴포넌트 상세 GitHub Actions - CI 파이프라인

워크플로우는 브랜치에 따라 분기한다.

dev/stage는 단순하다. mutable tag(dev, stage)로 ECR에 push하면 끝. Image Updater가 digest 변경을 알아서 감지한다.

prod은 좀 더 단계가 있다. git SHA 기반으로 immutable tag를 만들고, podo-cluster 저장소의 kustomization.yaml에 새 태그를 커밋한 뒤, n8n webhook을 호출해서 Slack 승인을 요청한다. ArgoCD - 클러스터 상태 관리

ArgoCD는 Git 저장소(podo-cluster)의 manifest와 실제 클러스터 상태를 계속 비교하면서 동기화해준다. 이게 GitOps의 핵심이다.

ApplicationSet으로 환경별 관리:

prune: true는 Git에서 리소스를 삭제하면 클러스터에서도 같이 삭제된다. selfHeal: true는 누군가 kubectl로 직접 뭔가 바꿔도 Git 상태로 자동 복구해준다. 실수 방지에 꽤 유용하다.

Image Updater로 자동 이미지 감지:

dev/stage에서는 ArgoCD Image Updater가 ECR에 올라간 이미지의 digest 변경을 감지해서 자동으로 sync한다. mutable tag라 태그 이름은 같지만 실제 내용물(digest)이 바뀌면 배포가 트리거된다. Screenshot 제안: ArgoCD Application 상세 화면 - Sync 상태(Synced/OutOfSync)와 Health 상태가 보이는 화면 n8n - Slack 승인 워크플로우

n8n은 prod 배포에서 GitHub Actions와 ArgoCD 사이의 승인 게이트 역할을 한다. 오픈소스 워크플로우 자동화 도구인데, 이런 연결 작업에 딱이다.

n8n을 선택한 이유가 몇 가지 있다: 시각적 워크플로우 빌더 - 노드를 끌어다 놓으면서 흐름을 만들 수 있어서, 나중에 다른 팀원이 봐도 바로 이해할 수 있다 셀프호스팅 - EKS 클러스터 안에서 돌리고 있어서 외부 의존성이 없다 재사용성 - 환경 변수만 바꾸면 같은 워크플로우를 여러 서비스에 적용할 수 있다 Screenshot 제안: n8n 워크플로우 에디터 화면 - Webhook → Slack 메시지 → 승인 대기 → ArgoCD Sync 노드들이 연결된 모습 Screenshot 제안: Slack에서 배포 승인 요청 메시지가 온 화면 - 승인/거절 버튼이 보이는 상태 kustomize - 환경별 설정 분리

base에 공통 리소스를 두고, overlay에서 환경별 차이만 덮어쓴다. prod overlay에만 images 섹션에 immutable tag가 들어간다.

환경별 전략 비교

| | dev | stage | prod | ||||| | 이미지 태그 | mutable (dev) | mutable (stage) | immutable (v1.2.3-abc1234) | | 배포 트리거 | Image Updater (digest) | Image Updater (digest) | manifest 업데이트 + Slack 승인 | | 승인 | 없음 | 없음 | Slack 버튼 (n8n 경유) | | ArgoCD sync | 자동 | 자동 | 수동 (승인 후) | | 롤백 | 재push | 재push | Git revert → 자동 sync |

삽질했던 것들

ArgoCD prod 도입을 일단 미뤘던 이야기

처음에는 ArgoCD를 바로 prod에도 적용하려고 했다. 그런데 배포 과정에서 롤백 이슈가 터졌고, dev/stage에서 충분히 검증하지 않은 상태에서 prod을 건드리는 건 위험하다고 판단했다. 그래서 dev/stage에서 먼저 안정화하고, 그 사이에 n8n 기반 승인 게이트를 만들어서 prod에는 좀 더 조심스럽게 적용했다.

stage 자동배포로 전환한 이유

원래 stage도 prod이랑 똑같이 immutable tag + Slack 승인 방식이었다. 근데 stage에서 하루에도 몇 번씩 배포하다 보니 매번 Slack에서 승인 버튼 누르는 게 너무 번거로웠다.

ApplicationSet에 서비스를 다시 추가하고, standalone Application을 삭제하는 방식으로 전환했다. preserveResourcesOnDeletion: true 덕분에 Application을 삭제해도 실제 k8s 리소스는 그대로 유지돼서, 서비스 중단 없이 전환할 수 있었다.

readiness probe 때문에 자동 롤백되던 문제