The Dockerfile
is one of the key features to Docker's success. The ability to build a new container image from a simple text file changed the technology game.
When it comes to modifying a Docker image, our first thought is modifying the underlying Dockerfile
. In today's article, I'm going to show you another way to create and change a Docker image. We will do this using the docker commit
command.
Starting with a Base Image
In today's article, we are going to take a base Redis container image and add a new user to the container. While this example may be a bit generic, it shows how we can use the docker commit
command.
Docker's commit
command allows users to take a running container and save its current state as an image. This means to add our new user, we will need a running container. To get started, let's go ahead and launch a Redis container with the docker run
command.
$ docker run -d redis 99eebc7db55d7039dcf6f36726c2492003ab06249b009e7b080648f4cc168015
In the above command, we can see that we started a container in the background using the redis
image. Let's first verify that the container is in fact running with the docker ps
command.
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 99eebc7db55d redis "docker-entrypoint..." 49 seconds ago Up 47 seconds 6379/tcp upbeat_panini
Now that we have a running container, let's verify that our new user doesn't already exist. To do this, we will be using another Docker command, docker exec
.
$ docker exec -it upbeat_panini /bin/bash root@99eebc7db55d:/data#
In the above output, we can see that the docker exec
command worked, but what did it do? Let's take a second to explore what exactly the above command does.
The docker exec command is used to execute a command against a running Docker container. In the case above, we told the docker exec
command to execute /bin/bash
. This along with the -i
(interactive) and -t
(pseudo TTY) flags enabled us to log in to the running container.
We can see that we are logged into the container via the bash
prompt, which now shows root@99eebc7db55d
. If we look at the docker ps
output above, we can see this same number. The number shows as the CONTAINER ID
number of the upbeat_panini
container. This is the same container we specified with our docker exec
command.
Now that we are logged into the container, let's go ahead and verify if our "new" user already exists. We can do this by searching for the user's username in the /etc/passwd
file.
root@99eebc7db55d:/data# grep example /etc/passwd
With the above output, we can see the user does not exist within this container.
Adding a User and Saving the Image
Since we have verified that the example
user does not exist on a base redis
container, let's go ahead and add that user to our running container. To do this, we will use the useradd
command.
root@99eebc7db55d:/data# useradd -g redis example
If we once again execute the grep
command against the /etc/passwd
file, we should see this user now exists.
root@99eebc7db55d:/data# grep example /etc/passwd example:x:1000:999::/home/example:/bin/sh
Now that our user is added, let's go ahead and exit
our container going back to the host system. From there, we can execute the docker commit
command to save our image changes.
When executing the docker commit
command, we will need to provide two parameters: the name of the running container upbeat_panini
and the name of our desired image output testredis:example
.
$ docker commit upbeat_panini testredis:example sha256:6f68e12ee78732258a4fdfedca3ab164a5c9ea330ed28c9cb0d531477706373b
In the above output, we can see that the docker commit
command returned a sha256
hash. Seeing this hash indicates that our docker commit
command was successful.
Let's double-check that our image was in fact created using the docker images
command.
$ docker images testredis:example REPOSITORY TAG IMAGE ID CREATED SIZE testredis example 6f68e12ee787 10 hours ago 183MB
With the above, we can see our testredis
image is in fact created.
!Sign up for a free Codeship Account
Seeing the Differences
With our new container image saved, let's go ahead and see how well our docker commit
worked. We can do this by issuing the same grep
command via a docker run
execution.
First, let's verify that our same base redis
container doesn't have the example
user.
$ docker run redis grep example /etc/passwd
From the above, we can see that the example
user doesn't exist within our base redis
image. This might be a bit confusing at first, but once we think about how Docker works, it makes complete sense.
Earlier when we logged into our upbeat_panini
container, we added the example
user. When we did this, we only added that user to the running container. Any changes we make to a running container has no effect on the original image. This is essentially the stateless nature of Docker containers.
While our base redis
image doesn't have our example user, what about testredis:example
?
$ docker run testredis:example grep example /etc/passwd example:x:1000:999::/home/example:/bin/sh
As expected, our new testredis:example
image does include an example
user. From here, we can use this image to create new containers or push it to a registry such as DockerHub.
Summary
In today's article, we explored a way to make changes to Docker images without a Dockerfile
. Learning about the docker commit
functionality is interesting; in this author's humble opinion, docker commit
should be used sparingly.
In the argument of using docker commit
versus a Dockerfile
, I personally believe that the Dockerfile
method is more future-proof. A Dockerfile
has the distinct advantage of being text, meaning it can be added to source control such as Git. This is in addition to being used to build a image and having that image pushed to a registry.
Do you have another opinion or a use case that a Dockerfile
doesn't fit but docker commit
does? Add your opinion in the comments below.