The course is part of this learning path
In this lecture, we will change pace. Instead of using Dockerfile to create images, we will explain how to use the commit command to create than and commit the changes to our containers.
We will start off as the root user, and through bash for CentOS and demonstrate how to use the Docker commit command through hands-on practice.
You will use the docker ps command to verify that no containers are running or stopped. We will be using the Ubuntu image that was previously created for this exercise.
Next, we will check to see if Python is installed. If it is not, then we will install it with apt. Then we will verify that the install worked properly.
Now that our container has Python installed, we going to turn it into an image using the commit command.
You will review the image, and see all the potential errors that are created by using a default image. We will recreate the image using the change command.
Finally, you will launch the image again and verify if everything looks the way you expect.
Welcome back. Throughout the course I've mentioned images and containers. Though, the difference between the two may not be clear. So, that's what we'll do in this lesson. We're going to take a look at the difference between images and containers. At high level, the difference between the two is similar to the difference between an executable and a running application.
Each running application is its own instance and independent of the others. The running application is also independent from the executable in that changes to the app won't impact the executable. In this analogy, the executable is like an image, and the running app is like a container. An image is a template that defines how the container will look once it creates an instance.
Images are built on the concept of layers. There is always a base layer, and then there is some number of additional layers that represent file changes. Each layer is stacked on top of the others, consisting of the differences between it and the previous layer. The union file system that we talked about in a previous lesson is used to merge the changes.
Because Docker builds images from layers, and the different layers are file diffs. The layers usually don't take up too much space on disk. Let's demonstrate the difference between images and containers in a terminal. To start, I'll switch to the root user that way I don't have to type sudo so much. Okay so, so far we've used two images.
One was the hello-world image, the other was for Ubuntu. We can see those images with the docker images command. Notice here there's a table that displays the image name, the tag, which are both tagged as latest, and there's the image ID which is the first few characters of the SHA256 hash for the image.
And take a look at the image sizes. The hello-world image is very small because it contains just a single binary. The Ubuntu image however is a bit larger because it contains all of the software that makes Ubuntu, Ubuntu. That means software such as the apt package manager is included in that. So these are the two images that I have locally.
Using the docker ps command will show all of the running containers. And as you can see there aren't any here. However, by adding the minus a flag you can see that this lists off all of the previously run containers. Let's explore the /var/lib/docker directory a bit, because this is where all of the images and containers are actually stored.
Looking at the contents of /var/lib/docker you can see we have a directory here for containers and one for images. Let's drill into the one for images, and into the image/overlay directory. Here you can see some directories for images and layers. From here let's drill into imagedb/content/sha256 and notice there are two files here with names consisting of sha256 strings.
If I run the docker images command again, notice that the image ID is a truncated version of these hashes. So this is the file here that is actually the hello-world image, and this one is the Ubuntu image. Now these files are just JSON files. Let's take a look, let's show that by printing out the output of the Ubuntu image.
I'm going to pipe it through the Python JSON module, that's going to make it a bit easier to read. Okay, there's a lot of info here, and honestly, you don't need to know any of this to successfully use Docker. The reason I'm showing this is to part one, I wanna demystify Docker just a bit, by pulling back the curtain.
And two, I want to kind of show you how the image is comprised of different layers. By scrolling down to the history array you can see that there are these objects here with the created_by property. These are the commands that need to be executed to turn the default layer named scratch into an Ubuntu container.
Each command produces a new image layer. So behind the scenes the Ubuntu image starts with the default starting image which is called scratch, it's just a lightweight image. And then it adds all of the files it needs and then executes a series of command on those files, and the final result is an Ubuntu image consisting of several layers.
Now I wanna emphasize again, understanding this directory structure or these file structures is not required for understanding Docker. It's just going to help you understand just a bit better how things are working behind the scenes. Now, as a Docker user, you won't be using these files directly, rather, you're going to use abstractions such as the Dockerfile which will be covered in a later lesson.
And when you want to view information about an image, you don't even need to browse to the /var/lib/docker directory, you can use things like the Docker inspect command, and it's going to give you most of the same info. Though inspect isn't going to show you the command you use for each layer like this.
So, we've talked about images a fair amount. They're the template used for creating containers. Let's highlight the difference by starting up a container, making some changes to it, and then starting another container to show how they're separate. Let's use the Ubuntu image and let's run the bash process.
So using the I and t flags, it's going to allow us an interactive terminal session. Okay, so we're currently at the bash prompt for this container. We're currently in the root directory. And there's nothing in the home directory. Okay, I wanna add a new directory for all of my awesome files in the home directory.
Now let's CD into that. And now let's create a file by echoing some text. Let's echo this only exists in this container. And we'll direct that output to container. txt. Okay, let's verify that it exists. And there's our text. Remember when we exit a bash process, the container will stop. That's because we told Docker to create a container based on Ubuntu, run the bash command.
So once that process terminates, Docker has done everything we've asked of it. Now running the Docker ps shows us that there are no running containers. Let's run another bash process with the Ubuntu image. And there it is. We have our bash prompt. Let's look in the home directory. And notice that nothing is here.
This is exactly what you'd expect. Because each container is independent of the others. Okay, we can see the two containers that we just created by using the Docker ps command with the minus a flag. And I wanna shrink this text down just a bit so that this table is easier to read. Okay, the container we just ran is the one at the top.
And the one before that is the one with the text file. The important part of this table at the moment is the list of names. Because I didn't supply any name for these containers, so, Docker provided these names. So you can optionally add your own name, and if not, Docker will name them. I'm going to copy the name of this container here.
This is the one with the text file. The container is currently stopped, so we're gonna learn a new command. And we're going to use the Docker start command. Followed by the name of the container. Okay, let's see if the container is running with the Docker ps command. And it is, so, perfect. You may have noticed that the bash prompt on the screen is not the one for our container.
That's because we started it and it's running in the background now. So now in order to interact with it, we need to use the Docker attach command and passing the container name to attach to it. Great. So, here we are at the bash terminal for our container. And this should have our text file. And let's verify.
And there it is. So, what did all of this prove? We created a container based on the Ubuntu image. And then we added a text file to that container. Then we started up a separate container, also based on Ubuntu, and the text file wasn't there. It didn't exist in that container. That's because each time you create a container, it's based on how the image looks at the moment the container is created.
Alright, let's wrap up here and summarize what we've covered. Images in Docker are self-contained packages that are used to create containers. Making containers an instance of an image. Alright. In the next lesson, we're going to look at how to create Docker images using the Dockerfile. So, if you're ready to keep learning, then I'll see you in the next lesson.
About the Author
Ben Lambert is the Director of Engineering and was previously the lead author for DevOps and Microsoft Azure training content at Cloud Academy. His courses and learning paths covered Cloud Ecosystem technologies such as DC/OS, configuration management tools, and containers. As a software engineer, Ben’s experience includes building highly available web and mobile apps.
When he’s not building the first platform to run and measure enterprise transformation initiatives at Cloud Academy, he’s hiking, camping, or creating video games.