[LS] Docker 101

Before jumping into the actual content, this post (and the upcoming ones) is one of the cheat sheets I keep in my Notion account. Since these notes were intended for personal use, the writing style is such that it is easy for me to understand. I want to share them online in case someone finds them useful.


Let's say you wanted a very quick intro to what is Docker and how to use it..


docker-cover.png

What is Docker

Docker is an open source platform that enables developers to package applications into containers that are easy to manage, build, deploy, run and update.

What is a Container

A container is a way to package an application with all the necessary dependencies including a virtual file system and configurations. This package is portable and easy to move around (e.g. between dev teams). Containers live in a container repository (DockerHub or private repositories). With containers you don't have to install any services directly in your OS because a container is its own isolated OS layer. It doesn't matter which OS you are using in your machine, a Docker container will be downloaded and run with the same command which makes setting up your local development environment much easier and much more efficient. A container is made of layers of images.

Image vs Container

A Docker image is an actual package (e.g. postgres) including the configuration, dependencies and the start script. It's the artifact that can be moved around. A Docker container is the running environment that is created when you pull the image in your local machine and you start it. So if it's not running it's an image/artifact that's lying around and if you start it it's a container. An analogy could be that an image provides the tools and a container is where they are used in practice.

Docker vs Virtual Machine

Operating systems have two layers (excluding hardware), the OS Kernel and the Applications layer.

Docker virtualizes the Applications layer. When you download an image, it contains the Applications layer of the OS and it uses the Kernel of the host machine. On the other hand VMs virtualize the complete OS. When you download a VM image it doesn't use your host Kernel, it puts up its own.

  • The size of Docker images are much smaller comparing them to VM images.

  • Docker Containers can run much faster than VMs because every time you start a VM it has to put the OS Kernel and the Applications layer on top of it.

  • Docker lacks on compatibility. You can run a VM image of any OS on any other OS host. You can't do that with Docker (you can still use Docker Desktop though).

Container Port vs HOST Port

If you have multiple containers that listen to the same host port (e.g. you run different versions of Redis in different containers) you will get conflict errors. A host port should not be used by multiple container ports. You need to create a binding between your host port and the container port you want (you can use the docker run -p ... command to do that).

Docker Network

Docker creates its isolated Docker network where the containers are running in. When you deploy two containers in the same network (e.g. mongo and mongo express) they can talk to each other by using just the container name without the localhost, the port number etc. The applications that run outside of the network can connect by using the localhost and a port number (e.g localhost:3000).

Docker Compose

If you have multiple containers that you need to run in parallel then docker run is not the most efficient way to do it. You can use docker-compose instead. With a docker compose file (.yaml) you can take all the configuration from the docker run commands and map them in a structured way. Docker compose will take care of creating a common network for all the services listed in the docker compose file so that they can talk to each other correctly.

Dockerfile

In order to build a Docker image from an application we basically have to copy the contents of that application into the Docker file. A Dockerfile is a blueprint for building images.

Syntax

The first line of all Dockerfiles is a FROM statement where you specify the image that will be the base of your new image. You can use ENV to specify environment variables. You can use RUN to execute any Linux command inside the container. An important notice here is that RUN commands run inside the container and not on the host. You can use the COPY command to copy files from the HOST on the container image. CMD is always part of the Dockerfile and is the entry point for running the application. When you make a change to the Dockerfile you MUST rebuild the image. One last thing, the docker compose file is always called Dockerfile.

Docker Volumes

When you restart the containers all the data are gone. In order to have persistent data you need to use docker volumes. The host machine has a physical file system that is mounted into the docker container to offer a virtual file system. If we change something on the physical file system it will automatically appear on the virtual file system. To create a named volume you can use something like docker run -v {name}:/var/lib/.../mysql/data command and reference the volume by the name you gave it. You can also specify the volumes on the docker compose file (.yaml).

Docker Commands

  • docker pull {image_name}:{version}: Downloads the image locally.

  • docker run {image_name}:{version}: If the image is not previously downloaded locally it pulls it from the DockerHub (or the specified private repository) and then it starts it by executing the start script right away.

    1. Use -d to run the container in a detached mode.

    2. Use -p{host_port}:{container_port} to bind ports between the host and the container (e.g. docker run -p6001:6379). This helps to resolve port conflicts.

    3. Use --name {name} to give a name to the container.

    4. Use --net {network_name} to connect to a network.

    5. Use -e {ENV_VAR=...} to set/override a value on a env variable.

    6. Use -v {name}:{path} to bind mount a volume.

  • docker ps: See a list of all the running containers.

    1. Use -a to see all the containers (running/stopped).
  • docker stop {cid/name}: Stops a container.

  • docker start {cid/name}: Starts a container.

  • docker exec -it {cid/name} /bin/bash: Get an interactive terminal access on the specified running container. You may need to use /bin/sh instead of /bin/bash.

  • docker network: Docker Networks

    1. Use ls to see a list of your docker networks.

    2. Use create {name} to create a new network.

  • docker logs {cid/name}: See the logs on a container.

    1. Use | tail to see the last part of the logs.

    2. Use -f to stream the logs.

  • docker-compose -f {file}.yaml: Run multiple containers.

    1. Use up to start the containers.

    2. Use down to stop the containers. It also deletes the network and recreates the next time you run the up command.

  • docker build -t {image_name}:{tag_name} {path_to_Dockerfile}: Build an image according to a Dockerfile.

  • docker rmi {id}: Delete an image.

  • docker rm {cid/name}: Delete a container.

  • docker images: List your local images.