Python 프로젝트 용 Docker 이미지를 빌드 할 때 패키지 재설치를 방지하는 방법은 무엇입니까?


128

내 Dockerfile은 다음과 같습니다.

FROM my/base

ADD . /srv
RUN pip install -r requirements.txt
RUN python setup.py install

ENTRYPOINT ["run_server"]

새 이미지를 빌드 할 때마다 종속성을 다시 설치해야하므로 내 지역에서 매우 느릴 수 있습니다.

cache설치된 패키지에 대해 생각하는 한 가지 방법 은 my/base이미지를 다음과 같은 최신 이미지 로 재정의하는 것입니다.

docker build -t new_image_1 .
docker tag new_image_1 my/base

따라서 다음에이 Dockerfile로 빌드 할 때 my / base에 이미 일부 패키지가 설치되어 있습니다.

그러나이 솔루션에는 두 가지 문제가 있습니다.

  1. 항상 기본 이미지를 재정의 할 수있는 것은 아닙니다.
  2. 새로운 이미지가 겹쳐지면 기본 이미지가 점점 커집니다.

그렇다면이 문제를 해결하기 위해 어떤 더 나은 솔루션을 사용할 수 있습니까?

편집하다##:

내 컴퓨터의 Docker에 대한 몇 가지 정보 :

☁  test  docker version
Client version: 1.1.2
Client API version: 1.13
Go version (client): go1.2.1
Git commit (client): d84a070
Server version: 1.1.2
Server API version: 1.13
Go version (server): go1.2.1
Git commit (server): d84a070
☁  test  docker info
Containers: 0
Images: 56
Storage Driver: aufs
 Root Dir: /var/lib/docker/aufs
 Dirs: 56
Execution Driver: native-0.2
Kernel Version: 3.13.0-29-generic
WARNING: No swap limit support

이미지 빌드를 마친 후 중간 이미지를 삭제합니까?
— Regan

당연하지,하지만 난 이미지를 다시 할 때, 나는 여전히 원래에 내놓고있어 때문에이 무관하다my/base
— 사토루

답변:


139

다음과 같은 Dockerfile을 빌드하십시오.

FROM my/base

WORKDIR /srv
ADD ./requirements.txt /srv/requirements.txt
RUN pip install -r requirements.txt
ADD . /srv
RUN python setup.py install

ENTRYPOINT ["run_server"]

Docker는의 requirements.txt다른 코드 파일 .이 변경 되었는지 여부에 관계없이을 변경하지 않는 한 pip 설치 중에 캐시를 사용합니다 . 여기에 예가 있습니다.


다음은 간단한 Hello, World!프로그램입니다.

$ tree
.
├── Dockerfile
├── requirements.txt
└── run.py   

0 directories, 3 file

# Dockerfile

FROM dockerfile/python
WORKDIR /srv
ADD ./requirements.txt /srv/requirements.txt
RUN pip install -r requirements.txt
ADD . /srv
CMD python /srv/run.py

# requirements.txt
pytest==2.3.4

# run.py
print("Hello, World")

도커 빌드의 출력 :

Step 1 : WORKDIR /srv
---> Running in 22d725d22e10
---> 55768a00fd94
Removing intermediate container 22d725d22e10
Step 2 : ADD ./requirements.txt /srv/requirements.txt
---> 968a7c3a4483
Removing intermediate container 5f4e01f290fd
Step 3 : RUN pip install -r requirements.txt
---> Running in 08188205e92b
Downloading/unpacking pytest==2.3.4 (from -r requirements.txt (line 1))
  Running setup.py (path:/tmp/pip_build_root/pytest/setup.py) egg_info for package pytest
....
Cleaning up...
---> bf5c154b87c9
Removing intermediate container 08188205e92b
Step 4 : ADD . /srv
---> 3002a3a67e72
Removing intermediate container 83defd1851d0
Step 5 : CMD python /srv/run.py
---> Running in 11e69b887341
---> 5c0e7e3726d6
Removing intermediate container 11e69b887341
Successfully built 5c0e7e3726d6

수정하자 run.py:

# run.py
print("Hello, Python")

다시 빌드를 시도하십시오. 다음은 출력입니다.

Sending build context to Docker daemon  5.12 kB
Sending build context to Docker daemon 
Step 0 : FROM dockerfile/python
---> f86d6993fc7b
Step 1 : WORKDIR /srv
---> Using cache
---> 55768a00fd94
Step 2 : ADD ./requirements.txt /srv/requirements.txt
---> Using cache
---> 968a7c3a4483
Step 3 : RUN pip install -r requirements.txt
---> Using cache
---> bf5c154b87c9
Step 4 : ADD . /srv
---> 9cc7508034d6
Removing intermediate container 0d7cf71eb05e
Step 5 : CMD python /srv/run.py
---> Running in f25c21135010
---> 4ffab7bc66c7
Removing intermediate container f25c21135010
Successfully built 4ffab7bc66c7

위에서 볼 수 있듯이 이번에는 Docker가 빌드 중에 캐시를 사용합니다. 이제 업데이트하겠습니다 requirements.txt.

# requirements.txt

pytest==2.3.4
ipython

다음은 도커 빌드의 출력입니다.

Sending build context to Docker daemon  5.12 kB
Sending build context to Docker daemon 
Step 0 : FROM dockerfile/python
---> f86d6993fc7b
Step 1 : WORKDIR /srv
---> Using cache
---> 55768a00fd94
Step 2 : ADD ./requirements.txt /srv/requirements.txt
---> b6c19f0643b5
Removing intermediate container a4d9cb37dff0
Step 3 : RUN pip install -r requirements.txt
---> Running in 4b7a85a64c33
Downloading/unpacking pytest==2.3.4 (from -r requirements.txt (line 1))
  Running setup.py (path:/tmp/pip_build_root/pytest/setup.py) egg_info for package pytest

Downloading/unpacking ipython (from -r requirements.txt (line 2))
Downloading/unpacking py>=1.4.12 (from pytest==2.3.4->-r requirements.txt (line 1))
  Running setup.py (path:/tmp/pip_build_root/py/setup.py) egg_info for package py

Installing collected packages: pytest, ipython, py
  Running setup.py install for pytest

Installing py.test script to /usr/local/bin
Installing py.test-2.7 script to /usr/local/bin
  Running setup.py install for py

Successfully installed pytest ipython py
Cleaning up...
---> 23a1af3df8ed
Removing intermediate container 4b7a85a64c33
Step 4 : ADD . /srv
---> d8ae270eca35
Removing intermediate container 7f003ebc3179
Step 5 : CMD python /srv/run.py
---> Running in 510359cf9e12
---> e42fc9121a77
Removing intermediate container 510359cf9e12
Successfully built e42fc9121a77

docker가 pip 설치 중에 캐시를 사용하지 않은 방법을 확인하십시오. 작동하지 않으면 Docker 버전을 확인하십시오.

Client version: 1.1.2
Client API version: 1.13
Go version (client): go1.2.1
Git commit (client): d84a070
Server version: 1.1.2
Server API version: 1.13
Go version (server): go1.2.1
Git commit (server): d84a070

2
도 커가 ADD명령을 볼 때마다 캐시가 무효화 되기 때문에 이것은 작동하지 않는 것 같습니다 .
— satoru

1
왜 작동하지 않는지 모르겠습니다. 그러나 requirements.txt (<src> on ADD ./requirements.txt /srv/requirements.txt) 에는 변경 사항이 없으므로 docker는 캐시를 사용해야합니다. Dockerfile 문서에 섹션 추가를 참조하십시오 .
— nacyot

16
예, requirements.txt가 변경되지 않으면 캐시를 사용합니다. 그러나 requirements.txt가 변경되면 모든 요구 사항이 다운로드됩니다. pip 캐시 볼륨을 Docker 컨테이너에 마운트하여 캐시에서로드 할 수있는 방법이 있습니까?
— Jitu

7
이 대답의 핵심은 requirements.txt ( ADD requirements.txt /srvpip ( RUN pip install -r requirements.txt) 를 실행하기 전에 추가하고 pip 를 실행 한 후 다른 모든 파일 을 추가하는 것입니다 . 따라서 다음 순서로 있어야합니다. (1) ADD requirements.txt /srv; (2) RUN pip install -r requirements.txt; () 3)ADD . /srv
— engelen

2
ADD 대신 COPY를 사용하는 경우에는 작동하지 않음을 유의하십시오
— veuncent

29

네트워크 활동을 최소화하려면 pip 호스트 시스템의 캐시 디렉토리를 있습니다.

호스트의 pip 캐시 디렉토리 바인드가 컨테이너의 pip 캐시 디렉토리에 마운트 된 상태로 Docker 컨테이너를 실행합니다. docker run명령은 다음과 같아야합니다.

docker run -v $HOME/.cache/pip-docker/:/root/.cache/pip image_1

그런 다음 Dockerfile 에서 명령 대신 ENTRYPOINT명령문 (또는 CMD명령문) 의 일부로 요구 사항을 설치하십시오 RUN. 이것은 (주석에서 지적했듯이) 이미지 빌드 중 ( RUN문이 실행될 때) 마운트를 사용할 수 없기 때문에 중요 합니다. Docker 파일은 다음과 같습니다.

FROM my/base

ADD . /srv

ENTRYPOINT ["sh", "-c", "pip install -r requirements.txt && python setup.py install && run_server"]

4
아니 OP는 자신의 사용 사례에서 찾고 있었다 그러나 당신이 좋은 생각 인 빌드 서버를 만드는 경우 어떻게
— 오뎅

2
이것은 문제에 대한 레시피, 특히 기본 호스트 캐시를 가리 키도록 제안하는 것처럼 보입니다. 잠재적으로 아치 관련 패키지를 혼합하고 있습니다.
— Giacomo Lacava

@GiacomoLacava 감사합니다, 아주 좋은 지적입니다. 내 대답을 조정하고 호스트의 캐시 디렉토리 재사용을 제안하는 부분을 제거했습니다.
— Jakub Kukul

24

이 질문에 이미 인기있는 답변이 있음을 이해합니다. 그러나 패키지 관리자를 위해 파일을 캐시하는 새로운 방법이 있습니다. 앞으로 BuildKit이 더 표준이 될 때 좋은 대답이 될 수 있다고 생각합니다.

Docker 18.09부터 BuildKit에 대한 실험적인 지원이 있습니다 . BuildKit은 외부 볼륨 을 RUN단계 로 마운트하기위한 실험적 지원을 포함하여 Dockerfile의 몇 가지 새로운 기능에 대한 지원을 추가 합니다. 이것은 우리가 $HOME/.cache/pip/.

다음 requirements.txt파일을 예로 사용하겠습니다 .

Click==7.0
Django==2.2.3
django-appconf==1.0.3
django-compressor==2.3
django-debug-toolbar==2.0
django-filter==2.2.0
django-reversion==3.0.4
django-rq==2.1.0
pytz==2019.1
rcssmin==1.0.6
redis==3.3.4
rjsmin==1.1.0
rq==1.1.0
six==1.12.0
sqlparse==0.3.0

일반적인 예제 Python Dockerfile은 다음과 같습니다.

FROM python:3.7
WORKDIR /usr/src/app
COPY requirements.txt /usr/src/app/
RUN pip install -r requirements.txt
COPY . /usr/src/app

DOCKER_BUILDKIT환경 변수를 사용하여 BuildKit을 활성화하면 pip약 65 초 안에 캐시되지 않은 단계를 빌드 할 수 있습니다 .

$ export DOCKER_BUILDKIT=1
$ docker build -t test .
[+] Building 65.6s (10/10) FINISHED                                                                                                                                             
 => [internal] load .dockerignore                                                                                                                                          0.0s
 => => transferring context: 2B                                                                                                                                            0.0s
 => [internal] load build definition from Dockerfile                                                                                                                       0.0s
 => => transferring dockerfile: 120B                                                                                                                                       0.0s
 => [internal] load metadata for docker.io/library/python:3.7                                                                                                              0.5s
 => CACHED [1/4] FROM docker.io/library/python:3.7@sha256:6eaf19442c358afc24834a6b17a3728a45c129de7703d8583392a138ecbdb092                                                 0.0s
 => [internal] load build context                                                                                                                                          0.6s
 => => transferring context: 899.99kB                                                                                                                                      0.6s
 => CACHED [internal] helper image for file operations                                                                                                                     0.0s
 => [2/4] COPY requirements.txt /usr/src/app/                                                                                                                              0.5s
 => [3/4] RUN pip install -r requirements.txt                                                                                                                             61.3s
 => [4/4] COPY . /usr/src/app                                                                                                                                              1.3s
 => exporting to image                                                                                                                                                     1.2s
 => => exporting layers                                                                                                                                                    1.2s
 => => writing image sha256:d66a2720e81530029bf1c2cb98fb3aee0cffc2f4ea2aa2a0760a30fb718d7f83                                                                               0.0s
 => => naming to docker.io/library/test                                                                                                                                    0.0s

이제 실험 헤더를 추가 RUN하고 Python 패키지를 캐시하는 단계를 수정 해 보겠습니다 .

# syntax=docker/dockerfile:experimental

FROM python:3.7
WORKDIR /usr/src/app
COPY requirements.txt /usr/src/app/
RUN --mount=type=cache,target=/root/.cache/pip pip install -r requirements.txt
COPY . /usr/src/app

이제 다른 빌드를 수행하십시오. 같은 시간이 걸립니다. 그러나 이번에는 새로운 캐시 마운트에서 Python 패키지를 캐싱합니다.

$ docker build -t pythontest .
[+] Building 60.3s (14/14) FINISHED                                                                                                                                             
 => [internal] load build definition from Dockerfile                                                                                                                       0.0s
 => => transferring dockerfile: 120B                                                                                                                                       0.0s
 => [internal] load .dockerignore                                                                                                                                          0.0s
 => => transferring context: 2B                                                                                                                                            0.0s
 => resolve image config for docker.io/docker/dockerfile:experimental                                                                                                      0.5s
 => CACHED docker-image://docker.io/docker/dockerfile:experimental@sha256:9022e911101f01b2854c7a4b2c77f524b998891941da55208e71c0335e6e82c3                                 0.0s
 => [internal] load .dockerignore                                                                                                                                          0.0s
 => [internal] load build definition from Dockerfile                                                                                                                       0.0s
 => => transferring dockerfile: 120B                                                                                                                                       0.0s
 => [internal] load metadata for docker.io/library/python:3.7                                                                                                              0.5s
 => CACHED [1/4] FROM docker.io/library/python:3.7@sha256:6eaf19442c358afc24834a6b17a3728a45c129de7703d8583392a138ecbdb092                                                 0.0s
 => [internal] load build context                                                                                                                                          0.7s
 => => transferring context: 899.99kB                                                                                                                                      0.6s
 => CACHED [internal] helper image for file operations                                                                                                                     0.0s
 => [2/4] COPY requirements.txt /usr/src/app/                                                                                                                              0.6s
 => [3/4] RUN --mount=type=cache,target=/root/.cache/pip pip install -r requirements.txt                                                                                  53.3s
 => [4/4] COPY . /usr/src/app                                                                                                                                              2.6s
 => exporting to image                                                                                                                                                     1.2s
 => => exporting layers                                                                                                                                                    1.2s
 => => writing image sha256:0b035548712c1c9e1c80d4a86169c5c1f9e94437e124ea09e90aea82f45c2afc                                                                               0.0s
 => => naming to docker.io/library/test                                                                                                                                    0.0s

약 60 초. 첫 번째 빌드와 유사합니다.

requirements.txt캐시를 강제로 무효화하고 다시 실행하려면를 약간 변경 (예 : 두 패키지 사이에 새 줄 추가)합니다.

$ docker build -t pythontest .
[+] Building 15.9s (14/14) FINISHED                                                                                                                                             
 => [internal] load build definition from Dockerfile                                                                                                                       0.0s
 => => transferring dockerfile: 120B                                                                                                                                       0.0s
 => [internal] load .dockerignore                                                                                                                                          0.0s
 => => transferring context: 2B                                                                                                                                            0.0s
 => resolve image config for docker.io/docker/dockerfile:experimental                                                                                                      1.1s
 => CACHED docker-image://docker.io/docker/dockerfile:experimental@sha256:9022e911101f01b2854c7a4b2c77f524b998891941da55208e71c0335e6e82c3                                 0.0s
 => [internal] load build definition from Dockerfile                                                                                                                       0.0s
 => => transferring dockerfile: 120B                                                                                                                                       0.0s
 => [internal] load .dockerignore                                                                                                                                          0.0s
 => [internal] load metadata for docker.io/library/python:3.7                                                                                                              0.5s
 => CACHED [1/4] FROM docker.io/library/python:3.7@sha256:6eaf19442c358afc24834a6b17a3728a45c129de7703d8583392a138ecbdb092                                                 0.0s
 => CACHED [internal] helper image for file operations                                                                                                                     0.0s
 => [internal] load build context                                                                                                                                          0.7s
 => => transferring context: 899.99kB                                                                                                                                      0.7s
 => [2/4] COPY requirements.txt /usr/src/app/                                                                                                                              0.6s
 => [3/4] RUN --mount=type=cache,target=/root/.cache/pip pip install -r requirements.txt                                                                                   8.8s
 => [4/4] COPY . /usr/src/app                                                                                                                                              2.1s
 => exporting to image                                                                                                                                                     1.1s
 => => exporting layers                                                                                                                                                    1.1s
 => => writing image sha256:fc84cd45482a70e8de48bfd6489e5421532c2dd02aaa3e1e49a290a3dfb9df7c                                                                               0.0s
 => => naming to docker.io/library/test                                                                                                                                    0.0s

약 16 초!

더 이상 모든 Python 패키지를 다운로드하지 않기 때문에 속도가 빨라집니다. 패키지 관리자 ( pip이 경우)에 의해 캐시되고 캐시 볼륨 마운트에 저장되었습니다. pip이미 다운로드 한 패키지를 재사용 할 수 있도록 볼륨 마운트가 실행 단계에 제공 됩니다. 이것은 Docker 계층 캐싱 외부에서 발생합니다 .

더 큰 이득은 훨씬 더 나을 것입니다 requirements.txt .

노트:

  • 이것은 실험적인 Dockerfile 구문이며 이와 같이 취급해야합니다. 현재 프로덕션 환경에서 이것을 빌드하고 싶지 않을 수 있습니다.
  • BuildKit 항목은 Docker Compose 또는 현재 Docker API를 직접 사용하는 다른 도구에서는 작동하지 않습니다. 이제 1.25.0부터 Docker Compose에서이를 지원합니다. docker-compose로 BuildKit을 어떻게 활성화합니까?를 참조하십시오 .
  • 현재 캐시를 관리하기위한 직접적인 인터페이스가 없습니다. 를 수행하면 제거됩니다 docker system prune -a.

바라건대, 이러한 기능은 빌드를 위해 Docker로 만들고 BuildKit이 기본값이 될 것입니다. 그럴 경우이 답변을 업데이트하려고합니다.


이 솔루션이 매우 잘 작동하는지 확인할 수 있습니다. 내 빌드가 1 분에서 2.2 초로 단축되었습니다. 감사합니다 @ andy-shinn.
— Kwuite


참고 : SUDO를 사용하여 docker를 실행하는 경우 다음을 수행해야합니다. sudo DOCKER_BUILDKIT = 1 ...
— Vinícius M

이 오류가 발생합니다 :-프론트 엔드 dockerfile.v0으로 해결하지 못했습니다. LLB 정의를 만들지 못했습니다. Dockerfile 구문 분석 오류 줄 10 : 알 수없는 플래그 : 마운트
— Mayur Dangar

상단의 주석을 놓친 것 같 Dockerfile거나 Docker 버전이 너무 오래되었습니다. 모든 디버깅 정보로 새 질문을 만들 것입니다.
— Andy Shinn

-10

더 나은 방법은 Python 사이트 패키지 디렉토리를 볼륨으로 추가하는 것입니다.

services:
    web:
        build: .
        command: python manage.py runserver 0.0.0.0:8000
        volumes:
            - .:/code
            -  /usr/local/lib/python2.7/site-packages/

이렇게하면 전체를 다시 빌드하지 않고도 새 라이브러리를 pip 설치할 수 있습니다.

편집 :이 답변을 무시하고 위 의 jkukul의 답변이 저에게 효과적 이었습니다. 내 의도는 사이트 패키지 폴더 를 캐시하는 것이 었습니다 . 다음과 같이 보일 것입니다.

volumes:
   - .:/code
   - ./cached-packages:/usr/local/lib/python2.7/site-packages/

다운로드 폴더를 캐싱하는 것은 훨씬 더 깨끗합니다. 그것은 또한 바퀴를 캐시하므로 작업을 제대로 수행합니다.


2
그리고이 dockerfile을 다른 컴퓨터에서 빌드하려고하면 어떻게 되나요? 이것은 지속 가능한 솔루션이 아닙니다.
— Aaron McMillin

정말로 혼란스러워서 버그가있는 것으로 판명되었고 실제로 그 이유를 모르겠습니다. 좀 더 자세히 설명해 주시겠습니까?
— jaywhy13

3
Docker 이미지는 호스트 시스템의 상태에 따라 다릅니다. 이것은 docker의 대부분의 유틸리티를 무효화합니다. 이미지에 필요한 모든 것이 설치되어야합니다. Dockerfile을 사용하여 모든 종속성을 설치하십시오. 패키지를 다시 다운로드하지 않으려면 jkukul에서 답변을 빌드 할 때마다 pip 캐시를 마운트하는 것이 좋습니다.
— Aaron McMillin

2
전구가 방금 꺼졌습니다. 실제로 호스트가 아닌 VM에서 사이트 패키지 디렉토리를 마운트하려고했습니다. 상당한 감독. 쫄쿨이 제안한 것과 똑같이하려고했던 것 같네요. 명료하게 해주셔서 감사합니다!
— jaywhy13

@AaronMcMillin 그는 실제로 호스트의 경로에 의존하지 않습니다. 그는 컨테이너의 사이트 패키지를 익명 볼륨에 마운트하고 있습니다. 그래도 여전히 나쁜 생각
— ruohola
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.