Docker
Tags: docker, devops, beginners
Published: 2021-07-07 09:36:43

## What is Docker?

Docker is a tool that allows developers to package applications and all their dependencies in a container (sandbox) and deploy and run the application on host operating system (Linux). 

## What are Containers, Images?

A container is a discrete process that has been isolated from all other processes on the host machine. This is made possible by [Kernel Namespaces and Cgroups](https://medium.com/@saschagrunert/demystifying-containers-part-i-kernel-space-2c53d6979504). This decoupling allows applications to run consistently anywhere isolated from the rest of the applications. Containers run images, which contain everything required to run: application code, runtime environment, dependencies, scripts, etc. Containers run layers of stacked images (eg. a base linux image (mostly alpine due to its compact size), node image and application image on top). 

## Docker vs VMs

Note that docker containers do not run in their own virtual machines. VMs run on virtual hardware provided by host OS, whereas containers share the host kernel, thus avoiding computation overhead for virtualizing hardware. Also note that Docker requires Linux runtime environment, so running Docker on Windows/Mac requires a single Linux virtual machine that is shared among all the containers run in the system.
## Installation

Download and install docker from [Docker docs](https://docs.docker.com/get-docker/).

Check if installation was successful using
```shell
docker version
```

## Docker Hub

[Docker hub](https://hub.docker.com/) is the official public repository from docker hosting images of widely used applications. 

## Pull from docker repository

Pull image from repository (by default Docker hub unless specified otherwise).

```shell
docker pull redis
```

## List all images

View all locally available images.

```shell
docker images
```

```shell
docker image ls
```

## Run container

Creates a new container from an image, and execute the container. Pulls image from repository (Docker Hub) if container is not found locally. Notice that different layers are pulled separately from the repository. Splitting the layers provides the advantage that if some layers already exist locally, they are skipped from downloading again.
 
```shell
docker run postgres:9.6
```

```
docker run docker/whalesay cowsay Hello World!
```

Run in detached mode. Returns id of the container and starts running the container.
```shell
docker run -d postgres:9.6
```
Re-attach detached container. Requires id of detached container.

```shell
docker attach ee8f2eb0b433
```

## docker ps

View all currently running containers. 
```shell
docker ps
```

View all running and stopped containers (history). Containers can be restarted using the given container id.
```shell
docker ps -a
``` 

## Stop container
Stops the running container. Requires id/name of container. Returns id/name of stopped container.

```shell
docker stop ee8f2eb0b433
``` 

## Start container
Can be used to restart stopped container. Requires id/name of stopped container. Returns id/name of started container. It does not create a new container, unlike `docker run`. All configurations are restored which were set while running `doc run` command.
```shell
docker start ee8f2eb0b433
```

## Port Mapping [Host port : Container port]
`docker ps` displays the port the application is listening inside the container. To connect to this port inside the container, we need the IP the application is running in. Every Docker container gets an IP assigned by default. This is an internal IP and can only be accessed from the host machine. Users outside of the docker host cannot access using this IP. For this, we need to use the IP of the docker host and map a free port from the docker host to the container port. You can also run multiple instances of application and map them to different ports on the host machine.

There should be a binding between the host port and container port. Conflicts will occur if you open two same ports on your host machine. Different containers can however listen on the same port because these two containers are completely isolated of one another. 

```shell
docker run -p6000:6379 redis
```
Port 6000 of host machine is binded with port 6379 of container running redis. Redis runs on port 6479. This can be viewed using `docker ps`.

## Display logs

View logs of docker container (eg. while using in detached mode). Requires id/name of running container.
 
```shell
docker logs 777f0e06c4c
```

```shell
docker logs 777f0e06c4c | tail
```

## Custom naming containers

```shell
docker run -d --name my-custom-name redis
```

## docker exec

Get the terminal of the running container as a root user. `-it` stands for interactive terminal. `-i` maps the standard input of the host to the Docker container. `-t` stands for pseudo terminal,which displays prompts from the terminal. Requires id/name of container. You can even navigate around the virtual file system, check logs, manage environment, etc.

```shell
docker exec -it 777f0e06c4c /bin/sh
```

```shell
docker exec 777f0e06c4c cat /etc/hosts
```
## Docker network

Docker creates it's own isolated network where the containers are running in. When two containers are deployed in the same docker network, they can refer to each other using just the container name because they are in the same network. Applications running outside of docker connect to this network using localhost:port-number. 

## Display docker networks

Displays all docker networks.
```shell
docker network ls
```

## Create new docker network

Creates new docker network

```shell
docker network create my-network
```

## Docker run with network option
To run containers in custom docker network, it should be specified in the docker run command.

```shell
docker run -d \
-p27017:27017 \ 
-e MONGO_INITDB_ROOT_USERNAME=admin \
-e MONGO_INITDB_ROOT_PASSWORD=password \
--name mongodb \
--net my-network \
mongo
```
```shell
docker run -d \
-p8081:8081 \
-e ME_CONFIG_MONGODB_ADMINUSERNAME=admin \
-e ME_CONFIG_MONGODB_ADMINPASSWORD=password \
--net my-network \
--name mongo-express \
-e ME_CONFIG_MONGODB_SERVER=mongodb \
mongo-express

```

`-e` sets the required environment variables.

## Docker inspect
View details of a container in JSON format such as mounts, configuration settings, network settings, etc. Requires id/name of container.

```shell
docker inspect dreamy_hellman
```

## Docker Compose

With docker compose, docker commands can be mapped to a file, so that configurations are well structured. Note that format of Docker-compose yaml file depends on the version of docker-yaml.

```yaml
version: '3'
services:
    mongodb:
        image: mongo
        ports:
            - 27017:27017
        environment:
            - MONGO_INITDB_ROOT_USERNAME=admin
            - MONGO_INITDB_ROOT_PASSWORD=password
    mongo-express:
        image: mongo-express
        ports:
            - 8080:8081
        environment:
            - ME_CONFIG_MONGODB_ADMINUSERNAME=admin
            - ME_CONFIG_MONGODB_ADMINPASSWORD=password
            - ME_CONFIG_MONGODB_SERVER=mongodb
        depends_on:
            - mongodb


```
The network configuration is not included in docker compose. Docker compose will automatically create a common network for all services listed in the docker compose file. Save it as a .yaml file.

## Using Docker compose yaml file

### Start a container

```shell
docker-compose -f my-docker-compose-file.yaml up
```

### Stop a container
```shell
docker-compose -f my-docker-compose-file.yaml down
```

## Dockerfile

A dockerfile is a document for building images. Filename must be `Dockerfile`.
```dockerfile
FROM node

ENV MONGO_INITDB_ROOT_USERNAME=admin \
    MONGO_INITDB_ROOT_PASSWORD=password

RUN mkdir -p /home/app

COPY . /home/app

WORKDIR /home/app

CMD ["node","server.js"]
```
`FROM` *image* - build on a base image
`ENV` - set environment variables
`RUN` - execute any Linux commands on the container environment
`COPY` - executes on the host. Copies file from host to the container
`WORKDIR` - set current working directory so that you don't need to provide full path from /home.
`CMD` - executes an entry point. There can be multiple RUN commands, but only one CMD command. The command and parameters should be separate entries in the list.

```dockerfile
FROM ubuntu

CMD sleep 5
```
Running `docker run sleeper sleep 10`, the `sleep 10` will overwrite the command specified on `CMD`. To provide custom arguments, use the `ENTRYPOINT` command.
```dockerfile
FROM ubuntu

ENTRYPOINT ["sleep"]
```

Running `docker run sleeper 10` will append the argument `10` to the list of `ENTRYPOINT`. 

To set a default value for argument, use the combination of both `ENTRYPOINT` and `CMD`. Both `ENTRYPOINT` and `CMD` should be specified in JSON format.
```dockerfile
FROM ubuntu

ENTRYPOINT ["sleep"]

CMD ["5"]
```

If no arguments is provieded, argument from `CMD` will be appended to `ENTRYPOINT`. If arguments are provided, they will be appended to `ENTRYPOINT` and the values on `CMD` will be disregarded. You can also overwrite the whole entrypoint command using 
```shell
docker run --entrypoint somethine-else sleeper 10
```

## Building image from Dockerfile

```shell
docker build -t my-app:1.0 .
```
my-app - image name
1.0 - tag
. - path to `Dockerfile`

## Delete image
Remove image from local host. Requires image id. Image id can be found running `docker images`. Image can be deleted if all containers built from the image are deleted.

```shell
docker rmi e455275b486c
```
### Delete container
Remove container from local host. Requires container id/name. Container id/name can be found running `docker ps`. 
```shell
docker rm silly_gagarin
```

### Data persistence using Docker volumes
Data from virtual file system of container is lost when the container is restarted or removed. Docker volumes is used where changes needs to be persisted between restarts. Using Docker volumes, a folder in the physical file system is mounted in the virtual file system of the container. The data is automatically replicated between host file system and virtual file system. Thus, data is persisted even when the container is turned down. 

## Docker Volume types
### Host volumes
Host volumes allows you to determine where on the host file system the data is replicated. 
```shell
docker run -v /home/mount/data:/var/lib/mysql/data
```
This is in the form host-file-system-path:virtual-file-system-path.

### Anonymous Volumes
Only path in the virtual file system is provided. Docker automatically maintains where in the host file system the replica should reside. For each container, a folder is generated in the host file system path `/var/lib/docker/volumes/{hash}/_data`.

```shell
docker run -v /var/lib/mysql/data
```
### Named Volumes
This is similar to anonymous volumes, but you can specify the name of the folder on the host file system. This type of volume can be referenced simply using the name, so you don't have to know the path.

```shell
docker run -v name:/var/lib/mysql/data
```
### Volumes in docker-compose
```yaml
version: '3'
services:
    mongodb:
        image: mongo
        ports:
            - 27017:27017
        environment:
            - MONGO_INITDB_ROOT_USERNAME=admin
            - MONGO_INITDB_ROOT_PASSWORD=password
        volumes:
            - my-volume:/data/db
    mongo-express:
        image: mongo-express
        ports:
            - 8080:8080
        environment:
            - ME_CONFIG_MONGODB_ADMINUSERNAME=admin
            - ME_CONFIG_MONGODB_ADMINPASSWORD=password
            - ME_CONFIG_MONGODB_SERVER=mongodb
volumes:
    my-volume:
        driver: local
```
Note that here we have used named volumes. Also note that we can mount the reference of the same folder on the host file system to more than one container, for example if the containers need to share the same data.

## Container Orchestration
Container orchestration contains of multiple docker hosts that can host containers, so that if one fails, the application is still available through the others. Some orchestration solutions can help automatically scale up the number of container instances when users increase and scale down when the demand decreases. They also help in load balancing requests across hosts. Some container orchestration solutions are docker swarm, kubernetes and mesos.

Happy Hacking!