The time came to take down a Docker container and deploy the image onto another server. Easy enough. Only, the original maintainer was long gone. Rumor had it that he was off the grid. So, even if he remembered what the precise run command was, there was no way to contact him.
I was in a bit of a bind. The container had to be moved, but I didn’t have full confidence in restarting it with the proper configuration. The bad news is that the docker run command with accompanying arguments isn’t stored in its original form. The good news is that I learned that the run command can be reverse engineered fairly easily.
When faced with the question “How is this container being run?” it’s tempting to take to Google, search whatever you probably searched to find this article, and be on your way. But such an approach is flawed.
It is imperative that anyone deploying docker apps in production understands the `docker inspect` command and how runtime arguments map to low-level fields. Furthermore, any docker app running in production should have the production run command(s) included in the documentation (or should just use compose). While the layperson can’t be sure that such a command was actually used to start a container, a little bit of docker inspecting can quickly reveal the truth. Aside from that, including the run command in the docs will serve as a jumping-off point for developers who will follow in your footsteps.
My guidance is that being able to reverse engineer a container’s run command by using inspect should be a prerequisite for maintaining a production app. If there’s one esoteric field that you’re not familiar with–okay–it’s acceptable to use a library, but it should be relegated to a learning tool, not a developer dependency.
The first solution I’ve used is Rekcod. Yes, it’s Docker spelled backwards. Yes, I’ve mistakenly swapped the ‘k’ and ‘c’ nearly every time I’ve used it. Can you guess how Rekcod works? Well, here’s a big hint. You can install it using npm then pass your container ID into reckod to instantly generate the run statement.
For example, I’m running Portainer somewhere:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES bfc10e0d639b portainer/portainer "/portainer" 2 days ago Restarting (1) 25 hours ago kickass_ritchie
All I have to do is run rekcod and I’ve got everything I need. Notice that it is likely to be more verbose than the actual original command.
[root@portainer ~]# rekcod bfc10e0d639b docker run --name kickass_ritchie -v /var/run/docker.sock:/var/run/docker.sock -v /opt/portainer:/data -p 9000:9000/tcp --restart always -h bfc10e0d639b --expose 9000/tcp -e 'PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin' -d --entrypoint "/portainer" portainer/portainer
Rekcod is a handy tool, but can be dangerous when used foolishly (as is the case with most things). Since it’s not tied to the docker project, it can fall out of sync with the major releases, meaning that the command generated may be wrong. Even now, it may generate the wrong command. Blind reliance on a tool like this–especially in a production environment–is a dangerous idea.
Another solution is Runlike. As for its functionality, well, it does the exact same thing. Runlike is a more useful learning tool because the python parser very clearly encapsulates the formatting of each field from its associated inspect field. And bonus points for pip! Something like that.
The last solution I’m aware of is guaranteed to provide the exact command, but must be implemented beforehand. If you are staring aimlessly at the docker ls output, you’ve missed your chance. Simply, you can implement either a bash command trap or a script that saves your docker command history. Then, each time you execute a docker run command, it will be saved–exactly–and can be recalled however you see fit. Depending on the docker setup, you might have to store additional metadata in order to map it to a specific instance, but this is a workable solution.
There are some unique solutions to this problem, and though I advocate for their usage, I more strongly emphasize taking the time to understand the docker inspect file itself.