Skip to content

Instantly share code, notes, and snippets.

@gaygenius
Created February 21, 2020 18:52
Show Gist options
  • Save gaygenius/7ca334ea4379727d30811eab783ff3bf to your computer and use it in GitHub Desktop.
Save gaygenius/7ca334ea4379727d30811eab783ff3bf to your computer and use it in GitHub Desktop.
# Docker tour (Python web app with Redis service)

Docker tour (Python web app with Redis service)

From Docker docs — Get started

concepts

Stack
A group of interrelated services that share dependencies, can be orchestrated and scaled together (e.g., add visualizer and redis services).
Swarm
A group of machines (nodes) running Docker and joined into a cluster. A swarm can run a multi-container, multi-machine application. Swarm managers run containers based on strategy (e.g., emptiest node, global). Non-manager nodes can join swarm as workers.
Service
Runs an image in a container in production. Defined by docker-compose.yml, which specifies what image to pull, what ports it uses, how many replicas, resource limits (e.g., % of CPU, memory). May specify networks, like the load-balanced network webnet
Container
Runs an app
Image
Defined by Dockerfile, which specifies access to resources and virtualizes disk drives.
Task
A single container running in a service, which unique IDs that numerically increment up to the number of replicas defined in docker-compose.yml.

setup

Install Docker Desktop for Mac

docker --version
docker info
docker run hello-world
docker image ls
docker container ls --all

# build the image for the app
docker build --tag=friendlyhello .
docker image ls

# run the app
docker run -p 4000:80 friendlyhello
curl http://localhost:4000
docker run -d -p 4000:80 friendlyhello    # run detached (background)
docker container ls
docker container stop <container id>

# publish image in hub.docker.com
docker login
docker tag image gaygenius/get-started:part2
docker push gaygenius/get-started:part2
docker run -p 4000:80 gaygenius/get-started:part2

# run a new load-balanced app
docker swarm init
docker stack deploy -c docker-compose.yml getstartedlab
docker service ls
docker stack services getstartedlab
docker service ps getstartedlab_web
docker container ls -q
curl http://localhost:4000
docker stack rm getstartedlab
docker swarm leave --force

Docker Machine

base=https://github.com/docker/machine/releases/download/v0.16.0 &&
  curl -L $base/docker-machine-$(uname -s)-$(uname -m) >/usr/local/bin/docker-machine &&
  chmod +x /usr/local/bin/docker-machine

Swarms and Stacks

# create a cluster
docker-machine create --driver virtualbox myvmv1
docker-machine create --driver virtualbox myvmv2
docker-machine ls

# make myvm1 a swarm manager
docker-machine ssh myvm1 "docker swarm init --advertise-addr <myvm1 ip>"

# make myvm2 a worker in the swarm
docker-machine ssh myvm1 "docker swarm join --token <token> <ip>:2377"

# configure shell to send commands to swarm manager
eval $(docker-machine env myvm1)
docker stack deploy -c docker-compose.yml getstartedlab
docker stack ps getstartedlab
curl http://<myvm2 IP>
docker stack rm getstartedlab
eval $(docker-machine env -u)

After deploying visualizer service, point browser to http://<myvm2 IP>:8080

Before deploying redis service:

docker-machine ssh myvm1 "mkdir ./data"

Dockerfile

# Use an official Python runtime as a parent image
FROM python:2.7-slim

# Set the working directory to /app
WORKDIR /app

# Copy the current directory contents into the container at /app
COPY . /app

# Install any needed packages specified in requirements.txt
RUN pip install --trusted-host pypi.python.org -r requirements.txt

# Make port 80 available to the world outside this container
EXPOSE 80

# Define environment variable
ENV NAME World

# Run app.py when the container launches
CMD ["python", "app.py"]```

requirements.txt

Flask
Redis

app.py

from flask import Flask
from redis import Redis, RedisError
import os
import socket

# Connect to Redis
redis = Redis(host="redis", db=0, socket_connect_timeout=2, socket_timeout=2)

app = Flask(__name__)

@app.route("/")
def hello():
    try:
        visits = redis.incr("counter")
    except RedisError:
        visits = "<i>cannot connect to Redis, counter disabled</i>"

    html = "<h3>Hello {name}!</h3>" \
           "<b>Hostname:</b> {hostname}<br/>" \
           "<b>Visits:</b> {visits}"
    return html.format(name=os.getenv("NAME", "world"), hostname=socket.gethostname(), visits=visits)

if __name__ == "__main__":
    app.run(host='0.0.0.0', port=80)

docker-compose.yml

version: "3"
services:
  web:
    image: gaygenius/get-started:part2
    deploy:
      replicas: 5
      resources:
        limits:
          cpus: "0.1"
          memory: 50M
      restart_policy:
        condition: on-failure
    ports:
      - "80:80"
    networks:
      - webnet
  visualizer:
    image: dockersamples/visualizer:stable
    ports:
      - "8080:8080"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"
    deploy:
      placement:
        constraints: [node.role == manager]
    networks:
      - webnet
  redis:
    image: redis
    ports:
      - "6379:6379"
    volumes:
      - "/home/docker/data:/data"
    deploy:
      placement:
        constraints: [node.role == manager]
    command: redis-server --appendonly yes
    networks:
      - webnet
networks:
  webnet:
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment