← 블로그

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에서의 실제 감각

내가 좋아하는 흐름은 대체로 이렇다.

  1. 코드 push
  2. GitHub Actions에서 테스트와 빌드
  3. GHCR에 이미지 push
  4. Coolify나 서버가 해당 이미지를 pull
  5. 배포 완료

이 구조의 장점은 “배포를 코드와 이미지의 기록 위에 올려놓는다”는 점이다.

그냥 배포가 되는 것이 아니라, 어떤 산출물이 배포되었는지 남는다.

언제까지 이 구조로 충분한가

대부분의 개인 제품, 작은 팀 제품, 초기 스타트업에는 꽤 오래 충분하다.

오히려 너무 일찍 ECR, GCR, 자체 레지스트리, 복잡한 빌드 클러스터로 가는 것이 과하다.

내 기준은 이렇다.

  • GitHub 중심 개발을 한다
  • CI/CD가 GitHub Actions다
  • 이미지 저장소를 단순하게 묶고 싶다

그러면 GHCR는 매우 좋은 기본값이다.

마무리

좋은 배포는 멋진 스크립트보다 명확한 책임 분리에서 나온다.

  • 개발자 머신은 코드 작성
  • CI는 빌드
  • GHCR는 산출물 저장
  • 서버는 실행

이렇게 나누면 배포는 훨씬 덜 흔들린다.