Python projeleri için Docker görüntüsü oluştururken paketleri yeniden yüklemekten nasıl kaçınılır?


128

Dockerfile'ım şuna benzer:

FROM my/base

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

ENTRYPOINT ["run_server"]

Her yeni imaj oluşturduğumda, bağımlılıkların yeniden yüklenmesi gerekiyor ve bu benim bölgemde çok yavaş olabilir.

cacheYüklenen paketleri düşündüğüm bir yol , my/basegörüntüyü daha yeni görüntülerle geçersiz kılmaktır :

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

Bu nedenle, bir dahaki sefere bu Dockerfile ile derlediğimde, / base zaten bazı paketlere sahip.

Ancak bu çözümün iki sorunu var:

  1. Bir temel görüntüyü geçersiz kılmak her zaman mümkün değildir
  2. Yeni görüntüler katmanlaştıkça temel görüntü büyür ve büyür

Peki bu sorunu çözmek için daha iyi bir çözüm kullanabilirim?

DÜZENLE##:

Makinemdeki docker hakkında bazı bilgiler:

  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

Görüntünüzü oluşturmayı bitirdikten sonra ara görüntüyü siliyor musunuz?
Regan

Elbette hayır, ama bu alakasız çünkü bir görüntüyü yeniden oluşturduğumda, hala orijinali temel alıyorummy/base
satoru

Yanıtlar:


139

Şuna benzer bir Dockerfile oluşturmaya çalışın:

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.txtadresindeki diğer kod dosyalarının .değiştirilip değiştirilmediğine bakılmaksızın, üzerinde herhangi bir değişiklik yapmadığınız sürece pip kurulumu sırasında önbelleği kullanır . İşte bir örnek.


İşte basit bir Hello, World!program:

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

Docker derlemesinin çıktısı:

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

Değiştirelim run.py:

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

Tekrar oluşturmayı deneyin, çıktı aşağıda verilmiştir:

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

Yukarıda görebileceğiniz gibi, bu sefer docker, derleme sırasında önbellek kullanır. Şimdi güncelleyelim requirements.txt:

# requirements.txt

pytest==2.3.4
ipython

Docker derlemesinin çıktısı aşağıdadır:

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'ın pip kurulumu sırasında önbelleği nasıl kullanmadığına dikkat edin. Çalışmazsa docker sürümünüzü kontrol edin.

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
Bu işe yaramıyor, çünkü docker bir ADDtalimat gördüğünde önbellek geçersiz kılınır.
satoru

1
Neden işe yaramadığından emin değilim. Ancak, gereksinimler.txt'de (<src> üzerinde ADD ./requirements.txt /srv/requirements.txt) herhangi bir değişiklik olmaz , bu durumda docker önbelleği kullanmalıdır. Dockerfile belgesine bölüm ekleme konusuna bakın .
nacyot

16
Evet, gereksinimler.txt değişmezse önbelleği kullanır. Ancak gereksinimler.txt değişirse, tüm gereksinimler indirilir. Önbellekten yüklemek için docker konteynerine bir pip önbellek birimi eklememin bir yolu var mı?
Jitu

