BuildKit으로 Docker 이미지 빌드
Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated
BuildKit은 Docker에서 사용하는 빌드 엔진으로 멀티 플랫폼 빌드와 빌드 캐싱을 제공합니다. BuildKit은 Docker 이미지를 빌드하기 위한 다음 방법을 제공합니다: 독립 실행형 모드의 BuildKit은 Docker 데몬 의존성 없이 rootless 이미지 빌드를 제공합니다.
BuildKit은 Docker에서 사용하는 빌드 엔진으로 멀티 플랫폼 빌드와 빌드 캐싱을 제공합니다.
BuildKit 방법#
BuildKit은 Docker 이미지를 빌드하기 위한 다음 방법을 제공합니다:
| 방법 | 보안 요구 사항 | 명령어 | 사용 시기 |
|---|---|---|---|
| BuildKit rootless | 권한 있는 컨테이너 불필요 | buildctl-daemonless.sh |
최대 보안 또는 Kaniko 대체 |
| Docker Buildx | docker:dind 필요 |
docker buildx |
익숙한 Docker 워크플로우 |
| Native BuildKit | docker:dind 필요 |
buildctl |
고급 BuildKit 제어 |
사전 요구 사항#
- Docker executor가 있는 GitLab Runner
- Docker Buildx를 사용하려면 Docker 19.03 이상
Dockerfile이 있는 프로젝트
BuildKit rootless#
독립 실행형 모드의 BuildKit은 Docker 데몬 의존성 없이 rootless 이미지 빌드를 제공합니다. 이 방법은 권한 있는 컨테이너를 완전히 없애고 Kaniko 빌드의 직접적인 대안을 제공합니다.
다른 방법과의 주요 차이점:
moby/buildkit:rootless이미지 사용- rootless 작동을 위한
BUILDKITD_FLAGS: --oci-worker-no-process-sandbox포함 - BuildKit 데몬을 자동으로 관리하는
buildctl-daemonless.sh사용 - Docker 데몬 또는 권한 있는 컨테이너 의존성 없음
- 수동 레지스트리 인증 설정 필요
컨테이너 레지스트리 인증#
GitLab CI/CD는 사전 정의된 변수를 통해 GitLab 컨테이너 레지스트리에 대한 자동 인증을 제공합니다. BuildKit rootless의 경우 Docker 구성 파일을 수동으로 생성해야 합니다.
GitLab 컨테이너 레지스트리 인증#
GitLab은 다음과 같은 사전 정의된 변수를 자동으로 제공합니다:
CI_REGISTRY: 레지스트리 URLCI_REGISTRY_USER: 레지스트리 사용자 이름CI_REGISTRY_PASSWORD: 레지스트리 비밀번호
rootless 빌드에 대한 인증을 구성하려면 작업에 before_script 구성을 추가합니다. 예를 들어:
before_script:
- mkdir -p ~/.docker
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > ~/.docker/config.json
여러 레지스트리 인증#
추가 컨테이너 레지스트리를 인증하려면 before_script 섹션에 인증 항목을 결합합니다. 예를 들어:
before_script:
- mkdir -p ~/.docker
- |
echo "{
\"auths\": {
\"${CI_REGISTRY}\": {
\"auth\": \"$(printf "%s:%s" "${CI_REGISTRY_USER}" "${CI_REGISTRY_PASSWORD}" | base64 | tr -d '\n')\"
},
\"docker.io\": {
\"auth\": \"$(printf "%s:%s" "${DOCKER_HUB_USER}" "${DOCKER_HUB_PASSWORD}" | base64 | tr -d '\n')\"
}
}
}" > ~/.docker/config.json
의존성 프록시 인증#
GitLab 의존성 프록시를 통해 이미지를 가져오려면 before_script 섹션에 인증을 구성합니다. 예를 들어:
before_script:
- mkdir -p ~/.docker
- |
echo "{
\"auths\": {
\"${CI_REGISTRY}\": {
\"auth\": \"$(printf "%s:%s" "${CI_REGISTRY_USER}" "${CI_REGISTRY_PASSWORD}" | base64 | tr -d '\n')\"
},
\"$(echo -n $CI_DEPENDENCY_PROXY_SERVER | awk -F[:] '{print $1}')\": {
\"auth\": \"$(printf "%s:%s" ${CI_DEPENDENCY_PROXY_USER} "${CI_DEPENDENCY_PROXY_PASSWORD}" | base64 | tr -d '\n')\"
}
}
}" > ~/.docker/config.json
자세한 내용은 CI/CD 내에서 인증을 참조하세요.
rootless 모드에서 이미지 빌드#
Docker 데몬 의존성 없이 이미지를 빌드하려면 이 예시와 유사한 작업을 추가합니다:
build-rootless:
image:
name: moby/buildkit:rootless
entrypoint: [""]
stage: build
variables:
BUILDKITD_FLAGS: --oci-worker-no-process-sandbox
before_script:
- mkdir -p ~/.docker
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > ~/.docker/config.json
script:
- |
buildctl-daemonless.sh build \
--frontend dockerfile.v0 \
--local context=. \
--local dockerfile=. \
--output type=image,name=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA,push=true
rootless 모드에서 멀티 플랫폼 이미지 빌드#
rootless 모드에서 여러 아키텍처용 이미지를 빌드하려면 대상 플랫폼을 지정하도록 작업을 구성합니다. 예를 들어:
build-multiarch-rootless:
image:
name: moby/buildkit:rootless
entrypoint: [""]
stage: build
variables:
BUILDKITD_FLAGS: --oci-worker-no-process-sandbox
before_script:
- mkdir -p ~/.docker
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > ~/.docker/config.json
script:
- |
buildctl-daemonless.sh build \
--frontend dockerfile.v0 \
--local context=. \
--local dockerfile=. \
--opt platform=linux/amd64,linux/arm64 \
--output type=image,name=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA,push=true
rootless 모드에서 캐싱 사용#
후속 빌드를 더 빠르게 하기 위한 레지스트리 기반 캐싱을 활성화하려면 빌드 작업에서 캐시 가져오기 및 내보내기를 구성합니다. 예를 들어:
build-cached-rootless:
image:
name: moby/buildkit:rootless
entrypoint: [""]
stage: build
variables:
BUILDKITD_FLAGS: --oci-worker-no-process-sandbox
CACHE_IMAGE: $CI_REGISTRY_IMAGE:cache
before_script:
- mkdir -p ~/.docker
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > ~/.docker/config.json
script:
- |
buildctl-daemonless.sh build \
--frontend dockerfile.v0 \
--local context=. \
--local dockerfile=. \
--export-cache type=registry,ref=$CACHE_IMAGE \
--import-cache type=registry,ref=$CACHE_IMAGE \
--output type=image,name=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA,push=true
rootless 모드에서 레지스트리 미러 사용#
레지스트리 미러는 이미지 가져오기를 더 빠르게 하고 속도 제한이나 네트워크 제한을 해결하는 데 도움이 됩니다.
레지스트리 미러를 구성하려면 미러 엔드포인트를 지정하는 buildkit.toml 파일을 생성합니다. 예를 들어:
build-mirror-rootless:
image:
name: moby/buildkit:rootless
entrypoint: [""]
stage: build
variables:
BUILDKITD_FLAGS: --oci-worker-no-process-sandbox --config /tmp/buildkit.toml
before_script:
- mkdir -p ~/.docker
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > ~/.docker/config.json
- cat <<'EOF' > /tmp/buildkit.toml
[registry."docker.io"]
mirrors = ["mirror.example.com"]
EOF
script:
- |
buildctl-daemonless.sh build \
--frontend dockerfile.v0 \
--local context=. \
--local dockerfile=. \
--output type=image,name=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA,push=true
이 예시에서 mirror.example.com을 레지스트리 미러 URL로 교체합니다.
프록시 설정 구성#
GitLab Runner가 HTTP(S) 프록시 뒤에서 작동하는 경우 작업의 변수로 프록시 설정을 구성합니다. 예를 들어:
build-behind-proxy:
image:
name: moby/buildkit:rootless
entrypoint: [""]
stage: build
variables:
BUILDKITD_FLAGS: --oci-worker-no-process-sandbox
http_proxy: <your-proxy>
https_proxy: <your-proxy>
no_proxy: <your-no-proxy>
before_script:
- mkdir -p ~/.docker
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > ~/.docker/config.json
script:
- |
buildctl-daemonless.sh build \
--frontend dockerfile.v0 \
--local context=. \
--local dockerfile=. \
--build-arg http_proxy=$http_proxy \
--build-arg https_proxy=$https_proxy \
--build-arg no_proxy=$no_proxy \
--output type=image,name=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA,push=true
이 예시에서 <your-proxy>와 <your-no-proxy>를 프록시 구성으로 교체합니다.
커스텀 인증서 추가#
커스텀 CA 인증서를 사용하는 레지스트리에 푸시하려면 빌드 전에 컨테이너의 인증서 저장소에 인증서를 추가합니다. 예를 들어:
build-with-custom-certs:
image:
name: moby/buildkit:rootless
entrypoint: [""]
stage: build
variables:
BUILDKITD_FLAGS: --oci-worker-no-process-sandbox
before_script:
- export SSL_CERT_FILE="$HOME/ca_chain.pem"
- cat /etc/ssl/certs/ca-certificates.crt > "$SSL_CERT_FILE"
- echo "$MY_CA_CERT" >> "$SSL_CERT_FILE"
- mkdir -p ~/.docker
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > ~/.docker/config.json
script:
- |
buildctl-daemonless.sh build \
--frontend dockerfile.v0 \
--local context=. \
--local dockerfile=. \
--output type=image,name=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA,push=true
이 예시에서 MY_CA_CERT 변수에 루트 및 중간 인증서를 포함한 CA 인증서의 전체 내용을 입력합니다.
Kaniko에서 BuildKit으로 마이그레이션#
BuildKit rootless는 Kaniko의 안전한 대안입니다. rootless 작동을 유지하면서 향상된 성능, 더 나은 캐싱, 강화된 보안 기능을 제공합니다.
구성 업데이트#
BuildKit rootless 방법을 사용하도록 기존 Kaniko 구성을 업데이트합니다. 예를 들어:
Kaniko 이전:
build:
image:
name: gcr.io/kaniko-project/executor:debug
entrypoint: [""]
script:
- /kaniko/executor
--context $CI_PROJECT_DIR
--dockerfile $CI_PROJECT_DIR/Dockerfile
--destination $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
BuildKit rootless 이후:
build:
image:
name: moby/buildkit:rootless
entrypoint: [""]
variables:
BUILDKITD_FLAGS: --oci-worker-no-process-sandbox
before_script:
- mkdir -p ~/.docker
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > ~/.docker/config.json
script:
- |
buildctl-daemonless.sh build \
--frontend dockerfile.v0 \
--local context=. \
--local dockerfile=. \
--output type=image,name=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA,push=true
대안적인 BuildKit 방법#
rootless 빌드가 필요하지 않은 경우 BuildKit은 docker:dind 서비스가 필요하지만 익숙한 워크플로우나 고급 기능을 제공하는 추가 방법을 제공합니다.
Docker Buildx#
Docker Buildx는 익숙한 명령어 구문을 유지하면서 BuildKit 기능으로 Docker 빌드 기능을 확장합니다. 이 방법에는 docker:dind 서비스가 필요합니다.
기본 이미지 빌드#
Buildx로 Docker 이미지를 빌드하려면 docker:dind 서비스로 작업을 구성하고 buildx 빌더를 생성합니다. 예를 들어:
variables:
DOCKER_TLS_CERTDIR: "/certs"
build-image:
image: docker:cli
services:
- docker:dind
stage: build
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker buildx create --use --driver docker-container --name builder
- docker buildx inspect --bootstrap
script:
- docker buildx build --tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA --push .
after_script:
- docker buildx rm builder
멀티 플랫폼 이미지 빌드#
멀티 플랫폼 빌드는 단일 빌드 명령으로 여러 아키텍처용 이미지를 생성합니다. 결과 매니페스트는 여러 아키텍처를 지원하며, Docker는 각 배포 대상에 적합한 이미지를 자동으로 선택합니다.
여러 아키텍처용 이미지를 빌드하려면 대상 아키텍처를 지정하는 --platform 플래그를 추가합니다. 예를 들어:
variables:
DOCKER_TLS_CERTDIR: "/certs"
build-multiplatform:
image: docker:cli
services:
- docker:dind
stage: build
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker buildx create --use --driver docker-container --name multibuilder
- docker buildx inspect --bootstrap
script:
- docker buildx build
--platform linux/amd64,linux/arm64
--tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
--push .
after_script:
- docker buildx rm multibuilder
빌드 캐싱 사용#
레지스트리 기반 캐싱은 빌드 레이어를 컨테이너 레지스트리에 저장하여 빌드 간 재사용합니다.
mode=max 옵션은 모든 레이어를 캐시로 내보내
후속 빌드에 대한 최대 재사용 가능성을 제공합니다.
빌드 캐싱을 사용하려면 빌드 명령에 캐시 옵션을 추가합니다. 예를 들어:
variables:
DOCKER_TLS_CERTDIR: "/certs"
CACHE_IMAGE: $CI_REGISTRY_IMAGE:cache
build-with-cache:
image: docker:cli
services:
- docker:dind
stage: build
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker buildx create --use --driver docker-container --name cached-builder
- docker buildx inspect --bootstrap
script:
- docker buildx build
--cache-from type=registry,ref=$CACHE_IMAGE
--cache-to type=registry,ref=$CACHE_IMAGE,mode=max
--tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
--push .
after_script:
- docker buildx rm cached-builder
Native BuildKit#
빌드 프로세스를 더 세밀하게 제어하기 위해 네이티브 BuildKit buildctl 명령을 사용합니다.
이 방법에는 docker:dind 서비스가 필요합니다.
BuildKit을 직접 사용하려면 BuildKit 이미지와 docker:dind 서비스로 작업을 구성합니다. 예를 들어:
variables:
DOCKER_TLS_CERTDIR: "/certs"
build-with-buildkit:
image: moby/buildkit:latest
services:
- docker:dind
stage: build
before_script:
- mkdir -p ~/.docker
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > ~/.docker/config.json
script:
- |
buildctl build \
--frontend dockerfile.v0 \
--local context=. \
--local dockerfile=. \
--output type=image,name=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA,push=true
문제 해결#
인증 오류로 빌드 실패#
레지스트리 인증 오류가 발생하는 경우:
CI_REGISTRY_USER및CI_REGISTRY_PASSWORD변수를 사용할 수 있는지 확인합니다.- 대상 레지스트리에 푸시 권한이 있는지 확인합니다.
- 외부 레지스트리의 경우 프로젝트의 CI/CD 변수에 인증 자격 증명이 올바르게 구성되어 있는지 확인합니다.
rootless 빌드가 권한 오류로 실패#
rootless 모드의 권한 관련 문제:
BUILDKITD_FLAGS: --oci-worker-no-process-sandbox가 설정되어 있는지 확인합니다.- GitLab Runner에 충분한 리소스가 할당되어 있는지 확인합니다.
Dockerfile에 권한 있는 작업이 시도되지 않는지 확인합니다.
Kubernetes 러너에서 [rootlesskit:child ] error: failed to share mount point: /: permission denied를 받는 경우 AppArmor가 BuildKit에 필요한 마운트 시스템 호출을 차단하고 있습니다.
이 문제를 해결하려면 러너 구성에 다음을 추가합니다:
[runners.kubernetes.pod_annotations]
"container.apparmor.security.beta.kubernetes.io/build" = "unconfined"
오류: invalid local: stat path/to/image/Dockerfile: not a directory#
invalid local: stat path/to/image/Dockerfile: not a directory 오류가 발생할 수 있습니다.
이 문제는 --local dockerfile= 매개변수에 디렉토리 경로 대신 파일 경로를 지정할 때 발생합니다. BuildKit은 Dockerfile이라는 파일이 포함된 디렉토리 경로를 예상합니다.
이 문제를 해결하려면 전체 파일 경로 대신 디렉토리 경로를 사용합니다. 예를 들어:
- 사용:
--local dockerfile=path/to/image - 대신:
--local dockerfile=path/to/image/Dockerfile
멀티 플랫폼 빌드 실패#
멀티 플랫폼 빌드 문제:
Dockerfile의 기본 이미지가 대상 아키텍처를 지원하는지 확인합니다.- 아키텍처별 종속성이 모든 대상 플랫폼에서 사용 가능한지 확인합니다.
- 아키텍처별 로직을 위해
Dockerfile에서 조건부 구문 사용을 고려합니다.
