* Google Cloud Platform: This tutorial leverages the [Google Cloud Platform](https://cloud.google.com/) to streamline provisioning of the compute infrastructure required to bootstrap a Kubernetes cluster from the ground up. [Sign up](https://cloud.google.com/free/) for $300 in free credits.
* Google Cloud Platform SDK: Follow the Google Cloud SDK [documentation](https://cloud.google.com/sdk/) to install and configure the `gcloud` command
line utility. Make sure to set a default compute region and compute zone.
* The [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) command line utility is used to interact with the Kubernetes
API Server.
* Linux or Mac environment with Python 3
## Provisioning Compute Resources
Kubernetes requires a set of machines to host the Kubernetes control plane and the worker nodes where containers are ultimately run. In this lab you will provision the compute resources required for running a secure and highly available Kubernetes cluster across a single [compute zone](https://cloud.google.com/compute/docs/regions-zones/regions-zones).
### Networking
The Kubernetes [networking model](https://kubernetes.io/docs/concepts/cluster-administration/networking/#kubernetes-model) assumes a flat network in which containers and nodes can communicate with each other. In cases where this is not desired [network policies](https://kubernetes.io/docs/concepts/services-networking/network-policies/) can limit how groups of containers are allowed to communicate with each other and external network endpoints.
> Setting up network policies is out of scope for this tutorial.
#### Virtual Private Cloud Network
In this section a dedicated [Virtual Private Cloud](https://cloud.google.com/compute/docs/networks-and-firewalls#networks)(VPC) network will be setup to host the Kubernetes cluster.
Create the `kubernetes-the-kubespray-way` custom VPC network:
A [subnet](https://cloud.google.com/compute/docs/vpc/#vpc_networks_and_subnets) must be provisioned with an IP address range large enough to assign a private IP address to each node in the Kubernetes cluster.
Create the `kubernetes` subnet in the `kubernetes-the-hard-way` VPC network:
Kubespray is relying on SSH to configure the controller and worker instances.
Test SSH access to the `controller-0` compute instance:
```ShellSession
IP_CONTROLLER_0=$(gcloud compute instances list --filter="tags.items=kubernetes-the-kubespray-way AND name:controller-0" --format="value(EXTERNAL_IP)")
USERNAME=$(whoami)
ssh $USERNAME@$IP_CONTROLLER_0
```
If this is your first time connecting to a compute instance SSH keys will be
generated for you. In this case you will need to enter a passphrase at the
prompt to continue.
> If you get a 'Remote host identification changed!' warning, you probably
already connected to that IP address in the past with another host key. You
can remove the old host key by running `ssh-keygen -R $IP_CONTROLLER_0`
Please repeat this procedure for all the controller and worker nodes, to
ensure that SSH access is properly functioning for all nodes.
## Set-up Kubespray
The following set of instruction is based on the [Quick Start](https://github.com/kubernetes-sigs/kubespray) but slightly altered for our
set-up.
As Ansible is a python application, we will create a fresh virtual
environment to install the dependencies for the Kubespray playbook:
```ShellSession
python3 -m venv venv
source venv/bin/activate
```
Next, we will git clone the Kubespray code into our working directory:
This kubeconfig file uses the internal IP address of the controller node to
access the API server. This kubeconfig file will thus not work of from
outside of the VPC network. We will need to change the API server IP address
to the controller node his external IP address. The external IP address will be
accepted in the
TLS negotation as we added the controllers external IP addresses in the SSL
certificate configuration.
Open the file and modify the server IP address from the local IP to the
external IP address of controller-0, as stored in $IP_CONTROLLER_0.
> Example
```ShellSession
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: XXX
server: https://35.205.205.80:6443
name: cluster.local
...
```
Now, we load the configuration for `kubectl`:
```ShellSession
export KUBECONFIG=$PWD/kubespray-do.conf
```
We should be all set to communicate with our cluster from our local workstation:
```ShellSession
kubectl get nodes
```
> Output
```ShellSession
NAME STATUS ROLES AGE VERSION
controller-0 Ready master 47m v1.17.9
controller-1 Ready master 46m v1.17.9
controller-2 Ready master 46m v1.17.9
worker-0 Ready <none> 45m v1.17.9
worker-1 Ready <none> 45m v1.17.9
worker-2 Ready <none> 45m v1.17.9
```
## Smoke tests
### Metrics
Verify if the metrics server addon was correctly installed and works:
```ShellSession
kubectl top nodes
```
> Output
```ShellSession
NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
controller-0 191m 10% 1956Mi 26%
controller-1 190m 10% 1828Mi 24%
controller-2 182m 10% 1839Mi 24%
worker-0 87m 4% 1265Mi 16%
worker-1 102m 5% 1268Mi 16%
worker-2 108m 5% 1299Mi 17%
```
Please note that metrics might not be available at first and need a couple of
minutes before you can actually retrieve them.
### Network
Let's verify if the network layer is properly functioning and pods can reach
each other:
```ShellSession
kubectl run myshell1 -it --rm --image busybox -- sh
hostname -i
# launch myshell2 in seperate terminal (see next code block) and ping the hostname of myshell2
ping <hostnamemyshell2>
```
```ShellSession
kubectl run myshell2 -it --rm --image busybox -- sh
hostname -i
ping <hostnamemyshell1>
```
> Output
```ShellSession
PING 10.233.108.2 (10.233.108.2): 56 data bytes
64 bytes from 10.233.108.2: seq=0 ttl=62 time=2.876 ms
64 bytes from 10.233.108.2: seq=1 ttl=62 time=0.398 ms
64 bytes from 10.233.108.2: seq=2 ttl=62 time=0.378 ms
^C
--- 10.233.108.2 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.378/1.217/2.876 ms
```
### Deployments
In this section you will verify the ability to create and manage [Deployments](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/).
Create a deployment for the [nginx](https://nginx.org/en/) web server:
```ShellSession
kubectl create deployment nginx --image=nginx
```
List the pod created by the `nginx` deployment:
```ShellSession
kubectl get pods -l app=nginx
```
> Output
```ShellSession
NAME READY STATUS RESTARTS AGE
nginx-86c57db685-bmtt8 1/1 Running 0 18s
```
#### Port Forwarding
In this section you will verify the ability to access applications remotely using [port forwarding](https://kubernetes.io/docs/tasks/access-application-cluster/port-forward-access-application-cluster/).
Retrieve the full name of the `nginx` pod:
```ShellSession
POD_NAME=$(kubectl get pods -l app=nginx -o jsonpath="{.items[0].metadata.name}")
```
Forward port `8080` on your local machine to port `80` of the `nginx` pod:
```ShellSession
kubectl port-forward $POD_NAME 8080:80
```
> Output
```ShellSession
Forwarding from 127.0.0.1:8080 -> 80
Forwarding from [::1]:8080 -> 80
```
In a new terminal make an HTTP request using the forwarding address:
```ShellSession
curl --head http://127.0.0.1:8080
```
> Output
```ShellSession
HTTP/1.1 200 OK
Server: nginx/1.19.1
Date: Thu, 13 Aug 2020 11:12:04 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Tue, 07 Jul 2020 15:52:25 GMT
Connection: keep-alive
ETag: "5f049a39-264"
Accept-Ranges: bytes
```
Switch back to the previous terminal and stop the port forwarding to the `nginx` pod:
```ShellSession
Forwarding from 127.0.0.1:8080 -> 80
Forwarding from [::1]:8080 -> 80
Handling connection for 8080
^C
```
#### Logs
In this section you will verify the ability to [retrieve container logs](https://kubernetes.io/docs/concepts/cluster-administration/logging/).
In this section you will verify the ability to [execute commands in a container](https://kubernetes.io/docs/tasks/debug-application-cluster/get-shell-running-container/#running-individual-commands-in-a-container).
Print the nginx version by executing the `nginx -v` command in the `nginx` container:
```ShellSession
kubectl exec -ti $POD_NAME -- nginx -v
```
> Output
```ShellSession
nginx version: nginx/1.19.1
```
### Kubernetes services
#### Expose outside of the cluster
In this section you will verify the ability to expose applications using a [Service](https://kubernetes.io/docs/concepts/services-networking/service/).
Expose the `nginx` deployment using a [NodePort](https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport) service:
> The LoadBalancer service type can not be used because your cluster is not configured with [cloud provider integration](https://kubernetes.io/docs/getting-started-guides/scratch/#cloud-provider). Setting up cloud provider integration is out of scope for this tutorial.
Retrieve the node port assigned to the `nginx` service: