Dockerfile의 다중 RUN vs. 단일 체인 RUN, 무엇이 더 낫습니까?
Dockerfile.1
여러 실행 RUN
:
FROM busybox
RUN echo This is the A > a
RUN echo This is the B > b
RUN echo This is the C > c
Dockerfile.2
그들과 합류 :
FROM busybox
RUN echo This is the A > a &&\
echo This is the B > b &&\
echo This is the C > c
각각 RUN
은 레이어를 생성하므로 항상 레이어가 적을수록 좋고 따라서 Dockerfile.2
더 좋다고 생각했습니다.
이것은 RUN
이전 RUN
(예 :)에 의해 추가 된 항목을 제거 할 때 분명히 사실 yum install nano && yum clean all
이지만 모든 항목이 RUN
추가 되는 경우 고려해야 할 몇 가지 사항이 있습니다.
레이어는 이전 레이어 위에 diff 만 추가해야하므로 이후 레이어가 이전 레이어에서 추가 된 항목을 제거하지 않으면 두 방법간에 디스크 공간 절약 이점이 많지 않을 것입니다.
레이어는 Docker Hub에서 병렬로 가져 오므로
Dockerfile.1
약간 더 크지 만 이론적으로는 더 빨리 다운로드됩니다.네 번째 문장 (예 :)을 추가
echo This is the D > d
하고 로컬로 다시Dockerfile.1
빌드하면 캐시 덕분에 빌드 속도가 빨라지지만Dockerfile.2
4 개의 명령을 모두 다시 실행해야합니다.
그래서 질문 : Dockerfile을 수행하는 더 좋은 방법은 무엇입니까?
가능한 경우 항상 동일한 파일을 삭제하는 명령으로 파일을 만드는 명령을 한 RUN
줄로 병합합니다 . 이는 각 RUN
줄이 이미지에 레이어를 추가 하기 때문입니다 . 출력은 문자 그대로 docker diff
생성되는 임시 컨테이너에서 볼 수있는 파일 시스템 변경 사항입니다 . 다른 계층에서 생성 된 파일을 삭제하면 통합 파일 시스템이하는 모든 작업은 새 계층에 파일 시스템 변경 사항을 등록하는 것입니다. 파일은 여전히 이전 계층에 존재하며 네트워크를 통해 배송되어 디스크에 저장됩니다. 따라서 소스 코드를 다운로드하고 압축을 풀고 바이너리로 컴파일 한 다음 마지막에 tgz 및 소스 파일을 삭제하면 이미지 크기를 줄이기 위해이 모든 작업이 단일 레이어에서 수행되기를 원합니다.
다음으로, 다른 이미지에서 재사용 가능성과 예상되는 캐싱 사용량에 따라 레이어를 개인적으로 분할했습니다. 모두 동일한 기본 이미지 (예 : 데비안)를 가진 4 개의 이미지가있는 경우 이러한 이미지의 대부분에 대한 공통 유틸리티 모음을 첫 번째 실행 명령으로 가져 와서 다른 이미지가 캐싱의 이점을 얻을 수 있습니다.
Dockerfile의 순서는 이미지 캐시 재사용을 볼 때 중요합니다. 매우 드물게 업데이트되는 구성 요소를 살펴 봅니다. 기본 이미지가 업데이트 될 때만 가능하며 Dockerfile에 해당 구성 요소를 배치합니다. Dockerfile의 끝 부분에는 빠르게 실행되고 자주 변경 될 수있는 명령 (예 : 호스트 특정 UID가있는 사용자 추가 또는 폴더 생성 및 권한 변경)이 포함됩니다. 컨테이너에 현재 개발중인 해석 된 코드 (예 : JavaScript)가 포함되어있는 경우 가능한 한 늦게 추가되어 재 빌드가 해당 단일 변경 사항 만 실행합니다.
이러한 각 변경 그룹에서 가능한 한 통합하여 레이어를 최소화합니다. 따라서 4 개의 서로 다른 소스 코드 폴더가있는 경우 단일 폴더에 배치되어 단일 명령으로 추가 할 수 있습니다. 패키지 관리자 오버 헤드 (업데이트 및 정리)의 양을 최소화하기 위해 가능한 경우 apt-get과 같은 모든 패키지 설치가 단일 RUN으로 병합됩니다.
다단계 빌드 업데이트 :
나는 다단계 빌드의 최종 단계가 아닌 단계에서 이미지 크기를 줄이는 것에 대해 훨씬 덜 걱정합니다. 이러한 단계에 태그가 지정되지 않고 다른 노드로 배송되지 않으면 각 명령을 별도의 RUN
줄로 분할하여 캐시 재사용 가능성을 극대화 할 수 있습니다 .
그러나 이것은 단계간에 복사하는 모든 것이 파일이고 환경 변수 설정, 진입 점 및 명령과 같은 나머지 이미지 메타 데이터가 아니기 때문에 레이어를 스쿼시하는 완벽한 솔루션은 아닙니다. 그리고 Linux 배포판에 패키지를 설치할 때 라이브러리 및 기타 종속성이 파일 시스템 전체에 흩어져 모든 종속성을 복사하기가 어려울 수 있습니다.
이 때문에 CI / CD 서버에서 바이너리를 빌드하기위한 대체물로 다단계 빌드를 사용하므로 CI / CD 서버에는 실행할 도구 만 docker build
있으면되고 jdk, nodejs, go 및 설치된 다른 컴파일 도구.
모범 사례에 나열된 공식 답변 (공식 이미지는 다음 사항을 준수해야 함)
레이어 수 최소화
Dockerfile의 가독성 (따라서 장기적인 유지 관리 가능성)과 Dockerfile이 사용하는 레이어 수 최소화 사이의 균형을 찾아야합니다. 사용하는 레이어 수에 대해 전략적이고 신중해야합니다.
docker 1.10부터 COPY
, ADD
및 RUN
문은 이미지에 새 레이어를 추가합니다. 이러한 진술을 사용할 때주의하십시오. 명령을 단일 RUN
명령문 으로 결합하십시오 . 가독성을 위해 필요한 경우에만 분리하십시오.
업데이트 : Docker> 17.05의 다중 스테이지
다단계 빌드를 사용하면 FROM
Dockerfile에서 여러 문을 사용할 수 있습니다 . 각 FROM
문은 하나의 단계이며 자체 기본 이미지를 가질 수 있습니다. 마지막 단계에서는 alpine과 같은 최소 기본 이미지를 사용하고 이전 단계의 빌드 아티팩트를 복사하고 런타임 요구 사항을 설치합니다. 이 단계의 최종 결과는 이미지입니다. 그래서 이것은 앞에서 설명한 것처럼 레이어에 대해 걱정하는 곳입니다.
평소와 같이 docker에는 다단계 빌드에 대한 훌륭한 문서 가 있습니다. 다음은 빠른 발췌입니다.
다단계 빌드에서는 Dockerfile에서 여러 FROM 문을 사용합니다. 각 FROM 명령어는 서로 다른 기본을 사용할 수 있으며 각 명령어는 빌드의 새 단계를 시작합니다. 한 단계에서 다른 단계로 아티팩트를 선택적으로 복사하여 최종 이미지에서 원하지 않는 모든 것을 남겨 둘 수 있습니다.
이에 대한 훌륭한 블로그 게시물은 https://blog.alexellis.io/mutli-stage-docker-builds/ 에서 찾을 수 있습니다.
포인트에 답하려면 :
예, 레이어는 일종의 diff와 같습니다. 변경 사항이 전혀 없으면 레이어가 추가되지 않는다고 생각합니다. 문제는 레이어 # 2에서 무언가를 설치 / 다운로드하면 레이어 # 3에서 제거 할 수 없다는 것입니다. 따라서 레이어에 어떤 것이 기록되면이를 제거해도 이미지 크기를 더 이상 줄일 수 없습니다.
레이어를 병렬로 끌어 올 수있어 잠재적으로 더 빠르게 만들 수 있지만 파일을 제거하더라도 각 레이어는 의심 할 여지없이 이미지 크기를 증가시킵니다.
Yes, caching is useful if you're updating your docker file. But it works in one direction. If you have 10 layers, and you change layer #6, you'll still have to rebuild everything from layer #6-#10. So it's not too often that it will speed the build process up, but it's guaranteed to unnecessarily increase the size of your image.
Thanks to @Mohan for reminding me to update this answer.
It seems the answers above are outdated. The docs note:
Prior to Docker 17.05, and even more, prior to Docker 1.10, it was important to minimize the number of layers in your image. The following improvements have mitigated this need:
[...]
Docker 17.05 and higher add support for multi-stage builds, which allow you to copy only the artifacts you need into the final image. This allows you to include tools and debug information in your intermediate build stages without increasing the size of the final image.
and
Notice that this example also artificially compresses two RUN commands together using the Bash && operator, to avoid creating an additional layer in the image. This is failure-prone and hard to maintain.
https://docs.docker.com/engine/userguide/eng-image/multistage-build/
Best practice seems to have changed to using multistage builds and keeping the Dockerfile
s readable.
It depends on waht you include in your image layers.
The key point is sharing as many layers as possible:
Bad Example:
Dockerfile.1
RUN yum install big-package && yum install package1
Dockerfile.2
RUN yum install big-package && yum install package2
Good Example:
Dockerfile.1
RUN yum install big-package
RUN yum install package1
Dockerfile.2
RUN yum install big-package
RUN yum install package2
Another suggestion is deleting is not so useful only if it happens on the same layer as the adding/installing action.
'IT story' 카테고리의 다른 글
Typescript에서 NPM 모듈 작성 (0) | 2020.09.02 |
---|---|
서버에서 클라이언트 측 JavaScript 오류 로깅 [닫힘] (0) | 2020.09.01 |
Visual Studio C ++에 대한 단위 테스트를 설정하는 방법 (0) | 2020.09.01 |
GitHub : 이전 버전의 파일 검색 (0) | 2020.09.01 |
"make"명령 줄 인수 (-D)에서 C 소스 코드로 매크로 정의를 전달하는 방법은 무엇입니까? (0) | 2020.09.01 |