도커 레이어 이해


27

우리는 다음과 같은 블록을 가지고 있습니다 Dockerfile.

RUN yum -y update
RUN yum -y install epel-release
RUN yum -y groupinstall "Development Tools"
RUN yum -y install python-pip git mysql-devel libxml2-devel libxslt-devel python-devel openldap-devel libffi-devel openssl-devel

RUN생성 된 도커 레이어를 줄이기 위해 이러한 명령을 통합해야한다고 들었 습니다.

RUN yum -y update \
    && yum -y install epel-release \
    && yum -y groupinstall "Development Tools" \
    && yum -y install python-pip git mysql-devel libxml2-devel libxslt-devel python-devel openldap-devel libffi-devel openssl-devel

도커를 처음 접했을 때 여러 RUN 명령을 지정하는이 두 버전의 차이점을 완전히 이해하지 못했습니다. 하나의 RUN명령을 하나의 명령으로 통합 할 때 와 여러 RUN명령 을 갖는 것이 적절한 경우는 언제 입니까?


답변:


35

도커 이미지는 실제로 파일 시스템 계층의 링크 된 목록입니다. Dockerfile의 각 명령어 는 해당 명령어 실행 전후에 파일 시스템의 차이점을 설명하는 파일 시스템 계층을 만듭니다. docker inspect부속은 파일 시스템 계층의 링크 된리스트가되는 그 특성을 표시하는 고정 표시기 이미지를 사용할 수있다.

이미지에 사용 된 레이어 수는 중요합니다

  • 동시 업로드 또는 다운로드 횟수에 영향을 미치므로 이미지를 밀거나 당길 때.
  • 컨테이너를 시작할 때, 컨테이너에 사용되는 파일 시스템을 생성하기 위해 레이어들이 서로 결합 될 때; 계층이 많을수록 성능이 저하되지만 다른 파일 시스템 백엔드는 이로 인해 다른 영향을받습니다.

이것은 이미지를 어떻게 구축해야하는지에 대한 몇 가지 결과를 초래합니다. 내가 줄 수있는 가장 중요한 조언은 다음과 같습니다.

조언 # 1 소스 코드가 관련된 빌드 단계가 Dockerfile 에서 가능한 한 늦게 진행되고 a &&또는 a를 사용하는 이전 명령과 연결되어 있지 않은지 확인하십시오 ;.

그 이유는 이전의 모든 단계가 캐시되고 해당 계층을 반복해서 다운로드 할 필요가 없기 때문입니다. 이것은 더 빠른 빌드와 더 빠른 릴리즈를 의미하며, 아마도 당신이 원하는 것입니다. 흥미롭게도 도커 캐시를 최적으로 사용하는 것은 놀랍게도 어렵습니다.

두 번째 조언은 덜 중요하지만 유지 관리 관점에서 매우 유용합니다.

조언 # 2 Dockerfile에 복잡한 명령을 쓰지 말고 복사하고 실행할 스크립트를 사용하십시오.

Dockerfile 과 같을 것이다이 다음과 같은 사항을

COPY apt_setup.sh /root/
RUN sh -x /root/apt_setup.sh
COPY install_pacakges.sh /root/
RUN sh -x /root/install_packages.sh

등등. 여러 명령을 바인딩 &&하는 데는 제한이 없습니다. 중복을 피하거나 문서화 목적으로 함수 등을 사용할 수있는 스크립트로 작성하는 것이 훨씬 쉽습니다.

사람들은 사전 프로세서 관심과로 인한 작은 오버 헤드를 피하기 위해 기꺼이 COPYa를 온 - 더 - 플라이 단계를하고 실제로 생성하는 Dockerfile을 어디에

COPY apt_setup.sh /root/
RUN sh -x /root/apt_setup.sh

순서는

RUN base64 --decode … | sh -x

여기서는 base64로 인코딩 된 버전입니다 apt_setup.sh.

세 번째 조언은 가능한 더 긴 빌드 비용으로 레이어의 크기와 수를 제한하려는 사람들을위한 것입니다.

조언 # 3with -idiom을 사용하여 중간 계층에는 있지만 결과 파일 시스템에는없는 파일을 피하십시오.

일부 도커 명령어에 의해 추가되고 나중의 명령어에 의해 제거 된 파일은 결과 파일 시스템에 존재하지 않지만, 도커 이미지를 구성하는 도커 레이어에서 두 번 언급된다. 명령을 추가하면 레이어에 이름과 전체 내용이 한 번, 명령이 삭제되면 레이어에서 삭제 알림으로 한 번.

예를 들어, 일시적으로 C 컴파일러와 이미지가 필요하다고 가정하고

# !!! THIS DISPLAYS SOME PROBLEM --- DO NOT USE !!!
RUN apt-get install -y gcc
RUN gcc --version
RUN apt-get --purge autoremove -y gcc

(보다 현실적인 예는 단순히 --version플래그로 그 존재를 주장하는 대신 컴파일러로 일부 소프트웨어를 빌드하는 것 입니다.)

