/learn-docker-with-hack

Learn the basics of docker and try out this tutorial to see if you have mastered the essentials.

Primary LanguagePythonMIT LicenseMIT

Learn Docker with Hack

Overview

If you are interested in getting a basic understanding of what Docker is, then you are in the right place. Below you will find summaries and relevant commands for the major principles of Docker.

In addition, a detailed tutorial will allow you to put some of this material into practice. The tutorial walks through dockerizing the flask application in this repo. It will cover creating a Dockerfile, building an image, volumes, and networking.

Virtual Workshop

Youtube | Slides

Table of Contents

What is Docker?

Docker is an open platform for developing, shipping, and running applications. Docker enables you to separate your applications from your infrastructure so you can deliver software quickly. With Docker, you can manage your infrastructure in the same ways you manage your applications.

Volumes Diagram

Prerequisites and Setup

Mac or Windows Install Docker Desktop

Docker Desktop is an easy-to-install application for your Mac or Windows environment that enables you to build and share containerized applications and microservices. Docker Desktop includes Docker Engine, Docker CLI client, Docker Compose, Notary, Kubernetes, and Credential Helper.

Docker Engine is an open source containerization technology for building and containerizing your applications. Docker Engine acts as a client-server application with:

  • A server with a long-running daemon process dockerd.
  • APIs which specify interfaces that programs can use to talk to and instruct the Docker daemon.
  • A command line interface (CLI) client docker.

Docker Engine can be installed with a simple shell script as well. Run the following command in terminal.

curl -sSL https://get.docker.com/ | sh

Docker Principles and Commands

Images

An image is a read-only template with instructions for creating a Docker container. Often, an image is based on another image, with some additional customization. For example, you may build an image which is based on the ubuntu image, but installs the Apache web server and your application, as well as the configuration details needed to make your application run.

Image Commands

Dockerfile

To build your own image, you create a Dockerfile with a simple syntax for defining the steps needed to create the image and run it. Each instruction in a Dockerfile creates a layer in the image. When you change the Dockerfile and rebuild the image, only those layers which have changed are rebuilt. This is part of what makes images so lightweight, small, and fast, when compared to other virtualization technologies.

Possible Dockerfile Instructions

  • FROM Sets the Base Image for subsequent instructions.
  • RUN Execute any commands in a new layer on top of the current image and commit the results.
  • CMD Provide defaults for an executing container.
  • EXPOSE Informs Docker that the container listens on the specified network ports at runtime. NOTE: does not actually publish ports to host.
  • ENV Sets environment variable.
  • ADD Copies new files, directories or remote file to image. Invalidates caches. Avoid ADD and use COPY instead.
  • COPY Copies new files or directories to image. By default this copies as root regardless of the USER/WORKDIR settings. Use --chown=<user>:<group> to give ownership to another user/group. (Same for ADD.)
  • ENTRYPOINT Configures a container that will run as an executable.
  • VOLUME Creates a mount point for externally mounted volumes or other containers.
  • USER Sets the user name for following RUN / CMD / ENTRYPOINT commands.
  • WORKDIR Sets the working directory.
  • ARG Defines a build-time variable.
  • ONBUILD Adds a trigger instruction when the image is used as the base for another build.
  • STOPSIGNAL Sets the system call signal that will be sent to the container to exit.
  • LABEL Apply key/value metadata to your images, containers, or daemons.
  • SHELL Override default shell is used by docker to run commands.
  • HEALTHCHECK Tells docker how to test a container to check that it is still working.

Docker Registry

A Docker registry stores Docker images. Docker Hub is a public registry that anyone can use, and Docker is configured to look for images on Docker Hub by default. You can even run your own private registry.

When you use the docker pull or docker run commands, the required images are pulled from your configured registry. When you use the docker push command, your image is pushed to your configured registry.

Registry Commands

Containers

A container is a runnable instance of an image. You can create, start, stop, move, or delete a container using the Docker API or CLI. You can connect a container to one or more networks, attach storage to it, or even create a new image based on its current state.

By default, a container is relatively well isolated from other containers and its host machine. You can control how isolated a container’s network, storage, or other underlying subsystems are from other containers or from the host machine.

A container is defined by its image as well as any configuration options you provide to it when you create or start it. When a container is removed, any changes to its state that are not stored in persistent storage disappear.

Container Commands

Lifecycle
Info
Import / Export
Executing Commands
  • docker exec Execute a command in container. (To enter a running container, attach a new shell process to a running container, use: docker exec -it <container_id> /bin/bash)

Volumes

Volumes are the preferred mechanism for persisting data generated by and used by Docker containers. While bind mounts are dependent on the directory structure and OS of the host machine, volumes are completely managed by Docker.

Volumes Diagram

Volume Commands

Networking

