Skip to content

[Feature request] Improve Docker image performance with an alternative base image instead of Alpine #2299

Closed

Description

@polarathene

Which version are you referring to
3.1dev

Please check this repo whether this is a known feature request
No existing issue came up after a few searches and browsing of results. The closest I found were some 2019 observations with an Alpine image.

Describe your feature request (if it's a technical feature)
It'd be nice if a Debian base image could be offered. It does not have the minimal disk usage that an alpine base image offers, but performance can be notably better.

If your feature request otherwise is related to a usage problem, please describe it
There are reports about DNS performance being problematic on Alpine images, but I did not notice a difference compared to testing against an IP address directly. So I assume it's more to do with musl vs glibc.

I have observed a repeatable difference between the current Alpine image and a Debian base image alternative of about 50% in time to perform a test with --preference, and alpine being 2-3x slower without --preference. The Debian image performed at native (outside of docker) or very close to.

Describe the solution you'd like

I adapted the current alpine Dockerfile to one for Debian:

# syntax=docker.io/docker/dockerfile:1
# HereDoc (EOF) feature (avoids needing `&& \`) requires BuildKit:
# https://docs.docker.com/engine/reference/builder/#here-documents

FROM debian:bullseye-slim

ARG DEBIAN_FRONTEND=noninteractive
RUN <<EOF
  ## Prepare package install
  apt-get -qq update  
  apt-get -qq upgrade  

  # Install packages
  apt-get install -qq --no-install-recommends bsdmainutils procps dnsutils

  # Cleanup
  apt-get -qq clean  
  rm -rf /var/lib/apt/lists/*  

  # Create testssl user
  adduser --system --group --uid 1000 --gecos "testssl user" --shell /bin/bash --disabled-password testssl

  # Prepare for COPY
  ln -s /home/testssl/testssl.sh /usr/local/bin/  
  mkdir -m 755 -p /home/testssl/etc /home/testssl/bin
EOF

USER testssl
WORKDIR /home/testssl/

COPY --chown=testssl:testssl etc/ /home/testssl/etc/
COPY --chown=testssl:testssl bin/ /home/testssl/bin/
COPY --chown=testssl:testssl testssl.sh /home/testssl/

ENTRYPOINT ["testssl.sh"]

CMD ["--help"]

Comparison

Here are some observations I noticed when testing against a mailserver container (with custom network and also with host network, not performance change), with a docker image and native install of testssl.sh:3.1dev.

Single test

# ~60 seconds to complete, same as native. Alpine ~150s:
$ docker run --rm -it --network test-network testssl:debian example.test:465

# 11-15 seconds to complete, likewise competitive with native (10-13s). Alpine 25-37s:
$ docker run --rm -it --network test-network testssl:debian example.test:465

Parallel tests

These results are from running against the docker-mailserver test suite:

  • --quiet --preference --mode parallel options are used, along with the ENV ADDTL_CA_FILES to verify chain of trust.

  • Time in milliseconds is reported at the end of each test case.

  • Each test case has the mail-server offer RSA, ECDSA, or both as the certificate, and with one of two cipher suite configs.

  • Each test case is running testssl.sh with --file to perform tests across 7 ports from two services (Postfix and Dovecot):

    --jsonfile-pretty port_25.json --starttls smtp example.test:25
    --jsonfile-pretty port_587.json --starttls smtp example.test:587
    --jsonfile-pretty port_465.json example.test:465
    
    --jsonfile-pretty port_110.json --starttls pop3 example.test:110
    --jsonfile-pretty port_995.json example.test:995
    
    --jsonfile-pretty port_143.json --starttls imap example.test:143
    --jsonfile-pretty port_993.json example.test:993
    
# Debian base image:
$ make clean generate-accounts test/tls_cipherlists

tls_cipherlists.bats
 ✓ [Security - TLS] (cipher lists) 'TLS_LEVEL=intermediate' + RSA [45863]
 ✓ [Security - TLS] (cipher lists) 'TLS_LEVEL=intermediate' + ECDSA [36584]
 ✓ [Security - TLS] (cipher lists) 'TLS_LEVEL=intermediate' + ECDSA with RSA fallback [55790]
 ✓ [Security - TLS] (cipher lists) 'TLS_LEVEL=modern' + RSA [43647]
 ✓ [Security - TLS] (cipher lists) 'TLS_LEVEL=modern' + ECDSA [36443]
 ✓ [Security - TLS] (cipher lists) 'TLS_LEVEL=modern' + ECDSA with RSA fallback [52742]

6 tests, 0 failures in 274 seconds

# Alpine base image (worse case was around 330 sec):
$ make clean generate-accounts test/tls_cipherlists

tls_cipherlists.bats
 ✓ [Security - TLS] (cipher lists) 'TLS_LEVEL=intermediate' + RSA [52541]
 ✓ [Security - TLS] (cipher lists) 'TLS_LEVEL=intermediate' + ECDSA [42963]
 ✓ [Security - TLS] (cipher lists) 'TLS_LEVEL=intermediate' + ECDSA with RSA fallback [62151]
 ✓ [Security - TLS] (cipher lists) 'TLS_LEVEL=modern' + RSA [45966]
 ✓ [Security - TLS] (cipher lists) 'TLS_LEVEL=modern' + ECDSA [45853]
 ✓ [Security - TLS] (cipher lists) 'TLS_LEVEL=modern' + ECDSA with RSA fallback [57875]

6 tests, 0 failures in 311 seconds

The benefit was less apparent with --mode parallel, about 40 seconds or so difference. That is on a system with an old i5-6500 (4 cores / 4 threads) that would be under full load by either base image while running.

Activity

  1. polarathene commented on Jan 29, 2023

    @polarathene
    ContributorAuthor

    Good news is that it seems something happened with the newer 3.17 release of Alpine that greatly reduces the performance delta (all tests report version 3.2rc2):

    # Alpine 3.16 = 210 seconds
    $ docker run --rm -it testssl:alpine-3.16 github.com
    
    # Alpine 3.17 = 120 seconds
    $ docker run --rm -it testssl:alpine-3.17 github.com
    
    # Debian Bullseye Slim = 103 seconds
    $ docker run --rm -it testssl:debian github.com
    
    # Native / Host = 103 seconds:
    $ testssl github.com

    Still not quite as good as changing the base image to Debian (or another base image with glibc), but pretty decent.

  2. drwetter commented on Jan 30, 2023

    @drwetter
    Collaborator

    Thanks @polarathene !

    You were faster as I could respond during the weekend. If you're sure 3.17 works 100% I'd first appreciate for both branches a PR. The remaining 10% we can worry about afterwards.

  3. jauderho commented on Jan 30, 2023

    @jauderho
    Contributor

    Alpine 3.17.1 is the latest so that's what should be used.

  4. drwetter commented on Jan 30, 2023

    @drwetter
    Collaborator

    With all due respect @jauderho but that is nonsense. Try docker run alpine:3.17 cat /etc/os-release.
    3.17.1 will be automatically chosen.

    You shall not fix the patchlevel of the Alpine version otherwise you forget it and you have security issues.

  5. drwetter commented on Jan 30, 2023

    @drwetter
    Collaborator

    @polarathene : if you want to help with a PR, there's also a "Dockerfile.git"

  6. jauderho commented on Jan 30, 2023

    @jauderho
    Contributor

    @drwetter That way is the implicit method which is not a best practice. The "correct" way is to explicitly specify the image including the hash and use Dependabot to keep things up to date.

    So FROM alpine:3.17.1@sha256:f271e74b17ced29b915d351685fd4644785c6d1559dd1f2d4189a5e851ef753a

  7. polarathene commented on Jan 30, 2023

    @polarathene
    ContributorAuthor

    The "correct" way is to explicitly specify the image including the hash and use Dependabot to keep things up to date.

    Yes, but someone needs to create a PR to do so, if that's something you'd like to see, perhaps you want to contribute what you can via PR and delegate anything else that requires @drwetter to configure for the repo?

    Explicit version bumping is simpler until then, and infrequent enough that I'm not bothered by it. I wouldn't normally expect a big difference in the performance between releases of Alpine, this was just an observation from investigating why the current image was much worse than a glibc base image that performed like runs without Docker involved.


    @jauderho I've not set up version pinning in CI before, so if you'd like to tackle that, awesome 👍

    @drwetter I'm happy to open a PR to bump to 3.17 for now. Did you want that for both Dockerfile and Dockerfile.git, or just one of those?


    I'd first appreciate for both branches a PR. The remaining 10% we can worry about afterwards.

    Which branches? 3.1dev and 3.0?

    If you're sure 3.17 works 100%

    I wouldn't expect these base image upgrades to cause much risk of regression.

    Presently we're (docker-mailserver) only using testssl.sh in our test suite to test multiple service ports with --preference and output JSON to check the cipher suites results are equal to what we expect them to be.

    I did test without --preference against Github and saw no issues with the terminal output there 👍

    I have noticed that using the --openssl /usr/bin/openssl option has changed from version 1.1.1s to 3.0.7 (both released in Nov 2022 with 94 ciphers), otherwise they both default to the openssl binary you bundle into the image from this repo.

  8. polarathene commented on Jan 31, 2023

    @polarathene
    ContributorAuthor

    Also, in mentioning --openssl /usr/bin/openssl, at least for my use-case, the performance improves a fair bit, especially in the parallel test runs:

    # drwetter/testssl.sh:3.1dev (Debian Bullseye --openssl)
    $ make clean generate-accounts test/tls_cipherlists
    
    tls_cipherlists.bats
     ✓ [Security] (TLS) (cipher lists) 'TLS_LEVEL=intermediate' + RSA [33609]
     ✓ [Security] (TLS) (cipher lists) 'TLS_LEVEL=intermediate' + ECDSA [28977]
     ✓ [Security] (TLS) (cipher lists) 'TLS_LEVEL=intermediate' + ECDSA with RSA fallback [30887]
     ✓ [Security] (TLS) (cipher lists) 'TLS_LEVEL=modern' + RSA [28895]
     ✓ [Security] (TLS) (cipher lists) 'TLS_LEVEL=modern' + ECDSA [27273]
     ✓ [Security] (TLS) (cipher lists) 'TLS_LEVEL=modern' + ECDSA with RSA fallback [31389]
    
    6 tests, 0 failures in 185 seconds
    
    
    # drwetter/testssl.sh:3.1dev (Alpine 3.16 --openssl)
    $ make clean generate-accounts test/tls_cipherlists
    
    tls_cipherlists.bats
     ✓ [Security] (TLS) (cipher lists) 'TLS_LEVEL=intermediate' + RSA [31366]
     ✓ [Security] (TLS) (cipher lists) 'TLS_LEVEL=intermediate' + ECDSA [31016]
     ✓ [Security] (TLS) (cipher lists) 'TLS_LEVEL=intermediate' + ECDSA with RSA fallback [33990]
     ✓ [Security] (TLS) (cipher lists) 'TLS_LEVEL=modern' + RSA [33131]
     ✓ [Security] (TLS) (cipher lists) 'TLS_LEVEL=modern' + ECDSA [31368]
     ✓ [Security] (TLS) (cipher lists) 'TLS_LEVEL=modern' + ECDSA with RSA fallback [34582]
    
    6 tests, 0 failures in 199 seconds
    • The delta is smaller now by only ~15 sec (sometimes less), while without --openssl I still see the 3.16 Alpine image hit 300+ seconds running those tests.
    • With Alpine 3.17 --openssl option is a bit slower and seems to manage around 220 sec, however if I install the OpenSSL 1.1.1s compat package (instead of using 3.0.7) and use --openssl /usr/bin/openssl1.1 - it performs just as well in the parallel tests, one result was 189 sec. Likewise the test against Github completed in 104 sec.
  9. drwetter commented on Jan 31, 2023

    @drwetter
    Collaborator

    The "correct" way is to explicitly specify the image including the hash and use Dependabot to keep things up to date.

    To be honest: I don't like that. It makes it depended on dependabot. with no reason. It worked before. And it causes more PRs.

    Do you want to amend the PRs or file new ones? Otherwise I'll close them and I'll make new ones. Just let me know.

  10. polarathene commented on Feb 1, 2023

    @polarathene
    ContributorAuthor

    @drwetter I've opened a new PR as requested.

    If the other changes don't interest you at all, feel free to close the PR and just bump the Alpine version 👍

  11. drwetter commented on Mar 14, 2023

    @drwetter
    Collaborator

    It´s as you said 138 MB includes lot's of unnecessary stuff because of the debian eco system:

    Setting up libicu67:amd64 (67.1-7) ...
    Setting up ncal (12.1.7+nmu3) ...
    Setting up libmaxminddb0:amd64 (1.5.2-1) ...
    Setting up libfstrm0:amd64 (0.6.0-1+b1) ...
    Setting up libprotobuf-c1:amd64 (1.3.3-1+b2) ...
    Setting up libuv1:amd64 (1.40.0-2) ...
    Setting up libncurses6:amd64 (6.2+20201114-2) ...
    Setting up bsdmainutils (12.1.7+nmu3) ...
    Setting up libncursesw6:amd64 (6.2+20201114-2) ...
    Setting up libmd0:amd64 (1.0.3-3) ...
    Setting up libbsd0:amd64 (0.11.3-1) ...
    Setting up libjson-c5:amd64 (0.15-2) ...
    Setting up libxml2:amd64 (2.9.10+dfsg-6.7+deb11u3) ...
    Setting up libprocps8:amd64 (2:3.3.17-5) ...
    Setting up bind9-libs:amd64 (1:9.16.37-1~deb11u1) ...
    Setting up libedit2:amd64 (3.1-20191231-2+b1) ...
    Setting up procps (2:3.3.17-5) ...
    Setting up bind9-host (1:9.16.37-1~deb11u1) ...
    Setting up bind9-dnsutils (1:9.16.37-1~deb11u1) ...
    Setting up dnsutils (1:9.16.37-1~deb11u1) ...
    

    maxmind?? libxml, + libjson?? :-(

    With ldnsutils it's smaller: 99.5MB and not using that stuff:

    Setting up ncal (12.1.7+nmu3) ...
    Setting up libncurses6:amd64 (6.2+20201114-2) ...
    Setting up libdbus-1-3:amd64 (1.12.24-0+deb11u1) ...
    Setting up bsdmainutils (12.1.7+nmu3) ...
    Setting up libncursesw6:amd64 (6.2+20201114-2) ...
    Setting up libprocps8:amd64 (2:3.3.17-5) ...
    Setting up libldns3:amd64 (1.7.1-2+b1) ...
    Setting up libpcap0.8:amd64 (1.10.0-2) ...
    Setting up procps (2:3.3.17-5) ...
    Setting up ldnsutils (1.7.1-2+b1) ...
    

    ncal? libpcap0?

  12. drwetter commented on Mar 14, 2023

    @drwetter
    Collaborator
    prompt> docker build -t testssl-deb .
    [..]
    
    ---> 18d763374fee
    Step 3/10 : RUN apt-get -qq update && apt-get -qq upgrade &&   apt-get install -qq --no-install-recommends procps ldnsutils bsdextrautils &&   apt-get -qq clean &&   rm -rf /var/lib/apt/lists/* &&   adduser --system --group --uid 1000 --gecos "testssl user" --shell /bin/bash --disabled-password testssl &&   ln -s /home/testssl/testssl.sh /usr/local/bin/ &&   mkdir -m 755 -p /home/testssl/etc /home/testssl/bin
    [..]
    Setting up libncurses6:amd64 (6.2+20201114-2) ...
    Setting up libdbus-1-3:amd64 (1.12.24-0+deb11u1) ...
    Setting up libncursesw6:amd64 (6.2+20201114-2) ...
    Setting up libprocps8:amd64 (2:3.3.17-5) ...
    Setting up libldns3:amd64 (1.7.1-2+b1) ...
    Setting up libpcap0.8:amd64 (1.10.0-2) ...
    Setting up procps (2:3.3.17-5) ...
    Setting up ldnsutils (1.7.1-2+b1) ...
    [..]
    prompt> docker image ls testssl-deb     
    REPOSITORY    TAG       IMAGE ID       CREATED              SIZE
    testssl-deb   latest    7225665fee76   About a minute ago   99.4MB
    

    Probably we can't get it any smaller, unless we're using a multistage image.

  13. drwetter commented on Mar 14, 2023

    @drwetter
    Collaborator

    This was w/o openssl and with libidn. Using a similar package selection under alpine I am @ 28.5MB.

    For 3.1dev we would need for both openssl, socat, curl and libidn.

  14. polarathene commented on Mar 16, 2023

    @polarathene
    ContributorAuthor

    TL;DR: I managed to get a glibc image with your required dependencies down to 60 MiB (Current 3.1 Alpine image is 36.5 MiB).


    I have looked into minimizing the images with Fedora and OpenSUSE as their package managers have an --installroot option that could be used to make a smaller image.

    I am not familiar with how to do that with Debian or if it would be any better size wise. That said Google does offer a gcr.io/distroless/base-nossl-debian11 image (or base-debian11 with openssl) that is 15 MB in size (20MB with openssl). That's without a package manager, and from what I've seen with these other packages the weight tends to increase a fair bit.


    Fedora was also including a bunch of extra dependencies for some packages that didn't seem necessary. Especially with util-linux (for hexdump) and vim-common (for xxd), both binaries likely not needing the bulk of it.

    With OpenSUSE it was better:

    • Although ldns + libidn2-0 for drill and the idn support adds about 13 MiB, which can be replaced with busybox-bind-utils to recover most of that (but then loses the benefit of drill + idn).
    • Once socat was added, it had a bunch of overlap with ldns, only adding approx 1 MiB.
    • grep, gawk, sed, coreutils all bring in procps and bash (30 MiB weight if these are the first packages considered, otherwise about 13.5 MiB, with 1 MiB of that for bash).
    • busybox-util-linux (provides hexdump) and busybox-vi (provides xxd), only about 2 MiB (on Fedora either one of the equivalents added about 30 MiB).
    • testssl.sh detects busybox ps and dd, otherwise the busybox equivalent packages could be used and bring down the weight by about 10 MiB more.
    • curl then adds close to 5 MiB. I think Fedora offered a curl-minimal.
    • openssl only adds about 1.5 MiB after all this.
    • Removing some content from /usr/share recovers 4-9MiB.

    This results in an image that is 46 MiB (original opensuse/leap base image is 113 MiB), and then the 14 MiB from this git repo that is added via COPY brings total image size to 60 MiB (uncompressed). The current 3.1dev alpine image is 36.5 MiB.

    It's probably possible to reduce that further (might have some luck with the Slim project), but this is as far as I'll go 😅

    Less than 30 MiB extra isn't that bad for a non-alpine image I think?


    Here's the Dockerfile, it is using some BuildKit features, but this works as-is with the Docker Engine v23 release out of the box docker build --tag testssl-leap:

    # syntax=docker.io/docker/dockerfile:1
    # HereDoc (EOF) feature (avoids needing `&& \`) requires BuildKit:
    # https://docs.docker.com/engine/reference/builder/#here-documents
    
    FROM opensuse/leap as builder
    
    ARG MOUNTPOINT=/build
    
    # --mount is only necessary for persisting the cache on the build host
    # Seems to be about 170M repo data from the refresh, may not be useful.
    RUN --mount=type=cache,target=/tmp/zypper-cache,sharing=locked <<EOF
      # Provides $VERSION_ID
      source /etc/os-release
    
      # Sync package repos to get latest updates:
      # --releasever required due to no version info in install root.
      # --installroot installs to location as if it was the system root.
      # --cache-dir with above cache --mount speeds this step up.
      zypper \
        --gpg-auto-import-keys \
        --releasever "${VERSION_ID}" \
        --installroot "${MOUNTPOINT}" \
        --cache-dir /tmp/zypper-cache \
        refresh
    
      zypper \
        --non-interactive \
        --releasever "${VERSION_ID}" \
        --installroot "${MOUNTPOINT}" \
        --cache-dir /tmp/zypper-cache \
        install --download-in-advance --no-recommends \
          bash \
          procps \
          grep \
          gawk \
          sed \
          coreutils \
          busybox-util-linux \
          busybox-vi \
          ldns \
          libidn2-0 \
          socat \
          curl \
          openssl
    
      # Clears the cache, but this is not stored in the install root location (like DNF does), thus not useful.
      # zypper --releasever "${VERSION_ID}" --installroot "${MOUNTPOINT}" clean --all
    
      # Unlike DNF, there isn't a `--nodocs` install option, manually remove some excess weight (9 MiB):
      rm -r "${MOUNTPOINT}/usr/share/"{licenses,man,locale,doc,help,info}
    EOF
    
    # Final image of multi-stage build,
    # Migrate the install root from earlier to it's own image now as the actual root:
    FROM scratch
    COPY --link --from=builder /build /
    
    # zypper package `busybox-adduser` fails to install with `--installroot`,
    # while the `shadow` package is too heavy just to add a user.
    #
    # Temporarily bind mount the `/bin` dir from another image that already
    # has the `adduser` command, and it'll update `/etc/{group,passwd,shadow}` for us:
    # Absolute path provided as some base images PATH would use those binaries instead,
    # `adduser` varies in supported args, so this should avoid any surprises:
    RUN --mount=type=bind,from=busybox:latest,source=/bin,target=/bin <<EOF
      /bin/adduser -D -s /bin/bash testssl
      /bin/ln -s /home/testssl/testssl.sh /usr/local/bin/
    EOF
    
    USER testssl
    WORKDIR /home/testssl/
    
    # Copy over build context (after filtered by .dockerignore): bin/ etc/ testssl.sh
    COPY --chown=testssl:testssl . /home/testssl/
    
    ENTRYPOINT ["testssl.sh"]
    CMD ["--help"]
    • Optionally build an image of only the builder stage with docker build --target builder --tag testssl-leap-builder .
    • dive is a useful tool to inspect layers (although the HereDoc EOF feature in RUN seems to cause some issues).
      • There is a fork of dive that is actively updated: docker run --rm -it -v /var/run/docker.sock:/var/run/docker.sock jauderho/dive testssl-leap-builder
    • Had a quick look at Go and Rust based alternatives to hexdump / xxd, and there are some interesting ones like hexyl but they're not drop-in replacements. If you really want to push down the image size, having support in testssl.sh for Go or Rust based alternatives for hexdump, socat, drill and perhaps even coreutils (there's a Rust one that I'm aware of) could probably shave off a good bulk of the weight.
    • I'm not sure if the static build of custom openssl is that relevant going forward? If it's generally not likely to provide much benefit testing modern servers, you could consider dropping it or providing it as an image variant (13.5MiB copied into image: 1.2MiB testssl.sh, 10.3MiB bin/openssl + .i686 & .krb variants, 2MiB etc/).
      • Presumably the .i686 file isn't useful in the image (I assume the same for .krb), while the x86_64 build wouldn't be useful on ARM hosts/containers?
  15. drwetter commented on Mar 16, 2023

    @drwetter
    Collaborator

    cool!

  16. drwetter commented on Mar 19, 2023

    @drwetter
    Collaborator

    For 3.0 I rather went to openssl on alpine, see #2340. For 3.1dev let's change the container image.

    For 3.1dev I am not sure how the multistage works in reality on the command line, and I am not sure whether the whole image size pulled is smaller.

    Let's finalize this or another image next week. Any Dockerfile is probably better to be not in HEREDOC format. And pulling automatically latest is not I'd like to do. If we'll go for opensuse opensuse/leap:15.4 is what is the best.

    The static build will probably be not the primary binary in 3.3dev but I won't change this in the release candidate phase.

  17. polarathene commented on Mar 20, 2023

    @polarathene
    ContributorAuthor

    For 3.1dev I am not sure how the multistage works in reality on the command line

    Hmm? I did the multi-stage in a Fedora 37 VM, installed Docker via the Docker Install Fedora docs, so that it uses v23 (Github Actions should also have that). I think it should work on older releases too.

    I made sure to add some comments in the Dockerfile above to explain some things if helpful.

    • Two stages (only the first one is named as builder).
    • When the image is built, it will build top-down, so the builder stage is done first.
    • Then the 2nd stage (FROM scratch) copies over the minimal OS install we made in /build folder of the first stage.
    • A testssl user probably isn't necessary, but as we don't have a package installed providing the adduser command, I am using a feature of RUN --mount to temporarily provide the adduser command from another docker image (busybox). That could be done in the builder image, but needs to account for /build/etc instead of /etc, I think openSUSE adduser command has an option to support that, the symlink likewise needs to be added to the /build dir.
    • The rest is just like the current Alpine image.

    If you meant how to build it, you shouldn't need to change anything. I cited the docker build command to try it out, same as without multi-stage.

    If you are interested in building an earlier stage instead of the full file, you can reference the stage name, so docker build --target builder --tag this-is-the-builder-stage ., afterwards docker run --rm -it this-is-the-builder-stage bash.

    Likewise, to get into the final image, since the ENTRYPOINT is set to the testssl.sh command, you'd need to override that instead to use bash, docker run --rm -it --entrypoint bash drwetter/testssl.sh:3.1dev (adjust image tag to whatever you used with docker build).


    I am not sure whether the whole image size pulled is smaller.

    The final image is the last stage which uses scratch (nothing) as the base image.

    You'll observe that docker image ls / docker image history image-name-here will show you the image size on disk.

    If you want to inspect a breakdown of that further, look at the dive tool I mentioned earlier which lets you navigate the image layers and their impact on the images filesystem showing where all the file weight is located.

    With the RUN --mount, we used for caching the package manager, that content is no longer stored in the image layers, so it's just like we cleared it manually afterwards, except locally when you build it you can re-use that cache. That is not exported with Dockers build cache, thus doesn't persist across CI builds, non-issue here.

    The builder stage itself will still be stored on disk as part of your build cache (locally or in CI), and CI can persist that if you've configured it to (like we do on docker-mailserver), probably not too useful for testssl.sh though 👍

    As a user, you don't have to worry about that, and you'd only pull down the final image that was built. Which should be approx 60 MiB on disk, but compressed should be less than 30 MiB on Dockerhub I think, which is how much network traffic will be used to pull it, and it'll be uncompressed once stored on disk.


    Any Dockerfile is probably better to be not in HEREDOC format.

    That is up to you, it's rather nice for Dockerfile to embed shell commands into a single RUN instead of the noisy && \ chaining on each line.

    In this case since it's only 4 commands in builder you could do that, it'd be better than individual RUNs (which would also be fine in builder stage as that doesn't affect the final image weight, but you'd get noise from the --mount cache instead).

    Another option you have, is to extract it out of the Dockerfile and have a separate .sh file that you COPY over.


    And pulling automatically latest is not I'd like to do. If we'll go for opensuse opensuse/leap:15.4 is what is the best.

    That would still pull updates to 15.4 tag, but absolutely a good idea 👍

    I forgot to be more specific there while experimenting 😅

  18. drwetter commented on Mar 21, 2023

    @drwetter
    Collaborator

    For 3.1dev I am not sure how the multistage works in reality on the command line

    Sorry that I confused you. I actually can't tell myself anymore what I meant by this 😅. I know how multi stage works

    Any Dockerfile is probably better to be not in HEREDOC format

    That is up to you, it's rather nice for Dockerfile to embed shell commands into a single RUN instead of the noisy && \ chaining on each line

    yes but people need a recent docker version.

    Do you want to issue the PR for Opensuse so that we can see before? I created the branch suse_docker first to see image sizes on Dockerhub and here.

    I forgot to be more specific there while experimenting

    no prob at all

  19. polarathene commented on Mar 22, 2023

    @polarathene
    ContributorAuthor

    yes but people need a recent docker version.

    Only to build. Most users will just pull the image from DockerHub.

    Building with HereDoc should work on older versions (see this blogpost from mid 2021, just needs an ENV prefix). We've been using the HereDoc syntax in docker-mailserver images since Oct 2022 and not had any issues from our users.

    If someone is building locally, they can probably get Docker Engine v23 (released early this year) which requires no ENV to opt-in. Likewise CI services like Github Actions keep that software up to date and it's less of a concern there.


    Do you want to issue the PR for Opensuse so that we can see before? I created the branch suse_docker first to see image sizes on Dockerhub and here.

    Yeah I can give that a go. From what I've seen elsewhere, DockerHub images tend to report being less than 50% of the uncompressed size.

  20. drwetter commented on Mar 22, 2023

    @drwetter
    Collaborator

    Only to build. Most users will just pull the image from DockerHub

    yes. But it'll confuse users who build locally. Just want to avoid questions and problems elsewhere. My systems here are using docker 20.10. Even Opensuse Tumbleweed (rolling distro) haven't made the step to v23

  21. polarathene commented on Mar 22, 2023

    @polarathene
    ContributorAuthor

    But it'll confuse users who build locally. Just want to avoid questions and problems elsewhere.

    Not been a problem for us. Do you have users that build locally often that aren't contributing?

    One option is just to inline a comment into the Dockerfile saying it builds on Docker Engine v23, and for earlier releases (past 2 years almost) use DOCKER_BUILDKIT=1 docker build ... .

    This requirement will likely also mean RUN --mount will not be acceptable, as it also relied on BuildKit feature this way AFAIK (Announced in May 2022).

  22. drwetter commented on Mar 22, 2023

    @drwetter
    Collaborator

    I have no idea about what the consuming or randomly contributing users are using. Who has?
    That explains why the RUN-mount thing didn't work for me either last week :-)

    Please let's not settle on an edge bleeding too much. Robustness now. Looking forward in next dev branch things on the desktops might look different.

  23. polarathene commented on Mar 22, 2023

    @polarathene
    ContributorAuthor

    Who has?

    It's not exactly something that breaks the usability of the image. This doesn't affect being able to run the image pulled from DockerHub, only building locally.

    Anyone doing that is probably capable of building it on a system that's had a Docker release in the past 1-2 years, and can use DOCKER_BUILDKIT=1 docker build ... like mentioned. That's just a documentation issue, and a rather easy one to cover in the Dockerfile if it's a concern.

    Docker also documents how to grab the latest (or earlier) releases for the bulk of distros users are running. You add an external repo from Docker, and now you've got access to the newer releases. Anyone running Docker Desktop (GUI app) uses BuildKit implicitly, just like you will with v23 of Docker Engine going forward.

    As I stated before, docker-mailserver which has a history of over 50 million pulls made this change in Sep 2022 and it's not been an issue to our users.


    That explains why the RUN-mount thing didn't work for me either last week :-)

    I assume adding the ENV does though? We have that tucked away in a Makefile so that make build internally prepends it to the docker build command.


    Please let's not settle on an edge bleeding too much. Robustness now.

    It was a bit frustrating to accommodate for, but I've refactored from those features and will send the PR shortly (EDIT: Done).

    Good news is the final size remains unaffected, bad news is the cache benefit for local builds is lost, and arguably it's less maintenance friendly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions