Today we would get rid of all unnecessary stuff in our Docker images.
To be honest, this task is not really useful in real life due to layered filesystem Docker uses. So even in case of huge images, you store base only once. Then it’s all about relatively small deltas.
Only sane reason which I’m able to imagine is security: let’s say we don’t want to simplify hackers life by providing system utilities. :) But another side is that we also make our IT guys life a little bit of hell. They would be almost unable to investigate issues on running containers.
Anyways, here’s how you make things done:
Prepare source image
Hello-world Node.JS app in my case:
# Dockerfile
FROM node:8-alpine
RUN apk update && apk add bash # We must add bash to let magic happen
WORKDIR /app
COPY ./main.js /app/main.js
CMD ["node", "/app/main.js"]
Then I just docker build
it tagged as my-nodejs-app
.
Strip the image
This is when I need some additional tool: strip-docker-image.
Here I use my own fork as original application doesn’t support Alpine at the moment. Update: now it does, author merged it in.
git clone https://github.com/agrrh/strip-docker-image
cd strip-docker-image
./bin/strip-docker-image -v \
-i my-nodejs-app:latest \
-t my-nodejs-app:stripped \
-f /usr/local/bin/node \
-f /app/main.js
Let’s see if it works:
$ docker run -ti my-nodejs-app:stripped
Server running at http://0.0.0.0:8081/
We need to go deeper
If we’re able to run something but our app?
$ docker run -ti my-nodejs-app:latest ls
main.js
$ docker run -ti my-nodejs-app:stripped ls
docker: Error response from daemon: OCI runtime create failed: container_linux.go:296: starting container process caused "exec: \"ls\": executable file not found in $PATH": unknown.
Great. Just what we needed.
But how much space did we saved?
# latest 72.7MB
my-nodejs-app latest 528c21f4392a About an hour ago 72.7MB
# stripped 38.7MB
my-nodejs-app stripped 1a09fb91336e About an hour ago 38.7MB
Sadly, not too much.
Let’s see how it looks from inside:
$ docker image save my-nodejs-app:stripped -o tmp.tar
$ tar xvf tmp.tar
$ tar tf <layer-id>/layer.tar
app/
app/main.js
lib/
lib/ld-musl-x86_64.so.1
usr/
usr/lib/
usr/lib/libgcc_s.so.1
usr/lib/libstdc++.so.6
usr/lib/libstdc++.so.6.0.22
usr/local/
usr/local/bin/
usr/local/bin/node
And that’s all content of our fully-functional containerized app.
Seems fun, but remember not to be penny wise and pound foolish.