Docker’s networking subsystem is pluggable, using drivers. Several drivers exist by default, and provide core networking functionality:

  • bridge: The default network driver. If you don’t specify a driver, this is the type of network you are creating. Bridge networks are usually used when your applications run in standalone containers that need to communicate. See bridge networks.

  • host: For standalone containers, remove network isolation between the container and the Docker host, and use the host’s networking directly. See use the host network.

  • overlay: Overlay networks connect multiple Docker daemons together and enable swarm services to communicate with each other. You can also use overlay networks to facilitate communication between a swarm service and a standalone container, or between two standalone containers on different Docker daemons. This strategy removes the need to do OS-level routing between these containers. See overlay networks.

Network Commands

Tutorial

Overview

In this tutorial, we are going to deploy a simple flask backend using docker. We will need two containers, one for the database and one for the flask app. We will also need to make use of volumes, so our database data can persist, and networking, so that our containers can talk to each other.

Make sure that you have all necessary software installed before proceeding. Instructions can be found here

Steps

Step 1: Clone Repo

  • Clone this repository from GitHub in order to copy all the source code to your local machine.
  • Open the repository in an editor. Get ready to add to the file named Dockerfile.
git clone https://github.com/zpinto/learn-docker-with-hack.git
code learn-docker-with-hack # if you use VSCode

Step 2: Create a Dockerfile

  • Now that you have your Dockerfile opened up, it is time to write the instructions that will build your image.

  • See if you can do it yourself using the information provided on Dockerfile instructions here. Once you have given it a try, verify your file with the snippet in the dropdown.

  • What the Dockerfile should do:

    • Start with base image python:3.9
    • Expose PORT 80 so your flask app can listen on it
    • Copy only the requirements.txt into the container at /app/requirements.txt
    • Change your working directory to /app
    • Run pip install requirements.txt in order to install the app's dependencies
    • Copy the flask app into the container at /app
    • Set the entrypoint to run the command gunicorn --bind 0.0.0.0:80 wsgi:app
Correct Dockerfile Code
  FROM python:3.9

  EXPOSE 80

  # We copy just the requirements.txt first to leverage Docker cache
  COPY ./requirements.txt /app/requirements.txt

  WORKDIR /app

  RUN pip install -r requirements.txt

  # Assuming dependencies do not change, everything above will not have to be rerun when rebuilding the image
  COPY . /app

  ENTRYPOINT [ "gunicorn", "--bind", "0.0.0.0:80", "wsgi:app" ]

Step 3: Build an Image

  • Now that our Dockerfile is ready to go, we can build our image.
  • Our image needs a name and a tag.
  • Run the build command from within the same directory as the Dockerfile to create an image called learn-docker-backend.
docker build -t learn-docker-backend:latest .

Step 4: Create a Network

  • Now we need to create a network for our containers to talk to each other on.
  • Our container containing the flask app will need to talk to the container running mongoDB
  • Run the following command to create a bridge network called hack-net
docker network create hack-net

Step 5: Create a Volume

  • In order to make sure that our database data persists passed the life of our mongoDB container, we need to create a volume that is managed by docker.
  • We will later mount this to our container.
  • Run the following command to create a volume named learn-docker-data
docker volume create learn-docker-data

Step 6: Spin up Containers

  • Now it is time to start up our containers.
  • Since our flask app depends on mongoDB, we need to create our mongoDB container first.
  • Open a terminal window and run the following command to spin up a mongoDB container on the network we created and mounted to our volume we created.
docker container run --rm --name hack-mongo --network hack-net --volume learn-docker-data:/data/db mongo:latest
  • Open a new terminal window in order to spin up our flask app container using the image we previously build from our Dockerfile.
  • In addition to adding this container to the hack-net network, we will pass an environment variable into this container so that it knows the URI for the mongoDB instance. We will also publish port 80 of our container to port 5000 of our local machine so we can talk to the flask app from outside of the bridge network we created.
  • Run the following command to create a container from the image learn-docker-backend.
docker container run --rm --network hack-net -e MONGO_URI_MASTER="mongodb://hack-mongo:27017/zothacks2020"  -p 5000:80 learn-docker-backend:latest
  • The following should give guidance as to what all args and flags are.
docker container run --rm --name <container-name> --network <connected-networks> -e <environment-variable>=<value>  -p <publish-port-localhost>:<publish-port-container> --volume <volume-name>:<container-mount-point> <image-name>:<version-number>

Step 7: Test Deployment

  • We can now test out our flask app by using postman to make some requests.
  • Follow the GIFs below.
  • Import the postman collection from the repo.

Import Collection

  • Create an environment in postman and add a variable called url. Set it to localhost:5000

Create Environment

  • Make some requests to the flask app. If the requests work, you have successfully deployed the app.

Make Requests

Step 8: Clean Up

  • In order to clean up and stop the containers, we will first find their IDs by running the following command.
docker container ls
  • Now we will stop these containers by running the following command. (containers will also be removed since they were started with --rm option)
docker container stop <container_id>
  • Remove the docker network and volume by running the following two command.
docker network rm hack-net
docker volume rm learn-docker-data

Additional Material