2. Creating a Container

The three things you’ll need to know to create a container are:

What is a container image?

What is a Dockerfile?

How do I build the image?

Container Images

To create a container image, you’ll use a Dockerfile which is a list of instructions used to move files, install dependencies, and attach volume/network meta information. Container images are atomic, which means they will not change between deployments — think of burning an OS image to a physical disk. Each time you start a container, you’ll get the exact same result, regardless of the underlying environment.

What happens when you build an image:

The set of instructions used to make your image is read and processed.

After the instruction is processed, the result is stored as a layer.

Once all instructions have been executed, the resulting layers are merged into a container image.

This atomic container image can now be deployed to Cycle, hosted locally, or shared with other developers.

In the past, standardizing development, testing, and production environments was a major problem. Now you can be confident that you’ll observe the same behavior on any system and that’s a really fantastic benefit!

The set of instructions we need in order to build the image is called a Dockerfile.

Dockerfile

Let’s pretend for a minute you just installed a new virtual machine on your host operating system. You want to see how your application will run on CentOS7 so you log in, upload your applications, install your dependencies, and secure the network endpoints.

A fresh Centos7 install

With an individual server, the process above is easy — but when you need to reproduce this result thousands of times, host it on multiple infrastructure providers, or share it with other developers, this can become very challenging.

Automation scripts can help but what happens if you have a network timeout while installing dependencies? What if the underlying OS at different infrastructure providers has slightly different versions of its underlying system binaries?

Containers are the answer to the above problems. By writing a Dockerfile with instructions similar to the process above, developers can easily create a fully atomic image that can be deployed with ease.

Docker describes a Dockerfile as, “A text document that contains all the commands a user could call on the command line to assemble an image”.

Below, we’ll dive into a few basic concepts re: Dockerfiles.

Picking a Base Image

A Dockerfile will have a base or parent image, declared by the FROM command. This is like saying, “hey Docker I know I at least want these dependencies, let’s start here”. For example, most Node applications will start with the line:

FROM node:alpine

Starting Your Main Process

The command you use to start your application will be at the end of the Dockerfile. So when the image is executed in the container runtime it knows how to start the process. Following our Node example, someone could use this command to start their node process:

CMD ["node", "index.js"]

Copy, Run, and Other Commands

As you progress on your image building journey, you’ll use other commands such as: COPY , RUN , LABEL , ADD , ENTRYPOINT , USER , VOLUME , EXPOSE , and ENV .

If you want to take a deeper dive into the Dockerfile reference you can access it here. Now that we have a basic Dockerfile we can build our image.

Building Your Image

Make a new directory with a Dockerfile in it.

On the first line of the Dockerfile write FROM alpine:latest

Save the Dockerfile and then run → docker build -t myfirstimage .

Now you have your first image, it’s that easy. Feel free to modify your new Dockerfile. When you want to rebuild the image use the docker build command from above. I’ve included a basic example of a Dockerfile below.

A very basic Dockerfile.

Tags

There are two ways to approach building different versions of your image.

Create a different image for each build. 👎 Tag the image with a container image tag name. 👍

To tag an image, simply append the tag name to the image name while running the docker build command:

docker build -t myfirstimage:v1

Tagging an image can convey information like the version of the application or the underlying operating system that’s being used. In the example Dockerfile above you can see that the node base image I’m using is tagged with alpine . This lets developers know that this node image was built on top of Alpine Linux, a minimalist distribution known for its small size. Building an image without specifying a tag name will result in a tag of latest .

Build Cache

When you build an image several times, Docker will use cached layers to build your image if possible, this is called the build-cache.

As each instruction is examined, Docker looks for an existing image in its cache that it can reuse, rather than creating a new (duplicate) image. ~Docker

You can use the build cache to your advantage by copying the files that change the most, closest to the end of your Dockerfile. Then when you go to rebuild your image, Docker will reuse the cache it has of the unchanged layers so you won’t have to wait for them to build again.

For example, when building a Node app copy your package.json files and run npm install before copying the rest of the app. That way if you need to rebuild due to changes to your source code, your image can reuse all the packages imported via your npm install again thanks to the cache. This can save a few minutes a build.

This pattern is included in the example Dockerfile above

Next, let’s take a look at how to share our images so others can create containers from them.