개발/ETC

Docker / Buildkit 에서 SOURCE_DATE_EPOCH와 rewrite-timestamp 설정

bitofsky 2024. 3. 13. 17:21

Docker / Buildkit 에서 SOURCE_DATE_EPOCH와 rewrite-timestamp 설정

컨테이너 이미지를 빌드하는 과정에서 동일한 이미지 레이어 구성을 가졌음에도 불구하고 빌드 캐시를 활용하지 않는 경우, 최종 이미지의 해시값이 매번 변경되는 현상이 발생한다. 이러한 변화는 이미지 매니페스트와 각 빌드 단계에서 생성되는 레이어에 현재 빌드 시간이 포함되어 있기 때문으로, 캐시를 재활용하지 않을 경우 동일한 컨텍스트, 소스, 도커파일이라 하더라도 매번 다른 이미지가 생성된다.

기술적 배경을 살펴보면, 도커파일 내에서 apt와 같은 패키지 매니저를 통해 원격 설치를 진행하는 경우, 매 호출 시 동일한 명령어로 이미지를 구성하더라도 실제 파일 시스템에는 변경 사항이 발생할 수 있어 이와 같은 동작이 이루어진다.

그러나 대다수의 시스템은 최종 서버 빌드를 위해 이러한 패키지 설치 단계를 별도의 이미지로 미리 생성하여 사용하는 경우가 많으며, 이 경우 빌드 캐시의 무효화나 빌드 시스템의 병렬 처리로 인해 각 빌드마다 동일한 이미지가 생성되지 않는 현상으로 인해 스토리지 및 네트워크 사용량이 증가하고, 해시 변경을 감지하는 후처리 작업이 더욱 복잡해지는 문제가 발생한다.

이러한 문제에 대응하기 위해, 많은 요구자들은 오랫동안 이미지 스냅샷과 메타데이터의 타임스탬프를 유닉스 타임 0으로 설정하는 것을 요청해왔으며, 이제서야 이러한 요구가 반영된 v0.13.0 버전의 빌드킷 업데이트가 이루어졌다. v0.11.0에서는 SOURCE_DATE_EPOCH 인식 기능이 추가되었으며, v0.13.0에서는 rewrite-timestamp 내보내기 옵션이 추가되었다.

SOURCE_DATE_EPOCH에 대한 자세한 정보는 다음 링크에서 확인할 수 있다: https://reproducible-builds.org/docs/source-date-epoch/

이것들과 함께 touch로 context 파일의 타임스템프들을 모두 동일하게 초기화 하면, 몇번 빌드하던지 모두 같은 이미지 해시가 만들어지게 된다.

# Dockerfile
# syntax=docker/dockerfile:1.5
FROM ubuntu:22.04@sha256:aa772c98400ef833586d1d517d3e8de670f7e712bf581ce6053165081773259d
RUN echo "Hello World" > hello.txt & touch -t 197001010000.00 hello.txt

# bash
docker buildx create --use --driver-opt image=moby/buildkit:v0.13.0
docker buildx build --no-cache --build-arg SOURCE_DATE_EPOCH=0 --output type=image,push=false,rewrite-timestamp=true .
[+] Building 1.1s (8/8) FINISHED                                                                                                                                          
 => [internal] load build definition from Dockerfile                                                                                                                 0.0s
 => => transferring dockerfile: 232B                                                                                                                                 0.0s
 => resolve image config for docker-image://docker.io/docker/dockerfile:1.5                                                                                          0.3s
 => CACHED docker-image://docker.io/docker/dockerfile:1.5@sha256:39b85bbfa7536a5feceb7372a0817649ecb2724562a38360f4d6a7782a409b14                                    0.0s
 => => resolve docker.io/docker/dockerfile:1.5@sha256:39b85bbfa7536a5feceb7372a0817649ecb2724562a38360f4d6a7782a409b14                                               0.0s
 => [internal] load .dockerignore                                                                                                                                    0.0s
 => => transferring context: 2B                                                                                                                                      0.0s
 => [internal] load metadata for docker.io/library/ubuntu:22.04@sha256:aa772c98400ef833586d1d517d3e8de670f7e712bf581ce6053165081773259d                              0.0s
 => CACHED [1/2] FROM docker.io/library/ubuntu:22.04@sha256:aa772c98400ef833586d1d517d3e8de670f7e712bf581ce6053165081773259d                                         0.0s
 => => resolve docker.io/library/ubuntu:22.04@sha256:aa772c98400ef833586d1d517d3e8de670f7e712bf581ce6053165081773259d                                                0.0s
 => [2/2] RUN echo "Hello World" > hello.txt & touch -t 197001010000.00 hello.txt                                                                                    0.1s
 => exporting to image                                                                                                                                               0.2s
 => => exporting layers                                                                                                                                              0.2s
 => => exporting manifest sha256:8be4c0a2a57b95ba10ccf6edc234b2969e46eeea549054ab322243faa44b9ef3                                                                    0.0s
 => => exporting config sha256:47c62c5eb5f254e29365ceda2a043bd48ffccf48f48e7caacad9428db7440a6e

위와 같은 Dockerfile에서 docker build를 하면 이제 항상 동일한 manifest sha가 생성될 것이다. (sha256:8be4c0a2a57b95ba10ccf6edc234b2969e46eeea549054ab322243faa44b9ef3)

현재 남아 있는 문제점으로는, COPY --link 단계에서 타임스탬프 재작성이 이루어지지 않는 것으로 보인다. 이 문제가 해결되기 전까지는 간단한 COPY 명령어를 사용하는 것이 좋을 것으로 보인다.