Docker 101

A beginner’s guide to containerization

Definition

Goals

  1. To solve dependency problems by keeping the dependency restricted inside the containers.
  2. To be able to write Dockerfiles to containerize a sample application.
  3. To be able to scale up the instances when needed. The lightweight nature of containers means developers can run dozens of containers at the same time, making it possible to emulate a production-ready distributed system.

Prerequisites

  1. Windows or Linux based systems
  2. Docker
  3. Working knowledge of web applications. No coding required.

Advantages of Containers over traditional VMs

  1. With a fully virtualized system, you get more isolation. However, it requires more resources. With Docker, you get less isolation. However, as it requires fewer resources, you can run thousands of container on a host.
  2. A VM can take a minimum of one minute to start, while a Docker container usually starts in a fraction of seconds.
  3. Containers are easier to break out of than a Virtual Machine.
  4. Unlike VMs there is no need to preallocate the RAM. Hence docker containers utilize less RAM compared to VMs. So only the amount of RAM that is required is used.

Getting Started on Ubuntu:

  • Update the apt package index
$ sudo apt-get update
$ sudo apt-get install \
apt-transport-https \
ca-certificates \
curl \
gnupg-agent \
software-properties-common
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
$ sudo add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) \
stable"
$ sudo apt-get update
$ sudo apt-get install docker-ce docker-ce-cli containerd.io

Getting Started on Windows:

  • Download the installer from here: Docker for Windows
  • Double-click Docker Desktop for Windows Installer.exe to run the installer.
  • Follow the install wizard to accept the license, authorize the installer, and proceed with the install.
  • You are asked to authorize Docker.app with your system password during the install process. Privileged access is needed to install networking components, links to the Docker apps, and manage the Hyper-V VMs.
  • Click Finish on the setup complete dialog to launch Docker.
  • By default, Docker is not started, start it up from All Programs on Windows, or by running systemctl start docker on Ubuntu.

Hello World!

Once Docker is started, go ahead and run this command (on Powershell for Windows users):

$ docker run hello-world

Creating your own images and containers

A Dockerfile is a file that you create which in turn produces a Docker image when you build it. It contains a bunch of instructions which informs Docker HOW the Docker image should get built.

from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello World!"
if __name__ == "__main__":
app.run(debug=True, host="0.0.0.0")
# Inherit from the Python Docker image
FROM python:3.7-slim
# Install the Flask package via pip
RUN pip install flask==1.0.2
# Copy the source code to app folder
COPY ./app.py /app/
# Change the working directory
WORKDIR /app/
# Set “python” as the entry point
ENTRYPOINT [“python”]
# Set the command as the script name
CMD [“app.py”]
$ docker build -t flask_app:0.1 .
$ docker images
REPOSITORY   TAG       IMAGE ID      CREATED       SIZE
flask_app 0.1 c6eb89fefb25 2 minutes ago 153MB
python 3.7-slim 4c2534c95211 4 weeks ago 143MB
$ docker run -d -p 5000:5000 — name simple_flask flask_app:0.1 
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED
03c650a4eb58 flask_app:0.1 "python app.py" 16 seconds ago
STATUS PORTS NAMES
Up 3 seconds 0.0.0.0:5000->5000/tcp simple_flask
 $ docker stop simple_flask
 $ docker rm simple_flask
$ docker rmi flask_app:0.1

Docker Hub | Docker Pull | Docker Push

$ docker pull <image:tag>
$ docker login
$ docker push <image_name:tag>
$ docker push <private_repo_name:tag>
$ docker tag rhel-httpd registry-host:5000/myadmin/rhel-httpd
$ docker images
$ docker push registry-host:5000/myadmin/rhel-httpd

Docker Compose

In the words of Docker Inc.

Step 1: Setup the code base

  1. Create your project directory
  2. Create a file app.py and add the following code:
import time

import redis
from flask import Flask

app = Flask(__name__)
cache = redis.Redis(host='redis', port=6379)


def get_hit_count():
retries = 5
while True:
try:
return cache.incr('hits')
except redis.exceptions.ConnectionError as exc:
if retries == 0:
raise exc
retries -= 1
time.sleep(0.5)


@app.route('/')
def hello():
count = get_hit_count()
return 'Hello World! I have been seen {} times.\n'.format(count)
flask
redis

Step 2: Creating a Dockerfile:

In your project directory, create a file named Dockerfile and add the following:

FROM python:3.7-alpine
WORKDIR /code
ENV FLASK_APP app.py
ENV FLASK_RUN_HOST 0.0.0.0
RUN apk add — no-cache gcc musl-dev linux-headers
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
COPY . .
CMD ["flask", "run"]

Step 3: Define services in a compose file

Create a file called docker-compose.yml in your project directory and paste the following:

version: '3'
services:
web:
build: .
ports:
- "5000:5000"
volumes:
- .:/code
environment:
FLASK_ENV: development
redis:
image: "redis:alpine"

Web service

The web service uses an image that’s built from the Dockerfile in the current directory. It then binds the container and the host machine to the exposed port, 5000. This example service uses the default port for the Flask web server, 5000.

Redis service

The redis service uses a public Redis image pulled from the Docker Hub registry.

Bonus point:
Volumes

The volumes key mounts the project directory (current directory) on the host to /code inside the container, allowing you to modify the code on the fly, without having to rebuild the image. The environment key sets the FLASK_ENV environment variable, which tells flask run to run in development mode and reload the code on change. This mode should only be used in development.

Step 4: Build and run your app with Compose

From your project directory, start up your application by running:

$ docker-compose up
Creating network "composetest_default" with the default driver
Creating composetest_web_1 ...
Creating composetest_redis_1 ...
Creating composetest_web_1
Creating composetest_redis_1 ... done
Attaching to composetest_web_1, composetest_redis_1
web_1 | * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
...
Hello World! I have been seen 1 times.
docker inspect <tag or id>

Step 5: Updating the application on the fly

Because the application code is now mounted into the container using a volume, you can make changes to its code and see the changes instantly, without having to rebuild the image.
Change the greeting in app.py and save it. For example, change the Hello World! message to Hello from Docker!:

return ‘Hello from Docker! I have been seen {} times.\n’.format(count)

Other Docker Compose commands

If you want to run your services in the background, you can pass the -d parameter (for “detached” mode) to docker-compose up and use docker-compose ps to see what is currently running.
The docker-compose run command also allows you to run one-off commands for your services. For example, to see what environment variables are available to the web service:

$ docker-compose run web env
$ docker-compose stop
$ docker-compose down --volumes

Bonus Step:

Scaling up your app!

  1. Edit docker-compose.yml.
web:
build: .
command: python app.py
ports:
- "5000"
volumes:
- .:/code
links:
- redis
redis:
image: redis
$ docker-compose up -d
$ docker-compose psName             Command                     State Ports
compose_redis_1 /entrypoint.sh redis-server Up 6379/tcp
compose_web_1 python app.py Up 0.0.0.0:32768->5000/tcp
docker-compose scale web=10
docker-compose stop
docker-compose rm

Summary

By now, you should have a working knowledge of Docker. That’s the conceptual framework. We’ve covered motivations for using it and the basic concepts. Furthermore, we’ve looked into how to containerize an app and in doing so covered some useful Docker commands. There is so much more to know about Docker like how to work with Databases, Volumes, how to link containers over custom network and why and how to spin up and manage multiple containers, also known as orchestration.

Every company is a software company; even if it’s not in the software business