|
| 1 | +Build a web app with Kubernetes |
| 2 | +=============================== |
| 3 | + |
| 4 | +This tutorial shows how to build a Docker image to serve a web app and how to deploy that service using [Kubernetes](https://kubernetes.io/) (k8s). Unless otherwise stated, the commands are assumed to run from the working directory containing this `README.md` file. |
| 5 | + |
| 6 | +Verify operation manually with Docker |
| 7 | +------------------------------------- |
| 8 | + |
| 9 | +Deploy a container for the MySQL database service: |
| 10 | +``` |
| 11 | +docker run -d --rm --name demo-app-db \ |
| 12 | + -p 3306:3306 \ |
| 13 | + -e MYSQL_ROOT_PASSWORD=password \ |
| 14 | + mysql:5.6 |
| 15 | +``` |
| 16 | +Build the demo app Docker image: |
| 17 | +``` |
| 18 | +docker build -t k8s-demo-app . |
| 19 | +``` |
| 20 | +Deploy a container for the web app frontend: |
| 21 | +``` |
| 22 | +docker run -d --rm --name demo-app \ |
| 23 | + --link demo-app-db:mysql \ |
| 24 | + -p 8080:8080 \ |
| 25 | + -e HTTP_PORT=8080 \ |
| 26 | + -e MYSQL_USER=root \ |
| 27 | + -e MYSQL_PASS=password \ |
| 28 | + -e MYSQL_SERVER=mysql \ |
| 29 | + -e MYSQL_PORT=3306 \ |
| 30 | + k8s-demo-app |
| 31 | +``` |
| 32 | +View the running containes using `docker ps`: |
| 33 | +``` |
| 34 | +$ docker ps |
| 35 | +
|
| 36 | +CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES |
| 37 | +5d26164017cc k8s-demo-app "python main.py" 6 seconds ago Up 5 seconds 0.0.0.0:8080->8080/tcp demo-app |
| 38 | +e9b65488d224 mysql:5.6 "docker-entrypoint.s…" 2 minutes ago Up 2 minutes 0.0.0.0:3306->3306/tcp demo-app-db |
| 39 | +``` |
| 40 | +Open [0.0.0.0:8080](0.0.0.0:8080) in your browser to view the web app in action. If you verified that it works, proceed to deployment via Kubernetes. |
| 41 | + |
| 42 | +Deployment using Kubernetes via Minikube |
| 43 | +---------------------- |
| 44 | + |
| 45 | +Launch a local single-node cluster using [minikube](https://kubernetes.io/docs/tutorials/hello-minikube/): |
| 46 | +``` |
| 47 | +minikube start |
| 48 | +``` |
| 49 | +(_optional_) Open the dashboard for real-time status and control of the cluster: |
| 50 | +``` |
| 51 | +minikube dashboard |
| 52 | +``` |
| 53 | +Configure your shell session for local development: |
| 54 | +``` |
| 55 | +eval $(minikube docker-env) |
| 56 | +``` |
| 57 | +Minikube runs a k8s cluster in a virtual machine (VM) which will not see the `k8s-demo-app` image you built earlier on the host machine (use `docker image list` to check), so build it "again" in Minikube: |
| 58 | +``` |
| 59 | +docker build -t k8s-demo-app . |
| 60 | +``` |
| 61 | +Verify that the image is available to Docker locally: |
| 62 | +``` |
| 63 | +$ docker image list |
| 64 | +
|
| 65 | +REPOSITORY TAG IMAGE ID CREATED SIZE |
| 66 | +k8s-demo-app latest 2aaa1d3c86a7 5 seconds ago 942MB |
| 67 | +... |
| 68 | +``` |
| 69 | +Apply the k8s configuration file `k8s-demo-app.yaml`: |
| 70 | +``` |
| 71 | +kubectl apply -f k8s-demo-app.yaml |
| 72 | +``` |
| 73 | +If everything is deployed properly, you should see green check mark icons in the dashboard web view. Alternatively, you can view status using commands: |
| 74 | +``` |
| 75 | +$ kubectl get deployment |
| 76 | +
|
| 77 | +NAME READY UP-TO-DATE AVAILABLE AGE |
| 78 | +demo-app 1/1 1 1 22m |
| 79 | +demo-app-db 1/1 1 1 22m |
| 80 | +
|
| 81 | +$ kubectl get pod |
| 82 | +
|
| 83 | +NAME READY STATUS RESTARTS AGE |
| 84 | +demo-app-58b96f7ffc-h528m 1/1 Running 3 22m |
| 85 | +demo-app-db-5fc7d9c565-wjmj2 1/1 Running 0 22m |
| 86 | +
|
| 87 | +$ kubectl get service |
| 88 | +
|
| 89 | +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE |
| 90 | +demo-app NodePort 10.106.45.30 <none> 8080:32065/TCP 22m |
| 91 | +demo-app-db NodePort 10.109.151.26 <none> 3306:32542/TCP 22m |
| 92 | +kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 42m |
| 93 | +``` |
| 94 | + |
| 95 | +Since your k8s cluster is running in a VM, you must use `minikube` to discover the demo app "external" URL so that you can open it in a browser: |
| 96 | +``` |
| 97 | +$ minikube service list |
| 98 | +|----------------------|---------------------------|-----------------------------|-----| |
| 99 | +| NAMESPACE | NAME | TARGET PORT | URL | |
| 100 | +|----------------------|---------------------------|-----------------------------|-----| |
| 101 | +| default | demo-app | http://192.168.99.104:32065 | |
| 102 | +| default | demo-app-db | http://192.168.99.104:32542 | |
| 103 | +... |
| 104 | +``` |
| 105 | +You can launch the web page directly using |
| 106 | +``` |
| 107 | +minikube service demo-app |
| 108 | +``` |
| 109 | + |
| 110 | +--- |
| 111 | + |
| 112 | +<img src="./demoapp.png" /> |
| 113 | + |
| 114 | +--- |
| 115 | + |
| 116 | +You will notice that when you refresh the page, the same "pod" name is displayed because there is only one pod for the demo-app "deployment". (In this demo, "pod" is equivalent to "container" because each pod consists of only a single container.) Edit the deployment configuration file `k8s-demo-app.yaml` to specify a number of "replicas": |
| 117 | +``` |
| 118 | +... |
| 119 | +apiVersion: apps/v1 |
| 120 | +kind: Deployment |
| 121 | +metadata: |
| 122 | + name: demo-app |
| 123 | +spec: |
| 124 | + replicas: 3 |
| 125 | + selector: |
| 126 | + matchLabels: |
| 127 | + app: demo-app |
| 128 | + template: |
| 129 | + ... |
| 130 | +``` |
| 131 | +Then simply reapply the config to the cluster: |
| 132 | +``` |
| 133 | +kubectl apply -f k8s-demo-app.yaml |
| 134 | +``` |
| 135 | +Now you should see three identical pods deployed (exact names will vary): |
| 136 | +``` |
| 137 | +$ kubectl get pod |
| 138 | +
|
| 139 | +NAME READY STATUS RESTARTS AGE |
| 140 | +demo-app-58b96f7ffc-h528m 1/1 Running 3 37m |
| 141 | +demo-app-58b96f7ffc-n7m6q 1/1 Running 0 9s |
| 142 | +demo-app-58b96f7ffc-vn9v5 1/1 Running 0 9s |
| 143 | +demo-app-db-5fc7d9c565-wjmj2 1/1 Running 0 37m |
| 144 | +``` |
| 145 | +When you refresh the web page a few times (using CTRL+SHIFT+R or equivalent cache-flushing method) you should see the pod name cycle through the three available pods. |
| 146 | + |
| 147 | +<img src="./loadbalancer.gif" /> |
0 commit comments