[docker] Dockerfile의 다중 RUN 대 단일 체인 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것이 무언가를 추가하는 경우 고려해야 할 몇 가지 사항이 있습니다.

  1. 레이어는 이전 레이어 위에 diff를 추가하기 만하므로, 나중에 레이어가 이전 레이어에 추가 된 것을 제거하지 않으면 두 방법 사이에 디스크 공간 절약 이점이 많지 않아야합니다 …

  2. Docker Hub에서 레이어를 병렬로 Dockerfile.1가져 오기 때문에 약간 더 크더라도 이론적으로 다운로드 속도가 더 빠릅니다.

  3. 4 번째 문장 (예 :)을 추가 echo This is the D > d하고 로컬로 재 구축 Dockerfile.1하면 캐시 덕분에 더 빨리 빌드되지만 Dockerfile.24 개의 명령을 모두 다시 실행해야합니다.

그렇다면 질문 : 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의 가독성 (및 장기 유지 관리 성)과 사용하는 계층 수를 최소화하는 것 사이의 균형을 찾아야합니다. 사용하는 레이어 수에 대해 전략적이고 신중해야합니다.

고정 표시기 1.10 이후 COPY, ADD그리고 RUN문은 이미지에 새 레이어를 추가합니다. 이 진술을 사용할 때는주의하십시오. 명령을 단일 RUN명령문 으로 결합하십시오 . 가독성이 필요한 경우에만 분리하십시오.

추가 정보 : https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/#/minimize-the-number-of-layers

업데이트 : docker> 17.05의 다단계

다단계 빌드를 사용하면 FROMDockerfile에서 여러 명령문을 사용할 수 있습니다 . 각 FROM문장은 단계이며 자체 기본 이미지를 가질 수 있습니다. 마지막 단계에서는 alpine과 같은 최소 기본 이미지를 사용하고 이전 단계의 빌드 아티팩트를 복사하고 런타임 요구 사항을 설치하십시오. 이 단계의 최종 결과는 이미지입니다. 따라서 여기에서 앞에서 설명한대로 레이어에 대해 걱정할 수 있습니다.

평소와 같이 docker는 다단계 빌드에 대한 훌륭한 문서 를 가지고 있습니다. 빠른 발췌 내용은 다음과 같습니다.

다단계 빌드를 사용하면 Dockerfile에서 여러 FROM 문을 사용합니다. 각 FROM 명령어는 서로 다른베이스를 사용할 수 있으며 각 빌드는 새로운 빌드 단계를 시작합니다. 최종 이미지에서 원하지 않는 모든 것을 남겨두고 한 단계에서 다른 단계로 아티팩트를 선택적으로 복사 할 수 있습니다.

이에 대한 훌륭한 블로그 게시물은 https://blog.alexellis.io/mutli-stage-docker-builds/ 에서 찾을 수 있습니다.

당신의 요점에 대답하려면 :

  1. 그렇습니다. 레이어는 diff와 비슷합니다. 변경 사항이 전혀 없으면 레이어가 추가 된 것으로 생각하지 않습니다. 문제는 일단 계층 # 2에서 무언가를 설치 / 다운로드하면 계층 # 3에서이를 제거 할 수 없다는 것입니다. 따라서 일단 무언가가 층에 쓰여지면, 그것을 제거하여 이미지 크기를 더 이상 줄일 수 없습니다.

  2. 레이어를 병렬로 가져올 수있어 잠재적으로 더 빠를 수 있지만 각 레이어는 파일을 제거하더라도 의심 할 여지없이 이미지 크기를 증가시킵니다.

  3. 예, 도커 파일을 업데이트하는 경우 캐싱이 유용합니다. 그러나 한 방향으로 작동합니다. 레이어가 10 개이고 레이어 # 6을 변경하더라도 레이어 # 6- # 10의 모든 항목을 다시 작성해야합니다. 따라서 너무 자주 빌드 프로세스를 가속화하지는 않지만 이미지 크기를 불필요하게 늘리는 것이 보장됩니다.


이 답변을 업데이트 하도록 알려주@Mohan 에게 감사합니다 .


답변

위의 답변이 오래된 것 같습니다. 문서 참고 사항 :

Docker 17.05 이전 및 Docker 1.10 이전에는 이미지의 레이어 수를 최소화하는 것이 중요했습니다. 다음과 같은 개선 사항으로 이러한 요구가 완화되었습니다.

[…]

Docker 17.05 이상은 다단계 빌드에 대한 지원을 추가하여 필요한 아티팩트 만 최종 이미지에 복사 할 수 있습니다. 이를 통해 최종 이미지의 크기를 늘리지 않고도 중간 빌드 단계에서 도구 및 디버그 정보를 포함 할 수 있습니다.

https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/#minimize-the-number-of-layers

이 예제는 또한 Bash && 연산자를 사용하여 두 개의 RUN 명령을 인위적으로 압축하여 이미지에 추가 레이어를 생성하지 않도록합니다. 이것은 실패하기 쉽고 유지 관리하기가 어렵습니다.

https://docs.docker.com/engine/userguide/eng-image/multistage-build/

모범 사례는 다단계 빌드를 사용하고 Dockerfile가독성을 유지하는 것으로 변경되었습니다 .


답변

이미지 레이어에 포함시켜야합니다.

핵심은 가능한 많은 레이어를 공유하는 것입니다.

나쁜 예 :

도커 파일 .1

RUN yum install big-package && yum install package1

도커 파일 .2

RUN yum install big-package && yum install package2

좋은 예 :

도커 파일 .1

RUN yum install big-package
RUN yum install package1

도커 파일 .2

RUN yum install big-package
RUN yum install package2

삭제 제안은 추가 / 설치 작업과 동일한 계층에서 발생하는 경우에만 유용하지는 않습니다.


답변