Create your own Dockerfiles, Images and Containers
Tags: docker
Published: 2021-08-30 20:49:21

In the first part of this series, I described what Docker is and how it works. 

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. <br/>
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
```
<ol start="3"> <li>Then you specify the requirements needed for your image, using further commands. A full list of commands and their meaning can be found <a href="https://docs.docker.com/engine/reference/builder/">here</a>. </li></ol>
<ol start="4"> <li>You usually end a Dockerfile with a command that will run when a container is started based on the image.</li></ol>  If you'd like to run `app.js` in the container, for example, you'd write<br/>
```
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: 

```dockerfile
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 
```shell
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. <br /><br /> 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. <br /><br /> You can also tag an image by using 
```shell
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
```shell
docker run <image id or name>
```
You can use `--name` to name your containers. 
```shell
docker run --name <your chosen container name> <image id or name>
```
If you don't, Docker will assign a random name for you. <br/>
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
```shell
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
- To list all locally stored images, you can use
```shell
docker image ls
``` 
- To rename an existing image, you can use 
```shell
docker tag <old name> <new name>
``` 
- To inspect an image use
```shell
docker image inspect <image id or name>
``` 
- To remove an image, you can use
```shell
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. 
- To remove all unused images, you can run 
```shell
docker image prune
```
If you want to remove all images, including tagged ones, use the `-a` flag with this command.

### Managing Containers

- To see all running containers, you can use
```shell
docker ps
``` 
where `ps` stands for processes. If you want to see both running and stopped containers, you can run
```shell
docker ps -a
```
where `a` stands for all. 
- To stop a container, you can use
```shell
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. 
- If nothing has changed, you can restart your container by running
```shell
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. <br/><br/> To attach a running container that you started in detached mode, simply run 
```shell
docker attach <container id or name>
```
If you want to restart a container in attached mode, simply run
```shell
docker start -a <container id or name>
```
Similarly, you can also use the `-d` flag for detached with the `docker run` command.
- To run a container in interactive mode (if your programme requires user input for example), use
```shell
docker run -it <container id or name>
```
where `-i` stands for interactive, and `-t` basically for the terminal.
- To remove containers that are not running, you can use
```shell
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
```shell
docker rm <container id or name> <container id or name>
``` 
- 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! 🐳

<sub>Cover image by <a href="https://unsplash.com/@phoebe_dill?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Phoebe Dill</a> on <a href="https://unsplash.com/s/photos/whale?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a></sub>