컨테이너 이미지 빌드 도구 비교: Docker·Jib·Buildpacks·Shipwright를 언제 쓸까

반응형

『GitOps Cookbook』 3장을 따라 하며 Dockerfile, Jib, Buildpacks, Buildah/Podman, Shipwright로 이미지를 만들어 봤다. 원래 기록은 설치 명령을 한 글에 모두 넣었지만, 버전과 로컬 환경에 강하게 묶여 금방 낡을 수 있었다. 이번에는 실습 결과는 남기고 어떤 문제에 어떤 빌드 방식을 선택할지를 중심으로 다시 정리한다.

짧게 답하면 다음과 같다.

상황 먼저 검토할 도구 이유
Dockerfile을 직접 통제해야 한다 Docker Buildx·BuildKit 빌드 단계와 베이스 이미지를 명시적으로 설계하기 쉽다
Java 앱을 Maven·Gradle 흐름에서 바로 이미지화한다 Jib Dockerfile이나 로컬 Docker daemon 없이 레이어를 구성할 수 있다
조직이 언어별 빌드 관행을 플랫폼으로 표준화한다 Cloud Native Buildpacks 소스 감지와 builder·buildpack 정책을 중앙에서 관리하기 좋다
Kubernetes API로 빌드를 선언하고 전략을 교체한다 Shipwright Build, BuildStrategy, BuildRun으로 소스·전략·출력을 분리한다
Docker CLI가 아닌 daemonless·rootless 도구가 필요하다 Buildah·Podman OCI 이미지 작업과 컨테이너 실행을 별도 도구로 운영할 수 있다

도구의 출력이 OCI 이미지 규격과 호환될 수 있다는 사실과 빌드 과정까지 서로 같은 것은 별개다. 캐시, 권한 모델, 지원 플랫폼, secret 전달 방식, 공급망 메타데이터는 도구마다 다르다.

『GitOps Cookbook』 책 표지
학습 대상: 『GitOps Cookbook』 3장 컨테이너

공통 기준부터 정한다

도구를 비교하기 전에 결과 이미지가 만족해야 할 조건을 먼저 적는 편이 낫다.

  • 같은 소스와 잠금 파일로 재현 가능한가?
  • latest 대신 commit SHA나 release version으로 추적할 수 있는가?
  • base image와 의존성 취약점을 갱신할 경로가 있는가?
  • secret이 Dockerfile layer, 빌드 로그, 셸 기록에 남지 않는가?
  • 대상 CPU 아키텍처를 base image와 builder가 모두 지원하는가?
  • 생성된 이미지의 digest, SBOM, provenance를 CI에서 보관할 수 있는가?

OCI Image Spec은 manifest, 선택적인 image index, filesystem layer와 configuration으로 이미지 형식을 정의한다. 여러 빌더가 이 형식을 만들 수 있지만, 같은 애플리케이션을 넣었다고 layer 구성과 재현성까지 같아지는 것은 아니다.

Docker Buildx와 BuildKit: 명시적인 Dockerfile

현재 Docker Build의 중심은 Buildx client와 BuildKit builder다. Dockerfile의 단계, cache mount, build secret, multi-stage build를 직접 제어하고 싶을 때 기본 선택지가 된다.

IMAGE="registry.example.com/team/sample-app"
TAG="$(git rev-parse --short HEAD)"

docker buildx build \
  --platform linux/amd64 \
  --tag "${IMAGE}:${TAG}" \
  --push .

docker buildx imagetools inspect "${IMAGE}:${TAG}"

플랫폼은 Mac의 CPU만 보고 정하면 안 된다. 실제 실행 노드, base image manifest, 빌드 중 실행되는 바이너리가 모두 대상 아키텍처와 맞아야 한다. 다중 아키텍처가 필요하면 각 플랫폼 빌드를 검증한 뒤 image index로 묶는다.

Dockerfile에서는 변경이 적은 의존성 파일을 먼저 복사해 cache hit를 높이는 방법이 유용하다. 반면 secret을 ARGCOPY로 넣으면 layer나 metadata에 남을 수 있으므로 BuildKit secret mount나 CI의 credential helper를 사용한다.

Dockerfile로 빌드한 Python 애플리케이션 이미지의 Docker Hub layer 화면
Dockerfile 실습으로 만든 Python 애플리케이션 이미지

Jib: Java 빌드 도구 안에서 이미지 만들기

Jib은 Java 애플리케이션의 dependencies, resources, classes를 나누어 layer를 만든다. Maven·Gradle 빌드와 컨테이너 이미지 생성을 한 흐름으로 묶고 싶을 때 특히 편하다.

IMAGE="registry.example.com/team/sample-java"
TAG="$(git rev-parse --short HEAD)"

./mvnw -Dimage="${IMAGE}:${TAG}" jib:build

위 명령은 프로젝트에 Jib plugin과 registry 인증이 이미 안전하게 설정되었다는 전제다. 비밀번호를 명령행 인자로 직접 넣지 않는다. CI에서는 짧은 수명의 토큰, credential helper, secret store를 사용하고 로그 마스킹을 확인한다.

Jib이 Docker daemon 없이 원격 registry로 push할 수 있다는 점은 장점이지만, 네이티브 라이브러리나 OS package 설치처럼 Dockerfile 수준의 조정이 필요한 애플리케이션에는 별도 설계가 필요하다.

Jib으로 빌드한 Java 애플리케이션 이미지의 Docker Hub layer 화면
Jib 실습으로 만든 Java 애플리케이션 이미지

Cloud Native Buildpacks: 빌드 관행을 builder에 담기

Cloud Native Buildpacks는 소스를 감지하고 적절한 buildpack을 선택해 이미지를 만든다. 애플리케이션 팀마다 Dockerfile을 복제하는 대신 플랫폼 팀이 builder와 buildpack 조합을 관리하려는 경우에 잘 맞는다.

IMAGE="registry.example.com/team/sample-node"
TAG="$(git rev-parse --short HEAD)"

pack build "${IMAGE}:${TAG}" --builder "<approved-builder>"

“Dockerfile이 없다”가 “설정이 없다”는 뜻은 아니다. 어떤 builder와 buildpack 버전을 썼는지, 대상 stack·architecture를 지원하는지, build-time과 run-time image를 어떻게 갱신할지 관리해야 한다. 조직에서 승인한 builder를 고정하고 결과를 실행·스캔하는 단계까지 CI에 넣는 편이 안전하다.

Buildah·Podman: kind 노드 안보다 전용 환경에서

원래 실습에서는 macOS에서 바로 실행하기 어려워 kind control-plane 컨테이너 안에 Buildah와 Podman을 설치했다. 학습 목적으로 명령을 확인할 수는 있지만, 이를 권장 운영 방식으로 보기는 어렵다.

kind 노드는 Kubernetes 테스트용으로 만든 일회성 컨테이너다. 그 안에서 package manager로 빌드 도구를 추가하면 노드를 다시 만들 때 사라지고, privileged 권한·storage driver·nested container 경계도 복잡해진다. macOS에서는 Podman machine 같은 지원 환경을 사용하고, CI에서는 격리된 runner와 rootless 구성을 검토하는 편이 낫다.

Buildah로 빌드한 HTTP 서버 이미지의 Docker Hub layer 화면
Buildah·Podman 실습으로 만든 HTTP 서버 이미지

Shipwright: Kubernetes에서 빌드를 선언하는 계층

Shipwright는 하나의 빌더가 아니라 Kubernetes에서 이미지 빌드를 표현하는 확장 가능한 프레임워크다.

Source: 무엇을 빌드할지
BuildStrategy: 어떤 도구와 단계로 빌드할지
Output: 어느 registry에 보낼지
BuildRun: 언제 그 Build를 실행할지

현재 공식 API 문서는 shipwright.io/v1beta1Build, BuildStrategy·ClusterBuildStrategy, BuildRun을 설명한다. 예전 글의 v1alpha1, 특정 Tekton·Shipwright release URL과 sample strategy는 역사적 실습 기록으로만 남기고, 새 클러스터에는 현재 release 문서와 CRD schema를 확인해야 한다.

Registry credential은 namespace의 Secret과 최소 권한 ServiceAccount로 전달한다. 공개 글이나 YAML 예제에 개인 계정·PAT를 넣지 않고, output image도 latest 하나에 덮어쓰기보다 immutable tag와 digest로 추적한다.

Shipwright와 BuildKit으로 빌드한 Go 애플리케이션 이미지의 Docker Hub layer 화면
Shipwright·BuildKit 실습으로 만든 Go 애플리케이션 이미지

실습에서 남은 것

당시 네 가지 경로로 실습을 끝낸 뒤 Docker Hub의 네 저장소에 이미지가 올라간 것을 확인했다. 그 결과 자체보다 더 오래 남은 것은 빌드 도구를 “명령어 모음”으로 비교하면 안 된다는 점이었다.

네 가지 컨테이너 빌드 실습 결과가 등록된 Docker Hub 저장소 목록
Docker·Jib·Buildah·Shipwright 실습 뒤 확인한 네 이미지 저장소
  • Dockerfile은 세부 통제권이 크지만 유지 책임도 애플리케이션 팀에 가깝다.
  • Jib은 Java build graph를 활용하지만 범용 OS 조립 도구는 아니다.
  • Buildpacks는 표준화에 강하지만 builder 운영이 중요한 제품 선택이 된다.
  • Shipwright는 Kubernetes API와 전략 계층을 제공하며, 실제 빌드 엔진의 보안 특성까지 대신 해결하지 않는다.

학습 환경의 편의보다 실행 환경, 공급망 검증, 팀의 운영 책임을 먼저 놓으면 도구 선택이 훨씬 선명해진다.

참고 자료

반응형
KEEP READING
카테고리 전체 보기 →

댓글