Build Offloading과 GHCR — 왜 내 로컬 머신 대신 레지스트리에 맡기는가
한 줄 결론
이미지 빌드는 가능하면 개발자 로컬 머신의 책임으로 두지 않는 것이 좋다. 빌드는 CI로 넘기고, 산출물은 GHCR에 쌓고, 배포는 레지스트리 기준으로 가져가는 구조가 더 재현 가능하고 안정적이다.
로컬 빌드의 한계
작은 프로젝트에서는 로컬에서 docker build 한 뒤 바로 서버에 올려도 된다.
하지만 프로젝트가 커지면 문제가 생긴다.
- 내 PC 환경에 따라 빌드 결과가 달라질 수 있다
- 팀원이 여러 명이면 “누가 빌드한 이미지인가”가 불분명하다
- 빌드 캐시가 개인 머신마다 흩어진다
- CI 기록이 남지 않는다
- 되돌릴 버전 관리가 약하다
즉 로컬 빌드는 빠른 실험엔 좋지만, 배포 기준점으로 삼기엔 약하다.
Build Offloading이란 무엇인가
내가 말하는 Build Offloading은 간단하다.
- 코드 push
- GitHub Actions가 이미지 빌드
- GHCR에 태그와 함께 push
- 서버는 GHCR에서 이미지를 pull
즉 개발자 머신은 코드를 보내고, 이미지 생성의 책임은 CI가 진다.
GHCR를 쓰는 이유
GHCR는 GitHub 생태계와 붙어 있어서 작은 팀이나 개인 프로젝트에서 매우 편하다.
장점은 이렇다.
- GitHub Actions와 바로 연결된다
- repo 권한과 패키지 권한이 같이 간다
- 태그 기반 버전 관리가 쉽다
- private package 관리도 비교적 단순하다
Docker Hub도 가능하지만, GitHub 중심 워크플로우에서는 GHCR가 관리 포인트가 적다.
내가 이 구조를 선호하는 이유
1. 빌드 결과의 기준점이 생긴다
“이번 배포는 어떤 이미지였나”가 명확해진다.
- commit SHA
- semantic tag
- latest
이런 기준을 레지스트리에서 추적할 수 있다.
2. 서버가 더 단순해진다
서버는 빌드 머신이 아니다.
서버 역할은 가능하면 단순해야 한다.
- 이미지 pull
- 컨테이너 재시작
- 헬스체크
이 정도면 좋다.
서버에서 소스 받아서 직접 빌드까지 하면 책임이 늘어난다.
3. 재현성이 높아진다
같은 commit에서 같은 CI가 만든 이미지를 누구나 다시 받을 수 있다. 이건 디버깅과 롤백에서 매우 중요하다.
DAEMON에서의 실제 감각
내가 좋아하는 흐름은 대체로 이렇다.
- 코드 push
- GitHub Actions에서 테스트와 빌드
- GHCR에 이미지 push
- Coolify나 서버가 해당 이미지를 pull
- 배포 완료
이 구조의 장점은 “배포를 코드와 이미지의 기록 위에 올려놓는다”는 점이다.
그냥 배포가 되는 것이 아니라, 어떤 산출물이 배포되었는지 남는다.
언제까지 이 구조로 충분한가
대부분의 개인 제품, 작은 팀 제품, 초기 스타트업에는 꽤 오래 충분하다.
오히려 너무 일찍 ECR, GCR, 자체 레지스트리, 복잡한 빌드 클러스터로 가는 것이 과하다.
내 기준은 이렇다.
- GitHub 중심 개발을 한다
- CI/CD가 GitHub Actions다
- 이미지 저장소를 단순하게 묶고 싶다
그러면 GHCR는 매우 좋은 기본값이다.
마무리
좋은 배포는 멋진 스크립트보다 명확한 책임 분리에서 나온다.
- 개발자 머신은 코드 작성
- CI는 빌드
- GHCR는 산출물 저장
- 서버는 실행
이렇게 나누면 배포는 훨씬 덜 흔들린다.