First (published) blog post of the year! :raising_hands:
Here is a rather dumb way of entering a Docker Compose container that didn’t have a shell. In this specific case, I was trying to enter a Woodpecker CI container without exiting it. Some Docker containers are incredibly stripped down to optimize away bloat (which is good!) but this may make debugging them relatively annoying.
These are my specific steps for running it, please replace the paths and container names with the ones relevant to your specific use-case.
At first, I tried an approach following this document. But once I got to actually running commands within the namespace, I realized that this exposes the exact same interface as Docker compose; if there was no shell available from the Docker container entirely, then I couldn’t run something from outside. I would need to get some shell into the container.
Fortunately, there’s a software that contains a lot of handy tools in one
binary: busybox. It’s a GPL software that contains a small implementation
of some coreutils (the Unix utilities like
cp) in a single static
I grabbed a copy of the busybox tool using:
$ nix build nixpkgs#pkgsStatic.busybox
(if you aren’t using Nix, you may want to grab one of the pre-built binaries from their website)
To make sure it’s statically linked (this means it doesn’t depend on any libraries already existing on your system, which may not be available within the container), run this:
$ ldd ./result/bin/busybox not a dynamic executable
You should be all good if it comes back with “not a dynamic executable”.
Otherwise, if you’re downloading off the website, make sure you look for
something that indicates you’re downloading a version built with
which means it’s using a static implementation of libc.
Now it’s just a matter of getting this binary into the container.
$ docker compose cp ./result/bin/busybox woodpecker-server:/
Now, I can run a shell, using the busybox program:
$ docker compose exec woodpecker-server /busybox sh / #
If you just ran busybox without the
sh param, then it would list all the
busybox utilities that were built into your static binary. At this point, I also
/ # /busybox --install -s /bin
within the container. This installs symlinks to busybox so it acts as each of
the individual programs it emulates. Now I can type
ls instead of having to
busybox ls every single time.