Dockerfile 스 니펫은 세 개의 레이어를 생성합니다. 첫 번째 레이어에는 전체 gcc 제품군이 포함되어 있으므로 최종 파일 시스템에 존재하지 않더라도 해당 데이터는 여전히 동일한 방식으로 이미지의 일부이며 다운로드 할 때마다 다운로드, 업로드 및 언 패킹해야합니다. 최종 이미지입니다.

with-idiom는 분리 자원 소유권과 자원을 이용하여 로직을 해제 함수형 프로그래밍의 일반적인 형태이다. 이 관용구를 쉘 스크립팅으로 바꾸는 것은 쉬우 며, Advice # 2COPY & RUN 에서 와 같이 이전 명령을 다음 스크립트로 바꿔서 사용할 수 있습니다 .

# with_c_compiler SIMPLE-COMMAND
#  Execute SIMPLE-COMMAND in a sub-shell with gcc being available.

with_c_compiler()
(
    set -e
    apt-get install -y gcc
    "$@"
    trap 'apt-get --purge autoremove -y gcc' EXIT
)

with_c_compiler\
    gcc --version

복잡한 명령을 기능으로 전환하여에 전달할 수 있습니다 with_c_compiler. 여러 with_whatever함수의 호출을 연결하는 것도 가능 하지만 그다지 바람직하지는 않습니다. (쉘의 좀 더 난해한 기능을 사용하면 with_c_compiler복잡한 명령을 수락 하는 것이 가능 하지만 모든 측면에서 이러한 복잡한 명령을 함수로 래핑하는 것이 바람직합니다.)

Advice # 2를 무시하고 싶다면 결과 Dockerfile 스 니펫은

RUN apt-get install -y gcc\
 && gcc --version\
 && apt-get --purge autoremove -y gcc

난독 화로 인해 읽고 유지하기가 쉽지 않습니다. 셸 스크립트 변형이 중요한 부분 gcc --version을 강조하는 반면 체인 &&변형이 소음의 한 부분에 해당 부분을 묻는 방법을 참조하십시오 .


1
스크립트를 사용하여 빌드하고 하나의 RUN 문에 여러 명령을 사용한 후 상자 크기의 결과를 포함시킬 수 있습니까?
030

1
이미지베이스의 구성 (예 : OS 항목)과 심지어 lib를 작성한 소스 설정과 혼합하는 것은 나쁜 생각처럼 보입니다. "소스 코드가 관련된 빌드 단계가 가능한 늦게 진행되었는지 확인하십시오"라고 말합니다. 해당 부분을 완전히 독립적 인 인공물로 만드는 데 문제가 있습니까?
JimmyJames

1
@ 030“상자”크기 란 무엇입니까? 나는 당신이 어떤 상자를 언급하는지 전혀 모른다.
Michael Le Barbier Grünewald

1
도커 이미지 크기
030

1
@JimmyJames 배포 시나리오에 따라 크게 다릅니다. 우리가 컴파일 된 프로그램을 가정한다면,“올바른 일”은 프로그램을 패키징하고 그 패키지 의존성과 패키지 자체를 두 개의 별개의 최종 단계로 설치하는 것입니다. 이는 도커 캐시의 유용성을 최대화하고 동일한 파일을 가진 계층의 반복 다운로드를 방지합니다. 긴 의존성 이미지 체인을 구축하는 것보다 도커 이미지를 구축하는 빌드 레시피를 공유하는 것이 더 쉽습니다.
Michael Le Barbier Grünewald

13

Dockerfile에서 생성하는 각 명령은 새로운 이미지 레이어를 생성합니다. 각 레이어는 항상 결과 이미지의 일부가 아닌 추가 데이터를 가져옵니다. 예를 들어, 한 레이어에 파일을 추가했지만 나중에 다른 레이어에서 제거하면 최종 이미지의 크기에 추가 된 파일 크기가 특수 "화이트 아웃"파일 형태로 포함됩니다.

다음 Dockerfile이 있다고 가정 해 봅시다.

FROM centos:6

RUN yum -y update 
RUN yum -y install epel-release

결과 이미지 크기는

bigimage     latest        3c5cbfbb4116        2 minutes ago    407MB

"유사한"Dockerfile과는 반대로 :

FROM centos:6

RUN yum -y update  && yum -y install epel-release

결과 이미지 크기는

smallimage     latest        7edeafc01ffe        3 minutes ago    384MB

단일 RUN 문에서 yum 캐시를 정리하면 크기가 더 작아집니다.

따라서 가독성 / 유지 보수 용이성과 레이어 수 / 이미지 크기 간의 균형을 유지하려고합니다.


4

RUN문은 각각의 레이어를 나타냅니다. 패키지를 다운로드하여 설치하고 제거하려고한다고 가정하십시오. 세 개의 RUN문장을 사용하면 별도의 레이어가 있으므로 이미지 크기가 줄어들지 않습니다. 하나의 RUN명령문을 사용하여 모든 명령을 실행 하면 디스크 이미지 크기가 줄어들 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.