---
# System prepended metadata

title: k8s TP

---

# Set up

We advise you to use Linux to for this TP because it much faster.

## 1. Minikube Installation

### On Linux 

Run the following code in terminal:

```
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
sudo install minikube-linux-amd64 /usr/local/bin/minikube
```

### On Windows (Not recommended, takes a lot of time)
1. You will need to install a Container or virtual machine manager (***If you already have VirtualBox, you can skip this step.***)
- Install Docker: [Docker Desktop Installer.exe](https://desktop.docker.com/win/main/amd64/Docker%20Desktop%20Installer.exe)
2. Download Minikube:
- Install Minikube: [minikube-installer.exe](https://storage.googleapis.com/minikube/releases/latest/minikube-installer.exe)
- Add the `minikube.exe` binary to your `PATH` by opening Windows PowerShell and run this code:
```
$oldPath = [Environment]::GetEnvironmentVariable('Path', [EnvironmentVariableTarget]::Machine)
if ($oldPath.Split(';') -inotcontains 'C:\minikube'){
  [Environment]::SetEnvironmentVariable('Path', $('{0};C:\minikube' -f $oldPath), [EnvironmentVariableTarget]::Machine)
}
```

### Getting started

Verify installation: `minikube version`

## 2. Kubectl Installation

### On linux

Run the following code in terminal:

```
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
```

### On Window 
Download by the following link : https://kubernetes.io/docs/tasks/tools/install-kubectl-windows/#install-kubectl-binary-with-curl-on-windows

### Getting started

Verify installation: `kubectl version`

# Exercise

## 1. Deploy application:

In this exercise, you will learn how to write manifest file for deployment, pod, service and ingress. In addition, you will also understand the high availability of Kubernestes

### 1.1. Deployment

1. Complete the deployment.yaml with this information:

- You will write a deployment 
- Number of pods: >= 4 (for later use)
- Image: `hashicorp/http-echo`
- Port: 5678
- Name and label: as you wish

2. Create 3 or more nodes (for later use)

```
minikube start --nodes 3
```

***Note: You can shut down minikube by running following command (if you need):*** 
```
minikube stop
minikube delete
```

- Verify there are 3 nodes : `kubectl get node`
- You can open a dashboard to have a better view about what happens: `minikube dashboard`
3. Apply deployment.yaml
```
kubectl apply -f <your yaml file>
```
4. Observe your deployment and pod:

<u>***Dashboard:***</u>  Look at the Workloads

<u>***Command Line:***</u>
```
kubectl get deployment
kubectl get pod -owide
```

### 1.2. Service

1. Complete the service.yaml with this information:

- You will write a service 
- Name: as you wish
- Port: 5678

2. Apply service.yaml
3. Observe your service

<u>***Dashboard:***</u>  Look at the Service

<u>***Command Line:***</u>
```
kubectl get service -owide
```

### 1.3. Ingress

1. Complete the ingress.yaml with this information:

- You will write an ingress
- Name: as you wish
- Port: 5678
- Path: as you wish

2. Install NGINX ingress controller
```
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/cloud/deploy.yaml
```
3. Apply ingress.yaml
4. Open another terminal and open a tunnel
```
minikube tunnel
```
5. Now you can connect to the deploy file. Use the IP address of your service
```
curl <IP_address>:<port>/<path>
```
#### Expected: You will see HELLO_WORLD in your terminal

### 1.4. High availability:

1. Before continue, let look again your pods and nodes. Note which pod is in which node
2. Now delete a node that have at least 1 pod by running:
> (**CAUTION** : **Do not delete the control plane** as minikube currently doesn't support multi control plane cluster) I'm begging you :<
```
minikube node delete <node-name>
```
3. Observe your pod, what do you see?

#### Expected: the pods in the deleted node is transfer to other nodes (see again using dashboard or command line)
## 2. Deploy Kafka

In this exercise, you will learn how to write manifest file for Namespace, StatefulSet, PV/PVC, Headless Service. In addition, you will also understand the Scalability and Disaster Recovery of Kubernetes

### 2.1. Preparation

1. Delete the minikube cluster of the last exercise : 
```
minikube stop
minikube delete
```

2. Start a new kubernetes cluster
```
minikube start
```

### 2.2. StatefulSet

1. Apply namespace.yaml

2. Set 'kafka' as your default name space 
```
kubectl config set-context --current --namespace=kafka
```
3. Take a look at statefulset.yaml and then apply it
5. Verify that the statefulset is well deployed
    a. The command`kubectl get pods -l app="kafka"`should display similar output
    ```
    NAME      READY   STATUS    RESTARTS   AGE
    kafka-0   1/1     Running   0          4m
    kafka-1   1/1     Running   0          4m
    kafka-2   1/1     Running   0          4m
    ```
    b. The command`kubectl get pvc -l app=kafka`should display similar output
    ```
    NAME           STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
    data-kafka-0   Bound    pvc-54e2de86-6c02-4605-9904-00e5cd1f44d1   100Mi      RWO            standard       27h
    data-kafka-1   Bound    pvc-e4ef092d-ca3e-4603-b2dc-9207676d4e14   100Mi      RWO            standard       27h
    data-kafka-2   Bound    pvc-ccf06bd0-f992-4554-be74-0905f701e824   100Mi      RWO            standard       27h
    ```


### 2.3. Create data in the PV
**Objective :** Enter a shell of pod and perform manual action

1. Tap into the shell of `kafka-2` pod via one of the following way : 
- <u>***Command Line:***</u>  `kubectl exec --stdin --tty <pod_name> -- /bin/bash`

OR
- <u>***Dashboard:***</u> 
![Screenshot 2024-01-30 103724](https://hackmd.io/_uploads/BklrVvI5a.png)
![Screenshot 2024-01-30 103740](https://hackmd.io/_uploads/BygB4vIq6.png)

2. Create a file in the persistant volume, the mount path of the **persistant volume** is defined at `spec.containers.volumeMounts` in the manifest of the stateful set
```
cd <path_to_PV> #TODO
touch dmm.txt
```
### 2.4. Scale down to simulate disaster
1. Scale down the StatefulSet to 2 replicas with the following command `kubectl scale statefulset <name_of_statefulset> --replicas=2 `

2. The command`kubectl get statefulset kafka` should display similar output 
    ```
    NAME    READY   AGE
    kafka   2/2     5m
    ```
3. Verify the all 3 PVs are still alive with `kubectl get pvc`
    ```
    # EXPECTED OUTPUT
    NAME           STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
    data-kafka-0   Bound    pvc-d07b8ed3-1b9b-4f82-b8d3-9f58c617b9a6   100Mi      RWO            standard       22m
    data-kafka-1   Bound    pvc-82223221-d996-4d0b-8406-1cad1f8ef50b   100Mi      RWO            standard       22m
    data-kafka-2   Bound    pvc-398288fe-7bd5-416f-9392-e2bcee7a9c33   100Mi      RWO            standard       22m
    ```

### 2.5. Find data in the PV

**Objectif :** find `dmm.txt` in the PV

1. Each pod in a StatefulSet has a specific name for its PV in the following format `data-<pod_name>`. You can inspect the PV by: 

+ **Retrieve the information in PV through the <u>Dashboard</u>**
    a. Get host path of the PV
    b. Open a bash session in the docker container of minikube cluster
    c. Find `dmm.txt` 

**OR**
+ **Retrieve the information by mounting the PV to a debug pod**
    a. Find PV name with `kubectl get pvc`
    b. Start a shell session in the debug pod and you can find the information of the PV at `spec.containers.volumeMounts.mountPath` folder.
    c. Exit the shell with command `exit`
    d. **IMPORTANT :** Delete the debug pod with `kubectl delete pod debug-pod`

### 2.6. Scale up

**Objective:** Verify that StatefulSet will automatically pick up existing PV.

1. Scale up the StatefulSet to 3 replicas and verify that the StatefulSet is scaled up correctly by checking pods in the cluster.
2. Tap into the shell of **kafka-2** pod and verify that `dmm.txt` is there.
 
### 2.7. Write correct config file for the client

1. Complete secret.yaml and apply it
- name: as you wish
- <TODO_data_key>: as you wish

2. Verify that the secret is well created 

```
kubectl get secret -n kafka
```
3. Create a ***kafka client CLI*** with the following template, **please complete the TODO part in the code to mount the secret to the pod correctly.**
4. Apply the manifest of the kafka client
5. Run the next kafka command in `kafka-cli` pod to verify that the config file is functioning correctly.
6. Create a topic (**Remember to change the <TODO_config_key**>):
    ```
    kafka-topics --create --topic test --partitions 3 --replication-factor 3 --bootstrap-server kafka-headless:9092 --command-config /etc/kafka/secrets/<TODO_config_key>.properties
    ```
    + Expected error: 
        ```
        WARN[AdminClient clientId=adminclient-1] Error connecting to node TODO:9092 (id: 1 rack: null) (org.apache.kafka.clients.NetworkClient)
        ```

**This is because the kafka broker is not advertised in the correct domain name of the pod**

### 2.8. Understanding StatefulSet headless service and domain name

**Objective :** Advertise the kafka broker at the correct domain name

**Context :** Each kafka broker must advertise at its correct domain name by setting the env var `KAFKA_ADVERTISED_LISTENERS` so that the client could make request to it.

The domain name of the pod can be determined by **<pod_name>.<headles_service>.<namespace>.svc.<cluster_domain>**

Below is some examples:
| Pod Hostname       | Cluster Domain       | Service (ns/name) | StatefulSet (ns/name) | StatefulSet Domain                | Pod DNS                                      |
|----------------|-----------------|--------------------------|-----------------------|--------------------------------------|----------------------------------------------|
| web-{0..N-1}   | cluster.local   | default/nginx            | default/web           | nginx.default.svc.cluster.local      | web-{0..N-1}.nginx.default.svc.cluster.local |
| web-{0..N-1}   | cluster.local   | foo/nginx                | foo/web               | nginx.foo.svc.cluster.local          | web-{0..N-1}.nginx.foo.svc.cluster.local     |
| web-{0..N-1}   | kube.local      | foo/nginx                | foo/web               | nginx.foo.svc.kube.local             | web-{0..N-1}.nginx.foo.svc.kube.local        |

1. Advertise the kafka broker at the correct domain name of the pod by correcting the following line in the **statefulset.yaml** (Replace only the TODO)
    ```
    # Please substitute "TODO" with the correct domain name 
    export KAFKA_ADVERTISED_LISTENERS=SASL://TODO:9092
    ```

Hint:
+ The default cluster domain is `cluster.local`
+ You can get the pod name of the StatefulSet with env var `POD_NAME`.
Reapply the manifest of the stateful set

### 2.9. VERIFY THE KAFKA CLUSTER IS FUNCTIONAL !!!
1. Create a topic (**TODO**):
    ```
    kafka-topics --create --topic test --partitions 3 --replication-factor 3 --bootstrap-server kafka-headless:9092 --command-config /etc/kafka/secrets/<TODO_config_key>.properties
    ```
    + Expected output: 
        ```
        Created topic test.
        ```
3. Produce message to the kafka broker (**TODO**): 
    ```
    kafka-console-producer --bootstrap-server kafka-headless:9092 --topic test --producer.config /etc/kafka/secrets/<TODO_config_key>.properties
    ```
    + Expected output: 
        ```
        > #ENTER_SOME_RANDOM_STRING
        ```

3. Consume message from the kafka broker (**TODO**): 
    ```
    kafka-console-consumer --bootstrap-server kafka-headless:9092 --topic test --consumer.config /etc/kafka/secrets/<TODO_config_key>.properties --from-beginning
    ```
    + Expected output (Something that you entered): 
        ```
        xfh
        dtr
        h
        f
        gs
        dfg
        rg
        dgf
        qsfef
        ^CProcessed a total of 9 messages
        ```