Kubernetes/Kubernetes Workshop/Step 2

== Step 2 Outcomes ==
* Run a webserver application on kubernetes/minikube and the k8s networking concept of a service
* Learn about replicas - multiple instances of the application
* Entering a pod and logging
At the end you should known how to run a service on minikube.
== Step 2 - Run a service application on k8s ==
K8s has extensive support for running service applications such as a web server, for example automated restarts, load balancing, dynamic scaling and others. 
Let’s use a simple web server to explore some of the options.
==== Hands on: Make a docker image that serves a simple “Hello World” webpage. ====
Here is a step by step that uses the apache web server in the docker interactive mode.
We will base it on the ubuntu docker image.
* Bring up a base ubuntu image and log into the shell
docker run -it ubuntu /bin/bash
* Install apache 
apt-get update && apt-get install apache2
* Replace index.html with Hello World 
apt-get install vim
vi /var/www/html/index.html
or
echo "<HTML><BODY>Hello World</BODY></HTML>" > /var/www/html/index.html
* Start Apache - in Docker we typically want to run the server binary in the foreground and not exit as it will terminate the container, so:
apachectl -DFOREGROUND
* If you exit the shell on the docker container it stops running and you lose all work. You can save the progress by running the command docker commit in another window:
** docker ps # to find the id of the container
** docker commit <id> <name of new image>
** docker image ls # to check
* docker commit is also used to specify what binary to run on startup. In addition we need to tell the container what port is to be exposed to the outside world. See https://docs.docker.com/engine/reference/commandline/commit/ for more info.
docker commit --change='CMD ["/usr/sbin/apachectl", "-DFOREGROUND"]' --change='EXPOSE 80' <id> <name of new image>
* Finally run the container and map the network port to a local port
** docker run -p 80:80 <name of new image>
** On the host machine: curl http://localhost will now get the page from the apache running in the container
** One can “shell” into the container (for debugging for example, check the logs, etc) by 
*** docker exec -it <id> /bin/bash
*** Debug, etc
*** Ctrl-d or exit to leave the container
** docker ps to see what is running
** docker kill <id>to terminate the container
==== Hands-on: make a Dockerfile for apache ====
Dockerfile:
FROM ubuntu
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update
RUN apt-get install -y apache2
RUN echo '<HTML><BODY>Hello World</BODY></HTML>' >/var/www/html/index.html
EXPOSE 80
CMD ["apachectl","-DFOREGROUND"]
* docker build . builds the image
** ENV noninteractive avoids the prompt for location
* docker run -p 80:80 <container id>
* (If you have previously started a container using port 80 you will get an error that the
port is already allocated. In that case use “docker ps” and then “docker kill <id>” to shut it down.)
* curl http://localhost
==== Hands-on: run service on minikube ====
Now let’s follow the same workflow on our k8s. We will publish the image on dockerhub, run it as a pod on the only node on minikube and then make it accessible from the outside by creating a service.
Copy the image to the docker hub and launch it in the local minikube.
* Create a repository “simpleapache” (by clicking the button on hub.docker.com)
* Tag the build: docker tag <id> <your user>/simpleapache
* Upload docker push <your user>/simpleapache
Sample output with user wkandek:
docker push wkandek/simpleapache:latest
The push refers to repository [docker.io/wkandek/simpleapache]
05ef66164473: Pushed 
ab4765c94154: Pushed 
5c7eeb133ba5: Pushed 
095624243293: Mounted from library/ubuntu 
a37e74863e72: Mounted from library/ubuntu 
8eeb4a14bcb4: Mounted from library/ubuntu 
ce3011290956: Mounted from library/ubuntu 
latest: digest: sha256:84987f5e8d8fb51506fb0f8c3202b63e07409c8f9c3000a5e66eeced1c2ecda0 size: 1783
* minikube start to bring up minikube
* kubectl run sa --image=<userid>/simpleapache --port=80
* kubectl expose pod sa --type=LoadBalancer --port=80
** The pod named “sa” is exposed.
* minikube service sa --url
* curl to the url given
To find out more information about the pods and the service, try these commands and see if the returned information makes sense.
* kubectl get pods 
** kubectl get pods -o wide
* kubectl get service
** kubectl get service -o wide
* kubectl describe pod sa
* kubectl describe service sa
==== Hands-on: more replicas on minikube ====
To assure high availability we typically want to run more than one instance of a service. K8s makes that pretty easy. We have to use a new k8s object called a deployment and can then set the number of replicas we want, i.e. where before we just instructed K8s to run an image, we will now create a deployment object for the image and define its parameters, including the number of replicas.
* kubectl create deployment <name> --image=<userid>/simpleapache
* kubectl get pods
* kubectl get deployments
* kubectl describe deployment <name>
** Look for replicas for example
* kubectl scale --current-replicas=1 --replicas=2 deployment sa
** kubectl get pods
** kubectl describe deployment <name>
*** Look for replicas for example
And expose the web servers on the pods through a service
* kubectl expose deployment sa --type=LoadBalancer --port=80
* minikube service sa --url
* Curl on the URL returned to verify functioning
How can we enassure that both pods get exercised? Let’s change the page served on on of the pods
* kubectl get pods
* kubectl exec -it <one of the pods> -- /bin/bash
** cd /var/www/html
** echo "<HTML><BODY>Hello 2nd World</BODY></HTML>" > ./index.html
** exit
* minikube service sa --url
* Multiple curls on the URL executed to verify functioning. Sample Output:
$ curl http://172.17.0.2:30024
<HTML><BODY>Hello 2nd World</BODY></HTML>
$ curl http://172.17.0.2:30024
<HTML><BODY>Hello 2nd World</BODY></HTML>
$ curl http://172.17.0.2:30024
<HTML><BODY>Hello 2nd World</BODY></HTML>
$ curl http://172.17.0.2:30024
<HTML><BODY>Hello World</BODY></HTML
$ curl http://172.17.0.2:30024
<HTML><BODY>Hello 2nd World</BODY></HTML>
We could also look at the apache log files to see if both servers get hit. K8s has a way to look at the stdout of the pods by using the command kubectl logs, so if we configure apache to log to stdout we can use that to see the log files.
Here is a Dockerfile that redirects the access.log to stdout (the RUN for sed is 1 line including the /etc/apache/sites-available… So exercise a docker build ., local test with docker, docker logs to check, then tag, push to repo, and test on k8s:
* kubectl get pods
* kubectl logs <name of pod>
Dockerfile for apache with stdout instead of access log:
FROM ubuntu
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update
RUN apt-get install -y apache2
RUN echo '<HTML><BODY>Hello World</BODY></HTML' >/var/www/html/index.html
RUN sed -r -i 's?\$\{APACHE_LOG_DIR\}/access.log?/dev/stdout?'  /etc/apache2/sites-available/000-default.conf
EXPOSE 80
CMD ["apachectl","-DFOREGROUND"]
Once done, minikube can be shutdown with minikube stop.
Category:Kubernetes Category: How-To