What is a Docker Container?š¦
In the previous post, we saw how to find Docker images form the DockerHub, and also how to create our own Docker image that executes
a function. In this post, we will go more over the execution part of Docker, mainly about how to run a Docker image as a container.
Docker Containers are Docker images that become containers at run time. Docker containers are short pieces of software that runs an application quickly, as a lightweight standalone package. The containers need an engine to run, and this is the Docker Engine.
Basically, Docker containers make it easier to decentralize a large piece of software such that it can run in isolated environments, and they are what we can refer to as microservices
. So in this part of the post, we will be running our first use case of microservices
. We will start with running a single Docker image first, then we will combine many Docker images to run together so that they can interact with each other and run the whole software.
Running a Containerš
In the previous post, we saw how we can either pull a pre-built Docker image from the DockerHub, or create our own custom image. We saw at the end that we had to run
a specific command in the terminal to get the output from a Docker image. The commands that we ran in the previous post ran the Docker image, and during runtime, it created a Docker container that runs the piece of code that we put inside our own image or the outputs from a pre-built image from the DockerHub.
If you followed along with the previous post, you should now see the Ubuntu
image in the terminal when you execute the below command in your terminal.
> docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
custom_image latest 432ba341135f 4 weeks ago 932MB
ubuntu latest 6b7dfa7e8fdb 5 weeks ago 77.8MB
In case you did not follow the previous post, we can run the following command in the terminal first to pull a Docker image and then continue:
docker pull ubuntu:latest
Now we are ready to proceed. To start the Docker container, we can start by typing the following command in the terminal
docker run ubuntu
If all is correct, you should see, well nothing. But why is that? We just ran our Ubuntu
image as a Docker container, but we don’t see anything on the terminal when we run it.
docker ps
command
To check whether a particular image is successfully run or not, we need to execute the following command in the terminal:
docker ps
The docker ps
command lists all the containers. So if you ran the Ubuntu
image as per the previous step, then the docker ps
command should show that the Ubuntu
container is running. Let’s try to check if that is the case. In the terminal, execute the following command:
docker ps
However, the output we see in the terminal should be the following:
> docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
Hmm, weird right? The documentation says that the docker ps
command gives us a list of the containers. But we do not see anything in the terminal. This is because the docker ps
command only list the containers that are running
by default. And for our Ubuntu
image, we did not give any specific command to run when we started the container via the docker run
command. Which is why it is not showing up under the docker ps
command. To check if the container was ranning when we started it, we need to pass the -a
flag to the docker ps
command. Let’s run the following command in the terminal to check this:
> docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4c42acd7b1cd ubuntu "bash" 10 minutes ago Exited (0) 10 minutes ago condescending_villani
We should see something like above in the terminal, with a different string under the NAMES
and the CONTAINER ID
column. Let’s see what terms shown in the output of the docker ps -a
command mean.
Below is a list explaining what each of the terms given in the output above mean:
CONTAINER ID
: This column lists the id that a Docker container is given when it is run. This is a random id that is generated everytime a Docker container is run. If we run the same Docker image multiple times to start containers, it is very unlikely that theCONTAINER ID
will be the same across different containers.IMAGE
: This column gives the name of the Docker image that was used to start the Docker container. In our case, since we started the container using the commanddocker run ubuntu
in the terminal, theIMAGE
section correctly lists theubuntu
image in its run.COMMAND
: This column lists the default command that is run when the Docker container is started. Since for the case of theubuntu
container, we did not specify any command, a defaultbash
command is run on startup.CREATED
: This field mentions the time when the container is started.STATUS
: This field gives us a status of the container. In our case, we see the status code as beingExited (0) 10
. This signifies that theubuntu
container was started, and is now shut down and is not running. TheSTATUS
field helps us figure out the current status of a Docker container once it is started, and it can take different values, ranging fromCreating ...
toUp ...
, which indicates that the container is either starting up or it is running for the amount of time mentioned after theUp
string, respectively.PORTS
: This field gives a list of ports that are to be exposed from inside the Docker container to the local user’s machine, so that the local user can access the port outside of Docker’s networkNAMES
: This field outputs the name that the docker container has when it is running. The name of a Docker container when it is run is also generated at random everytime a Docker container is started, but unlike theCONTAINER ID
field, the name can be controlled and kept static for a particular Docker container.
So, these are all the fields that are showed by the docker ps -a
command. Now let’s start tampering with the docker run
command to get a bit more out of the containers when we run them.
Passing arguments to docker run
commandā¼ļø
In the previous step, we just used the docker run
command out of the box on the Ubuntu
image. However, that did not give us much, not even a friendly terminal to let us inside the container that is running. Let’s now try to get inside the Ubuntu
container to check what it has.
To do this, we first need to make the Ubuntu
container execute a different command at runtime when it is started. For this, we can pass additional arguments to the docker run
command in the terminal as follows:
docker run ubuntu sleep 3600
The sleep
command that we pass is not a Docker argument, but a Linux
system argument.
Tip
: You can check more about the sleep command by running man sleep
in your terminal (Only works for Linux-based OS like Ubuntu or MacOS)
The sleep
command will override the default bash
command from the Ubuntu
container, and make the container sleep for the 3600 seconds 60 minutes. That should give us enough time to check what is inside the container.
So, once we have run the above command, we should see that the terminal does not exit like in the initial docker run
command, but is in a way just stuck. However, if you open a new terminal and list all the containers with docker ps -a
, we should now see the following:
> docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8ec5ceccbd72 ubuntu "sleep 3600" 3 seconds ago Up 1 second fervent_mclaren
Notice how in the STATUS
field it now shows Up 1 second
. And also take a look COMMAND
section to see that it now shows sleep 3600
as the command instead of bash
like the previous case. That means the command that we passed while starting the container worked, and is executed as soon as the Ubuntu
container is started. Let us now see what is inside the Ubuntu
container.
Going inside a running container
Now that the ubuntu
container is running, we can look inside it. The default method to look what is inside a container is to get a terminal, and we can do the same for the ubuntu
container. To get a terminal inside the container, we can run the following command:
> docker exec -it <identifier>
The exec
command is used to run a command inside a running container. And to let Docker know which container we want to choose to execute the command, we need to pass an <identifier>
above, which can either be the NAME
of the container or the CONTAINER ID
. Let’s pass the CONTAINER ID
for now, but we can pass NAME
in exactly the same way.
Pro Tip
: For passing the CONTAINER ID
as an identifier for the exec
command, we do not need to pass the full container id. Instead, we can just pass the first 3 characters of the CONTAINER ID
to the exec command, and it will still be executed inside the correct running container.
To execute and get a shell inside the running container, run the following command:
docker exec -it 8ec bash
Please make sure to pass the correct first 3 characters from your CONTAINER ID
, since every CONTAINER ID
is randomly generated, and your CONTAINER ID
will not be the same as mine.
Once we run the above command in a new terminal, we should see the following output:
> docker exec -it 8ec bash
root@8ec5ceccbd72:/#
Well, well. We are now inside our running Docker container!!!š
We can now ls
to check what files/directories the container has, and we can look around inside them as part of exploration. Let’s now see how we can give a name to a Docker container when we run it, and why is it useful.
Some tips for running Docker containersš”
- It is very beneficial to give your Docker container a name, since doing so will always maintain a streamlined process of seeing inside the container and accessing it for checking your processes. This can be done by passing in the
--name
flag to thedocker run
command, which is aDocker
flag unlike thesleep
command we saw earlier. Let’s execute the following command in the terminal:
docker run --name my_ubuntu_image ubuntu sleep 1000
You can pass either my_ubutu_image
as the name of your container, or you can get creative and pass something that you like as a name for your Docker container. Once we run the above command, you can open a new terminal and check running containers with docker ps -a
. Once you do, you should now see something like the following output:
> docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f04164a6395f ubuntu "sleep 1000" 3 seconds ago Up 1 second my_ubuntu_image
Notice how the docker container now has in the NAMES
field the name my_ubuntu_image
, or whatever name you passed along in the --name
flag. Now if you want to exec
inside the running container, instead of passing the CONTAINER ID
, we can pass the name of the container and it will go inside. Let’s try this out with docker exec -it my_ubuntu_image bash
to get bash inside the docker container. You should definitely see something like the following:
> docker exec -it my_ubuntu_image bash
root@f04164a6395f:/#
Now we can always refer to our container while running commands with its name.
- Now that we have a
Ubuntu
container running, we need to know how to stop it. We need to stop old containers before running new ones, since Docker does not let us run two containers with the same name twice. To check this, run the following command again in a new terminal:
docker run --name my_ubuntu_image ubuntu sleep 1000
You should get the following error message:
> docker run --name my_ubuntu_image ubuntu
docker: Error response from daemon: Conflict. The container name "/my_ubuntu_image" is already in use by container "f04164a6395f2c472bc5c6f6e59e304baa793b5d7edf106066e0764b51e1ad5d". You have to remove (or rename) that container to be able to reuse that name.
The error message tells us that there is a conflict with the container name, since we are already running a Ubuntu
container with the same name. To rerun a new container with the same name, we first need to stop the one that is running currently. For this, we need to run the following command in a new terminal:
> docker stop my_ubuntu_image
Once you run this command, the terminal will be back after the command is executed successfully, and if you now get a list of all the containers, you should see the following:
> docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f04164a6395f ubuntu "sleep 1000" 12 minutes ago Exited (137) 9 seconds ago my_ubuntu_image
As you can see, the STATUS
field now shows Exited ...
under it instead of the previously shown Up for x seconds
. This means that our Ubuntu
container has stopped running, and we are ready to start another container with the same desired name.
- As per the above step, you now know how to stop a docker container. However, if you run the container again, you will still get the same error message with the
name conflict
as before. That is because docker not only needs you to stop the container, but to completely remove it before starting a new container. To do this, we can run the following command in a new terminal:
docker system prune
You should then show the following prompt in your terminal window:
> docker system prune
WARNING! This will remove:
- all stopped containers
- all networks not used by at least one container
- all dangling images
- all dangling build cache
Are you sure you want to continue? [y/N]
docker system prune
simply removes all the unnecessary data from your Docker environment, like stopped containers, containers that could not start correctly, or cache that a container created on your machine when it was started. In the above prompt, type y
and press enter to let docker clear all stopped containers and the data related to them. Once this is successful, you should see a message as follows:
Deleted Containers:
f04164a6395f2c472bc5c6f6e59e304baa793b5d7edf106066e0764b51e1ad5d
Total reclaimed space: 98B
This message indicates that all the stopped containers are now removed, alongwith the caches that they had created when started. To check if there are any containers still, you can run
docker ps -a
.
If you want to manually remove only one specific container from the stopped containers afters stopping it, you can run the following command instead:
docker rm <container name or id>
This will remove only the container whose name or id you passed.
The methodology of running containers from pre-built docker images can be extended to running custom images as containers that we create according to the previous post, or any custom container of your choice. All the list of commands remain the same, only the pre-built ubuntu
image from the above commands needs to be replaced with your own custom image.
Running multiple containers and make them interact with each other
So far, we have seen how to run a single image as a container, give it a name, pass some commands to override the default commands, how to stop and remove it. However, large pieces of software are rarely built on only one running container, and there are always many containers handling one specific task of the software independent of other tasks that are run by the software.
For this, we need to run multiple containers at once, and these containers also need to be able to interact with each other in order to make the software run successfully. Once simple examples where different containers can be run to do different tasks is run a PostgreSQL
database to store some results from an API, a container to run the API itself, and a pgAdmin UI tool to interact with the database to check if the data that the API returns are being stored successfully inside the postgreSQL database. This sounds like a very general purpose use-case that many companies have in common, and this can be managed very efficiently via docker containers. We will see more about this in the next post of Docker Compose
files, which allows us to run multiple containers at once.