Docker is a tool that helps you build, test, ship and run applications seamlessly across various machines. It replicates the environment our software needs on any machine. You can get Docker for your machine from docs.docker.com/get-docker/
It has grown in popularity over the last decade due to being lightweight and fast as compared to virtual-machines that are bulky and slow. Unlike VMs, docker does not need a full blown OS of its own to be loaded to start and does not compete for resources other than what the application it is running will use. VMs on the other hand are pretty resource intensive on our processors, disks and memory hence running multiple VMs for various applications becomes a challenge in a limited capacity architecture.
┌────────────────────────┐ ┌───────────────────────┐ │ ┌───────────┐ │ │ ┌───────────┐ │ │ │ App │ │ │ │ App │ │ │ └───────────┘ │ │ └───────────┘ │ │ ┌────────┐ ┌────────┐ │ │ ┌────────┐ ┌───────┐ │ │ │ Libs │ │ Deps │ │ │ │ Libs │ │ Deps │ │ │ └────────┘ └────────┘ │ │ └────────┘ └───────┘ │ │ ┌───────────────────┐ │ │ ┌──────────────────┐ │ │ │ Guest OS │ │ │ │ Guest OS │ │ │ └───────────────────┘ │ │ └──────────────────┘ │ │ VM1 │ │ VM2 │ └────────────────────────┘ └───────────────────────┘ ┌──────────────────────────────────────────────────┐ │ Hypervisor │ └──────────────────────────────────────────────────┘ ┌──────────────────────────────────────────────────┐ │ Host OS │ └──────────────────────────────────────────────────┘ ┌──────────────────────────────────────────────────┐ │ Hardware Infrastructure │ └──────────────────────────────────────────────────┘ (VM based architecture) ┌────────────────────────┐ ┌───────────────────────┐ │ ┌───────────┐ │ │ ┌───────────┐ │ │ │ App │ │ │ │ App │ │ │ └───────────┘ │ │ └───────────┘ │ │ ┌────────┐ ┌────────┐ │ │ ┌────────┐ ┌───────┐ │ │ │ Libs │ │ Deps │ │ │ │ Libs │ │ Deps │ │ │ └────────┘ └────────┘ │ │ └────────┘ └───────┘ │ │ Container1 │ │ Container2 │ └────────────────────────┘ └───────────────────────┘ ┌──────────────────────────────────────────────────┐ │ Docker │ └──────────────────────────────────────────────────┘ ┌──────────────────────────────────────────────────┐ │ OS │ └──────────────────────────────────────────────────┘ ┌──────────────────────────────────────────────────┐ │ Hardware Infrastructure │ └──────────────────────────────────────────────────┘ (Docker based architecture)
Couple of terms we will encounter frequently are Docker Images and Docker Containers. Images are packages or templates of containers all stored in a container registry such as Docker Hub. Containers are standalone, executable instances of these images which include code, runtime, system tools, system libraries and settings - everything required to get the software up and running. Coming to Docker, it follows a client-server architecture wherein the CLI client communicates with the server component, which here is, the Docker Engine using RESTful API to issue commands.
The Docker CLI ¶
1# after installing Docker from https://docs.docker.com/get-docker/
2# To list available commands, either run `docker` with no parameters or execute
3# `docker help`
4$ docker
5
6>>> docker [OPTIONS] COMMAND [ARG...]
7 docker [ --help | -v | --version ]
8
9 A self-sufficient runtime for containers.
10
11 Options:
12 --config string Location of client config files (default "/root/.docker")
13 -c, --context string Name of the context to use to connect to the daemon (overrides DOCKER_HOST env var and default context set with "docker context use")
14 -D, --debug Enable debug mode
15 --help Print usage
16 -H, --host value Daemon socket(s) to connect to (default [])
17 -l, --log-level string Set the logging level ("debug"|"info"|"warn"|"error"|"fatal") (default "info")
18 --tls Use TLS; implied by --tlsverify
19 --tlscacert string Trust certs signed only by this CA (default "/root/.docker/ca.pem")
20 --tlscert string Path to TLS certificate file (default "/root/.docker/cert.pem")
21 --tlskey string Path to TLS key file (default "/root/.docker/key.pem")
22 --tlsverify Use TLS and verify the remote
23 -v, --version Print version information and quit
24
25 Commands:
26 attach Attach to a running container
27 # […]
28
29$ docker run hello-world
30# `docker run <container-name>` is used to run a container, it will pull the
31# images from Docker Hub if they don't already exist in your system. Here the
32# docker client connects to the daemon which in turn pulls the "hello-world"
33# image from the Docker Hub. The daemon then builds a new container from the
34# image which runs the executable that produces the output streamed back to the
35# client that we see on our terminals.
36
37$ docker run -d ubuntu sleep 60s
38# The -d (or --detach) flag is when we want to run a container in the background
39# and return back to the terminal. Here we detach an ubuntu container from the
40# terminal, the output should be the id and the command exits. If we check
41# running containers, we should still see ours there:
42# CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
43# 133261b4894a ubuntu "sleep 60s" 3 seconds ago Up 2 seconds vigorous_gould
44
45$ docker run <container-id> -p 3000:8000
46# The -p (or --publish) flag is used to expose port 8000 inside the container to
47# port 3000 outside the container. This is because the app inside the container
48# runs in isolation, hence the port 8000 where the app runs is private to the
49# container.
50
51$ docker run -i
52# or
53$ docker run -it
54# Docker runs our containers in a non-interactive mode i.e. they do not accept
55# inputs or work dynamically while running. The -i flag keeps input open to the
56# container, and the -t flag creates a pseudo-terminal that the shell can attach
57# to (can be combined as -it)
58
59$ docker ps -a
60# The `docker ps` command only shows running containers by default. To see all
61# containers, use the -a (or --all) flag
62# Running the above command should output something similar in the terminal:
63# CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
64# 82f84bf6912b hello-world "/hello" 9 minutes ago Exited (0) 9 minutes ago eloquent_sammet
65
66
67$ docker stop hello-world
68# or
69$ docker start hello-world
70# The stop command simply stops one or more containers, and the start command
71# starts the container(s) up again! `docker start -a ubuntu` will attach our
72# detached container back to the terminal i.e. runs in the foreground
73
74$ docker create alpine
75# `docker create` creates a new container for us with the image specified (here,
76# alpine), the container does not auto-start unlike `docker run`. This command
77# is used to set up a container configuration and then `docker start` to shoot
78# it up when required. Note that the status is "Created":
79# CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
80# 4c71c727c73d alpine "/bin/sh" 29 seconds ago Created naughty_ritchie
81
82$ docker rm 82f84
83# Removes one or more containers using their container ID.
84# P.S.: we can use only the first few characters of the entire ID to identify
85# containers
86
87$ docker images
88# Displays all images and their information, created here means the latest image
89# tag updated on Docker Hub:
90# REPOSITORY TAG IMAGE ID CREATED SIZE
91# ubuntu latest a8780b506fa4 9 days ago 77.8MB
92# alpine latest 9c6f07244728 3 months ago 5.54MB
93# hello-world latest feb5d9fea6a5 13 months ago 13.3kB
94
95$ docker rmi
96# Removes one or more images from your system which do not have their instances
97# (or containers as we know them) running. If the image has an attached
98# container, either delete the container first or use the -f (or --force) flag
99# to forcefully delete both the container and image.
100
101$ docker pull busybox
102# The pull command downloads the specified image on our system from Docker Hub.
103
104$ docker exec -it 7b272 bash
105# This command is used to run a command in the running container's default
106# directory. Here 7b272 was our ubuntu container and the above command would
107# help us interact with the container by opening a bash session.
108
109$ docker logs <container-id>
110# Displays the information logged by the specified container
111# root@7b27222e4bb7:/# whoami
112# root
113# root@7b27222e4bb7:/# pwd
114# /
115# root@7b27222e4bb7:/# ls
116# bin boot dev etc home lib lib32 lib64 libx3 srv sys tmp usr var
117# root@7b27222e4bb7:/# exit
118# exit
119
120# More commands can be found at https://docs.docker.com/engine/reference/commandline/docker/
The Dockerfile ¶
The Dockerfile is a blueprint of a Docker image. We can mention the artifacts from our application along with their configurations into this file in the specific syntax to let anyone create a Docker image of our application.
A few things to keep in mind: ¶
- It is always strictly named
Dockerfile
without any extensions - We have to build our custom image on top of some already available Docker base
image. (there is an empty image called
scratch
which literally lets you build an image from scratch) - All capitalised commands are part of the syntax, they are not case-sensitive but used like a convention
- Below is a sample Dockerfile but you can read in depth from the official docs.
1FROM <base-image>
2# define base image
3
4ENV USERNAME='admin'\
5 PWD='****'
6# optionally define environmental variables
7
8RUN apt-get update
9# run linux commands inside container env, does not affect host env
10# This executes during the time of image creation
11
12COPY <src> <target>
13# executes on the host, copies files from src (usually on the host) to target
14# on the container
15
16ENTRYPOINT ["some-script.sh"]
17# executes an entire script as an entrypoint
18
19CMD [<args>,...]
20# always part of dockerfile, introduces entry point linux command e.g.
21# `CMD node server.js`
22# This executes after image creation only when the container from the image
23# is running.
Build your images ¶
Use the docker build
command after wrapping your application into a Docker
image to run ( or build) it.
1$ docker build <path-to-dockerfile>
2# used to build an image from the specified Dockerfile
3# instead of path we could also specify a URL
4# -t tag is optional and used to name and tag your images for e.g.
5# `$ docker build -t my-image:0.1 ./home/app`
6# rebuild images everytime you make changes in the dockerfile
Push your image to DockerHub ¶
If you want your application’s Docker image to be made publicly available for any Docker user, you might wanna push it to the Docker Hub which is a registry of Docker images. Make sure you have an account with a username and password on Docker Hub.
When pushing an image to Docker Hub, we must specify our Docker Hub username as part of the source image name. We need to create the target image with the tag name of username/image-name much like GitHub repositories.
1$ docker login
2# to login to Docker Hub using your username and password
3
4$ docker tag <src-image>[:<src-tag>] <target-image>[:<target-tag>]
5# this tags a local src-image to a public target-image
6# e.g. `docker tag my-sample-app:1.0.0 akshitadixit/my-sample-app`
7# if tags are not specified, they're defaulted to `latest`
8
9$ docker push <target-image>[:<target-tag>]
10# uploads our image to Docker Hub
11# e.g. `docker push akshitadixit/my-sample-app`
12# this image will be accessible under your profile's repositories as
13# `https://hub.docker.com/r/username/image-name`