TL;DR: I used a
FROM scratch docker build to make a website (including web
server) that uses 8MB of disk space and 5KB of memory.
%CPU %MEM VSZ RSS COMMAND 0.0 0.2 8052 5220 /bin/site IMAGE ID SIZE b3b83fa14acd 7.93MB
This blog is not a static site. Knowing me, I'll end up wanting to do something dynamic on it at some point, so I might as well set up for that anyway. Instead, it is a small C++ application, which currently keeps everything in memory.
I was first going to just build a statically linked binary to deploy it, but at
least one of the libraries I wanted to use didn't have a static library (
file) available in my package manager. Instead, I implemented modern
technology's solution to the problem: a docker image (I run other services in
docker, so this was a natural choice).
My first step was to create a Dockerfile that built the C++ application in an
environment similar to my development machine. Starting from my distro's base
image, installing the libraries and tools, and calling
make on my project
resulted in a working, running container serving my site. Because this is a
small C++ application that directly serves HTTP, it even runs in just over 5k
FROM gentoo/stage3-amd64 # install libraries # copy source in WORKDIR /build RUN make # we now have a /build/bin/site executable CMD ["bin/site"]
But this docker image has a full operating system in it. It's hundreds of MB.
It doesn't need to be that big. The appropriate solution here is to use a
multi-stage docker build, so the final image doesn't have all your build tools
in it. Because I'm trying to be minimal, my final image is
FROM gentoo/stage3-amd64 AS site-build # install libraries # copy source in WORKDIR /build RUN make # we now have a /build/bin/site executable FROM scratch AS site-deploy COPY --from=site-build /build/bin/site /bin/ CMD ["/bin/site"]
Of course, if I try to run this, it blows up - the 'site' binary is dynamically
linked, and it can't find any of the required shared libraries. We can see what
libraries an application needs by using
$ ldd /bin/bash linux-vdso.so.1 (0x00007fff315f1000) libreadline.so.8 => /lib64/libreadline.so.8 (0x00007f0ea09a8000) libtinfo.so.6 => /lib64/libtinfo.so.6 (0x00007f0ea096b000) libc.so.6 => /lib64/libc.so.6 (0x00007f0ea07ab000) libtinfow.so.6 => /lib64/libtinfow.so.6 (0x00007f0ea076c000) /lib64/ld-linux-x86-64.so.2 (0x00007f0ea0b11000)
So, on my machine, to run 'bash' in a from-scratch container, I also need to
copy these libraries into the container. While I could do this manually by
adding a bunch of
COPY commands to the Dockerfile, I'm also a programmer,
which means I'm allergic to manual work. Instead, I threw together a tiny
program to parse the output of the ldd command and copy all the required files
into a single target directory:
Not-documented-or-cleaned-up Github repository here.
Using this tool, in the -build image, we copy everything we need into a /dist folder, and then just copy that entire folder into the scratch container.
FROM gentoo/stage3-amd64 AS site-build # install libraries # copy source in WORKDIR /build RUN make # we now have a /build/bin/site executable RUN cp bin/site /dist/bin RUN ldd-dep-cp bin/site /dist/lib64 FROM scratch AS site-deploy COPY --from=site-build /dist/ / CMD ["/bin/site"]
The final result of this is a minimal image containing only what is required for this program to run, weighing in at just 7.93MB.