Làm thế nào để tránh cài đặt lại các gói khi xây dựng hình ảnh Docker cho các dự án Python?


128

Dockerfile của tôi giống như

FROM my/base

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

ENTRYPOINT ["run_server"]

Mỗi khi tôi xây dựng một hình ảnh mới, các phần phụ thuộc phải được cài đặt lại, điều này có thể rất chậm trong khu vực của tôi.

Một cách tôi nghĩ đến đối với cachecác gói đã được cài đặt là ghi đè my/basehình ảnh bằng các hình ảnh mới hơn như sau:

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

Vì vậy, lần tới khi tôi xây dựng với Dockerfile này, / base của tôi đã được cài đặt một số gói.

Nhưng giải pháp này có hai vấn đề:

  1. Không phải lúc nào cũng có thể ghi đè hình ảnh cơ sở
  2. Hình ảnh cơ sở ngày càng lớn hơn khi các hình ảnh mới hơn được xếp lớp trên đó

Vì vậy, giải pháp tốt hơn tôi có thể sử dụng để giải quyết vấn đề này?

BIÊN TẬP##:

Một số thông tin về docker trên máy của tôi:

  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

Bạn có xóa hình ảnh trung gian sau khi xây dựng xong hình ảnh của mình không?
Regan

Tất nhiên là không, nhưng điều này là không thích hợp vì khi tôi xây dựng lại một hình ảnh, tôi vẫn dựa trên bản gốcmy/base
Satoru

Câu trả lời:


139

Cố gắng xây dựng một Dockerfile trông giống như sau:

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 sẽ sử dụng bộ nhớ cache trong quá trình cài đặt pip miễn là bạn không thực hiện bất kỳ thay đổi nào đối với requirements.txt, bất kể thực tế là các tệp mã khác tại .có bị thay đổi hay không. Đây là một ví dụ.


Đây là một Hello, World!chương trình đơn giản :

$ 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")

Đầu ra của bản dựng docker:

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

Hãy sửa đổi run.py:

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

Hãy thử xây dựng lại, dưới đây là kết quả:

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

Như bạn có thể thấy ở trên, docker thời gian này sử dụng bộ nhớ đệm trong quá trình xây dựng. Bây giờ, hãy cập nhật requirements.txt:

# requirements.txt

pytest==2.3.4
ipython

Dưới đây là đầu ra của bản dựng docker:

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

Lưu ý cách docker không sử dụng bộ nhớ cache trong khi cài đặt pip. Nếu nó không hoạt động, hãy kiểm tra phiên bản docker của bạn.

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
Điều này dường như không hoạt động, bởi vì bất cứ khi nào docker nhìn thấy một ADDlệnh, bộ nhớ cache sẽ bị vô hiệu.
satoru

1
Tôi không chắc tại sao nó không hoạt động. Nhưng không có bất kỳ thay đổi nào đối với tests.txt (<src> on ADD ./requirements.txt /srv/requirements.txt), khi đó docker phải sử dụng bộ nhớ cache. Xem thêm seciton trên tài liệu Dockerfile.
nacyot

16
Có, nó sẽ sử dụng bộ nhớ đệm nếu tệp tests.txt không thay đổi. Nhưng nếu tệp tin tests.txt thay đổi thì tất cả các yêu cầu sẽ được tải xuống. Có cách nào để tôi có thể gắn khối lượng bộ đệm pip vào bộ chứa docker để tải từ bộ đệm không?
Jitu

7
Chìa khóa cho câu trả lời này là bạn thêm tệp tin request.txt ( ADD requirements.txt /srvtrước khi chạy pip ( RUN pip install -r requirements.txt) và thêm tất cả các tệp khác sau khi chạy pip. Do đó, chúng phải theo thứ tự sau: (1) ADD requirements.txt /srv; (2) RUN pip install -r requirements.txt; ( 3)ADD . /srv
engelen

2
Hãy lưu ý rằng điều này không làm việc khi sử dụng COPY thay vì Thanh
veuncent

29

Để giảm thiểu hoạt động mạng, bạn có thể chỉ pip đến thư mục bộ nhớ cache trên máy chủ của mình.

Chạy vùng chứa docker của bạn với liên kết thư mục bộ nhớ cache pip của máy chủ lưu trữ được gắn vào thư mục bộ nhớ cache pip của vùng chứa của bạn. docker runlệnh sẽ giống như sau:

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

Sau đó, trong Dockerfile của bạn, hãy cài đặt các yêu cầu của bạn như một phần của ENTRYPOINTcâu lệnh (hoặc CMDcâu lệnh) thay vì dưới dạng RUNlệnh. Điều này rất quan trọng, bởi vì (như đã chỉ ra trong nhận xét), mount không có sẵn trong quá trình xây dựng hình ảnh (khi các RUNcâu lệnh được thực thi). Tệp Docker sẽ trông như thế này:

FROM my/base

ADD . /srv

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

4
Không phải những gì OP đang tìm kiếm trong trường hợp sử dụng của mình nhưng nếu bạn thực hiện một máy chủ build đây là một ý tưởng tuyệt vời
Oden

2
Đây có vẻ như là một công thức cho các vấn đề, đặc biệt là gợi ý trỏ đến bộ nhớ cache của máy chủ mặc định. Bạn có khả năng trộn các gói cụ thể cho vòm.
Giacomo Lacava

@GiacomoLacava cảm ơn, đó là một điểm rất tốt. Tôi đã điều chỉnh câu trả lời của mình và xóa phần đề xuất sử dụng lại thư mục bộ nhớ cache của máy chủ.
Jakub Kukul

24

Tôi hiểu câu hỏi này đã có một số câu trả lời phổ biến. Nhưng có một cách mới hơn để lưu tệp vào bộ nhớ cache cho trình quản lý gói. Tôi nghĩ rằng nó có thể là một câu trả lời tốt trong tương lai khi BuildKit trở nên tiêu chuẩn hơn.

Kể từ Docker 18.09 có hỗ trợ thử nghiệm cho BuildKit . BuildKit bổ sung hỗ trợ cho một số tính năng mới trong Dockerfile bao gồm hỗ trợ thử nghiệm để gắn khối lượng bên ngoài vào RUNcác bước. Điều này cho phép chúng tôi tạo bộ nhớ đệm cho những thứ như $HOME/.cache/pip/.

Chúng tôi sẽ sử dụng requirements.txttệp sau làm ví dụ:

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

Một ví dụ điển hình của Python Dockerfilecó thể giống như sau:

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

Với BuildKit được bật bằng cách sử dụng DOCKER_BUILDKITbiến môi trường, chúng ta có thể tạo pipbước chưa được lưu trong khoảng 65 giây:

$ 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

Bây giờ, chúng ta hãy thêm tiêu đề thử nghiệm và sửa đổi RUNbước để lưu vào bộ nhớ cache các gói 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

Hãy tiếp tục và thực hiện một bản dựng khác ngay bây giờ. Nó sẽ mất cùng một khoảng thời gian. Nhưng lần này nó đang lưu vào bộ nhớ đệm các gói Python trong mount bộ đệm mới của chúng tôi:

$ 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

Khoảng 60 giây. Tương tự như bản dựng đầu tiên của chúng tôi.

Thực hiện một thay đổi nhỏ đối với requirements.txt(chẳng hạn như thêm một dòng mới giữa hai gói) để buộc vô hiệu bộ nhớ cache và chạy lại:

$ 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

Chỉ khoảng 16 giây!

Chúng tôi đang nhận được tốc độ này vì chúng tôi không còn tải xuống tất cả các gói Python nữa. Chúng đã được trình quản lý gói piplưu vào bộ nhớ cache ( trong trường hợp này) và được lưu trữ trong ổ đĩa gắn kết bộ nhớ cache. Khối lượng gắn kết được cung cấp cho bước chạy để pipcó thể sử dụng lại các gói đã tải xuống của chúng tôi. Điều này xảy ra bên ngoài bất kỳ bộ nhớ đệm lớp Docker nào .

Lợi nhuận sẽ tốt hơn nhiều khi lớn hơn requirements.txt .

Ghi chú:

  • Đây là cú pháp Dockerfile thử nghiệm và nên được xử lý như vậy. Bạn có thể không muốn xây dựng với điều này trong sản xuất tại thời điểm này.
  • Nội dung BuildKit không hoạt động trong Docker Compose hoặc các công cụ khác sử dụng trực tiếp API Docker vào lúc này. Hiện đã có hỗ trợ cho việc này trong Docker Compose kể từ 1.25.0. Xem Làm cách nào để kích hoạt BuildKit với docker-soạn?
  • Không có bất kỳ giao diện trực tiếp nào để quản lý bộ nhớ cache vào lúc này. Nó được thanh lọc khi bạn làm a docker system prune -a.

Hy vọng rằng những tính năng này sẽ được đưa vào Docker để xây dựng và BuildKit sẽ trở thành mặc định. Nếu / khi điều đó xảy ra, tôi sẽ cố gắng cập nhật câu trả lời này.


Tôi có thể xác nhận giải pháp này hoạt động rất tốt. Quá trình xây dựng của tôi đã giảm từ hơn một phút xuống chỉ còn 2,2 giây. Cảm ơn @ andy-shinn.
Kwuite

2
Bây giờ cũng Docker-Compose: stackoverflow.com/questions/58592259/…
Rexcirus

Lưu ý: Nếu bạn đang sử dụng SUDO để chạy docker, có thể bạn cần thực hiện: sudo DOCKER_BUILDKIT = 1 ...
Vinícius M

Tôi gặp lỗi này: - không giải quyết được với dockerfile.v0 phía trước: không thể tạo định nghĩa LLB: Lỗi phân tích cú pháp Dockerfile dòng 10: Cờ không xác định: mount
Mayur Dangar

Có vẻ như bạn đã bỏ lỡ nhận xét ở đầu Dockerfilehoặc phiên bản Docker quá cũ. Tôi sẽ tạo một câu hỏi mới với tất cả thông tin gỡ lỗi của bạn.
Andy Shinn

-10

Tôi thấy rằng một cách tốt hơn là chỉ cần thêm thư mục gói trang web Python dưới dạng một ổ đĩa.

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

Bằng cách này, tôi có thể cài đặt thư viện mới mà không cần phải xây dựng lại toàn bộ.

CHỈNH SỬA : Bỏ qua câu trả lời này, câu trả lời của jkukul ở trên phù hợp với tôi. Mục đích của tôi là lưu vào bộ nhớ cache thư mục gói trang . Điều đó sẽ trông giống như sau:

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

Tuy nhiên, bộ đệm ẩn thư mục tải xuống đã sạch hơn rất nhiều. Điều đó cũng lưu trữ các bánh xe, vì vậy nó hoàn thành tốt nhiệm vụ.


2
Và điều gì sẽ xảy ra khi bạn cố gắng xây dựng tệp dockerfile này trên một máy khác. Đây không phải là một giải pháp bền vững.
Aaron McMillin

Thực sự bối rối, điều này hóa ra có lỗi và tôi thực sự không chắc tại sao. Bạn có thể cung cấp thêm một số chi tiết.
jaywhy 13

3
Hình ảnh docker của bạn tùy thuộc vào trạng thái từ hệ thống máy chủ. Điều này làm mất đi hầu hết các tiện ích của docker. Mọi thứ hình ảnh cần phải được cài đặt trong đó. sử dụng Dockerfile để cài đặt tất cả các phụ thuộc. Nếu bạn muốn tránh tải lại các gói mỗi khi bạn xây dựng câu trả lời từ jkukul để gắn bộ đệm pip là cách tốt nhất.
Aaron McMillin

2
Bóng đèn vừa tắt, cảm ơn. Tôi thực sự đang cố gắn thư mục gói trang từ máy ảo, không phải máy chủ. Khá là giám sát. Tôi nghĩ về tinh thần, tôi đã cố gắng làm giống như jkulkul đề nghị. Cảm ơn vì sự rõ ràng!
jaywhy 13

@AaronMcMillin Anh ấy thực sự không phụ thuộc vào đường dẫn trên máy chủ. Anh ta đang gắn các gói trang web trong vùng chứa vào một ổ đĩa ẩn danh. Tuy nhiên, vẫn là một ý tưởng tồi
ruohola
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.