Skip to content

Latest commit

 

History

History
363 lines (318 loc) · 12.7 KB

guide.md

File metadata and controls

363 lines (318 loc) · 12.7 KB

PyconUk - Kubernetes Workshop

Home instructions

Session 1 - 90m

  1. Introduction to speakers

  2. Learning objectives

    • Demonstrate Kubernetes to your colleagues.
    • Deploy a web application on Kubernetes.
    • Manage your deployment e.g. scaling up and down, rolling updates and rollbacks.
    • Deploy multiple services on Kubernetes.
    • Describe the Kubernetes API to your colleagues.
    • Demonstrate using kubectl to interact with the Kubernetes API.
  3. Installation

    • Laptop requirements:
      • ~4 GB of RAM
      • ~15 GB of free disk space
      • Virtualbox
      • ssh client
    • Download the virtual disk - Ask us for a USB or download from here
    • Unzip the virtual disk
    • Create a virtual machine:
      • Type: Linux, version: Ubuntu 64 bit
      • 2048 RAM
      • "use existing virtual hard disk file" (use the unziped virtaul disk from the previous step)
      • Create
      • machine -> settings
      • network -> advanced -> port-forwarding -> create new rule: host port: 2222, guest port: 22
    • You VM image has microk8s (https://asciinema.org/a/182634) installed. This includes Docker and Kubernetes.
    • Start the VM
    • Verify that everything is working
      • ssh into your machine: ssh [email protected] -p 2222 (if using putty: there is a field for the port)
      • login with username: osboxes password: osboxes.org
      • Verify kubectl get pods --all-namespaces, if this fails, wait for a one minute (can take up to several minutes), then try again, expected output, example:
        NAMESPACE     NAME                                              READY     STATUS    RESTARTS   AGE
        kube-system   heapster-v1.5.2-577898ddbf-kt27r                  4/4       Running   8          4d
        kube-system   kube-dns-864b8bdc77-cwsmw                         3/3       Running   6          4d
        kube-system   kubernetes-dashboard-6948bdb78-pnsq6              1/1       Running   2          4d
        kube-system   monitoring-influxdb-grafana-v4-7ffdc569b8-5wq4s   2/2       Running   4          4d
        
      • update the repo to the latest: cd ~/pyconuk-2018-k8s && git pull
      • It's usefull to use ipython, as it makes copying wrongly indented code from the guide easier: pip3 install ipython
      • pull the latest redis image to save time later in the workshop: docker pull redis (sudo password is the same as the login password)
    • We will encourage you to pair with someone
  4. Hello World! (code is in step0)

    • The most basic Flask app in the world:
    from flask import Flask
    app = Flask(__name__)
    
    @app.route("/")
    def hello():
        return "Hello World!"
    • go the the example directory for this code: cd step0
    • install flask: pip3 install flask
    • You can run it like: FLASK_APP=hello.py flask run
    • Open a second ssh terminal or use fg/bg if you are familiar with this: curl localhost:5000, output: Hello World!
  5. Hello Docker World!

    • A very basic dockerfile for our hello world app can look like:
    FROM python
    RUN pip install flask
    ADD hello.py /
    ENV FLASK_APP=/hello.py
    CMD ["flask", "run", "-h", "0.0.0.0"]
    
    • Build a docker image: docker build . -t hello:local
    • Now you have a docker image: docker images |grep hello
    • Run the image: docker run --net host hello:local
    • access the service again: curl localhost:5000
  6. A very brief intro to Docker

    • Versus virtualenv, conda and VMs
    • A brief explanation of images, containers etc.
    • image is a lightweight virtual machine image with isolation
    • Docker is like virtualenv but it isolated not just python packages but the filesystem, network interfaces and system libraries, and other. Docker also standarizes (a lot of things) on how you run applications.
  7. Interactive Console (code is in step1)

    import code
    import io
    import contextlib
    
    import flask
    
    
    app = flask.Flask(__name__)
    app.consoles = {}
    
    
    class WebConsole:
    
        def __init__(self):
            self.console = code.InteractiveConsole()
    
        def run(self, code):
            output = io.StringIO()
            with contextlib.redirect_stdout(output):
                with contextlib.redirect_stderr(output):
                    for line in code.splitlines():
                        self.console.push(line)
            return {'output': str(output.getvalue())}
    
    
    @app.route('/api/<uname>/run/', methods=['POST'])
    def run(uname):
        if not uname in app.consoles:
            app.consoles[uname] = WebConsole()
        return flask.jsonify(
            app.consoles[uname].run(
                flask.request.get_json()['input']
            )
        )
  8. Assignment #1: webconsole in docker

    • Display instructions from step 5 + the ones below
    • Run the application directly
    • Create a Dockerfile for webconsole
    • Build the image
    • Run the image
    • What happens when you try and use requests POST something to: /api/<username>/run/, for example:
       import requests
       requests.post('http://localhost:5000/api/ali/run/',
                     json={'input': 'print("Hello World")'}).json()
  9. Introduction to Kubernetes

    • Challenges of building modern applications
      • Complexity
      • Load characteristics
      • Horizontal scaling
      • CI/CD
    • Microservice - when are they useful and why?
    • Kubernetes - what and why? - solves most of the microservices problems
  10. Kubernetes Web Console (code in step2)

    • Build the image:
      • cd ../step2
      • ./build.sh
    • Run as service deployment:
    kubectl run webconsole \
        --image pyconuk-2018-k8s:step2 \
        --port 5000 \
        --replicas 2 \
        --expose
  • Explain pods, deployments, and services
  • Access the service:
    • Grab the service ip: export WEBCONSOLE_IP=$(kubectl get service webconsole -o go-template="{{ .spec.clusterIP }}")
    • Use the service:
      import requests
      import os
      requests.post(f'http://{os.environ["WEBCONSOLE_IP"]}:5000/api/ali/run/',
                    json={'input': 'print("Hello World")'}).json()
  1. Show some kubernetes features
  • Try and scale the deployment - show the new pods being created kubectl scale deployment webconsole --replicas 5
  • Look at the new pods: kubectl get pods
  • Try and kill a pod - show that it gets recreated kubectl delete <pod-name>
  • Simulate a service failure:
    import requests
    import os
    requests.get(f'http://{os.environ["WEBCONSOLE_IP"]}:5000/api/crash/')
  • Show restarts on the pod: kubectl get pods
  1. Introduction to kubectl and Kubernetes API

    • $ kubectl --help has sections for basic commands - beginners and intermediate.
  2. Assignment:

    • Build the image:
      • cd ../step2
      • ./build.sh
    • Run as service deployment:
    kubectl run webconsole \
        --image pyconuk-2018-k8s:step2 \
        --port 5000 \
        --replicas 2 \
        --expose
    • Lets explore a few kubectl commands.
        kubectl get pods
        kubectl get deployments
        kubectl get services
        kubectl --help
        kubectl explain
        kubectl describe
    
    • Assignment: See if you can figure out a problem with our application

Session 2 - 90m

  1. Introduction to kubernetes manifests:

    • Show:
    kubectl run webconsole \
        --image pyconuk-2018-k8s:step2 \
        --port 5000 \
        --replicas 2 \
        --expose \
        --dry-run -o yaml 
    
    • Walk through of the simplified yaml produced:
    apiVersion: extensions/v1beta1
    kind: Deployment
    metadata:
      name: webconsole
    spec:
      replicas: 2
      template:
        metadata:
          labels:
            app: webconsole
        spec:
          containers:
          - image: pyconuk-2018-k8s:step2
            name: webconsole
            ports:
              - name: api
                containerPort: 5000
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: webconsole
    spec:
      ports:
      - name: webconsole
        port: 5000
        targetPort: api
      selector:
        app: webconsole
    • Questions?
  2. Infrastructure as code, why is this nice?

    • Ask the room what the preference is, between cli and yaml and why?
    • You can code review YAML, and essentially it's infrastructure as code.
    • Yaml is declarative, for instance if you want to scale up your application you would edit the yaml, and re-apply instead of running specific commands on the cli.
  3. Back to the example, demonstrate the problem:

    • grab the service ip: export WEBCONSOLE_IP=$(kubectl get service webconsole -o go-template="{{ .spec.clusterIP }}")
    • demonstrate:
    import requests
    import os
    requests.post(f'http://{ os.environ["WEBCONSOLE_IP"] }:5000/api/paul/run/',
                  json={'input': 'a = 1'}).json()
    requests.post(f'http://{ os.environ["WEBCONSOLE_IP"] }:5000/api/paul/run/',
                  json={'input': 'print(a)'}).json()
    • Ask if people understand why?
    • Explain the issue with load balancing
  4. Split the Web Console to solve the problem (code is in step3):

  5. Assignment, run step 3:

    • Remove the old service and deployment:
    kubectl delete service webconsole
    kubectl delete deployment webconsole
    
    • Build: ./step3/build.sh
    • Apply the manifest: kubectl apply -f step3/consolehub/deployment.yaml
    • grab the service ip: export CONSOLEHUB_IP=$(kubectl get service consolehub -o go-template="{{ .spec.clusterIP }}")
    • Use the application, check if the problem is solved, example:
    import requests
    import os
    requests.post(f'http://{ os.environ["CONSOLEHUB_IP"] }/api/paul/start/').json()
    requests.post(f'http://{ os.environ["CONSOLEHUB_IP"] }/api/paul/run/',
                    json={'input': 'a = 1'}).json()
    requests.post(f'http://{ os.environ["CONSOLEHUB_IP"] }/api/paul/run/',
                    json={'input': 'print(a)'}).json()
  6. Problems with the current implementation:

  • Tight coupling of the application code and infrastructure
  • You create the job synchronously
  1. Introduce the final version (code in step 4):

  2. Demonstrate rolling update (see assignment)

    import requests
    import os
    requests.post(f'http://{ os.environ["CONSOLEHUB_IP"] }/api/paul/run/',
                    json={'input': 'print(a)'}).json()
  3. Assignment, do the rolling update:

    • Build: ./step4/build.sh
    • Apply the manifest: kubectl apply -f step4/k8s_manifests
    • use kubectl get pods to see pods shutdown and start in a rolling way
  4. Next steps:

  5. Q&A