Skip to content

[CI] Epic: possible enhancements to current Dockerfile #4021

Open

Description

@apostasie

What is the problem you're trying to solve

When building dockerfile

  • reduce network traffic
  • reduce cache size
  • speed-up build, both locally and on the CI

Describe the solution you'd like

While previous PRs proposing a full rewrite of the Dockerfile have not been accepted, we could still try and address some of the issues incrementally.

Here is a list of the issues I gathered from my own rewrite so far (at https://github.com/farcloser/lepton/blob/main/Dockerfile)

Feel free to comment on which ones do matter to nerdctl and which should not be considered.

  1. We are retrieving very large amount of data from github just by cloning. The reasons for that are two fold: we are cloning full clones, while we could shallow clones (eg: https://github.com/farcloser/lepton/blob/main/Dockerfile#L288). For containerd alone, a full clone is 180MB - and we are cloning once per architecture, and we could clone once for all platforms instead. ([CI] Dockerfile enhancements: cloning #4022)

  2. When bulding multi-architecture, many stages are duplicated across arch.
    This is true for clone stages as mentioned, but for others as well.

  3. Retrieving binary releases does also generate large amount of traffic, and also makes for inconsistent release tarballs, where different binaries have been compiled with different (outdated and/or vulnerable) versions of go.
    We could instead just compile everything we need from source, with the same golang version.

  4. CGO binaries are not consistently harden (bind now, ro relocation, pie). Compiling from source would also enable that.

  5. the rootless ssh shenanigan is probably not necessary (linger systemd-run --system --scope su --login should be enough) - removing the need to install and setup ssh

  6. go modules for nerdctl should be vendored, once, at clone-time, instead of being retrieved at runtime for every target

  7. go mods cache could be using a cache layer to reduce both network traffic and cache size, across all projects

  8. full README mentions libsecomp (and license) being statically linked, but not zlib, libcap, glib, nor libslirp. Maybe we should?

  9. usefuleness of xx vs. Docker Hub dependency / additional third party tool could be reviewed, especially if we are considering other items in the list (eg: hardening, cache reduction). For our use case, xx does not do much.

  10. COPYing the entire nerdctl local context will bust the cache for every stage with a fresh checkout, further forcing repeat operations on subsequent operations. More careful / selective copy could significantly increase cacheability (eg: the only case where we need the entire context is when retrieving the commit SHA, which could/should be put towards the end of any pipeline). Furthermore, copying is unnecessary in some cases where a bind mount would be enough and reduce useless cache size

  11. buildkit systemd service files are now provided from their repo - the current copy / replace from containerd unit could be replaced

  12. windows CNI install is outdated and not currently parameteriz-able as we depend on an old containerd install script. It could be owned by nerdctl instead with proper version+revision and built like the rest.

  13. We are still using two different golang images seemingly with no reason, and could use just one instead.

  14. We could have a "sanity" stage for the full-release, that would verify that binaries are running, built for the right architecture, that the shasum matches, and that CGO binaries have been hardened.

  15. The way we build the test-integration stage is very baroque. If we are to start testing on the host instead of inside the container, we need a simple, sound way to setup the environment for testing

I know we disagree on below, so, keeping them separately for reference as not-desirable (will also move more items down here from above as discussion go and some items are deemed undesirable):

N1. golang should be retrieved and installed from go servers instead of from Hub (reduction in traffic with Hub, removal of all version scheme conversion monkeying, no delay in availability, no unexpected changes on the same image tag)

Finally, I will update the list if new suggestions come up.

Activity

  1. AkihiroSuda commented on Mar 19, 2025

    @AkihiroSuda
    Member

    the rootless ssh shenanigan is probably not necessary (linger systemd-run --system --scope su --login should be enough) - removing the need to install and setup ssh

    We have to move the tests out of the Ubuntu container so as to allow testing compatibilities with Alma, etc.
    Then we can remove that ssh stuffs

  2. apostasie commented on Mar 19, 2025

    @apostasie
    ContributorAuthor

    the rootless ssh shenanigan is probably not necessary (linger systemd-run --system --scope su --login should be enough) - removing the need to install and setup ssh

    We have to move the tests out of the Ubuntu container so as to allow testing compatibilities with Alma, etc. Then we can remove that ssh stuffs

    Working on it on my fork.

    Likely solution I want to explore for the CI:

    • build full-release in container and get tarball on the host (be it a runner or a lima instance)
    • disable host pre-installed services (containerd, buildkit, docker)
    • execute installation script from the tarball
    • add any necessary addition / modifications for testing on the host, in the GHA yaml file (buildkit namespace, install completion, etc)
    • call ./test-integration.sh
  3. apostasie commented on Mar 19, 2025

    @apostasie
    ContributorAuthor

    Note about exploring for buildkit namespace: service unit could reference an env file to decide of namespace or other parameters (eg: https://github.com/farcloser/lepton/blob/main/Dockerfile.d/lib/systemd/system/buildkit.service#L23-L24)

    It would make changing namespace easier for test rig (just write to the env file then systemctl restart), instead of having to manipulate unit files.

  4. changed the title [-]Possible enhancements to current Dockerfile[/-] [+][CI] Epic: possible enhancements to current Dockerfile[/+] on Mar 19, 2025
  5. polarathene commented on Nov 6, 2025

    @polarathene

    I only had a brief glance over the Dockerfile

    • You have a lot of noise in there that is better extracted out to shell scripts IMO.
    • You've got various RUN layers that could be bundled together via HEREDOC syntax (or if using a shell script all handled within that).
    • Those RUN layers may benefit from cache mounts instead of relying on layer caching that gets busted wastefully by prior layers in the sequence (unless splitting out to non-dependent stages). RUN's that perform builds can leverage a build cache with cache mount, and any meaningful cache there can be cached separately from layer cache which sometimes is trivial to reconstruct.
    • When you're pulling from GH releases, you have options like eget that greatly simplify away noise there for getting the desired release artifact per platform.
    • Consider using COPY --link where appropriate.
    • You really shouldn't be using VOLUME.
      • It wastefully creates anonymous volumes if you're not careful (especially bad if the path given has existing data at that location to copy each time to the host system for each anonymous volume copy).
      • Docker Compose has extra logic to persist such volumes across image upgrades (as they're tied to the service rather than the image instead), but that convenience is not without caveats either and users should honestly be preferring explicit volumes when they want to persist data.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions