Skip to content

Commit a8f2bc3

Browse files
authored
Use multi-stage build in Dockerfile (#142)
This resolves the conversation at #117 (comment) Currently we install the build dependencies like g++ (`RUN apk add...`) in a separate layer. We try and purge them in a later step but this doesn't achieve anything because docker caches everything on a layer by layer basis, i.e. the previous layer will still contain them and hang around. This PR fixes it by using a multi-stage build which reduces the image size by ~90%: ``` $ docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE lnt-new-dockerfile latest 22cfaa9c28b0 14 minutes ago 88MB lnt-old-dockerfile <none> 76a17f3f65ed 5 seconds ago 797MB ``` This patch also only copies over the minimal source files required so as to make sure the dependencies layer is invalidated as infrequently as possible. E.g. here is an example of building the image after touching a regular python source file. We no longer need to rebuild or redownload the dependencies, that layer is cached: ``` $ docker build -f docker/lnt.dockerfile . [+] Building 22.0s (15/15) FINISHED docker:default => [internal] load build definition from lnt.dockerfile 0.0s => => transferring dockerfile: 1.73kB 0.0s => [internal] load metadata for docker.io/library/python:3.10-alpine 0.4s => [internal] load .dockerignore 0.0s => => transferring context: 2B 0.0s => [internal] load build context 0.5s => => transferring context: 562.77kB 0.4s => CACHED [builder 1/7] FROM docker.io/library/python:3.10-alpine@sha256:b4da816c29d5d3067a979e299ea3e4856476a2b 0.0s => [final 2/4] RUN apk update && apk add --no-cache libpq 2.8s => CACHED [builder 2/7] RUN apk update && apk add --no-cache g++ postgresql-dev yaml-dev git libpq 0.0s => CACHED [builder 3/7] COPY pyproject.toml . 0.0s => CACHED [builder 4/7] COPY lnt/testing/profile lnt/testing/profile 0.0s => CACHED [builder 5/7] RUN pip install --user ".[server]" 0.0s => [builder 6/7] COPY . . 2.1s => [builder 7/7] RUN pip install --user . 17.0s => [final 3/4] COPY --from=builder /root/.local /root/.local 0.9s => [final 4/4] COPY docker/docker-entrypoint.sh docker/docker-entrypoint-log.sh docker/lnt-wait-db /usr/local/bi 0.0s => exporting to image 0.6s => => exporting layers 0.6s => => writing image sha256:527e0140763072f26d84ef42b0bb9dda9857625acb3474ac5f159960c8d20bc4 0.0s ``` We need to use a mock version for setuptools_scm, since it looks for a .git folder and we want to avoid copying that. That would cause the build dependency layers to be invalidated on every commit.
1 parent 070f5f9 commit a8f2bc3

File tree

1 file changed

+22
-11
lines changed

1 file changed

+22
-11
lines changed

docker/lnt.dockerfile

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,24 +26,35 @@
2626
# Log files for the instance.
2727
#
2828

29-
FROM python:3.10-alpine
29+
FROM python:3.10-alpine AS builder
3030

31-
# Install dependencies
32-
RUN apk update \
33-
&& apk add --no-cache --virtual .build-deps git g++ postgresql-dev yaml-dev \
34-
&& apk add --no-cache libpq
31+
# Install build dependencies
32+
RUN apk update && apk add --no-cache g++ postgresql-dev yaml-dev git libpq
33+
# Fake a version for setuptools so we don't need to COPY .git
34+
ENV SETUPTOOLS_SCM_PRETEND_VERSION=0.1
35+
COPY pyproject.toml .
36+
# pip will build cperf ext-modules so COPY over its sources
37+
COPY lnt/testing/profile lnt/testing/profile
38+
RUN pip install --user ".[server]"
3539

36-
# Install LNT itself, without leaving behind any sources inside the image.
37-
RUN --mount=type=bind,source=.,target=./lnt-source \
38-
cp -R lnt-source /tmp/lnt-src && \
39-
cd /tmp/lnt-src && \
40-
pip3 install -r requirements.server.txt && apk --purge del .build-deps && \
41-
rm -rf /tmp/lnt-src
40+
# Copy over sources and install LNT
41+
# Let setuptools_scm use .git to pick the version again
42+
ENV SETUPTOOLS_SCM_PRETEND_VERSION=
43+
COPY . .
44+
RUN pip install --user .
45+
46+
FROM python:3.10-alpine AS final
47+
48+
# Install runtime dependencies
49+
RUN apk update && apk add --no-cache libpq
50+
51+
COPY --from=builder /root/.local /root/.local
4252

4353
# Prepare volumes that will be used by the server
4454
VOLUME /var/lib/lnt /var/log/lnt
4555

4656
# Set up the actual entrypoint that gets run when the container starts.
4757
COPY docker/docker-entrypoint.sh docker/lnt-wait-db /usr/local/bin/
58+
ENV PATH=/root/.local/bin:$PATH
4859
ENTRYPOINT ["docker-entrypoint.sh"]
4960
EXPOSE 8000

0 commit comments

Comments
 (0)