Intro to Docker (Part 2)

Intro to Docker (Part 2)

Create your own Dockerfiles, Images and Containers

In Intro to Docker (Part 1), I described what Docker is and how it works in theory.

To recap, Docker is a service that helps us package and isolate our code in containers. Containers are standardised, independent units of software and running instances of images. They contain our code and all required dependencies. Images are read-only templates for containers that are built by the Docker Engine and based on Dockerfiles. Dockerfiles are text files that you write that include all the commands needed to build a specific image in the order in which they should be executed.

Now that we've covered the most fundamental Docker concepts, let's take a look at how you can create your own Dockerfiles, images and containers, assuming you've already installed Docker on your machine.

How to create a Dockerfile

  1. To create your own Dockerfile, simply start by creating a file named Dockerfile ๐Ÿ˜„ It doesn't have an extension and shouldn't include one.
  2. If you want to create a base image, one that isn't based on an existing image, start by writing
    FROM scratch
    
    However, most images are based on a base image, also called a parent image for that particular image. Since the exact process for a base image will depend on what you want to package, we will focus on creating an image with a parent image. If your code is written in Node.js, for example, you'd start by writing
    FROM node
    
  3. Then you specify the requirements needed for your particular image, using further commands. A full list of commands and their meaning can be found here.
  4. You usually end a Dockerfile with a command that will run when a container is started based on the image. If you'd like to run app.js in the container, for example, you'd write
    CMD ["node", "app.js"]
    
    If you specify several CMD, only the last one will be executed. If you specify none, the CMD of the parent image will be executed.

An example Dockerfile for a simple node application might look like this:

FROM node 

WORKDIR /app 

COPY ./someFolder /app 

RUN npm install 

EXPOSE 80 

CMD ["node", "app.js"]
  • FROM specifies the base image or parent image, in this case node
  • WORKDIR tells Docker that all subsequent commands should be executed inside a particular folder, in this case the app folder, including COPY and RUN
  • COPY copies the content of one folder into a folder in the image file system, in this case it will copy the contents of someFolder into the app folder. If the app folder doesn't exist yet, it will be created in the image file system. What you specify depends on which files on your local machine you want to go into the image
  • RUN specifies the command that will be run when the image is created, in this case npm install to install all the dependencies of your node application
  • EXPOSE, as you might have guessed it, exposes a specific port in the Docker container, in this case port 80, which can be accessed by a local machine
  • CMD makes sure node server.js will be run when a container is started

How to create Images and Containers

Once we have written a Dockerfile, we can run docker build in the terminal, along with the path or url where the Dockerfile is located. This command tells the Docker Engine to build a custom image based on the Dockerfile. The image is built with a specific id that you can use to run a container based on the image.

Creating Images

To build an image, use

docker build <file or url of Dockerfile>

Images are read-only. That means that you need to rerun the docker build command and rebuild your image if you've made any changes to your Dockerfile.

The order of the commands in a Dockerfile matter, because images are layer-based. When you build an image, every command in the Dockerfile creates a new image layer and the result of each command is cached, so it's not rerun if nothing changes. If you modify the port in the Dockerfile above, for example, by writing EXPOSE 50 instead of 80 everything before that command will not be rerun.

You can also tag an image by using

docker build -t name:tag

You might want to use tags to version your images, though they can be used for all kinds of purposes.

Creating Containers

To run a container based on an image, use

docker run <image id or name>

You can use --name to name your containers.

docker run --name <your chosen container name> <image id or name>

If you don't, Docker will assign a random name for you.

You can also run Docker containers on a specific port. If we want to use port 3000 on our local machine to access port 80 in the Docker container that we specified in the Dockerfile above, for example, we could write

docker run -p 3000:80 <image id or name>

where p stands for publish.

How to manage Images and Containers

After creating some images and containers, you might want to manage or delete them. Here are some common commands that will help you do that.

Managing Images

  1. To list all locally stored images, you can use
    docker image ls
    
  2. To rename an existing image, you can use
    docker tag <old name> <new name>
    
  3. To inspect an image use
    docker image inspect <image id or name>
    
  4. To remove an image, you can use
    docker rmi <image id or name>
    
    Please note that images belonging to a container, whether running or not, cannot be removed. To remove those images, you need to remove the corresponding containers first.
  5. To remove all unused images, you can run
    docker image prune
    
    If you want to remove all images, including tagged ones, use the -a flag with this command.

Managing Containers

  1. To see all running containers, you can use
    docker ps
    
    where ps stands for processes. If you want to see both running and stopped containers, you can run
    docker ps -a
    
    where a stands for all.
  2. To stop a container, you can use
    docker stop <container id or name>
    
    If you're in a hurry, you can also use docker kill, which will stop the container immediately instead of giving it some time to shut down gracefully.
  3. If nothing has changed, you can restart your container by running
    docker start <container id or name>
    
    A difference between docker run and docker start is that the former runs the container in attached mode by default. This means that the container is started in the foreground and the output is printed in the terminal, which you cannot use at the same time. docker start on the other hand, starts the container in detached mode, so it's running in the background and you can still use your terminal.

    To attach a running container that you started in detached mode, simply run
    docker attach <container id or name>
    
    If you want to restart a container in attached mode, simply run
    docker start -a <container id or name>
    
    Similarly, you can also use the -d flag for detached with the docker run command.
  4. To run a container in interactive mode (if your programme requires user input for example), use
    docker run -it <container id or name>
    
    where -i stands for interactive, and -t basically for the terminal.
  5. To remove containers that are not running, you can use
    docker rm <container id or name>
    
    You can remove several containers at the same time by listing several container ids or names while leaving a space between them like so
    docker rm <container id or name> <container id or name>
    
  6. To remove containers automatically after they are stopped, use the --rm flag.

Sharing Images on Docker Hub

To share your images on Docker Hub, use docker login to log in to the default Docker Hub repository or to a specific registry, and docker push <image id or name> to upload your image to the Docker Hub. To use an image from the Docker Hub, simply run docker pull <image id or name>.

When you use docker run <image id or name> and you don't have a local image with that name, Docker will automatically pull the latest version from Docker Hub. If you do, Docker will not check that it's the latest version, so to ensure that it's the most recent version, you need to pull it from Docker Hub first.

Final Words

And that's it. Those are all the commands you need to create, manage and delete some images and containers, and to share your images on Docker Hub. I realise that it's quite a lot and I might create a summarising cheatsheet at some point, but this is it for now.

If you're new to Docker, I'd suggest creating a simple sample application in a language you're familiar with and playing around with the Docker commands above.

Happy dockering! ๐Ÿณ

Did you find this article valuable?

Support J.H. by becoming a sponsor. Any amount is appreciated!