如何减少针对Python应用程序的此多阶段Docker构建的大小

时间:2018-08-03 19:14:55

标签: python docker

如何避免在此处使用/root/.cache?不确定我是否了解多阶段构建的工作原理。

# ---- Base python ----
FROM python:2.7-alpine3.7 AS base
# Create app directory
WORKDIR /app

# ---- Dependencies ----
FROM base AS dependencies
COPY requirements.txt ./
RUN apk add --no-cache libgcc git build-base  && \
    pip install -r requirements.txt && \
    apk del git build-base binutils-libs binutils gmp isl libgomp libatomic pkgconf libgcc && \
    apk del mpfr3 mpc1 libstdc++ gcc musl-dev libc-dev g++ make fortify-headers ca-certificates libssh2 && \
    apk del libcurl expat pcre2 git && \
    pip uninstall -y mock PyMySQL coverage pytest pytest-cov pytest-xdist wheel setuptools && \
    rm -rf /usr/share/locale/* && \
    rm -rf /var/cache/debconf/*-old && \
    rm -rf /var/lib/apt/lists/* /var/lib/dpkg   && \
    rm -rf /usr/share/doc/* && \
    rm -rf /usr/share/man/?? && \
    rm -rf /usr/share/man/??_*

# ---- Copy Files/Build ----
FROM dependencies AS build
WORKDIR /app
COPY . /app
COPY --from=dependencies /root/.cache /root/.cache
WORKDIR /app

也许我应该使用virtualenv?

1 个答案:

答案 0 :(得分:2)

Docker多阶段构建通过创建多个阶段来进行工作,目的是使最终映像具有最少所需的组件

在您的情况下,您将重新使用初始构建阶段作为最终映像

FROM python:2.7-alpine3.7 AS base
..
FROM base AS dependencies
..
FROM dependencies AS build

但是,您还将删除在依赖项阶段安装的不需要的软件包,从而消除了多阶段映像的优势,因此看不到任何可见的大小变化。

为了进行演示,由于您尚未发布requirements.txt文件,因此我假设它将安装flask

λ cat requirements.txt
flask==1.0.2

考虑您的Dockerfile,但是删除删除软件包的命令

# ---- Base python ----
FROM python:2.7-alpine3.7 AS base
# Create app directory
WORKDIR /app

# ---- Dependencies ----
FROM base AS dependencies
COPY requirements.txt ./
RUN apk add --no-cache libgcc git build-base  && \
    pip install -r requirements.txt

# ---- Copy Files/Build ----
FROM dependencies AS build
WORKDIR /app
COPY . /app
COPY --from=dependencies /root/.cache /root/.cache
WORKDIR /app

构建图像:

docker build -t so:51678830 .
Sending build context to Docker daemon  3.072kB
Step 1/10 : FROM python:2.7-alpine3.7 AS base
 ---> b1d3c201a89a
Step 2/10 : WORKDIR /app
 ---> Using cache
 ---> 675f27f73cf9
Step 3/10 : FROM base AS dependencies
 ---> 675f27f73cf9
Step 4/10 : COPY requirements.txt ./
 ---> Using cache
 ---> 288a87ba0ecf
Step 5/10 : RUN apk add --no-cache libgcc git build-base  &&         pip install -r requirements.txt
 ---> Running in 6520cfe93603
fetch http://dl-cdn.alpinelinux.org/alpine/v3.7/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.7/community/x86_64/APKINDEX.tar.gz
(1/23) Installing binutils-libs (2.30-r1)
(2/23) Installing binutils (2.30-r1)
(3/23) Installing gmp (6.1.2-r1)
(4/23) Installing isl (0.18-r0)
(5/23) Installing libgomp (6.4.0-r5)
(6/23) Installing libatomic (6.4.0-r5)
(7/23) Installing pkgconf (1.3.10-r0)
(8/23) Installing libgcc (6.4.0-r5)
(9/23) Installing mpfr3 (3.1.5-r1)
(10/23) Installing mpc1 (1.0.3-r1)
(11/23) Installing libstdc++ (6.4.0-r5)
(12/23) Installing gcc (6.4.0-r5)
(13/23) Installing musl-dev (1.1.18-r3)
(14/23) Installing libc-dev (0.7.1-r0)
(15/23) Installing g++ (6.4.0-r5)
(16/23) Installing make (4.2.1-r0)
(17/23) Installing fortify-headers (0.9-r0)
(18/23) Installing build-base (0.5-r0)
(19/23) Installing libssh2 (1.8.0-r2)
(20/23) Installing libcurl (7.61.0-r0)
(21/23) Installing expat (2.2.5-r0)
(22/23) Installing pcre2 (10.30-r0)
(23/23) Installing git (2.15.2-r0)
Executing busybox-1.27.2-r11.trigger
OK: 186 MiB in 53 packages
Collecting flask==1.0.2 (from -r requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/7f/e7/08578774ed4536d3242b14dacb4696386634607af824ea997202cd0edb4b/Flask-1.0.2-py2.py3-none-any.whl (91kB)
Collecting Werkzeug>=0.14 (from flask==1.0.2->-r requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/20/c4/12e3e56473e52375aa29c4764e70d1b8f3efa6682bef8d0aae04fe335243/Werkzeug-0.14.1-py2.py3-none-any.whl (322kB)
Collecting click>=5.1 (from flask==1.0.2->-r requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/34/c1/8806f99713ddb993c5366c362b2f908f18269f8d792aff1abfd700775a77/click-6.7-py2.py3-none-any.whl (71kB)
Collecting itsdangerous>=0.24 (from flask==1.0.2->-r requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/dc/b4/a60bcdba945c00f6d608d8975131ab3f25b22f2bcfe1dab221165194b2d4/itsdangerous-0.24.tar.gz (46kB)
Collecting Jinja2>=2.10 (from flask==1.0.2->-r requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/7f/ff/ae64bacdfc95f27a016a7bed8e8686763ba4d277a78ca76f32659220a731/Jinja2-2.10-py2.py3-none-any.whl (126kB)
Collecting MarkupSafe>=0.23 (from Jinja2>=2.10->flask==1.0.2->-r requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/4d/de/32d741db316d8fdb7680822dd37001ef7a448255de9699ab4bfcbdf4172b/MarkupSafe-1.0.tar.gz
Building wheels for collected packages: itsdangerous, MarkupSafe
  Running setup.py bdist_wheel for itsdangerous: started
  Running setup.py bdist_wheel for itsdangerous: finished with status 'done'
  Stored in directory: /root/.cache/pip/wheels/2c/4a/61/5599631c1554768c6290b08c02c72d7317910374ca602ff1e5
  Running setup.py bdist_wheel for MarkupSafe: started
  Running setup.py bdist_wheel for MarkupSafe: finished with status 'done'
  Stored in directory: /root/.cache/pip/wheels/33/56/20/ebe49a5c612fffe1c5a632146b16596f9e64676768661e4e46
Successfully built itsdangerous MarkupSafe
Installing collected packages: Werkzeug, click, itsdangerous, MarkupSafe, Jinja2, flask
Successfully installed Jinja2-2.10 MarkupSafe-1.0 Werkzeug-0.14.1 click-6.7 flask-1.0.2 itsdangerous-0.24
Removing intermediate container 6520cfe93603
 ---> 6d73b486a703
Step 6/10 : FROM dependencies AS build
 ---> 6d73b486a703
Step 7/10 : WORKDIR /app
 ---> Running in 0ed8fa7f13e7
Removing intermediate container 0ed8fa7f13e7
 ---> e61cf72b31ae
Step 8/10 : COPY . /app
 ---> f6c7a4d3d8df
Step 9/10 : COPY --from=dependencies /root/.cache /root/.cache
 ---> 2c15e7080c91
Step 10/10 : WORKDIR /app
 ---> Running in 717bd7cf7b1d
Removing intermediate container 717bd7cf7b1d
 ---> cfe7ee0c5880
Successfully built cfe7ee0c5880
Successfully tagged so:51678830

检查图像的大小:

docker images

| REPOSITORY                    TAG                 IMAGE ID            CREATED              SIZE  |
|--------------------------------------------------------------------------------------------------|
| so                            51678830            4477dac77289        About a minute ago   234MB |

让我们修改Dockerfile,使其使用“基本”作为最终映像,但从依赖项通过缓存的pip wheel复制

# ---- Base python ----
FROM python:2.7-alpine3.7 AS base
# Create app directory
WORKDIR /app

# ---- Dependencies ----
FROM base AS dependencies
COPY requirements.txt ./
RUN apk add --no-cache libgcc git build-base  && \
    pip install -r requirements.txt

# ---- Copy Files/Build ----
FROM base
WORKDIR /app
COPY . /app
COPY --from=dependencies /root/.cache /root/.cache
WORKDIR /app

构建图像

docker build -t so:51678830 .
Sending build context to Docker daemon  3.072kB
Step 1/10 : FROM python:2.7-alpine3.7 AS base
 ---> b1d3c201a89a
Step 2/10 : WORKDIR /app
 ---> Running in aca871ed01e3
Removing intermediate container aca871ed01e3
 ---> 89d357832427
Step 3/10 : FROM base AS dependencies
 ---> 89d357832427
Step 4/10 : COPY requirements.txt ./
 ---> 6ecbfe862e27
Step 5/10 : RUN apk add --no-cache libgcc git build-base  &&         pip install -r requirements.txt
 ---> Running in 465256e75563
fetch http://dl-cdn.alpinelinux.org/alpine/v3.7/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.7/community/x86_64/APKINDEX.tar.gz
(1/23) Installing binutils-libs (2.30-r1)
(2/23) Installing binutils (2.30-r1)
(3/23) Installing gmp (6.1.2-r1)
(4/23) Installing isl (0.18-r0)
(5/23) Installing libgomp (6.4.0-r5)
(6/23) Installing libatomic (6.4.0-r5)
(7/23) Installing pkgconf (1.3.10-r0)
(8/23) Installing libgcc (6.4.0-r5)
(9/23) Installing mpfr3 (3.1.5-r1)
(10/23) Installing mpc1 (1.0.3-r1)
(11/23) Installing libstdc++ (6.4.0-r5)
(12/23) Installing gcc (6.4.0-r5)
(13/23) Installing musl-dev (1.1.18-r3)
(14/23) Installing libc-dev (0.7.1-r0)
(15/23) Installing g++ (6.4.0-r5)
(16/23) Installing make (4.2.1-r0)
(17/23) Installing fortify-headers (0.9-r0)
(18/23) Installing build-base (0.5-r0)
(19/23) Installing libssh2 (1.8.0-r2)
(20/23) Installing libcurl (7.61.0-r0)
(21/23) Installing expat (2.2.5-r0)
(22/23) Installing pcre2 (10.30-r0)
(23/23) Installing git (2.15.2-r0)
Executing busybox-1.27.2-r11.trigger
OK: 186 MiB in 53 packages
Collecting flask==1.0.2 (from -r requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/7f/e7/08578774ed4536d3242b14dacb4696386634607af824ea997202cd0edb4b/Flask-1.0.2-py2.py3-none-any.whl (91kB)
Collecting Werkzeug>=0.14 (from flask==1.0.2->-r requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/20/c4/12e3e56473e52375aa29c4764e70d1b8f3efa6682bef8d0aae04fe335243/Werkzeug-0.14.1-py2.py3-none-any.whl (322kB)
Collecting click>=5.1 (from flask==1.0.2->-r requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/34/c1/8806f99713ddb993c5366c362b2f908f18269f8d792aff1abfd700775a77/click-6.7-py2.py3-none-any.whl (71kB)
Collecting itsdangerous>=0.24 (from flask==1.0.2->-r requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/dc/b4/a60bcdba945c00f6d608d8975131ab3f25b22f2bcfe1dab221165194b2d4/itsdangerous-0.24.tar.gz (46kB)
Collecting Jinja2>=2.10 (from flask==1.0.2->-r requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/7f/ff/ae64bacdfc95f27a016a7bed8e8686763ba4d277a78ca76f32659220a731/Jinja2-2.10-py2.py3-none-any.whl (126kB)
Collecting MarkupSafe>=0.23 (from Jinja2>=2.10->flask==1.0.2->-r requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/4d/de/32d741db316d8fdb7680822dd37001ef7a448255de9699ab4bfcbdf4172b/MarkupSafe-1.0.tar.gz
Building wheels for collected packages: itsdangerous, MarkupSafe
  Running setup.py bdist_wheel for itsdangerous: started
  Running setup.py bdist_wheel for itsdangerous: finished with status 'done'
  Stored in directory: /root/.cache/pip/wheels/2c/4a/61/5599631c1554768c6290b08c02c72d7317910374ca602ff1e5
  Running setup.py bdist_wheel for MarkupSafe: started
  Running setup.py bdist_wheel for MarkupSafe: finished with status 'done'
  Stored in directory: /root/.cache/pip/wheels/33/56/20/ebe49a5c612fffe1c5a632146b16596f9e64676768661e4e46
Successfully built itsdangerous MarkupSafe
Installing collected packages: Werkzeug, click, itsdangerous, MarkupSafe, Jinja2, flask
Successfully installed Jinja2-2.10 MarkupSafe-1.0 Werkzeug-0.14.1 click-6.7 flask-1.0.2 itsdangerous-0.24
Removing intermediate container 465256e75563
 ---> d9a23965de75
Step 6/10 : FROM base
 ---> 89d357832427
Step 7/10 : WORKDIR /app
 ---> Running in bb975f43890d
Removing intermediate container bb975f43890d
 ---> ba0d78774039
Step 8/10 : COPY . /app
 ---> 3510860b3538
Step 9/10 : COPY --from=dependencies /root/.cache /root/.cache
 ---> 812c65bd156b
Step 10/10 : WORKDIR /app
 ---> Running in 4191ef84468e
Removing intermediate container 4191ef84468e
 ---> 236d8ee0bea4
Successfully built 236d8ee0bea4
Successfully tagged so:51678830

有了这个,我们得到的图像大小为60.5MB

码头工人图像

| REPOSITORY                    TAG                 IMAGE ID            CREATED              SIZE   |
|---------------------------------------------------------------------------------------------------|
| so                            51678830            e53282dc4fc7        2 seconds ago        60.5MB |

但是,我们仅将缓存文件复制到了最终映像上,我们仍然需要安装它,然后才能删除/root/.cache。 Dockerfile现在将如下所示:

# ---- Base python ----
FROM python:2.7-alpine3.7 AS base
# Create app directory
WORKDIR /app

# ---- Dependencies ----
FROM base AS dependencies
COPY requirements.txt ./
RUN apk add --no-cache libgcc git build-base  && \
    pip install -r requirements.txt

# ---- Copy Files/Build ----
FROM base
WORKDIR /app
COPY . /app
COPY --from=dependencies /root/.cache /root/.cache
COPY requirements.txt ./
RUN pip install -r requirements.txt && rm -rf /root/.cache

构建图像:

docker build -t so:51678830 .


Sending build context to Docker daemon  3.584kB
Step 1/11 : FROM python:2.7-alpine3.7 AS base
 ---> b1d3c201a89a
Step 2/11 : WORKDIR /app
 ---> Running in 8471e5fe8fac
Removing intermediate container 8471e5fe8fac
 ---> 646de3787bbc
Step 3/11 : FROM base AS dependencies
 ---> 646de3787bbc
Step 4/11 : COPY requirements.txt ./
 ---> 4b6c6690ddf7
Step 5/11 : RUN apk add --no-cache libgcc git build-base  &&         pip install -r requirements.txt
 ---> Running in aaa83a183ead
fetch http://dl-cdn.alpinelinux.org/alpine/v3.7/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.7/community/x86_64/APKINDEX.tar.gz
(1/23) Installing binutils-libs (2.30-r1)
(2/23) Installing binutils (2.30-r1)
(3/23) Installing gmp (6.1.2-r1)
(4/23) Installing isl (0.18-r0)
(5/23) Installing libgomp (6.4.0-r5)
(6/23) Installing libatomic (6.4.0-r5)
(7/23) Installing pkgconf (1.3.10-r0)
(8/23) Installing libgcc (6.4.0-r5)
(9/23) Installing mpfr3 (3.1.5-r1)
(10/23) Installing mpc1 (1.0.3-r1)
(11/23) Installing libstdc++ (6.4.0-r5)
(12/23) Installing gcc (6.4.0-r5)
(13/23) Installing musl-dev (1.1.18-r3)
(14/23) Installing libc-dev (0.7.1-r0)
(15/23) Installing g++ (6.4.0-r5)
(16/23) Installing make (4.2.1-r0)
(17/23) Installing fortify-headers (0.9-r0)
(18/23) Installing build-base (0.5-r0)
(19/23) Installing libssh2 (1.8.0-r2)
(20/23) Installing libcurl (7.61.0-r0)
(21/23) Installing expat (2.2.5-r0)
(22/23) Installing pcre2 (10.30-r0)
(23/23) Installing git (2.15.2-r0)
Executing busybox-1.27.2-r11.trigger
OK: 186 MiB in 53 packages
Collecting flask==1.0.2 (from -r requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/7f/e7/08578774ed4536d3242b14dacb4696386634607af824ea997202cd0edb4b/Flask-1.0.2-py2.py3-none-any.whl (91kB)
Collecting Werkzeug>=0.14 (from flask==1.0.2->-r requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/20/c4/12e3e56473e52375aa29c4764e70d1b8f3efa6682bef8d0aae04fe335243/Werkzeug-0.14.1-py2.py3-none-any.whl (322kB)
Collecting click>=5.1 (from flask==1.0.2->-r requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/34/c1/8806f99713ddb993c5366c362b2f908f18269f8d792aff1abfd700775a77/click-6.7-py2.py3-none-any.whl (71kB)
Collecting itsdangerous>=0.24 (from flask==1.0.2->-r requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/dc/b4/a60bcdba945c00f6d608d8975131ab3f25b22f2bcfe1dab221165194b2d4/itsdangerous-0.24.tar.gz (46kB)
Collecting Jinja2>=2.10 (from flask==1.0.2->-r requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/7f/ff/ae64bacdfc95f27a016a7bed8e8686763ba4d277a78ca76f32659220a731/Jinja2-2.10-py2.py3-none-any.whl (126kB)
Collecting MarkupSafe>=0.23 (from Jinja2>=2.10->flask==1.0.2->-r requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/4d/de/32d741db316d8fdb7680822dd37001ef7a448255de9699ab4bfcbdf4172b/MarkupSafe-1.0.tar.gz
Building wheels for collected packages: itsdangerous, MarkupSafe
  Running setup.py bdist_wheel for itsdangerous: started
  Running setup.py bdist_wheel for itsdangerous: finished with status 'done'
  Stored in directory: /root/.cache/pip/wheels/2c/4a/61/5599631c1554768c6290b08c02c72d7317910374ca602ff1e5
  Running setup.py bdist_wheel for MarkupSafe: started
  Running setup.py bdist_wheel for MarkupSafe: finished with status 'done'
  Stored in directory: /root/.cache/pip/wheels/33/56/20/ebe49a5c612fffe1c5a632146b16596f9e64676768661e4e46
Successfully built itsdangerous MarkupSafe
Installing collected packages: Werkzeug, click, itsdangerous, MarkupSafe, Jinja2, flask
Successfully installed Jinja2-2.10 MarkupSafe-1.0 Werkzeug-0.14.1 click-6.7 flask-1.0.2 itsdangerous-0.24
Removing intermediate container aaa83a183ead
 ---> eca67b911ca4
Step 6/11 : FROM base
 ---> 646de3787bbc
Step 7/11 : WORKDIR /app
 ---> Running in 0b81da90cc35
Removing intermediate container 0b81da90cc35
 ---> 17489d37a114
Step 8/11 : COPY . /app
 ---> 1f12ed2b9456
Step 9/11 : COPY --from=dependencies /root/.cache /root/.cache
 ---> 850bd5d693e3
Step 10/11 : COPY requirements.txt ./
 ---> b508d7762e8e
Step 11/11 : RUN pip install -r requirements.txt && rm -rf /root/.cache
 ---> Running in 125afe6f16b0
Collecting flask==1.0.2 (from -r requirements.txt (line 1))
  Using cached https://files.pythonhosted.org/packages/7f/e7/08578774ed4536d3242b14dacb4696386634607af824ea997202cd0edb4b/Flask-1.0.2-py2.py3-none-any.whl
Collecting Werkzeug>=0.14 (from flask==1.0.2->-r requirements.txt (line 1))
  Using cached https://files.pythonhosted.org/packages/20/c4/12e3e56473e52375aa29c4764e70d1b8f3efa6682bef8d0aae04fe335243/Werkzeug-0.14.1-py2.py3-none-any.whl
Collecting click>=5.1 (from flask==1.0.2->-r requirements.txt (line 1))
  Using cached https://files.pythonhosted.org/packages/34/c1/8806f99713ddb993c5366c362b2f908f18269f8d792aff1abfd700775a77/click-6.7-py2.py3-none-any.whl
Collecting itsdangerous>=0.24 (from flask==1.0.2->-r requirements.txt (line 1))
Collecting Jinja2>=2.10 (from flask==1.0.2->-r requirements.txt (line 1))
  Using cached https://files.pythonhosted.org/packages/7f/ff/ae64bacdfc95f27a016a7bed8e8686763ba4d277a78ca76f32659220a731/Jinja2-2.10-py2.py3-none-any.whl
Collecting MarkupSafe>=0.23 (from Jinja2>=2.10->flask==1.0.2->-r requirements.txt (line 1))
Installing collected packages: Werkzeug, click, itsdangerous, MarkupSafe, Jinja2, flask
Successfully installed Jinja2-2.10 MarkupSafe-1.0 Werkzeug-0.14.1 click-6.7 flask-1.0.2 itsdangerous-0.24
Removing intermediate container 125afe6f16b0
 ---> d122f42fcb72
Successfully built d122f42fcb72
Successfully tagged so:51678830

最终映像为70MB

| REPOSITORY                    TAG                 IMAGE ID            CREATED             SIZE   |
|--------------------------------------------------------------------------------------------------|
| so                            51678830            d122f42fcb72        4 seconds ago       69.6MB |

希望让事情变得清晰