7
Bu cevabın anahtarı, gereksinim.txt eklemenizdir ( ADD requirements.txt /srvpip ( RUN pip install -r requirements.txt) 'i çalıştırmadan önce ve diğer tüm dosyaları pip çalıştırdıktan sonra eklemenizdir . Bu nedenle, şu sırayla olmaları gerekir: (1) ADD requirements.txt /srv; (2) RUN pip install -r requirements.txt; ( 3)ADD . /srv
engelen

2
Lütfen bunun EKLE yerine KOPYALA kullanıldığında işe yaramayacağını unutmayın
2017

29

Ağ etkinliğini en aza indirmek için pip, ana makinenizdeki bir önbellek dizinine işaret edebilirsiniz.

Docker konteynerinizi, barındırıcınızın pip önbellek dizini bağlayıcınızın pip önbellek dizinine bağlı olarak çalıştırın. docker runkomut şöyle görünmelidir:

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

Ardından Dockerfile'ınızda gereksinimlerinizi bir komut yerine ENTRYPOINTifadenin (veya CMDifadenin) bir parçası olarak kurun RUN. Bu önemlidir, çünkü (yorumlarda belirtildiği gibi) görüntü oluşturma sırasında ( RUNifadeler yürütüldüğünde) bağlama kullanılamaz . Docker dosyası şöyle görünmelidir:

FROM my/base

ADD . /srv

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

4
OP'nin kullanım durumunda aradığı şey değil, ancak bir yapı sunucusu yapıyorsanız bu harika bir fikir
oden

2
Bu, sorunlar için bir reçete gibi görünüyor, özellikle de varsayılan ana bilgisayar önbelleğine işaret etme önerisi. Potansiyel olarak arşive özgü paketleri karıştırıyorsunuz.
Giacomo Lacava

@GiacomoLacava teşekkürler, bu çok iyi bir nokta. Cevabımı ayarladım ve ana bilgisayarın önbellek dizinini yeniden kullanmayı öneren kısmı kaldırdım.
Jakub Kukul

24

Bu sorunun zaten bazı popüler cevapları olduğunu anlıyorum. Ancak, paket yöneticileri için dosyaları önbelleğe almanın daha yeni bir yolu var. Bence, gelecekte BuildKit daha standart hale geldiğinde iyi bir cevap olabilir.

Docker 18.09'dan itibaren BuildKit için deneysel destek vardır . BuildKit dahil Dockerfile bazı yeni özellikler için destek ekler dış hacimleri monte etmek için deneysel destek içine RUNadımlar. Bu, gibi şeyler için önbellek oluşturmamızı sağlar $HOME/.cache/pip/.

Aşağıdaki requirements.txtdosyayı örnek olarak kullanacağız :

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

Tipik bir Python örneği Dockerfileşöyle görünebilir:

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

BuildKit DOCKER_BUILDKITortam değişkenini kullanarak etkinleştirildiğinde , önbelleğe alınmamış pipadımı yaklaşık 65 saniyede oluşturabiliriz:

$ 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

Şimdi deneysel başlığı ekleyelim RUNve Python paketlerini önbelleğe alma adımını değiştirelim :

# 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

Devam edin ve şimdi başka bir yapı yapın. Aynı miktarda zaman almalı. Ancak bu sefer Python paketlerini yeni önbellek bağlantımızda önbelleğe alıyor:

$ 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

Yaklaşık 60 saniye. İlk yapımıza benzer.

requirements.txtÖnbellek geçersiz kılmaya zorlamak ve tekrar çalıştırmak için (iki paket arasına yeni bir satır eklemek gibi) üzerinde küçük bir değişiklik yapın :

$ 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

Sadece yaklaşık 16 saniye!

Bu hızlanmayı alıyoruz çünkü artık tüm Python paketlerini indirmiyoruz. Paket yöneticisi tarafından önbelleğe alındı ​​( pipbu durumda) ve bir önbellek birimi yuvasında saklandılar. Birim bağlama, pipzaten indirilmiş paketlerimizi yeniden kullanabilmek için çalıştırma adımına sağlanır . Bu, herhangi bir Docker katmanı önbelleğe alma işleminin dışında gerçekleşir .

Kazançlar daha büyük olduğunda çok daha iyi olmalı requirements.txt.

Notlar:

  • Bu deneysel Dockerfile sözdizimidir ve bu şekilde ele alınmalıdır. Şu anda üretimde bununla inşa etmek istemeyebilirsiniz.
  • BuildKit öğeleri, şu anda Docker Compose veya Docker API'yi doğrudan kullanan diğer araçlar altında çalışmıyor. 1.25.0'dan itibaren Docker Compose'da artık bunun için destek var. Bkz Eğer liman işçisi-oluşturmadeneyimine ile BuildKit nasıl etkinleştirebilirim?
  • Şu anda önbelleği yönetmek için herhangi bir doğrudan arayüz bulunmamaktadır. Yaptığınızda temizlenir a docker system prune -a.

Umarım, bu özellikler onu oluşturmak için Docker'a dönüştürür ve BuildKit varsayılan olur. Bu olursa / olduğunda bu cevabı güncellemeye çalışacağım.


Bu çözümün çok iyi çalıştığını doğrulayabilirim. Yapım bir dakikadan fazla bir süredir sadece 2,2 saniyeye düştü. @ Andy-shinn'e teşekkürler.
Kwuite


Not: Docker'ı çalıştırmak için SUDO kullanıyorsanız, muhtemelen yapmanız gereken: sudo DOCKER_BUILDKIT = 1 ...
Vinícius M

Bu Hatayı alıyorum: - ön uç dockerfile.v0 ile çözülemedi: LLB tanımı oluşturulamadı: Dockerfile ayrıştırma hatası satırı 10: Bilinmeyen bayrak: bağlama
Mayur Dangar

Bu Dockerfile, Docker sürümünün üst kısmındaki yorumu kaçırmışsınız gibi geliyor veya Docker sürümü çok eski. Tüm hata ayıklama bilgilerinizle yeni bir soru oluştururum.
Andy Shinn

-10

Python site paketleri dizinini bir birim olarak eklemenin daha iyi bir yolu olduğunu buldum.

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

Bu şekilde, tam bir yeniden oluşturma yapmadan yeni kitaplıkları kurabilirim.

DÜZENLEME : Bu cevabı göz ardı edin, jkukul'un yukarıdaki cevabı benim için çalıştı. Niyetim site paketleri klasörünü önbelleğe almaktı. Bu daha çok şuna benzerdi:

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

İndirme klasörünü önbelleğe almak çok daha temiz. Bu aynı zamanda tekerlekleri önbelleğe alır, böylece görevi düzgün bir şekilde yerine getirir.


2
Ve bu dockerfile'ı farklı bir makinede oluşturmaya çalıştığınızda ne olur? Bu sürdürülebilir bir çözüm değil.
Aaron McMillin

Gerçekten kafam karıştı, bu hatalıydı ve aslında neden olduğundan emin değildim. Biraz daha ayrıntı verebilir misin?
jaywhy13

3
Docker görüntünüz, ana bilgisayar sistemindeki duruma bağlıdır. Bu, docker uygulamasının çoğunu geçersiz kılar. Görüntünün ihtiyaç duyduğu her şey içine yüklenmelidir. tüm bağımlılıkları yüklemek için Dockerfile'ı kullanın. Jkukul'dan pip önbelleğini takmak için yanıtı her oluşturduğunuzda paketleri yeniden indirmekten kaçınmak istiyorsanız, gitmenin yolu budur.
Aaron McMillin

2
Ampul az önce patladı teşekkürler. Aslında site paketleri dizinini ana bilgisayardan değil, sanal makineden bağlamaya çalışıyordum. Oldukça gözetim. Sanırım ruhen Jkulkul'un önerdiği şeyi yapmaya çalışıyordum. Netlik için teşekkürler!
jaywhy13

@AaronMcMillin Aslında ana bilgisayardaki bir yola bağlı değil. Konteynere site paketlerini anonim bir hacme yerleştiriyor. Yine de kötü bir fikir
ruohola
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.