In this lesson, we are going to walk through how to create your own images using Dockerfile.
We will start by explaining what Dockerfile is and how it is used to create images. We will specify a starting place for your image, and then go over some of the changes that can be made.
After that, you will start by removing all of the containers previously made using the prune command. If you are working in your own container already, and don’t want to remove everything, use the rm command and specify the containers to remove.
You will create a container that is lightweight enough to only run the required binary without all the extra files you saw in the Ubuntu container made previously.
We will discuss Dockerfile, and how it uses a from instruction to begin the actual building.
Then, you will use hands-on practice to create the Dockerfile. Then we will walk through the process to turn the Dockerfile into the final image.
You will learn how to add a repository or tag to the image in order to create a container with the image.
Finally, we will discuss how the Dockerfile we just made maps to the JSON image file, and how to complete the process of turning our Dockerfile into a format that Docker can work with.
Welcome back, so far we've used existing images that come from Docker registries. In this lesson, we're going to cover how to create your own image, using a Dockerfile. The Dockerfile is text file with commands that you can use to create your own image. This allows you to specify the starting place for you image, and then you can specify the changes you'd like to have made to that image.
Let's start off with a very basic example, first, I want to start by removing all of the containers that I've created. So I'm going to use the Docker container prune command, now, I feel like there should be some flashing lights and loud sirens going off, because this is going to delete all of the stocked containers.
If you don't want to remove all of your containers, then don't use this command. Instead, use the Docker rm command, and specify the containers that you want to remove. Okay, so I'll say yes to the prompt, great. Next, let's look at the images, with a command that is probably becoming familiar to you now, which is the Docker images command.
So you can see there are two images, we have Ubuntu and hello world. We're going to create a new image, here's what I want for this demo, I want a lightweight container, that runs a binary of my own creation, and by lightweight, what I mean is, I'm looking for a container that only has as much as it needs to run my binary.
I don't want a bunch of additional, files, such as what you'd see in an Ubuntu image. Here's the code for the binary, this is a very basic, hello world app, written in Go, and if you want to run this for yourself, the bottom comment here is how you would compile it, so that it will run in the container.
Though, I will mean you'll need to have Go installed and configured. Now I've already compiled the app, and I have the binary ready, so let's look at how to create an image with Dockerfile that can run this binary. A Dockerfile is actually called Dockerfile, it's one word, no extension. Here's the Dockerfile that we'll use for this demo.
It's starts out with a from instruction, which is used to specify the starting image. This allows you to set the image that you want to build on top of. This makes Docker rather flexible, because you can use any of the community images, an image from the store, or you own base image, as the start. This demo uses a special image called scratch, the reason it's special is that it's not something that you can just run, the same way the we ran the hello world or Ubuntu images.
This is meant to be used a minimalist base. So, from scratch, tells Docker, to start our image using the scratch image. The next instruction is the copy instruction, which allows you to copy files from your host, into a layer of the image. In this example, I'm telling Docker, to copy the file named hello, into the root directory of the image.
After the command is processed, a new layer will be created, containing this binary. Any commands that we run afterward, will be able to interact with that hello binary. The copy instruction can copy files, and directories, and it also supports wild cards. And I'm not going to go into each of the aspects of all of these instructions.
So I'll leave a link in the course description, for the documentation so you check it out for yourself. Finally, down at the bottom, we have the command instruction, which is the default command to run, when the container starts up, unless one is specified on the command line. So there should be one command instruction per Dockerfile.
If you have multiple, then it's always going to use the bottom most. This syntax here, has the command to run as an array, where the first element is the binary to execute. And then additional elements in the array, are arguments for the executable. So this is telling Docker to run the hello world binary when the container starts.
Okay, in theory, this Dockerfile should meet the requirements for the demo is set. I said, it should be lightweight, and allow me to run a binary. Because we're using the default scratch image, it's going to be lightweight. And using the copy and command instructions, allow us to run the hello binary. Let's turn this Dockerfile into an image now.
I'm here at the bash prompt, for our CENTOS7 VM, and I'm in a directory containing the Dockerfile, and hello binary. If I run this binary here, it's going to show us what the results will look like when we run it inside of a Docker container. So there you go. Now I'm currently logged in as root, so I don't need to type sudo in front of everything, and if I list off the existing images, you can see that they're currently are the two, that we've used throughout the course so far.
And we don't have any containers at the moment. So, the command to build an image from a Dockerfile, is Docker build, followed by the directory, where the Dockerfile is located. Now you can specify a Dockerfile manually, if you want to have a name other than Dockerfile, we're not going to covert that, but it's minus f, for file.
Check that out in the documentation, if that's something you want to use. In this case, I'm going to use the current directory, because the Dockerfile is located here. And notice it goes through our instructions from the Dockerfile. And it ends with a success message. So, now if we list the images again, it's going to show our new image.
And there it is. So what I want you notice is that, it doesn't actually have a repository or a tag, it does have an ID, though, using an ID to reference an image is, one of the most unintuitive ways you can interact with an image. So I built it this way intentionally, to show you what happens when you build this, without providing a repo name.
Let's remove this image. I want to remove this, and build it again with a repo name. To remove it, we can use the Docker rmi, as in, remove image, and, let's pass in the ID. Okay, now to verify that it's gone, there we go. So, let's do this again, only this time, I'm going to use the minus t flag, to specify the repo and tag.
Let's call this greeting. And there we have it. Notice here at the bottom, it says, it was successfully tagged with greeting, and a colon, and then the word latest. Listing the images again, notice the repository is named greeting, and then there's a tag that says latest. Tags in Docker have their own structure, and they allow you to supply a repo name, and a tag, at the same time.
Because I only provided the repository portion of the Docker tag, it's going to automatically use a tag of latest. In a later lesson, we're going to get into tagging a bit, so for now, the important part of this, is that we have an image, and it has a name that we can use to reference it. And then, if you notice here, it has a size of around 1.
3 megabytes. Now that we have an image, we can actually create a container based on it. So let's give that a try. And we'll do that with the Docker run command, and passing in the repository name of the image. And there it is, there's the output that we expected from the hello binary. So we can use the Docker ps command, with a minus a flag, to show that the container ran, and then it exited successfully, based on this status code of zero here.
I'm going to remove this with a prune command, just to keep things clean for later lessons, as well as to keep showing the common commands. Okay, let's dive into things a bit more in depth now. So let's do a little bit of exploration, again, to help demystify Docker, it's not essential to mastering Docker, but hopefully this will help to, kind of make sense of things a bit.
Earlier in the course, I showed where the image file was stored, and that it's just a JSON file. I want to show how the Dockerfile we just used, maps to that image file, that Docker creates behind the scenes. So here's a listing of the images. If I list the images, you can see the ID for the image we want, the one we just created, it starts with 934.
And it matches up to this image file here. So let's print the contents, of that, then we'll pipe it through JSON beautifier, okay. Here in the history section, notice two objects, the first one makes reference to copying a file, and over here in our Dockerfile, this maps to this copy command. Then the second object, which references the command, maps to the cmd instruction in the Dockerfile.
So Docker turns our simple Dockerfile, into a format that it knows how to work with, making it easier for us, as end users, to use relatively simple extractions. Alright, let's wrap up here. There's still a lot about the Dockerfile that hasn't been covered. And later in the course, we'll cover some more instructions as we go on.
For now, the takeaway is that, you can create your own images using the Dockerfile. It allows you to build your image, based on an existing image, and include any files you may need, and then set the default command to execute, when the container starts up. In the next lesson, we're going to look at another way to create an image, which is, to use an existing container, as a base, make some changes, and then commit that.
So if you're ready to learn more, then I'll see you in the next lesson.
Ben Lambert is a software engineer 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 software, he’s hiking, camping, or creating video games.