Docker From Scratch

Hands-on Workshop

Slides: http://bit.ly/docker-workshop-rj

What is Docker?

  • A cloud infrastructure company (formerly dotCloud) that created Docker as an internal tool
  • Container Runtime daemon that runs and manages Containers on top of Linux Kernel
  • CLI client to the Docker Daemon
  • Container Image Packaging Format, now evolved into OCI Image

Containers vs Virtual Machines

Containers and VMs

(Source: Docker Blog: Are Containers Replacing Virtual Machines? by Jenny Fong)

History of Containers

  • 1979: Development of chroot in Unix V7
  • 2003: Google Created its internal cluster management system Borg
  • 2004: Development Process Containers, which eventually became cgroups
  • 2008: cgroups got absorbed in Linux Kernel in 2008, which led to development of LXC
  • 2013: Docker was built on top of LXC
  • 2015: Open Container Initiative (OCI) was created by Docker, CoreOS and others to standardize containers. Docker donated its container runtime runc to the OCI. Kubernetes was born.
  • 2017 Onwards: Kubernetes emerged as a victor in Container Orchestrator Wars™. Seeds of alternative ecosystem were sewn with development of alternative tools like CRI-O, and Podman. Evolution is still ongoing.

Enough Talk. Let’s Get Our Hands Dirty!

Linux/Mac:

curl -o id_rsa https://pastebin.com/raw/rp9NVugW
curl -o id_rsa.pub https://pastebin.com/raw/VZS2MkuT
ssh-add id_rsa
ssh root@<your-machine-ip>

Windows:

Download https://pastebin.com/raw/EAMDxdZT and use it with PuTTY

Run a task

docker run busybox echo "Hello Laravel Rajkot"

# List running containers
docker ps

# List all containers
docker ps -a

# Remove Exited Container
docker rm <container-name>

Run an interactive container

docker run -it --rm busybox sh

# List running processes
ps aux

# Exit from the container
exit

ps aux

Run a background container: nginx

# Visit the IP address of your docker host to see whether nginx is running

docker run -d -p 80:80 nginx:alpine

# Visit the IP address of your docker host to see nginx running

docker ps
# Check access logs
docker logs -f <nginx-container-name>

# Open Interactive Shell within a running container
docker exec -it <nginx-container-name> sh

# Navigate to the location where nginx default page is stored
cd /usr/share/nginx/html

Bind Mounts and Volumes

  • Docker Bind Mount: Mounting host directory/file into a container
  • Docker Volume: Create a virtual Volume managed by Docker that is mounted into a container

  • Volumes are prescribed as “preferred mechanism” for persistence by Docker due to various reasons mentioned here.

  • Bind Mounts are still simpler to manage for single host.

Override the nginx default page with bind mount

curl -o /tmp/index.html https://pastebin.com/raw/k8DyhGqf

docker run --rm -p 80:80 -v /tmp/index.html:/usr/share/nginx/html/index.html nginx:alpine

# Refresh the page

Docker Images and Dockerfile

  • Dockerfile - A template/script that generates Docker Image
  • Docker Image - Application environment packaged into files
  • Docker Container - Running application from Docker Image

docker images

Build our own image

Dockerfile

FROM nginx:alpine

ADD /tmp/index.html /usr/share/nginx/html/

Commands

curl -o Dockerfile https://pastebin.com/raw/WDznhx8n

docker build -t hello-nginx .

# See the image you built listed
docker images

docker run --rm -p 80:80 hello-nginx

Real-World Dockerfile

FROM node:10-alpine

EXPOSE 3000

ENV NODE_ENV production

RUN mkdir /app
WORKDIR /app
ADD package.json yarn.lock /app/
RUN yarn install --production --pure-lockfile
ADD . /app

CMD ["yarn", "docker:start"]

Image Building Best Practices

  • One process service per container.
  • Keep images stateless.
  • Build immutable images for each software release. Don’t mount code as volume.
  • Keep images as small in size as possible. Add only stuff that you need.
  • Minimize docker instructions/layers.
  • Utilize multi-stage builds to keep images simple wherever possible.
  • Utilize build cache as much as possible.
  • Create images to be polymorphic based on environment variables and arguments
Okay, it's nice to run a single process inside a container. But, my applications are more complex than that.

docker-compose

  • Nice little utility that lets you declare multiple docker commands withink a single yaml file.
  • If docker -> service, then docker-compose -> application
version: '3'
services:
  web:
    build: .
    ports:
      - "3000:3000"
    depends_on:
      - mysql
      - redis
  mysql:
    image: "mysql:5.7"
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: my-secret-password
  redis:
    image: "redis:alpine"
    deploy:
      resources:
        limits:
          cpus: '0.50'
          memory: 50M
        reservations:
          cpus: '0.25'
          memory: 20M

Let’s try it!

curl -o mysql-compose.yml https://pastebin.com/raw/BKudcBhb

docker-compose -f mysql-compose.yml up

# Observe the logs, let mysql server start and visit port 8080 on your IP address

docker-compose -f mysql-compose.yml down

Let’s run a Laravel Application inside Docker

git clone https://github.com/deltasquare4/laravel-hello-world

docker-compose up -d

docker stats

Advantages

  • No more “works on my machine”. Because of Immutable Code + Environment, literally the same thing runs in all the environments.
  • Dockerfile serves as documentation of your environment.
  • Going back to the older version never breaks. All the dependencies and environment are baked into the image.
  • Spinning up the application locally is very easy.
  • Forces good application design, and makes automation around deployment and scaling easier.
  • Offers isolation from other components from security persepctive.
  • Your applications are not tied to a cloud provider/VPS anymore. You can run it anywhere within minutes.

Drawbacks

  • Docker daemon runs as root. Can be a potential security risk.
  • All containers run as child processes of Docker daemon. Bringing down the daemon will bring down all the containers.
  • Persistent data is complicated. Servers cannot be treated as throwaway completely in case of stateful load.
  • Docker does much more than container management; is bloated as a result.

Thanks!

Feedback: https://forms.gle/xMUpyhZPPxBnMTU19

Slides: https://github.com/deltasquare4/docker-from-scratch-workshop Laravel App Repo: https://github.com/deltasquare4/laravel-hello-world Base Image Repo: https://github.com/deltasquare4/docker-php-base

Rakshit Menpara (@deltasquare4)

improwised.com

Bonus 1: Registry

Create a Docker ID Here: https://hub.docker.com/signup

docker login

docker tag <image>:<tag> <image>:<new-tag>

docker push <image>:<new-tag>

Bonus 2: Portainer

Run Portainer with instructions here

Bonus 3: Buildah + Skopeo + Podman

These next-generation Open Source tools are designed to replace Docker, and address many of the disadvantages.