flatnotes icon indicating copy to clipboard operation
flatnotes copied to clipboard

Reduce size of Docker image

Open Code-Otto opened this issue 2 years ago • 10 comments

With some modifications to the Dockerfile I have achieved a 100MB size reduction in the resulting container image under my x86_64 test machine (264MB -> 165MB)

Changes done are:

  • Used the python:3.11-alpine3.19 image as runtime container base
  • Replaced gosu with su-exec from Alpine Linux world
  • Cleaned pip and pipenv package cache from resulting runtime image

I've found it to work great under my setup so far, however have not tested the authentication methods thoroughly yet

Code-Otto avatar Mar 26 '24 20:03 Code-Otto

Hey @Code-Otto, thanks for the contribution. I like the ideas here but unfortunately this PR doesn't currently build on linux/arm64. Are you able to look at this?

dullage avatar Mar 27 '24 13:03 dullage

Looks like the cffi Python library (and probably many more) does not offers prebuilt musl-libc/arm64 binaries so pipenv tries to compile it from sources but since our container lacks a build environment and C compiler it fails

I'm facing this same problem trying to build for the armv7 architecture (wanna run Flatnotes in my home Raspberry Pi server) and got an idea on the works, which if functional should also fix arm64

I'll keep you updated

Code-Otto avatar Mar 27 '24 17:03 Code-Otto

Got a solution fixing linux/arm64 builds and enabling linux/arm/v7, linux/arm/v6 and linux/i386 platform compatibility

I've moved the pipenv installation to a new separate build stage in the Dockerfile, from where the newly installed Python libraries are copied to the final runtime container This pipenv-build stage gets populated with C / C++ / Rust compilation tools and required libraries (As found to be required on a trial-and-error basis) so pipenv can compile sources whenever prebuilts are not available

As a nice side effect, the pipenv tool and its dependencies no longer make it into the runtime container saving around 35MB from the runtime container

Code-Otto avatar Mar 28 '24 00:03 Code-Otto

This seems like a good approach. Do you think using python:3.11-slim-bullseye for the Pipenv build stage would negate the need to manually add dependencies? As this image isn't deployed there isn't a need to use Alpine.

dullage avatar Mar 28 '24 12:03 dullage

That'd be nice but unfortunately since Alpine and Debian use different sets of libraries (IE musl v.s glibc libc) the binaries compiled in them cannot be guaranteed to run on each other That's why the Pipenv build stage image needs to match what's at the runtime image

Code-Otto avatar Mar 28 '24 14:03 Code-Otto

Ah, that's a shame.

If I'm honest, I'm not keen on the additional complexity here and if it were just for the smaller image I wouldn't be keen. But enabling builds for other architectures would be great so I'll run some tests as soon as I get a chance.

Cheers for your efforts 👍🏻

dullage avatar Apr 03 '24 20:04 dullage

Okay, that's understandable

We could turn back to using Debian images here to revert the new dependency maintenance burden, while keeping the main approach to make armv7/6 builds possible (And release container sizes would still be getting some reductions)

Also should this ultimately not merge feel free to take any changes you like from this PR and adapt them into a solution of your own

Code-Otto avatar Apr 03 '24 21:04 Code-Otto

I haven't try but some python libraries could be installed with alpine's apk

https://pkgs.alpinelinux.org/package/v3.19/community/armv7/py3-josepy https://pkgs.alpinelinux.org/package/v3.19/main/armv7/py3-cffi

qaqland avatar Apr 04 '24 05:04 qaqland

Been testing with a Debian container as a base and I'm afraid the statement from my previous comment is wrong: no matter Debian or Alpine our armv7/6 builds will require the build dependencies (libffi libffi-dev libssl3 openssl-dev) to be manually installed

I've summarized the current situtation here:

Original Dockerfile/Debian x86_64 arm64 armv7/6, i386
Can build :white_check_mark: :white_check_mark: :x:
PyPi prebuilts *[1] :white_check_mark: :white_check_mark: -
Smallest size :x: :x: -
New Dockerfile/Debian x86_64 arm64 armv7/6, i386
Can build :white_check_mark: :white_check_mark: :white_check_mark:
PyPi prebuilts *[1] :white_check_mark: :white_check_mark: :x:
Smallest size :x: :x: :x:
New Dockerfile/Alpine x86_64 arm64 armv7/6, i386
Can build :white_check_mark: :white_check_mark: :white_check_mark:
PyPi prebuilts *[1] :white_check_mark: :x: :x:
Smallest size :white_check_mark: :white_check_mark: :white_check_mark:

[1] Prebuilt library binaries available from PyPi, no need for the build stage and manual dependency installation

Some of the possible ways forward I can think of:

  • No armv7/6 compatibility, no maintenance burden -> Keep the original Dockerfile as is and apply the changes regarding cleaning pip/pipenv cache, still would save ~20MB

  • armv7/6 compatibility, some maintenance burden -> Turn my Dockerfile solution back to Debian. If the pipenv build setup breaks x86_64 and arm64 builds will still work like usual because PyPi prebuilts are available and only other architectures builds would break. Higher savings from no pip/pipenv tools ending up at the release image

  • armv7/6 compatibility, smallest containers, highest maintenance burden -> My current Dockerfile solution as is. If the pipenv build setup breaks only x86_64 will still build correctly (PyPi prebuilts available). Highest savings

Also an hybrid solution: add my solution as a new 'experimental' Dockerfile for advanced users who want a more compact/raspberry-pi image and leave the original as the default, safe route

Looking forward for your thoughts on it. Hopefully I've not made this even more confusing with my verbosity :sweat_smile:

@qaqland thanks for the heads up, this could maybe open up an alternate route... (?)

Code-Otto avatar Apr 04 '24 15:04 Code-Otto

Great job @Code-Otto, this is an awesome breakdown!

Although support for an older version of arm has been requested before I think it was a one-off and I don't expect the demand to be high (and will lessen as time progresses). Equally, a smaller container size is a noble goal but weighed up against additional complexity and maintenance, my preference is always for simplicity (hence the design ethos for flatnotes).

Your 'experimental' Dockerfile idea is good though and I'd be happy to include one. Maybe Dokerfile.experimental? I'm equally happy to include the cache savings as well.

Thanks again for your efforts.

dullage avatar Apr 04 '24 20:04 dullage

Pushed a new commit moving the new Dockerfile to Dockerfile.experimental and restoring the previous Dockerfile with added pip/pipenv cache cleanup, as well as a entrypoint script tweak so it's compatible with the images produced by both them

Code-Otto avatar Apr 12 '24 15:04 Code-Otto