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.