I'm going to let you in on a DevOps secret here: The thing all DevOpsy people love to do is build a super fancy and complex system, then find a way to deal with it like a regular shell. Or connect to it with SSH and then treat it like a regular shell.
Docker is no different! You are running a computer inside some other computer. Maybe that computer is an EC2 instance or a laptop. You can even get really crazy and run a VM that is then running Docker.
Most of the time when I use Docker I am using it to package and distribute an application. Sometimes I'm using it for something cooler like a distributed computing project. But a lot of times I'm throwing a Dockerfile in a GitHub repo so that I don't have to install CLIs that I just know will eventually conflict on my laptop.
Long story short, you can tell Docker to run the command
bash, which drops you into a shell:
docker run -it name-of-image bash # docker run -it continuumio/miniconda3:latest bash # docker run -it node:latest bash
But keep reading for more. ;-)
Try it out
Google your favorite programming language's Docker up. For me this is Python, and specifically I like conda. Then run a few commands to make sure that you are in fact in that shell.
# From Host echo $(pwd) # Drop into docker shell docker run -it continuumio/miniconda3:latest bash # Now you are in the docker shell! echo $(pwd) echo $USER
Cool, huh? This is perfect for debugging a container that absolutely should be working properly. It's also great for my most common "I don't want to install this to my computer" use case.
Debug a Docker Build with Docker Run
Treating your Docker image like a regular shell will come in handy when trying to debug Docker builds.
Let's say you have a Dockerfile for an image you are trying to build. Normally what happens is that when running
docker build -t my-image . (-t is for tag) Docker will run through each of your RUN steps, and stop when it gets to a command that does not exit properly.
In a UNIX shell, the exit code 0 means that all is well with a command. So to illustrate this point I have made our Dockerfile have a RUN command that exits with 1.
FROM continuumio/miniconda3:latest RUN apt-get update -y; \ apt-get upgrade -y; \ apt-get install -y \ vim-tiny vim-athena build-essential RUN conda update conda \ && conda clean --all --yes RUN exit 1
docker build -t my-image .
This will get you an output that looks like:
(base) ➜ my-image docker build -t my-image . Sending build context to Docker daemon 2.048kB Step 1/4 : FROM continuumio/miniconda3:latest ---> 406f2b43ea59 Step 2/4 : RUN apt-get update -y; apt-get upgrade -y; apt-get install -y vim-tiny vim-athena build-essential ---> Using cache ---> 726af29a48a0 Step 3/4 : RUN conda update conda && conda clean --all --yes ---> Using cache ---> 19478bb3ce67 Step 4/4 : RUN exit 1 ---> Running in 7c98aab6b52c The command '/bin/sh -c exit 1' returned a non-zero code: 1
You can confirm that your Docker image wasn't built by running
docker images and checking for
my-image. It won't be there because it wasn't successfully built.
Now what we can do is to comment out that troublesome Dockerfile RUN command.
FROM continuumio/miniconda3:latest RUN apt-get update -y; \ apt-get upgrade -y; \ apt-get install -y \ vim-tiny vim-athena build-essential RUN conda update conda \ && conda clean --all --yes #RUN exit 1
Then what you will see is:
Sending build context to Docker daemon 2.048kB Step 1/3 : FROM continuumio/miniconda3:latest ---> 406f2b43ea59 Step 2/3 : RUN apt-get update -y; apt-get upgrade -y; apt-get install -y vim-tiny vim-athena build-essential ---> Using cache ---> 726af29a48a0 Step 3/3 : RUN conda update conda && conda clean --all --yes ---> Using cache ---> 19478bb3ce67 Successfully built 19478bb3ce67 Successfully tagged my-image:latest
You can now drop into your Docker image and start interactively running commands!
docker run -it my-image bash # you can also run # docker run -it my-image:latest bash
From here, one by one, you can start debugging your RUN commands to see what went wrong. If you're not sure if a command exited properly or not, run
# First run docker run -it my-image bash to get to the shell # Print the string hello echo "hello" # hello echo $? # 0 # Run a non existant command hello $(hello) # bash: hello: command not found echo $? # 127
You can keep running these steps, commenting out your Dockerfile, dropping into a shell, and figuring out problematic commands, until your Docker images builds perfectly.
Hopefully I have shown you that using a Docker image is no different from the terminal on your computer. Using Docker images is an awesome way to distribute applications.
Try to take your favorite CLI application or next GitHub project, and instead of creating an install script, package it up with Docker. ;-)