BuildKit으로 Docker 이미지 빌드
BuildKit으로 Docker 이미지 빌드 (Build Docker images with BuildKit)
BuildKit은 Docker가 사용하는 빌드 엔진으로, 멀티 플랫폼 빌드와 빌드 캐싱을 제공해요. 이미지를 루트리스(rootless)로, 또는 친숙한 Docker 워크플로우와 함께 더 높은 제어로 빌드하고 싶을 때 방법을 나눠서 살펴볼게요.
출처: 문서
본문
BuildKit 방법 (BuildKit methods)
BuildKit은 Docker 이미지를 빌드하는 여러 방법을 제공해요.
| 방법 | 보안 요구사항 | 명령 | 필요한 경우 |
|---|---|---|---|
| BuildKit rootless | 특권(privileged) 컨테이너 불필요 | buildctl-daemonless.sh |
최대 보안 또는 Kaniko 대체 |
| Docker Buildx | docker:dind 필요 |
docker buildx |
친숙한 Docker 워크플로우 |
| 네이티브 BuildKit | docker:dind 필요 |
buildctl |
고급 BuildKit 제어 |
사전 요구사항 (Prerequisites)
- Docker executor가 있는 GitLab Runner.
- Docker Buildx를 쓰려면 Docker 19.03 이상.
Dockerfile이 있는 프로젝트.
BuildKit rootless
독립형(standalone) 모드의 BuildKit은 Docker 데몬 의존성 없이 루트리스 이미지 빌드를 제공해요. 이 방법은 특권 컨테이너를 완전히 없애고 Kaniko 빌드의 직접적인 대체재가 됩니다.
[!NOTE] 루트리스 빌드에도 BuildKit이 사용자 네임스페이스와 마운트 지점을 만들 때 쓰는 시스템 호출을 허용하는 러너가 필요해요. GitLab.com의 호스팅 러너는 특권 모드로 실행되므로 이 호출을 허용하고 추가 구성이 필요 없습니다. 특권 모드가 없는 Docker executor를 쓰는 자체 관리 러너에서는 빌드가 권한 오류로 실패할 수 있어요. 자세한 내용은 루트리스 빌드가 권한 오류로 실패할 때를 참고하세요. 러너 보안 설정을 바꿀 수 없다면 루트리스 Buildah로 이미지를 빌드하세요.
다른 방법과의 주요 차이점은 이렇습니다.
moby/buildkit:rootless이미지 사용.- 루트리스 동작을 위한
BUILDKITD_FLAGS: --oci-worker-no-process-sandbox포함. buildctl-daemonless.sh로 BuildKit 데몬을 자동 관리.- Docker 데몬이나 특권 컨테이너 의존성 없음.
- 레지스트리 인증을 수동으로 설정해야 함.
컨테이너 레지스트리 인증 (Authenticate with container registries)
GitLab CI/CD는 사전 정의 변수를 통해 GitLab 컨테이너 레지스트리에 대한 자동 인증을 제공해요. BuildKit rootless에서는 직접 Docker 구성 파일을 만들어야 합니다.
GitLab 컨테이너 레지스트리 인증 (Authenticate with the GitLab container registry)
GitLab이 자동으로 제공하는 사전 정의 변수는 이렇습니다.
CI_REGISTRY: 레지스트리 URL.CI_REGISTRY_USER: 레지스트리 사용자 이름.CI_REGISTRY_PASSWORD: 레지스트리 비밀번호.
루트리스 빌드용 인증을 구성하려면 job에 before_script 구성을 추가하세요. 예를 들면 이렇습니다.
before_script:
- mkdir -p ~/.docker
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > ~/.docker/config.json
여러 레지스트리 인증 (Authenticate with multiple registries)
추가 컨테이너 레지스트리에 인증하려면 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
의존성 프록시 인증 (Authenticate with the dependency proxy)
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 내 인증 문서를 참고하세요.
루트리스 모드로 이미지 빌드 (Build images in rootless mode)
Docker 데몬 의존성 없이 이미지를 빌드하려면 이 예시와 비슷한 job을 추가하세요.
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
entrypoint: [""] 오버라이드는 필수예요. 기본적으로 moby/buildkit:rootless 이미지는 BuildKit 데몬을 장기 실행 서비스로 시작합니다. 이 오버라이드가 없으면 job이 빌드 명령 대신 데몬을 실행해서 타임아웃까지 멈춰 있습니다.
루트리스 모드로 멀티 플랫폼 이미지 빌드 (Build multi-platform images in rootless mode)
루트리스 모드에서 여러 아키텍처용 이미지를 빌드하려면 job에서 대상 플랫폼을 지정하세요. 예를 들면 이렇습니다.
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
루트리스 모드에서 캐싱 사용 (Use caching in rootless mode)
이후 빌드를 빠르게 하려면 레지스트리 기반 캐싱을 활성화하도록 빌드 job에 캐시 import·export를 구성하세요. 예를 들면 이렇습니다.
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
루트리스 모드에서 레지스트리 미러 사용 (Use a registry mirror in rootless mode)
레지스트리 미러는 이미지 pull을 빠르게 하고, 요율 제한이나 네트워크 제약을 완화하는 데 도움이 돼요. 레지스트리 미러를 구성하려면 미러 엔드포인트를 지정하는 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로 바꾸세요.
프록시 설정 구성 (Configure proxy settings)
GitLab Runner가 HTTP(S) 프록시 뒤에서 동작한다면 job의 변수로 프록시 설정을 구성하세요. 예를 들면 이렇습니다.
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>를 여러분의 프록시 구성으로 바꾸세요.
사용자 지정 인증서 추가 (Add custom certificates)
사용자 지정 CA 인증서가 있는 레지스트리에 푸시하려면 데몬이 시작되기 전에 BuildKit 구성 파일에서 인증서를 구성하세요. 예를 들면 이렇습니다.
build-with-custom-certs:
image:
name: moby/buildkit:rootless
entrypoint: [""]
stage: build
variables:
BUILDKITD_FLAGS: --oci-worker-no-process-sandbox
before_script:
- mkdir -p "$HOME/.docker"
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > "$HOME/.docker/config.json"
- REG_HOST="${CI_REGISTRY%%/*}"
- mkdir -p "$HOME/.config/buildkit/certs/$REG_HOST"
- echo "$CA_CERT" > "$HOME/.config/buildkit/certs/$REG_HOST/ca.pem"
- |
cat > "$HOME/.config/buildkit/buildkitd.toml" << EOT
[registry."$REG_HOST"]
ca = ["$HOME/.config/buildkit/certs/$REG_HOST/ca.pem"]
EOT
- export SSL_CERT_FILE="$HOME/.config/buildkit/certs/$REG_HOST/ca.pem"
script:
- |
buildctl-daemonless.sh build \
--frontend dockerfile.v0 \
--local context=. \
--local dockerfile=. \
--output type=image,name=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA,push=true
이 예시에서:
REG_HOST="${CI_REGISTRY%%/*}"는 레지스트리 URL에서 호스트 이름을 추출해요.buildkitd.toml은 BuildKit이 대상 레지스트리의 CA 인증서를 신뢰하도록 구성해요. BuildKit은$HOME/.config/buildkit/에서 이 파일을 자동으로 발견합니다.SSL_CERT_FILE은 BuildKit 데몬이 완전히 초기화되기 전에 이루어지는 TLS 연결을 커버하기 위해buildkitd.toml과 함께 필요해요.
루트와 중간 인증서를 포함한 전체 인증서 체인을 CA_CERT CI/CD 변수에 추가하세요. PEM 인증서는 개행을 포함하기 때문에 CA_CERT 값은 마스킹할 수 없어요. 값을 마스킹하려면 파일 타입 변수를 대신 쓰고 before_script에서 echo "$CA_CERT"를 cat "$CA_CERT"로 바꾸세요. 대상 레지스트리가 GitLab 인스턴스와 같은 인증 기관을 사용하고 러너가 tls-ca-file로 구성되어 있다면 CA_CERT 변수 대신 사전 정의된 CI_SERVER_TLS_CA_FILE 변수를 참조할 수 있어요.
Kaniko에서 BuildKit으로 마이그레이션 (Migrate from Kaniko to BuildKit)
BuildKit rootless는 특권 컨테이너 없이 더 나은 성능, 더 좋은 캐싱, 강화된 보안 기능을 제공하는 Kaniko의 안전한 대안이에요.
구성 업데이트 (Update your configuration)
기존 Kaniko 구성을 BuildKit rootless 방식으로 업데이트하세요. 예를 들면 이렇습니다.
이전, 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
사용자 지정 CA 인증서 (Custom CA certificates)
Kaniko job이 사용자 지정 CA 인증서를 썼다면 BuildKit rootless용으로 그 인증서를 명시적으로 구성해야 해요. Kaniko와 달리 moby/buildkit:rootless 이미지는 시스템 인증서 저장소를 포함하지 않습니다. 데몬이 시작되기 전에 BuildKit 구성 파일에서 CA 인증서를 구성해야 해요.
사용자 지정 CA 인증서 구성을 BuildKit rootless로 마이그레이션하려면:
CA_CERT라는 CI/CD 변수에 전체 인증서 체인을 저장합니다. 루트와 중간 인증서를 포함하세요.- job 구성을
buildkitd.toml파일과SSL_CERT_FILE환경 변수를 쓰도록 업데이트합니다. 전체 예시는 사용자 지정 인증서 추가를 참고하세요.
대체 BuildKit 방법 (Alternative BuildKit methods)
루트리스 빌드가 필요 없다면 BuildKit은 docker:dind 서비스가 필요하지만 친숙한 워크플로우나 고급 기능을 제공하는 추가 방법들을 제공해요.
Docker Buildx
Docker Buildx는 친숙한 명령 문법을 유지하면서 BuildKit 기능으로 Docker 빌드를 확장합니다. 이 방법은 docker:dind 서비스가 필요해요.
기본 이미지 빌드 (Build basic images)
Buildx로 Docker 이미지를 빌드하려면 docker:dind 서비스로 job을 구성하고 buildx builder를 만드세요. 예를 들면 이렇습니다.
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
멀티 플랫폼 이미지 빌드 (Build multi-platform images)
멀티 플랫폼 빌드는 단일 빌드 명령으로 여러 아키텍처의 이미지를 만들어요. 결과 매니페스트가 여러 아키텍처를 지원하고, 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
빌드 캐싱 사용 (Use build caching)
레지스트리 기반 캐싱은 빌드 레이어를 컨테이너 레지스트리에 저장해서 빌드 간에 재사용해요. 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
네이티브 BuildKit (Native BuildKit)
빌드 과정을 더 세밀하게 제어하려면 네이티브 BuildKit buildctl 명령을 쓰세요. 이 방법은 docker:dind 서비스가 필요해요. BuildKit을 직접 쓰려면 BuildKit 이미지와 docker:dind 서비스로 job을 구성하세요. 예를 들면 이렇습니다.
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
트러블슈팅 (Troubleshooting)
BuildKit으로 이미지를 빌드할 때 이런 문제를 만날 수 있어요.
인증 오류로 빌드 실패 (Build fails with authentication errors)
레지스트리 인증 실패가 발생한다면:
CI_REGISTRY_USER와CI_REGISTRY_PASSWORD변수가 사용 가능한지 확인.- 대상 레지스트리에 푸시 권한이 있는지 확인.
- 외부 레지스트리라면 프로젝트의 CI/CD 변수에 인증 자격 증명이 올바르게 구성되어 있는지 확인.
루트리스 빌드가 권한 오류로 실패 (Rootless build fails with permission errors)
루트리스 빌드가 권한 오류로 실패하면 다음을 확인하세요.
BUILDKITD_FLAGS: --oci-worker-no-process-sandbox가 설정되어 있는지.- GitLab Runner에 충분한 리소스가 할당되었는지.
Dockerfile에서 특권 작업을 시도하지 않는지.
Kubernetes 러너에서는 AppArmor 관련 마운트 권한 오류가 루트리스 컨테이너를 막을 수도 있어요. 자세한 내용은 Kubernetes executor의 AppArmor 마운트 권한 오류를 참고하세요.
실패가 다음 오류와 일치한다면, 러너 보안 정책이 루트리스 BuildKit이 필요로 하는 시스템 호출을 차단하고 있는 것입니다.
오류: fork/exec /proc/self/exe: operation not permitted
특권 모드가 없는 Docker executor를 쓰는 러너에서 다음 오류 중 하나를 볼 수 있어요.
could not connect to unix:///run/user/1000/buildkit/buildkitd.sock after 10 trials
[rootlesskit:parent] error: failed to start the child: fork/exec /proc/self/exe: operation not permitted
이 문제는 러너의 seccomp 프로필이 루트리스 BuildKit이 필요로 하는 시스템 호출을 차단할 때 발생해요. GitLab.com의 호스팅 러너는 특권 모드로 실행되므로 영향을 받지 않습니다. 자체 관리 러너에서 이 문제를 해결하려면 Docker executor의 security_opt 설정을 BuildKit이 필요로 하는 시스템 호출만 허용하도록 구성하세요.
[!WARNING]
security_opt를seccomp:unconfined로 설정하지 마세요. 오류를 해결하긴 하지만 컨테이너의 기본 seccomp 프로필을 비활성화해서 위험한 시스템 호출에 대한 보호를 제거하고 격리를 약화시켜요. 대신 필요한 호출만 허용하는 사용자 지정 seccomp 프로필을 쓰거나, 루트리스 Buildah로 이미지를 빌드하세요.
오류: 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
멀티 플랫폼 빌드 실패 (Multi-platform builds fail)
멀티 플랫폼 빌드 이슈에 대해:
Dockerfile의 기본 이미지가 대상 아키텍처를 지원하는지 확인.- 모든 대상 플랫폼에 아키텍처별 의존성이 있는지 확인.
- 아키텍처별 로직을 위해
Dockerfile에서 조건문 사용을 고려.
더 알아보기
루트리스 빌드를 원한다면 moby/buildkit:rootless 이미지에 entrypoint: [""] 오버라이드와 BUILDKITD_FLAGS 설정을 꼭 챙기세요. GitLab 레지스트리 인증은 자동이 아니라 직접 ~/.docker/config.json을 만들어야 한다는 점이 가장 흔한 실수 지점이에요. 더 친숙한 흐름을 원한다면 Docker Buildx, 더 세밀한 제어를 원한다면 네이티브 buildctl을 고르면 됩니다. Kaniko에서 옮겨 온다면 CA 인증서 설정을 먼저 확인하세요.