Jenkins + EC2 빌드 서버를 GitHub Actions로 교체한 이유 — 월 33,500원과 간헐적 빌드 실패
CPU 98% 유휴 상태인 t3a.small 서버가 Next.js 빌드에서 반복 실패한 원인을 분석하고, 이벤트 기반 GitHub Actions CI/CD로 전환한 과정을 정리한다.
KOIN 프로젝트는 Jenkins를 올린 AWS EC2 인스턴스를 빌드 서버로 사용하고 있었는데, Next.js 빌드가 간헐적으로 실패하는 문제가 반복됐다. 처음에는 단발성 오류라고 넘겼는데, 실패 패턴이 일정했다.
1. 98% 유휴인데 빌드가 실패하는 서버
서버 구성은 t3a.small(2코어, 2GB RAM)이었고, Datadog으로 4주간 모니터링한 결과를 보면 CPU 유휴율 평균이 98.73%였다. 평균 부하도 0.05 안팎으로 낮았고, 대부분의 시간 동안 서버가 사실상 놀고 있었다.
그런데 빌드는 같은 기간에 간헐적으로 실패하고 있었다. 실패가 생기는 지점도 매번 비슷했는데, Next.js 정적 페이지 생성(Static Generation) 단계에서 반복적으로 중단됐다. CPU 유휴율을 볼 때 CPU가 병목이 아니라는 건 분명했다.
2. 메모리가 문제였다
메모리 수치를 보니 상황이 좀 더 명확해졌다. 평소 메모리 사용량 평균이 1,221 MiB인데, 총 메모리가 1,932 MiB니까 여유가 711 MiB 정도였다. 스왑은 이미 평균 520 MiB가 사용 중인 상태였고, 최대 740 MiB까지 올라가는 구간도 있었다.
여기에 빌드 프로세스가 추가로 올라오면 메모리 피크를 감당하지 못하는 구조였다. 빌드 시점의 상세 스파이크 데이터를 별도로 수집하지 않아서 피크가 정확히 어느 구간에서 얼마나 올라가는지는 확인하지 못했다. 다만 평상시에 이미 스왑을 쓰고 있던 상태에서 빌드가 올라오면 무리가 간다는 건 수치만 봐도 짐작할 수 있었다.
3. GitHub Actions로 전환
과거 참여 인원이 많아 스테이지·프로덕션 배포가 잦던 시절에는 비용이 들더라도 빌드 서버를 유지할 이유가 있었다. 그때는 빌드 빈도가 높았고, 서버가 계속 켜져 있어도 그 비용을 감수할 만했다.
그런데 참여 인원이 줄고 나서 다시 계산해보니 선택지는 서버 증설보다 상시 대기 서버 자체를 없애는 쪽에 가까웠다. 빌드가 없는 시간이 훨씬 길어졌는데도 월 약 33,500원이 계속 나가고 있었고, 유휴율이 98%를 넘는 서버에 메모리만 더 붙이는 건 현재 운영 방식과도 맞지 않았다.
그래서 빌드가 필요한 순간에만 자원을 쓰는 GitHub Actions로 옮겼다. KOIN 레포지토리가 GitHub로 관리되고 있고 퍼블릭 레포지토리라 별도 비용 없이 GitHub-hosted runner를 쓸 수 있었기 때문이다. 2026년 3월 기준 GitHub-hosted runner는 4 vCPU, 16GB RAM, 14GB SSD를 제공했는데, t3a.small보다 메모리 여유가 크게 늘어나니 적어도 스왑을 안고 빌드를 돌리던 구조에서는 벗어날 수 있었다.
워크플로우는 브랜치 전략에 맞춰 세 단계로 분리해서 구성했다.
- PR 단계: lint, 타입 체크, 빌드 검증 — PR이 열리거나 업데이트될 때 실행
- develop 브랜치: 개발 서버 자동 배포
- main 브랜치: 수동 프로덕션 배포
- 공통: 배포 결과(성공, 실패) Slack 알림
환경 변수와 인증 정보도 GitHub Secrets로 옮겼다. 기존에는 EC2 인스턴스에 직접 접근해서 설정하던 작업이라 프론트엔드 개발자가 배포 흐름을 건드리려면 서버 접근 권한이 필요했다. 환경 변수 하나를 추가하려고 해도 백엔드 개발자에게 요청하고 기다려야 했는데, Secrets로 옮기고 나서는 이 작업을 레포지토리 수준에서 직접 처리할 수 있었다. 빌드 인프라를 바꾸면서 운영 권한까지 같이 옮긴 셈이라, 이 변화는 다음 결과로도 이어졌다.
4. 결과
전환 뒤 이 글을 정리한 2026년 3월 시점까지는 정적 페이지 생성 단계 실패가 다시 나오지 않았다. 이건 GitHub Actions가 항상 안정적이라는 뜻보다는, 적어도 t3a.small 환경에서 보였던 메모리 압박성 실패 패턴이 사라졌다는 관찰에 가깝다. GitHub Actions 러너가 빌드 시점에 필요한 만큼 메모리를 쓰고 내려가는 구조라 상시 대기 서버의 메모리 압박이 줄었고, EC2 인스턴스와 EBS를 제거하면서 월 약 33,500원도 함께 줄였다.
구조상으로는 빌드를 코드로 관리하게 됐다는 점이 더 크게 남았다. Jenkins 설정을 바꾸려면 서버에 들어가야 했는데, 이제는 워크플로우 파일을 수정하는 PR을 올리면 된다. 배포 이력도 GitHub Actions 탭에서 바로 볼 수 있으니 빌드 서버 로그를 따로 뒤질 필요가 없어졌고, 앞으로 배포 흐름을 바꿔야 할 때도 서버 접속보다 워크플로우 PR로 다루는 편이 현재 팀 규모에는 더 잘 맞는다.