diff --git a/.vscode/settings.json b/.vscode/settings.json
index 1e07351..88b3d47 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,11 +1,10 @@
{
- "cSpell.words": [
- "istio",
- "kubectl",
- "kustomization",
- "kustomize"
- ],
- "cSpell.ignoreWords": [
- "istio"
- ]
-}
\ No newline at end of file
+ "files.watcherExclude": {
+ "**/.git/objects/**": true,
+ "**/.git/subtree-cache/**": true,
+ "**/node_modules/**": true,
+ "**/.hg/store/**": true,
+ "**/mkdocs-site/**": true
+ },
+ "files.useExperimentalFileWatcher": true
+}
diff --git a/Labs/00-VerifyCluster/README.md b/Labs/00-VerifyCluster/README.md
index 9ef2997..fe44bf9 100644
--- a/Labs/00-VerifyCluster/README.md
+++ b/Labs/00-VerifyCluster/README.md
@@ -9,21 +9,8 @@
- **`kubectl`** - short for Kubernetes Controller - is the CLI for Kubernetes cluster and is required in order to be able to run the labs.
- In order to install `kubectl` and if required creating a local cluster, please refer to [Kubernetes - Install Tools](https://kubernetes.io/docs/tasks/tools/)
-
-
----
-## Lab Highlights:
- - [01. Installing minikube](#01-Installing-minikube)
- - [02. Start minikube](#02-Start-minikube)
- - [03. Check the minikube status](#03-Check-the-minikube-status)
- - [04. Verify that the cluster is up and running](#04-Verify-that-the-cluster-is-up-and-running)
- - [05. Verify that you can "talk" to your cluster](#05-Verify-that-you-can-talk-to-your-cluster)
- - [05.01. Verify that you can "talk" to your cluster](#0501-Verify-that-you-can-talk-to-your-cluster)
-
---
-
-
### 01. Installing minikube
- If you don't have an existing cluster you can use google cloud for the labs hands-on
@@ -96,12 +83,13 @@ Kubernetes control plane is running at https://192.168.49.2:8443
KubeDNS is running at https://192.168.49.2:8443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
```
-- Verify that `kubectl` is installed and configured (You should get something like the following)
+- Verify that `kubectl` is installed and configured
```sh
kubectl config view
```
+- You should get something like the following
```yaml
apiVersion: v1
clusters:
@@ -132,31 +120,4 @@ users:
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
minikube Ready control-plane,master 3m9s v1.20.0
-```
-
-### 05.01. Verify that you can "talk" to your cluster
-
-```sh
-# In this sample we have minikube pod running
-$ kubectl get nodes
-NAME STATUS ROLES AGE VERSION
-minikube Ready control-plane,master 3m9s v1.20.0
-```
-
-
-
----
-
-
-
-
-
-
\ No newline at end of file
+```
\ No newline at end of file
diff --git a/Labs/01-Namespace/README.md b/Labs/01-Namespace/README.md
index 15aa47b..0231512 100644
--- a/Labs/01-Namespace/README.md
+++ b/Labs/01-Namespace/README.md
@@ -9,28 +9,13 @@
# Namespaces
- Kubernetes supports **multiple virtual clusters** backed by the same **physical cluster**.
-- These virtual clusters are called namespaces.
-- Namespaces are the default way for Kubernetes to separate resources.
-- Using name spaces we can isolate the development, improve security and more.
-- Kubernetes clusters has a build in namespace called **default** and might contain more namespaces like like `kube-system` for example.
-
-
-
----
-## Lab Highlights:
-- [K8S Hands-on](#k8s-hands-on)
-- [Namespaces](#namespaces)
- - [Lab Highlights:](#lab-highlights)
- - [Pre-Requirements](#pre-requirements)
- - [01. Create Namespace](#01-create-namespace)
- - [01.01. Create Namespace](#0101-create-namespace)
- - [02. Setting the default Namespace for `kubectl`](#02-setting-the-default-namespace-for-kubectl)
- - [03. Verify that you've updated the namespace](#03-verify-that-youve-updated-the-namespace)
- - [Note:](#note)
+- These virtual clusters are called `namespaces`.
+- `Namespaces` are the default way for Kubernetes to separate resources.
+- Using `namespaces` we can isolate the development, improve security and much more.
+- Kubernetes clusters has a builtin `namespace` called **default** and might contain more `namespaces`, like `kube-system`, for example.
---
-
### Pre-Requirements
@@ -43,17 +28,17 @@
### 01. Create Namespace
-### 01.01. Create Namespace
-
```sh
-# In this sample `codewizard` is the desired name space
+# In this sample `codewizard` is the desired namespace
$ kubectl create namespace codewizard
-namespace "codewizard" created
+namespace/codewizard created
-### !!! Try to create the following name space (with _ & -):
+### !!! Try to create the following namespace (with _ & -), and see what happens:
$ kubectl create namespace my_namespace-
```
+---
+
### 02. Setting the default Namespace for `kubectl`
- To set the default namespace run:
@@ -64,6 +49,8 @@ $ kubectl config set-context $(kubectl config current-context) --namespace=codew
Context minikube modified.
```
+---
+
### 03. Verify that you've updated the namespace
```sh
@@ -74,32 +61,14 @@ CURRENT NAME CLUSTER AUTHINFO NAMESPACE
* minikube minikube minikube codewizard
```
-### Note:
+---
-- When using `kubectl` you can pass the `-n` flag in order to execute the `kubectl` command on a desired namespace
+### 0.4 Using the `-n` Flag:
+
+- When using `kubectl` you can pass the `-n` flag in order to execute the `kubectl` command on a desired `namespace`.
- For example:
```sh
# get resources of a specific workspace
$ kubectl get pods -n
```
-
-
-
----
-
-
-
-
-
-
diff --git a/Labs/02-Deployments-Imperative/README.md b/Labs/02-Deployments-Imperative/README.md
index b481c16..87d01a6 100644
--- a/Labs/02-Deployments-Imperative/README.md
+++ b/Labs/02-Deployments-Imperative/README.md
@@ -6,14 +6,6 @@
---
# Deployment - Imperative
-### Pre-Requirements
-
-- K8S cluster - Setting up minikube cluster instruction
-
-[](https://console.cloud.google.com/cloudshell/editor?cloudshell_git_repo=https://github.com/nirgeier/KubernetesLabs)
-**CTRL + click to open in new window**
-
----
## Creating deployments using `kubectl create`
@@ -21,37 +13,32 @@
[praqma/network-multitool](https://github.com/Praqma/Network-MultiTool)
- This is a multitool for container/network testing and troubleshooting.
-
-
---
-## Lab Highlights:
-- [K8S Hands-on](#k8s-hands-on)
-- [Deployment - Imperative](#deployment---imperative)
- - [Pre-Requirements](#pre-requirements)
- - [Creating deployments using `kubectl create`](#creating-deployments-using-kubectl-create)
- - [Lab Highlights:](#lab-highlights)
- - [01. Create namespace](#01-create-namespace)
- - [02. Deploy multitool image](#02-deploy-multitool-image)
- - [03. Test the deployment](#03-test-the-deployment)
- - [03.01. Create a Service using `kubectl expose`](#0301-create-a-service-using-kubectl-expose)
- - [03.02. Find the port \& the IP which was assigned to our pod by the cluster.](#0302-find-the-port--the-ip-which-was-assigned-to-our-pod-by-the-cluster)
- - [03.03. Test the deployment](#0303-test-the-deployment)
+
+### Pre-Requirements
+
+- K8S cluster - Setting up minikube cluster instruction
+
+[](https://console.cloud.google.com/cloudshell/editor?cloudshell_git_repo=https://github.com/nirgeier/KubernetesLabs)
+**CTRL + click to open in new window**
---
-
-### 01. Create namespace
+### 01. Create Namespace
+
+- As completed in the previous lab, create the desired namespace [codewizard]:
```sh
-# Create the desired namespace [codewizard]
$ kubectl create namespace codewizard
namespace/codewizard created
```
-- In order to set this is as your default namespace refer to: set default namespace
+- In order to set this is as the default namespace, please refer to set default namespace.
-### 02. Deploy multitool image
+---
+
+### 02. Deploy Multitool Image
```sh
# Deploy the first container
@@ -60,10 +47,12 @@ deployment.apps/multitool created
```
- `kubectl create deployment` actually creating a replica set for us.
-- We can verify it:
+- We can verify it by running:
```
$ kubectl get all -n codewizard
+
+## Expected output:
NAME READY UP-TO-DATE AVAILABLE
deployment.apps/multitool 1/1 1 1
@@ -74,21 +63,24 @@ NAME READY STATUS RESTARTS
pod/multitool-7885b5f94f-9s7xh 1/1 Running 0
```
-## 03. Test the deployment
+---
+
+## 03. Test the Deployment
-- The above deployment contains a container [`multitool`]
-- In order of us to be able to access this `multitool` container we need to create a resource of type `Service` which will "open" the server for incoming traffic
+- The above deployment contains a container named, `multitool`.
+- In order for us to be able to access this `multitool` container, we need to create a resource of type `Service` which will "open" the server for incoming traffic.
-### 03.01. Create a Service using `kubectl expose`
+#### Create a service using `kubectl expose`
```sh
# "Expose" the desired port for incoming traffic
-# This command is equivalent to declare a `kind: Service` im yaml file
+# This command is equivalent to declare a `kind: Service` im YAML file
+
$ kubectl expose deployment -n codewizard multitool --port 80 --type NodePort
service/multitool exposed
```
-- Verify that the service have been created
+- Verify that the service have been created by running:
```sh
$ kubectl get service -n codewizard
@@ -97,12 +89,13 @@ $ kubectl get service -n codewizard
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/multitool NodePort 10.102.73.248 80:31418/TCP 3s
```
+
-### 03.02. Find the port & the IP which was assigned to our pod by the cluster.
+#### Find the port & the IP which was assigned to our pod by the cluster.
- Grab the port from the previous output.
- Port: In the above sample its `31418` [`80:31418/TCP`]
- - IP: we will need to gtrab the cluster ip using `kubectl cluster-info`
+ - IP: we will need to grab the cluster ip using `kubectl cluster-info`
```sh
@@ -125,10 +118,12 @@ NODE_PORT=$(kubectl get -o \
- In this sample the cluster-ip is `192.168.49.2`
-### 03.03. Test the deployment
+
-- Test to see if the deployment worked using the `ip & port` you got above
-- Execute `curl` with the following prameters: `http://${CLUSTER_IP}:${NODE_PORT}`
+#### Test the deployment
+
+- Test to see if the deployment worked using the `ip address and port number` we have retrieved above.
+- Execute `curl` with the following parameters: `http://${CLUSTER_IP}:${NODE_PORT}`
```sh
curl http://${CLUSTER_IP}:${NODE_PORT}
@@ -139,22 +134,4 @@ curl 192.168.49.2:30436
# The output should be similar to this:
Praqma Network MultiTool (with NGINX) ...
```
-
-
----
-
-
-
-
-
-
\ No newline at end of file
+- If you get the above output, congratulations! You have successfully created a deployment using imperative commands.
\ No newline at end of file
diff --git a/Labs/03-Deployments-Declarative/README.md b/Labs/03-Deployments-Declarative/README.md
index df5cd60..a59f84d 100644
--- a/Labs/03-Deployments-Declarative/README.md
+++ b/Labs/03-Deployments-Declarative/README.md
@@ -13,36 +13,26 @@
[](https://console.cloud.google.com/cloudshell/editor?cloudshell_git_repo=https://github.com/nirgeier/KubernetesLabs)
**CTRL + click to open in new window**
-
-
---
-## Lab Highlights:
- - [01. Create namespace](#01-Create-namespace)
- - [02. Deploy nginx using yaml file (declarative)](#02-Deploy-nginx-using-yaml-file-declarative)
- - [03. Verify that the deployment is created:](#03-Verify-that-the-deployment-is-created)
- - [04. Check if the pods are running:](#04-Check-if-the-pods-are-running)
- - [05. Update the yaml file with replica's value of 5](#05-Update-the-yaml-file-with-replicas-value-of-5)
- - [06. Update the deployment using `kubectl apply`](#06-Update-the-deployment-using-kubectl-apply)
- - [07. Scaling down with `kubectl scale`](#07-Scaling-down-with-kubectl-scale)
----
-
+### 01. Create Namespace
-### 01. Create namespace
+- As completed in the previous lab, create the desired namespace [codewizard]:
```sh
-# Create the desired namespace [codewizard]
$ kubectl create namespace codewizard
namespace/codewizard created
```
-- In order to set this is as your default namespace refer to: set default namespace
+- In order to set this is as the default namespace, please refer to set default namespace.
+
+---
### 02. Deploy nginx using yaml file (declarative)
-- Lets create the yaml file for the deployment.
-- If this is your first k8s yaml file its recommended that you will type it to get the feeling of the structure
+- Let's create the `YAML` file for the deployment.
+- If this is your first `k8s` `YAML` file, its advisable that you type it in order to get the feeling of the structure.
- Save the file with the following name: `nginx.yaml`
```yaml
@@ -78,7 +68,9 @@ spec:
deployment.extensions/nginx created
```
-### 03. Verify that the deployment is created:
+---
+
+### 03. Verify that the deployment has been created:
```
$ kubectl get deployments -n codewizard
@@ -87,6 +79,8 @@ multitool 1 1 1 1
nginx 1 1 1 1
```
+---
+
### 04. Check if the pods are running:
```
@@ -96,30 +90,35 @@ multitool-7885b5f94f-9s7xh 1/1 Running 0
nginx-647fb5956d-v8d2w 1/1 Running 0
```
-### 0Playing with K8S replicas
+### 05. Playing with K8S replicas
-- Lets play with the replica and see K8S in action
+- Let's play with the replica and see K8S in action.
- Open a second terminal and execute:
```sh
$ kubectl get pods -n codewizard --watch
```
-### 05. Update the yaml file with replica's value of 5
+---
+
+### 05. Update the `nginx.yaml` file with replica's value of 5:
```yaml
spec:
replicas: 5
```
+---
+
### 06. Update the deployment using `kubectl apply`
```
$ kubectl apply -n codewizard -f nginx.yaml --record=true
deployment.apps/nginx configured
```
+
-- Switch to the second terminal and you should see something like
+- Switch to the second terminal and you should see something like the following:
```sh
$ kubectl get pods --watch -n codewizard
@@ -144,18 +143,23 @@ nginx-dc8bb9b45-x7j4g 1/1 Running 0 3s
nginx-dc8bb9b45-wkc68 1/1 Running 0 3s
```
-> Can you explain what do you see?
+
+- Can you explain what do you see?
+
+ `Why are there more containers than requested?`
- Whey there are more containers that requested?
+---
### 07. Scaling down with `kubectl scale`
+
+- Scaling down using `kubectl`, and not by editing the `YAML` file:
+
```sh
-# Scaling using kubectl and not by editing the yaml file
kubectl scale -n codewizard --replicas=1 deployment/nginx
```
-- Switch to the second terminal and now you should see something like:
+- Switch to the second terminal. The current output should show something like this:
```
NAME READY STATUS RESTARTS AGE
@@ -182,23 +186,3 @@ nginx-dc8bb9b45-wkc68 0/1 Terminating 0 6m27s
nginx-dc8bb9b45-x7j4g 0/1 Terminating 0 6m27s
nginx-dc8bb9b45-x7j4g 0/1 Terminating 0 6m27s
```
-
-
-
----
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Labs/04-Rollout/README.md b/Labs/04-Rollout/README.md
index 16ad864..05a0217 100644
--- a/Labs/04-Rollout/README.md
+++ b/Labs/04-Rollout/README.md
@@ -5,10 +5,10 @@
---
-# Rollout (RollingUpdate)
+# Rollout (Rolling Update)
-- In this step we will deploy the same application with several different versions and we will "switch" between them
-- For learning purposes we will play a little bit with the cli
+- In this step we will deploy the same application with several different versions and we will "switch" between them.
+- For learning purposes we will play a little with the `CLI`.
---
### Pre-Requirements
@@ -17,36 +17,20 @@
[](https://console.cloud.google.com/cloudshell/editor?cloudshell_git_repo=https://github.com/nirgeier/KubernetesLabs)
**CTRL + click to open in new window**
-
-
----
-## Lab Highlights:
- - [01. Create namespace](#01-Create-namespace)
- - [02. Create the desired deployment](#02-Create-the-desired-deployment)
- - [03. Expose nginx as service](#03-Expose-nginx-as-service)
- - [04. Verify that the pods and the service are running](#04-Verify-that-the-pods-and-the-service-are-running)
- - [05. Change the number of replicas to 3](#05-Change-the-number-of-replicas-to-3)
- - [06. Verify that now we have 3 replicas](#06-Verify-that-now-we-have-3-replicas)
- - [07. Test the deployment](#07-Test-the-deployment)
- - [08. Deploy another version of nginx](#08-Deploy-another-version-of-nginx)
- - [09. Investigate rollout history:](#09-Investigate-rollout-history)
- - [10. Lets see what was changed during the previous updates:](#10-Lets-see-what-was-changed-during-the-previous-updates)
- - [11. Undo the version upgrade by rolling back and restoring previous version](#11-Undo-the-version-upgrade-by-rolling-back-and-restoring-previous-version)
- - [12. Rolling Restart](#12-Rolling-Restart)
-
---
-
-
### 01. Create namespace
+- As completed in the previous lab, create the desired namespace [codewizard]:
+
```sh
-# Create the desired namespace [codewizard]
$ kubectl create namespace codewizard
namespace/codewizard created
```
-- In order to set this is as your default namespace refer to: set default namespace
+- In order to set this is as the default namespace, please refer to set default namespace.
+
+---
### 02. Create the desired deployment
@@ -54,20 +38,27 @@ namespace/codewizard created
> `save-config`
> If true, the configuration of current object will be saved in its annotation.
> Otherwise, the annotation will be unchanged.
- > This flag is useful when you want to perform kubectl apply on this object in the future.
+ > This flag is useful when you want to perform `kubectl apply` on this object in the future.
+- Let's run the following:
```sh
-# In case we already have this delployed we will get an error message
+
$ kubectl create deployment -n codewizard nginx --image=nginx:1.17 --save-config
```
+Note that in case we already have this deployed, we will get an error message.
-### 03. Expose nginx as service
+---
+
+### 03. Expose nginx as a service
```sh
-# Again: In case we already have this service we will get an error message as well
+
$ kubectl expose deployment -n codewizard nginx --port 80 --type NodePort
service/nginx exposed
```
+Again, note that in case we already have this service we will get an error message as well.
+
+---
### 04. Verify that the pods and the service are running
@@ -88,6 +79,8 @@ NAME DESIRED CURRENT READY AGE
replicaset.apps/nginx-db749865c 1 1 1 66s
```
+---
+
### 05. Change the number of replicas to 3
```sh
@@ -95,6 +88,8 @@ $ kubectl scale deployment -n codewizard nginx --replicas=3
deployment.apps/nginx scaled
```
+---
+
### 06. Verify that now we have 3 replicas
```sh
@@ -105,15 +100,20 @@ nginx-db749865c-jgcvb 1/1 Running 0 86s
nginx-db749865c-lmgtv 1/1 Running 0 4m44s
```
+---
+
### 07. Test the deployment
```sh
# !!! Get the Ip & port for this service
-$ kubectl get services -n codewizard -o wide # Write down the port number
+$ kubectl get services -n codewizard -o wide
+
+# Write down the port number
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
nginx NodePort 10.102.79.9 80:31204/TCP 7m7s app=nginx
-$ kubectl cluster-info # Get the cluster IP
+# Get the cluster IP and port
+$ kubectl cluster-info
Kubernetes control plane is running at https://192.168.49.2:8443
# Using the above : test the nginx
@@ -135,6 +135,8 @@ Accept-Ranges: bytes
...
```
+---
+
### 08. Deploy another version of nginx
```sh
@@ -157,9 +159,11 @@ ETag: "5d528b4c-264"
Accept-Ranges: bytes
```
+---
+
### 09. Investigate rollout history:
-- The rollout history command print out all the saved records
+- The rollout history command print out all the saved records:
```sh
$ kubectl rollout history deployment nginx -n codewizard
@@ -170,13 +174,15 @@ REVISION CHANGE-CAUSE
3 kubectl set image deployment nginx nginx=nginx:1.15 --record=true
```
-### 10. Lets see what was changed during the previous updates:
+---
+
+### 10. Let's see what was changed during the previous updates:
-- Print out the rollout changes
+- Print out the rollout changes:
```sh
# replace the X with 1 or 2 or any number revision id
-$ kubectl rollout history deployment nginx -n codewizard --revision=1
+$ kubectl rollout history deployment nginx -n codewizard --revision= # replace here
deployment.apps/nginx with revision #1
Pod Template:
Labels: app=nginx
@@ -191,6 +197,8 @@ Pod Template:
Volumes:
```
+---
+
### 11. Undo the version upgrade by rolling back and restoring previous version
```
@@ -205,34 +213,14 @@ deployment.apps/nginx rolled back
$ curl -sI :
```
-### 12. Rolling Restart
+---
-**Note:**
+### 12. Rolling Restart
-- If we deploy out deplymnet with `imagePullPolicy: always` we can use `rollout restart` to force K8S to grab the latest image
-- **This is the fastest restart method those days**
+- If we deploy using `imagePullPolicy: always` set in the `YAML` file, we can use `rollout restart` to force `K8S` to grab the latest image.
+- **This is the fastest restart method these days**
```
# Force pods restart
kubectl rollout restart deployment [deployment_name]
```
-
-
-
----
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Labs/05-Services/README.md b/Labs/05-Services/README.md
index 838fab5..9d07f0c 100644
--- a/Labs/05-Services/README.md
+++ b/Labs/05-Services/README.md
@@ -7,7 +7,7 @@
# Service Discovery
-- In the following labs we will learn what is `Service`, we will go over the different `Service` types.
+- In the following lab we will learn what is a `Service` and go over the different `Service` types.
---
### Pre-Requirements
@@ -18,46 +18,24 @@
---
-## What is a `Service`
+## 01. Some general notes on what is a `Service`
+
-### Few general notes:
- `Service` is a unit of application behavior bound to a unique name in a `service registry`.
- `Service` consist of multiple `network endpoints` implemented by workload instances running on pods, containers, VMs etc.
-- `Service` allow us to gain access any given pod / container (e.g. web service).
-- A service is (normally) created on top of an existing and exposing the deployment to the world using ip(s) & port(s).
-- K8S define 3 main ways (+FQDN internally) to define a service, which means that we have 4 different ways to access Pods.
-- There are several proxy mode which inplements diffrent behaviour, for example in `user proxy mode` for each `Service` `kube-proxy` opens a port (randomly chosen) on the local node. Any connections to this "proxy port" are proxied to one of the Service's backend Pods (as reported via Endpoints)
-- All the service types are assigned with a Cluster-IP
-- Every service also creates `Endoint(s)`, which point to the actual pods. Endpoint are usually referred to as back-ends of a particular service.
-
-
+- `Service` allow us to gain access to any given pod or container (e.g., a web service).
+- A `service` is (normally) created on top of an existing deployment and exposing it to the "world", using IP(s) & port(s).
+- `K8S` define 3 main ways (+FQDN internally) to define a service, which means that we have 4 different ways to access Pods.
+- There are several proxy mode which inplements diffrent behaviour, for example in `user proxy mode` for each `Service` `kube-proxy` opens a port (randomly chosen) on the local node. Any connections to this "proxy port" are proxied to one of the Service's backend Pods (as reported via Endpoints).
+- All the service types are assigned with a `Cluster-IP`.
+- Every service also creates `Endoint(s)`, which point to the actual pods. `Endpoints` are usually referred to as `back-ends` of a particular service.
---
-## Lab Highlights:
- - [01. Create namespace and clear previous data if there is any](#01-Create-namespace-and-clear-previous-data-if-there-is-any)
- - [02. Create the required resources for this hand-on](#02-Create-the-required-resources-for-this-hand-on)
- - [03. Expose the nginx with ClusterIP](#03-Expose-the-nginx-with-ClusterIP)
- - [04. Test the nginx with ClusterIP](#04-Test-the-nginx-with-ClusterIP)
- - [04.01. Test the nginx with ClusterIP](#0401-Test-the-nginx-with-ClusterIP)
- - [04.02. Test the nginx using the deployment name](#0402-Test-the-nginx-using-the-deployment-name)
- - [04.03. using the full DNS name](#0403-using-the-full-DNS-name)
- - [05. Create NodePort](#05-Create-NodePort)
- - [05.01. Delete previous service](#0501-Delete-previous-service)
- - [05.02. Create `NodePort` Service](#0502-Create-NodePort-Service)
- - [05.03. Test the `NodePort` Service](#0503-Test-the-NodePort-Service)
- - [06. Create LoadBalancer (only if you are on real cloud)](#06-Create-LoadBalancer-only-if-you-are-on-real-cloud)
- - [06.01. Delete previous service](#0601-Delete-previous-service)
- - [06.02. Create `LoadBalancer` Service](#0602-Create-LoadBalancer-Service)
- - [06.03. Test the `LoadBalancer` Service](#0603-Test-the-LoadBalancer-Service)
-
----
-
-
### 01. Create namespace and clear previous data if there is any
```sh
-# If the namespace already exist and contains data form previous steps, lets clean it
+# If the namespace already exists and contains data form previous steps, let's clean it
kubectl delete namespace codewizard
# Create the desired namespace [codewizard]
@@ -65,6 +43,8 @@ $ kubectl create namespace codewizard
namespace/codewizard created
```
+---
+
### 02. Create the required resources for this hand-on
```sh
@@ -89,18 +69,21 @@ NAME DESIRED CURRENT READY AGE
replicaset.apps/multitool-74477484b8 1 1 1 30s
replicaset.apps/nginx-6799fc88d8 1 1 1 8s
```
+
+
-## Service types
+# Service types
-- As learned in the lecture there are several services type.
- Lets practice them
+- As previously mentioned, there are several services type. Let's practice them:
### Service type: ClusterIP
-- If not specified, the default service type id 'ClusterIP`
-- Expose the deployment as a service `--type=ClusterIP`
-- `ClusterIP` will expose the pods within the cluster and since we don't have an external ip it will not be reached from outside the cluster.
-- When the service is created K8S attach DNs record to the service with the following format: `..svc.cluster.local`
+- If not specified, the default service type is `ClusterIP`.
+- In order to expose the deployment as a service, use: `--type=ClusterIP`
+- `ClusterIP` will expose the pods within the cluster. Since we don't have an `external IP`, it will not be reachable from outside the cluster.
+- When the service is created `K8S` attaches a DNS record to the service in the following format: `..svc.cluster.local`
+
+---
### 03. Expose the nginx with ClusterIP
@@ -109,7 +92,7 @@ replicaset.apps/nginx-6799fc88d8 1 1 1 8s
$ kubectl expose deployment nginx -n codewizard --port 80 --type ClusterIP
service/nginx exposed
-# Check the services and see the type
+# Check the services and see it's type
# Grab the ClusterIP - we will use it in the next steps
$ kubectl get services -n codewizard
@@ -117,47 +100,69 @@ NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
nginx ClusterIP 10.109.78.182 80/TCP
```
+---
+
### 04. Test the nginx with ClusterIP
-- Since the Service is a cluster ip we will test if we can access the service using the multitool pod
+- Since the service is a `ClusterIP`, we will test if we can access the service using the multitool pod.
```sh
-# Get the name of the multitool pod which we will use
+# Get the name of the multitool pod to be used
$ kubectl get pods -n codewizard
NAME
multitool-XXXXXX-XXXXX
-# Run an interactive shell inside the network-multitool-container
-# (same as with docker)
+# Run an interactive shell inside the network-multitool-container (same concept as with Docker)
$ kubectl exec -it -n codewizard -- sh
```
- Connect to the service in **any** of the following ways:
-#### 04.01. Test the nginx with ClusterIP
+#### Test the nginx with ClusterIP
+
+##### 1. using the IP from the services output. grab the server response:
```sh
-# 1. using the ip from the services output grab the server response
bash-5.0# curl -s
-HTTP/1.1 200 OK
-Server: nginx/1.19.6
-Date: Fri, 15 Jan 2021 23:10:30 GMT
-Content-Type: text/html
-Content-Length: 612
-Last-Modified: Tue, 15 Dec 2020 13:59:38 GMT
-Connection: keep-alive
-ETag: "5fd8c14a-264"
-Accept-Ranges: bytes
```
-#### 04.02. Test the nginx using the deployment name
+```html
+# Expected output:
+
+
+
+Welcome to nginx!
+
+
+
+
Welcome to nginx!
+
If you see this page, the nginx web server is successfully installed and
+working. Further configuration is required.
+
+
For online documentation and support please refer to
+nginx.org.
+Commercial support is available at
+nginx.com.
+
+
Thank you for using nginx.
+
+
+```
+
+
+
+##### 2. Test the nginx using the deployment name - using the service name since its the DNS name behind the scenes
```sh
-# 2. using the service name since its the DNS name behind the scenes
bash-5.0# curl -s nginx
```
```html
+# Expected output:
@@ -187,32 +192,36 @@ bash-5.0# curl -s nginx
```
-#### 04.03. using the full DNS name
+
-- For every service we have a full FQDN (Fully qualified domain name) so we can use it as well
+##### 3. using the full DNS name - for every service we have a full `FQDN` (Fully qualified domain name) so we can use it as well
```sh
# bash-5.0# curl -s ..svc.cluster.local
bash-5.0# curl -s nginx.codewizard.svc.cluster.local
```
+
+---
-### Service type: NodePort
+# Service type: NodePort
-- `NodePort`: Exposes the Service on each Node's IP at a **static port** (the NodePort).
-- A ClusterIP Service, to which the NodePort Service routes, **is automatically created**.
-- NodePort Service is reachable from outside the cluster, by requesting :.
+- `NodePort`: Exposes the Service on each Node's IP at a **static port** (the `NodePort`).
+- A `ClusterIP` Service, to which the `NodePort` Service routes, **is automatically created**.
+- `NodePort` service is reachable from outside the cluster, by requesting `:`.
### 05. Create NodePort
-#### 05.01. Delete previous service
+##### 1. Delete previous service
```sh
# Delete the existing service from previous steps
$ kubectl delete svc nginx -n codewizard
-service "nginx" deleted
+service "nginx" deleted from codewizard namespace
```
-#### 05.02. Create `NodePort` Service
+
+
+##### 2. Create `NodePort` service
```sh
# As before but this time the type is a NodePort
@@ -225,12 +234,13 @@ $ kubectl get svc -n codewizard
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
nginx NodePort 100.65.29.172 80:32593/TCP
```
+
-#### 05.03. Test the `NodePort` Service
+##### 3. Test the `NodePort` service
-- Now if we can find the host and the nodePort we can connect directly to the pod
+- If we have the host IP and the node port number, we can connect directly to the pod.
-- If you followed the previous labs, you should be able to do it your self by now......
+- If you followed the previous labs, you should be able to do it yourself by now......
```sh
# Tiny clue....
@@ -243,25 +253,32 @@ Welcome to nginx!
Thank you for using nginx.
```
-### Service type: LoadBalancer
-
---
-Note: **We cannot test LoadBalancer locally on localhost only on real cluster which can create LoadBalancer)**
----
+# Service type: LoadBalancer
+
+
+
+!!! warning "Note"
+ **We cannot test a `LoadBalancer` service locally on a localhost, but only on a cluster which can provide an `external-IP`**
+
+
### 06. Create LoadBalancer (only if you are on real cloud)
-#### 06.01. Delete previous service
+
+
+##### 1. Delete previous service
```sh
# Delete the existing service from previous steps
$ kubectl delete svc nginx -n codewizard
service "nginx" deleted
```
+
-#### 06.02. Create `LoadBalancer` Service
+##### 2. Create `LoadBalancer` Service
```sh
# As before this time the type is a LoadBalancer
@@ -274,30 +291,11 @@ $ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
nginx LoadBalancer 100.69.15.89 35.205.60.29 80:31354/TCP
```
+
-#### 06.03. Test the `LoadBalancer` Service
+##### 3. Test the `LoadBalancer` Service
```sh
# Testing load balancer only require us to use the EXTERNAL-IP
$ curl -s
```
-
-
-
----
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Labs/06-DataStore/README.md b/Labs/06-DataStore/README.md
index 0e03737..3225e95 100644
--- a/Labs/06-DataStore/README.md
+++ b/Labs/06-DataStore/README.md
@@ -6,47 +6,28 @@
---
# Data Store
-### Pre-Requirements
-- K8S cluster - Setting up minikube cluster instruction
-
-[](https://console.cloud.google.com/cloudshell/editor?cloudshell_git_repo=https://github.com/nirgeier/KubernetesLabs)
-**CTRL + click to open in new window**
-
----
-
## Secrets and ConfigMaps
-- Secrets/ConfigMap are ways to store and inject configurations into your deployments.
-- Secrets usually store passwords,certificates, API keys and more.
+- Secrets & ConfigMap are ways to store and inject configurations into your deployments.
+- Secrets usually store passwords, certificates, API keys and more.
- ConfigMap usually store configuration (data).
-
---
-## Lab Highlights:
- - [01. Create namespace and clear previous data if there is any](#01-Create-namespace-and-clear-previous-data-if-there-is-any)
- - [02. Build the docker container](#02-Build-the-docker-container)
- - [02.01. write the server code](#0201-write-the-server-code)
- - [02.02. Write the DockerFile](#0202-Write-the-DockerFile)
- - [02.03. Build the docker container](#0203-Build-the-docker-container)
- - [02.04. Test the container](#0204-Test-the-container)
- - [03. Using K8S deployment & Secrets/ConfigMap](#03-Using-K8S-deployment--SecretsConfigMap)
- - [03.01. Writing the deployment & Service file](#0301-Writing-the-deployment--Service-file)
- - [03.02. Deploy to cluster](#0302-Deploy-to-cluster)
- - [03.03. Test the app](#0303-Test-the-app)
- - [04. Using Secrets & config maps](#04-Using-Secrets--config-maps)
- - [04.01. Create the desired secret and config map for this lab](#0401-Create-the-desired-secret-and-config-map-for-this-lab)
- - [04.02. Updating the Deployment to read the values from Secrets & ConfigMap](#0402-Updating-the-Deployment-to-read-the-values-from-Secrets--ConfigMap)
- - [04.03. Update the deployment to read values from K8S resources](#0403-Update-the-deployment-to-read-values-from-K8S-resources)
- - [04.04. Test the changes](#0404-Test-the-changes)
+
+
+### Pre-Requirements
+- K8S cluster - Setting up minikube cluster instruction
+
+[](https://console.cloud.google.com/cloudshell/editor?cloudshell_git_repo=https://github.com/nirgeier/KubernetesLabs)
+**CTRL + click to open in new window**
---
-
-## Lets play with Secrets first
+# First, Let's play a bit with Secrets
-### 01. Create namespace and clear previous data if there is any
+### 01. Create namespace and clear previous data (if there is any)
```sh
# If the namespace already exist and contains data from previous steps, lets clean it
@@ -57,13 +38,16 @@ $ kubectl create namespace codewizard
namespace/codewizard created
```
-### You can skip section #2 if you don't wish to build and push your docker container
+!!! warning "Note"
+ **You can skip section number 02. if you don't wish to build and push your docker container**
+
+---
### 02. Build the docker container
-#### 02.01. write the server code
+##### 1. write the server code
- For this demo we will use a tiny NodeJS server which will consume the desired configuration values from the secret
-- This is the code of our server [server.js](server.js)
+- This is the code of our server [server.js](server.js):
```js
//
@@ -85,11 +69,14 @@ require("http")
.listen(process.env.PORT || 5000 );
```
-#### 02.02. Write the DockerFile
+
+
-- First lets wrap it up as docker container
-- If you wish you can skip this and use the existing docker image: `nirgeier/k8s-secrets-sample`
-- In the docker file we will set the `ENV` for or variables
+##### 2. Write the DockerFile
+
+- First, let's wrap it up as `docker container`
+- If you wish, you can skip this and use the existing docker image: `nirgeier/k8s-secrets-sample`
+- In the `Dockerfile` we will set the `ENV` for or variables
```Dockerfile
# Base Image
@@ -110,11 +97,13 @@ COPY server.js .
ENTRYPOINT node server.js
```
-#### 02.03. Build the docker container
+
+
+##### 3. Build the docker container
```sh
-# The container name is prefixed withthe Dockerhub account
+# The container name is prefixed with the Dockerhub account
# !!! You should replace the prefix to your dockerhub account
-# In the sample the username wis `nirgeier`
+# In the sample the username is `nirgeier`
$ docker build . -t nirgeier/k8s-secrets-sample
# The output should be similar to this
@@ -154,13 +143,16 @@ Removing intermediate container 223629e16589
Successfully built f5cbb1895d66
Successfully tagged nirgeier/k8s-secrets-sample:latest
```
-#### 02.04. Test the container
+
+
+
+##### 4. Test the container
```sh
# Run the docker container which you build earlier,
# replace the name if you used your own name
-# and check the response from the serrver
-# It should print out the variables which were defined in side the DockerFile
+# and check the response from the server.
+# It should print out the variables which were defined inside the DockerFile
$ docker run -d -p5000:5000 nirgeier/k8s-secrets-sample --name server
# Get the response from the container
@@ -172,20 +164,28 @@ Language: Hebrew
Token : Hard-To-Guess
```
+
+
- Stop the container
+
```sh
-# Stop the running contatiner
-# We are using the name which we passed in the `docker run` command --name
+# Stop the running container
+# We are using the name which we passed in the `docker run` command --name
docker stop server
```
+
- Push the container to your docker hub account if you wish
+---
+
+
### 03. Using K8S deployment & Secrets/ConfigMap
-### 03.01. Writing the deployment & Service file
+##### 1. Writing the deployment & service file
+
+- Deploy the docker container that you have prepared in the previous step with the following `Deployment` file.
+- In this sample we will define the values in the `YAML` file, later on we will use Secrets/ConfigMap [variables-from-yaml.yaml](./variables-from-yaml.yaml)
-- Deploy the docker container you prepared in the previous step with the following `Deployment` file.
-- In this sample we will define the values in the yaml file,later on we will use Secrets/ConfigMap [variables-from-yaml.yaml](./variables-from-yaml.yaml)
```yaml
apiVersion: v1
kind: Namespace
@@ -236,17 +236,21 @@ spec:
port: 5000
targetPort: 5000
```
+
-### 03.02. Deploy to cluster
-```
+##### 2. Deploy to cluster
+```sh
$ kubectl apply -n codewizard -f variables-from-yaml.yaml
deployment.apps/codewizard-secrets configured
service/codewizard-secrets created
```
-### 03.03. Test the app
+
+
+##### 3. Test the app
- We will need a second container for executing the curl request.
-- We will us a busyBox image for this purpose
+- We will use a `busyBox image` for this purpose.
+
```sh
# grab the name of the pod
$ kubectl get pods -n codewizard
@@ -269,9 +273,11 @@ Token : Hard-To-Guess2
```
+---
+
### 04. Using Secrets & config maps
-### 04.01. Create the desired secret and config map for this lab
+##### 1. Create the desired secret and config map for this lab
```sh
# Create the secret
@@ -322,9 +328,12 @@ English
Events:
```
-### 04.02. Updating the Deployment to read the values from Secrets & ConfigMap
+
+
+##### 2. Update the deployment to read the values from Secrets & ConfigMap
- Change the `env` section to the following:
+
```yaml
env:
- name: LANGUAGE
@@ -339,57 +348,50 @@ Events:
key: TOKEN # The key in the secret
```
-### 04.03. Update the deployment to read values from K8S resources
+
+
+##### 3. Update the deployment to read values from K8S resources
```sh
$ kubectl apply -n codewizard -f variables-from-secrets.yaml
deployment.apps/codewizard-secrets configured
service/codewizard-secrets unchanged
```
-### 04.04. Test the changes
-- Refer to step 3.3 for testing your server
-```sh
-# Login to the server
-# In this sample this is the pod name: codewizard-secrets-76d99bdc54-s66vl
-kubectl exec -it codewizard-secrets-76d99bdc54-s66vl -n codewizard -- sh
+
-# Test the changes to verify that they are set from the Secret/ConfigMap
-curl localhost:5000
+##### 4. Test the changes
-# Out put should be
-Language: English
-Token : Hard-To-Guess3
-```
----
-### !!! Note
-> Pods are not recreated or updated automatically when secrets or ConfigMaps change so you will have to restart your pods
+- Refer to [step 3.3](#3-test-the-app) for testing your server
-- To update existing secrets or ConfigMap:
-```
-$ kubectl create secret generic token -n codewizard --from-literal=Token=Token3 -o yaml --dry-run=client | kubectl replace -f -
-secret/token replaced
-```
-- Test your server to and verify that you see the old values
-- Delete the old pods so they can come back to life with the new values
-- Test your server again, now you should see view the changes
+ ```sh
+ # Login to the server
+ # In this sample, the pod name is: codewizard-secrets-76d99bdc54-s66vl
+ kubectl exec -it codewizard-secrets-76d99bdc54-s66vl -n codewizard -- sh
-
+ # Test the changes to verify that they are set from the Secret/ConfigMap
+ curl localhost:5000
+
+ # Out put should be
+ Language: English
+ Token : Hard-To-Guess3
+ ```
---
-
+
----
+!!! warning "Note"
+ Pods are not recreated or updated automatically when `Secrets` or `ConfigMaps` change, so you will have to restart your pods manually
-
+- To update existing secrets or ConfigMap:
-
+```
+$ kubectl create secret generic token -n codewizard --from-literal=Token=Token3 -o yaml --dry-run=client | kubectl replace -f -
+secret/token replaced
+```
+
+- Test your server and verify that you see the old values.
+- Delete the old pods so they can come back to life with the new values.
+- Test your server again, now you should be able to see the changes.
-
\ No newline at end of file
diff --git a/Labs/07-nginx-Ingress/README.md b/Labs/07-nginx-Ingress/README.md
index 2ce0c5d..8037616 100644
--- a/Labs/07-nginx-Ingress/README.md
+++ b/Labs/07-nginx-Ingress/README.md
@@ -7,150 +7,176 @@
# Nginx-Ingress
-### ✅ !!! Important -
-> We cannot see it in action on local host (meaning that it will not get external ip) unless we use the explicit
-http://host:port
-
-
-- Kubernetes ingress object is a DNS
-- To enable an ingress object, we need an ingress controller
-- In this demo we will use nginx-ingress
-- To get started with nginx-ingress, we will deploy out previous app
- ```sh
- # Create 3 containers
- $ kubectl run ingress-pods --image=nirgeier/k8s-secrets-sample --replicas=3
-
- # Expose the service
- $ kubectl expose deployment ingress-pods --port=5000
- ```
-- Now lets deploy the nginx-ingress (grabbed from the official site)
- ```yaml
- apiVersion: apps/v1
- kind: Deployment
- metadata:
- name: default-http-backend
- spec:
- replicas: 1
- selector:
- matchLabels:
- app: default-http-backend
- template:
- metadata:
- labels:
- app: default-http-backend
- spec:
- terminationGracePeriodSeconds: 60
- containers:
- - name: default-http-backend
- # Any image is permissable as long as:
- # 1. It serves a 404 page at /
- # 2. It serves 200 on a /healthz endpoint
- image: gcr.io/google_containers/defaultbackend:1.0
- livenessProbe:
- httpGet:
- path: /healthz
- port: 8080
- scheme: HTTP
- initialDelaySeconds: 30
- timeoutSeconds: 5
- ports:
- - containerPort: 8080
- resources:
- limits:
- cpu: 10m
- memory: 20Mi
- requests:
- cpu: 10m
- memory: 20Mi
- ```
-- Next create the service
- ```yaml
- apiVersion: v1
- kind: Service
- metadata:
- name: default-http-backend
- spec:
- selector:
+
+!!! Important -
+ We cannot see it in action on a `localhost` (meaning that it will not get an external IP) unless we use the explicit `http://host:port` format.
+
+
+- Kubernetes `ingress` object is a `DNS`
+- To enable an `ingress object`, we need an `ingress controller`
+- In this demo we will use `Nginx-Ingress`
+
+
+---
+
+
+### Pre-Requirements
+- K8S cluster - Setting up minikube cluster instruction
+
+[](https://console.cloud.google.com/cloudshell/editor?cloudshell_git_repo=https://github.com/nirgeier/KubernetesLabs)
+**CTRL + click to open in new window**
+
+---
+
+### 01. Deploy sample app
+
+- To get started with `Nginx-Ingress`, we will deploy out previous app:
+
+```sh
+# Create 3 containers
+$ kubectl create deployment ingress-pods --image=nirgeier/k8s-secrets-sample --replicas=3
+
+# Expose the service
+$ kubectl expose deployment ingress-pods --port=5000
+```
+
+
+---
+
+### 02. Deploy default backend
+
+- Now lets deploy the `Nginx-Ingress` (grabbed from the official site):
+
+```yaml
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: default-http-backend
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
app: default-http-backend
- ports:
- - protocol: TCP
- port: 80
- targetPort: 8080
- type: NodePort
- ```
-### Import ssl certificate
+ template:
+ metadata:
+ labels:
+ app: default-http-backend
+ spec:
+ terminationGracePeriodSeconds: 60
+ containers:
+ - name: default-http-backend
+ # Any image is permissable as long as:
+ # 1. It serves a 404 page at /
+ # 2. It serves 200 on a /healthz endpoint
+ image: gcr.io/google_containers/defaultbackend:1.0
+ livenessProbe:
+ httpGet:
+ path: /healthz
+ port: 8080
+ scheme: HTTP
+ initialDelaySeconds: 30
+ timeoutSeconds: 5
+ ports:
+ - containerPort: 8080
+ resources:
+ limits:
+ cpu: 10m
+ memory: 20Mi
+ requests:
+ cpu: 10m
+ memory: 20Mi
+```
+
+---
+
+
+### 03. Create service
+
+- Next, let's create the service:
+
+```yaml
+apiVersion: v1
+kind: Service
+metadata:
+ name: default-http-backend
+spec:
+ selector:
+ app: default-http-backend
+ ports:
+ - protocol: TCP
+ port: 80
+ targetPort: 8080
+ type: NodePort
+```
+
+---
+
+
+
+### 04. Import `ssl` certificate
- In this demo we will use certificate.
- The certificate is in the same folder as this file
-- The certificate is for the host name: `ingress.local`
- ```sh
- # If you wish to create the certificate use this script
- ### ---> The common Name fiels is your host for later on
- ### Common Name (e.g. server FQDN or YOUR name) []:
- $ openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout certificate.key -out certificate.crt
-
- # Create a pem file
- # The purpose of the DH parameters is to exchange secrets
- $ openssl dhparam -out certificate.pem 2048
- ```
-- Store the certificate in secret
- ```sh
- # Store the certificate
- $ kubectl create secret tls tls-certificate --key certificate.key --cert certificate.crt
- secret/tls-certificate created
-
- # Store the DH parameters
- $ kubectl create secret generic tls-dhparam --from-file=certificate.pem
- secret/tls-dhparam created
- ```
-### Deploy the ingress
-- Now that we have the certificate we can deploy the Ingress
- ```yaml
- # Ingress.yaml
- apiVersion: extensions/v1beta1
- kind: Ingress
- metadata:
- name: my-first-ingress
- annotations:
- kubernetes.io/ingress.class: "nginx"
- nginx.org/ssl-services: "my-service"
- spec:
- tls:
- - hosts:
- - myapp.local
- secretName: tls-certificate
- rules:
- - host: myapp.local
- http:
- paths:
- - path: /
- backend:
- serviceName: ingress-pods
- servicePort: 5000
- ```
-
-### Enable the ingress addon
-- The ingress is not enabled by default and we have to "turn it on"
- ```sh
- $ minikube addons enable ingress
- ✅ ingress was successfully enabled
- ```
-
-
+- The certificate is for the hostname: `ingress.local`
+
+```sh
+# If you wish to create the certificate use this script
+### ---> The common Name fiels is your host for later on
+### Common Name (e.g. server FQDN or YOUR name) []:
+$ openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout certificate.key -out certificate.crt
+
+# Create a pem file
+# The purpose of the DH parameters is to exchange secrets
+$ openssl dhparam -out certificate.pem 2048
+```
+
+- Store the certificate in secret:
+
+```sh
+# Store the certificate
+$ kubectl create secret tls tls-certificate --key certificate.key --cert certificate.crt
+secret/tls-certificate created
+
+# Store the DH parameters
+$ kubectl create secret generic tls-dhparam --from-file=certificate.pem
+secret/tls-dhparam created
+```
---
-
-
-
-
\ No newline at end of file
diff --git a/Labs/09-StatefulSet/README.md b/Labs/09-StatefulSet/README.md
index 1b08e7d..ad7725d 100644
--- a/Labs/09-StatefulSet/README.md
+++ b/Labs/09-StatefulSet/README.md
@@ -8,52 +8,56 @@
# StatefulSets
+
+
+
## The Difference Between a `Statefulset` And a `Deployment`
#### `Stateless` application
-- A stateless application is one that does not care which network it is using, and it does not need permanent storage and can be scaled up and down without the need to re-use the same Network or persistance.
-- Deployment is the suitable Kind for Stateless applications
-- The most trivial example of stateless app is a Web Server
+- A stateless application is one that does not care which network it is using, and it does not need permanent storage and can be scaled up and down without the need to re-use the same network or persistence.
+- Deployment is the suitable kind for Stateless applications.
+- The most trivial example of stateless app is a `Web Server`.
#### `Stateful` application
-- Stateful application are apps which in order to work properly need to use the same resources like Network, Storage etc).
-- Usually with Stateful applications you’ll need to ensure that pods can reach each other through a **unique identity that does not change** (examples: hostnames, IP).
-- The most trivial example of Stateful app is a Database of any kind
+- Stateful applications are apps which in order to work properly need to use the same resources, such as network, storage etc.
+- Usually with `Stateful` applications you will need to ensure that pods can reach each other through a **unique identity that does not change** (e.g., hostnames, IP).
+- The most trivial example of Stateful app is a database of any kind.
---
-## `Stateful` Notes
+!!! warning "Stateful Notes"
+ - Like a Deployment, a `StatefulSet` manages Pods that are based on an **identical container spec**.
+ - Unlike a Deployment, **a `StatefulSet` maintains a sticky identity for each of their Pods**.
+ - These pods are created from the same spec, but are not interchangeable: each has a persistent identifier that it maintains across any rescheduling.
+ - Deleting and/or scaling down a `StatefulSet` will not delete the volumes associated with the `StatefulSet`. This is done to ensure data safety.
+ - `StatefulSet` keeps a unique identity for each Pod and assign the same identity to those pods when they are rescheduled (update, restart etc).
+ - The storage for a given Pod must either be provisioned by a `PersistentVolume` provisioner, based on the requested storage class, or pre-provisioned by an admin.
+ - `StatefulSet` manages the deployment and scaling of a set of Pods, and **provides guarantees about the ordering and uniqueness of these Pods**.
+ - A `stateful` app needs to use a dedicated storage.
-- Like a Deployment, a StatefulSet manages Pods that are based on an **identical container spec**.
-- Unlike a Deployment, **a StatefulSet maintains a sticky identity for each of their Pods**.
-- These pods are created from the same spec, but are not interchangeable: each has a persistent identifier that it maintains across any rescheduling.
-- Deleting and/or scaling a `StatefulSet` down will not delete the volumes associated with the `StatefulSet`. This is done to ensure data safety.
-- `StatefulSet` keeps a unique identity for each Pod and assign the same identity to those pods when they rescheduled (update, restart etc).
-- The storage for a given Pod must either be provisioned by a `PersistentVolume` Provisioner based on the requested storage class, or pre-provisioned by an admin.
-- `StatefulSet` Manages the deployment and scaling of a set of Pods, and **provides guarantees about the ordering and uniqueness of these Pods**.
-- A stateful app needs use dedicated storage
+---
### Stable Network Identity
-- A Stateful application node **must** have a unique hostname and IP address so that other nodes in the same application knows how to reach it.
-- A `ReplicaSet` assign **a random hostname and IP address** to each Pod and we use a Service which expose those Pods for us.
+- A `Stateful` application node **must** have a unique hostname and IP address so that other nodes in the same application know how to reach it.
+- A `ReplicaSet` assign **a random hostname and IP address** to each Pod. In such a case, we must use a service which exposes those Pods for us.
### Start and Termination Order
-- Each `StatefulSet` follow this naming pattern: `$(statefulSet name)-$(ordinal)`
-- Stateful applications restarted or re-created following the creation order.
+- Each `StatefulSet` follows this naming pattern: `$(statefulSet name)-$(ordinal)`
+- `Stateful` applications restarted or re-created, following the creation order.
- A `ReplicaSet` does not follow a specific order when starting or killing its pods.
### StatefulSet Volumes
-- StatefulSet **does not create a volume for you**.
-- When a StatefulSet is deleted, the respective volumes **are not deleted with it**.
+- `StatefulSet` **does not create a volume for you**.
+- When a `StatefulSet` is deleted, the respective volumes **are not deleted with it**.
---
-### To address all those requirements, Kubernetes offers the StatefulSet primitive.
+### To address all these requirements, Kubernetes offers the `StatefulSet primitive`.
---
@@ -64,39 +68,6 @@
[](https://console.cloud.google.com/cloudshell/editor?cloudshell_git_repo=https://github.com/nirgeier/KubernetesLabs)
**CTRL + click to open in new window**
-
-
----
-## Lab Highlights:
-- [K8S Hands-on](#k8s-hands-on)
-- [StatefulSets](#statefulsets)
- - [The Difference Between a `Statefulset` And a `Deployment`](#the-difference-between-a-statefulset-and-a-deployment)
- - [`Stateless` application](#stateless-application)
- - [`Stateful` application](#stateful-application)
- - [`Stateful` Notes](#stateful-notes)
- - [Stable Network Identity](#stable-network-identity)
- - [Start and Termination Order](#start-and-termination-order)
- - [StatefulSet Volumes](#statefulset-volumes)
- - [To address all those requirements, Kubernetes offers the StatefulSet primitive.](#to-address-all-those-requirements-kubernetes-offers-the-statefulset-primitive)
- - [Pre-Requirements](#pre-requirements)
- - [Lab Highlights:](#lab-highlights)
- - [01. Create namespace and clear previous data if there is any](#01-create-namespace-and-clear-previous-data-if-there-is-any)
- - [02. Create and test the Stateful application](#02-create-and-test-the-stateful-application)
- - [03. Test the Stateful application](#03-test-the-stateful-application)
- - [04. Scale down the StatefulSet and check that its down](#04-scale-down-the-statefulset-and-check-that-its-down)
- - [04.01. Scale down the `Statefulset` to 0](#0401-scale-down-the-statefulset-to-0)
- - [04.02. Verify that the pods Terminated](#0402-verify-that-the-pods-terminated)
- - [04.03. Verify that the DB is not reachable](#0403-verify-that-the-db-is-not-reachable)
- - [05. Scale up again and verify that we still have the prevoius data](#05-scale-up-again-and-verify-that-we-still-have-the-prevoius-data)
- - [05.01. scale up the `Statefulset` to 1 or more](#0501-scale-up-the-statefulset-to-1-or-more)
- - [05.02. Verify that the pods is in Running status](#0502-verify-that-the-pods-is-in-running-status)
- - [05.03. Verify that the pods is using the previous data](#0503-verify-that-the-pods-is-using-the-previous-data)
-
----
-
-
-
-
---
@@ -360,22 +331,3 @@ psql \
(1 row)
```
-
-
----
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Labs/10-Istio/README.md b/Labs/10-Istio/README.md
index d3d70da..1f5edb9 100644
--- a/Labs/10-Istio/README.md
+++ b/Labs/10-Istio/README.md
@@ -1,64 +1,40 @@
-
-
-
# K8S Hands-on

---
-
-## PreRequirements
+# Istio
-- [Helm](https://helm.sh/docs/intro/install/)
-- K8S cluster
-- **kubectl** configured to interact with your cluster.
+Istio is an open-source service mesh that provides a way to manage microservices traffic, security, and observability in a Kubernetes cluster.
---
-[](https://console.cloud.google.com/cloudshell/editor?cloudshell_git_repo=https://github.com/nirgeier/KubernetesLabs)
+## Pre Requirements
-
-### **CTRL + click to open in new window**
+- [Helm](https://helm.sh/docs/intro/install/) installed
+- K8S cluster - Setting up minikube cluster instruction
+- [**kubectl**](https://kubernetes.io/docs/tasks/tools/)) configured to interact with your cluster
----
-- [Introduction](#introduction)
- - [`Istio`](#istio)
- - [`Kiali`](#kiali)
-- [Part 01 - Installing Istio and Kiali](#part-01---installing-istio-and-kiali)
- - [Step 01: Install Istio Using Istioctl](#step-01-install-istio-using-istioctl)
- - [Step 02: Verify Istio installation](#step-02-verify-istio-installation)
- - [Step 03: Install Kiali](#step-03-install-kiali)
- - [Step 04: Verify Kiali installation](#step-04-verify-kiali-installation)
-- [Part 02 - Viewing the Network with Istio](#part-02---viewing-the-network-with-istio)
- - [Step 01: Enable Istio Injection](#step-01-enable-istio-injection)
- - [Step 2: Deploy Sample Application](#step-2-deploy-sample-application)
- - [Step 03: Verify the Sample Application](#step-03-verify-the-sample-application)
- - [Step 04: Expose the Application](#step-04-expose-the-application)
-- [Part 03 - Visualizing the Network with Kiali](#part-03---visualizing-the-network-with-kiali)
- - [Step 01: Access Kiali Dashboard](#step-01-access-kiali-dashboard)
- - [Step 02: Explore the Service Mesh Topology](#step-02-explore-the-service-mesh-topology)
-- [Part 04: Creating a Demo Istio VirtualService](#part-04-creating-a-demo-istio-virtualservice)
- - [Step 1: Define a VirtualService](#step-1-define-a-virtualservice)
- - [Step 02: Apply the VirtualService](#step-02-apply-the-virtualservice)
- - [Step 03: Verify the Routing](#step-03-verify-the-routing)
-- [Conclusion](#conclusion)
+
+[](https://console.cloud.google.com/cloudshell/editor?cloudshell_git_repo=https://github.com/nirgeier/KubernetesLabs)
+### **CTRL + click to open in new window**
+
---
-
-# Istio and Kiali
-- This guide provides a detailed walkthrough on installing and configuring **Istio** and **Kiali** in a Kubernetes cluster.
-- You will also learn how to visualize your service mesh with Istio and Kiali, create a demo **Istio VirtualService**.
+## Istio and Kiali
+- This guide provides a detailed walkthrough on installing and configuring **Istio** and **Kiali** on a Kubernetes cluster.
+- We will also learn how to visualize your service mesh with Istio and Kiali and create a demo **Istio VirtualService**.
---
-## Introduction
+## 01. Introduction
### `Istio`
@@ -75,8 +51,8 @@
| **Fault Injection** | Supports fault injection to simulate network failures, latency, or errors in microservices to test resilience and robustness of the application. |
| **Mutual TLS (mTLS)** | Istio can automatically encrypt traffic between services using mutual TLS (mTLS) to ensure secure communication and provide strong identity-based access control. |
| **Authentication & Authorization** | Provides identity and access management through role-based access control (RBAC) and integration with external identity providers (e.g., OAuth, JWT). |
- | **Telemetry & Observability** | Istio collects metrics, logs, and traces for monitoring service performance and behavior. Integrates with tools like Prometheus, Grafana, and Jaeger. |
- | **Distributed Tracing** | Integrates with tracing systems like **Jaeger** and **Zipkin** to provide end-to-end tracing for debugging and monitoring service interactions. |
+ | **Telemetry & Observability** | Istio collects metrics, logs, and traces for monitoring service's performance and behavior. It integrates with tools like Prometheus, Grafana, and Jaeger. |
+ | **Distributed Tracing** | Istio integrates with tracing systems like **Jaeger** and **Zipkin** to provide end-to-end tracing for debugging and monitoring service interactions. |
| **Policy Enforcement** | Istio provides fine-grained control over traffic policies, such as rate limiting, quotas, and security policies, using its Policy and Telemetry components. |
| **Resilience & Retries** | Istio can retry failed requests, set timeouts, and apply circuit breakers to prevent cascading failures and enhance the reliability of services. |
| **Sidecar Proxy (Envoy)** | Istio uses **Envoy** as a sidecar proxy to intercept and manage network traffic, providing a transparent proxy between microservices. |
@@ -89,6 +65,7 @@
- Istio core components:
+
| **Components** | **Description** |
| --------------- | ------------------------------------------------------------------------ |
| **Envoy Proxy** | A sidecar proxy that intercepts traffic to and from microservices. |
@@ -96,18 +73,19 @@
| **Mixer** | Provides policy enforcement and telemetry data collection. |
| **Citadel** | Handles security-related tasks like identity and certificate management. |
+---
-### `Kiali`
+### `Kiali`
- **Kiali** is a graphical user interface (GUI) for `Istio`.
-- It helps you visualize the service mesh and provides insights into how microservices are interacting with each other.
+- It helps you visualize the service mesh and provides insights on how microservices are interacting with each other.
- `Kiali` integrates deeply with Istio, allowing you to view:
- - The **service mesh topology** showing services, traffic flow, and dependencies.
- - Metrics like request rates, latencies, and error rates.
- - **Distributed tracing** (if enabled) for better debugging and troubleshooting.
- - **Istio configuration** to visualize resources like VirtualServices, DestinationRules, and more.
+ - The **service mesh topology**, showing services, traffic flow, and dependencies.
+ - Metrics like request rates, latencies, and error rates.
+ - **Distributed tracing** (if enabled) for better debugging and troubleshooting.
+ - **Istio configuration** to visualize resources like VirtualServices, DestinationRules, and more.
-- `Kiali` simplifies the operation of Istio by offering an intuitive way to manage and visualize your service mesh.
+- `Kiali` simplifies the operation of `Istio` by offering an intuitive way to manage and visualize your service mesh.
---
@@ -116,9 +94,9 @@
### Step 01: Install Istio Using Istioctl
-- Istio supply an installer
+- Istio supplies an installer
- Go to the [Istio releases page](https://github.com/istio/istio/releases) and download the latest version of Istio.
-- Alternatively, use `istioctl` to install Istio.
+- Alternatively, use `istioctl` to install Istio, as follows:
```bash
# Install Istio using istioctl
@@ -129,7 +107,7 @@
# Add bin directory to your $PATH
export PATH=$PWD/bin:$PATH
- # Install istio will all features enabled (demo profile)
+ # Install istio with all features enabled (demo profile)
istioctl install --set profile=demo -y
```
@@ -138,28 +116,33 @@
### Step 02: Verify Istio installation
- Check the Istio system components in the `istio-system` namespace:
- ```bash
- # Verify Istio installation
- kubectl get pods -n istio-system
- ```
-- You should see several pods, including the Istio control plane components like
- - `istiod`
- - `istio-ingressgateway`
- - `istio-egressgateway`
+
+```bash
+# Verify Istio installation
+kubectl get pods -n istio-system
+```
+
+- You should see several pods, including the Istio control plane components like:
+ - `istiod`
+ - `istio-ingressgateway`
+ - `istio-egressgateway`
-
- **`kubectl get pods -n istio-system`**
- ```plaintext
- NAME READY STATUS RESTARTS AGE
- istio-egressgateway-684f5dc857-bzww6 1/1 Running 0 21m
- istio-ingressgateway-6b5bd79c5c-9n8tg 1/1 Running 0 21m
- istiod-68885d595-vv2ft 1/1 Running 0 22m
- ```
+```bash
+kubectl get pods -n istio-system
+```
+
+
+ ```plaintext
+ NAME READY STATUS RESTARTS AGE
+ istio-egressgateway-684f5dc857-bzww6 1/1 Running 0 21m
+ istio-ingressgateway-6b5bd79c5c-9n8tg 1/1 Running 0 21m
+ istiod-68885d595-vv2ft 1/1 Running 0 22m
+ ```
### Step 03: Install Kiali
-- We will install kiali with Helm
+- We will install Kiali using Helm:
```bash
# Add the Kiali Helm chart repository
@@ -183,40 +166,42 @@
### Step 04: Verify Kiali installation
-- Once installed, check the status of Kiali:
- ```bash
- kubectl get pods -n istio-system
- ```
+- Once installed, check the status of `Kiali`:
-- You should see the Kiali pod running, along with the Istio components from previous step.
- ```plaintext
- NAME READY STATUS RESTARTS AGE
- istio-egressgateway-684f5dc857-bzww6 1/1 Running 0 28m
- istio-ingressgateway-6b5bd79c5c-9n8tg 1/1 Running 0 28m
- istiod-68885d595-vv2ft 1/1 Running 0 28m
- kiali-68ccc848b6-j4q28 1/1 Running 0 27m
- ```
+```bash
+kubectl get pods -n istio-system
+```
+
+- You should see the `Kiali` pod running, along with the `Istio` components from previous step.
+
+```plaintext
+NAME READY STATUS RESTARTS AGE
+istio-egressgateway-684f5dc857-bzww6 1/1 Running 0 28m
+istio-ingressgateway-6b5bd79c5c-9n8tg 1/1 Running 0 28m
+istiod-68885d595-vv2ft 1/1 Running 0 28m
+kiali-68ccc848b6-j4q28 1/1 Running 0 27m
+```
---
## Part 02 - Viewing the Network with Istio
-- Istio uses a sidecar proxy model, where an Envoy proxy is deployed alongside each microservice pod.
+- `Istio` uses a sidecar proxy model, where an envoy proxy is deployed alongside each microservice pod.
- This proxy intercepts and manages traffic between the services.
### Step 01: Enable Istio Injection
- You need to enable **Istio sidecar injection** for your Kubernetes namespace.
-- This will ensure that new pods in the `default` namespace will automatically have the Envoy proxy sidecar injected.
+- This will ensure that new pods in the `default` namespace will automatically have the envoy proxy sidecar injected.
- For example, to enable injection in the `default` namespace:
```bash
kubectl label namespace default istio-injection=enabled
```
-### Step 2: Deploy Sample Application
+### Step 02: Deploy Sample Application
-- To see Istio in action, deploy a sample application, such as **Bookinfo** , which is available in Istio's demo repository.
+- To see `Istio` in action, deploy a sample application, such as **Bookinfo**, which is available in `Istio`'s demo repository.
```bash
# Deploy the sample application supplied by istio
@@ -233,7 +218,7 @@
### Step 04: Expose the Application
-- To expose the application via Istio's ingress gateway, create an Istio **Gateway** and **VirtualService** .
+- To expose the application via `Istio's` ingress gateway, create an `Istio` **Gateway** and **VirtualService** .
```bash
# This will expose the 'Bookinfo' application to the external world via Istio ingress gateway.
@@ -248,8 +233,8 @@
### Step 01: Access Kiali Dashboard
-- Once Kiali is installed, you can access its dashboard.
-- First, define a port-forward the Kiali service:
+- Once `Kiali` is installed, you can access it's dashboard.
+- First, port-forward to the `Kiali` service:
```bash
kubectl port-forward \
@@ -263,22 +248,22 @@
### Step 02: Explore the Service Mesh Topology
-- Once inside the Kiali dashboard, Open the `Mesh` View
+- Once inside the `Kiali` dashboard, open the `Mesh` View
- You will see a **graph** of your services in the mesh.
- The graph shows the interactions between microservices, along with traffic flows, success/error rates, and latency.
-- You can use the Kiali interface to:
- - **Zoom in/out** of the topology.
- - View detailed metrics for each service.
- - Understand the traffic flow, including retries, timeouts, and error rates.
+- You can use the `Kiali` interface to:
+ - **Zoom in/out** of the topology.
+ - View detailed metrics for each service.
+ - Understand the traffic flow, including retries, timeouts, and error rates.
---
## Part 04: Creating a Demo Istio VirtualService
-- In Istio, **VirtualServices** are used to define the routing rules for your services.
+- In `Istio`, **VirtualServices** are used to define the routing rules for your services.
-### Step 1: Define a VirtualService
+### Step 01: Define a VirtualService
- Create a `VirtualService` resource to route traffic to the `ratings` service in the **Bookinfo** demo app.
```yaml
@@ -298,7 +283,7 @@
```
### Step 02: Apply the VirtualService
-- Apply the `VirtualService`
+- Apply the `VirtualService`.
- This will route all traffic for the `ratings` service to version `v2`.
```bash
@@ -307,13 +292,13 @@
### Step 03: Verify the Routing
-- You can use Kiali to visualize the traffic flow and verify that the routing is happening as expected.
-- The Kiali dashboard should reflect the new route configuration for `ratings`.
+- You can use `Kiali` to visualize the traffic flow and verify that routing is happening as expected.
+- The `Kiali` dashboard should reflect the new route configuration for `ratings`.
---
## Conclusion
-- You have now successfully installed Istio and Kiali, set up a service mesh, and visualized your network's behavior.
-- The combination of Istio's powerful traffic management features and Kiali's intuitive visualization interface makes it easier to manage and monitor microservices in a Kubernetes cluster.
+- You have now successfully installed `Istio` and `Kiali`, set up a service mesh, and visualized your network's behavior.
+- The combination of `Istio's` powerful traffic management features and `Kiali's` intuitive visualization interface makes it easier to manage and monitor microservices in a Kubernetes cluster.
diff --git a/Labs/11-CRD-Custom-Resource-Definition/README.md b/Labs/11-CRD-Custom-Resource-Definition/README.md
index 55fbdba..5613ba9 100644
--- a/Labs/11-CRD-Custom-Resource-Definition/README.md
+++ b/Labs/11-CRD-Custom-Resource-Definition/README.md
@@ -7,41 +7,19 @@
# Custom resources definition (CRD)
### Intro
-- **Custom resources definition (CRD)** was added to Kubernetes 1.7
-- CRD added the ability to define Custom objects/resources
+- `Custom resources definition` (**CRD**) was added to Kubernetes 1.7.
+- `CRD` added the ability to define custom objects/resources.
---
#### What is a Custom Resource Definition(CRD)
-- Custom resources
- A resource is an endpoint in the Kubernetes API that stores a collection of API objects of a certain kind; for example, the built-in pods resource contains a collection of Pod objects.
- A custom resource is an **extension of the Kubernetes API** that is not necessarily available in a default Kubernetes installation. It represents a customization of a particular Kubernetes installation. However, many core Kubernetes functions are now built using custom resources, making Kubernetes more modular.
+- A resource is an endpoint in the Kubernetes API that stores a collection of API objects of a certain kind; for example, the builtin pods resource contains a collection of Pod objects.
- Custom resources can appear and disappear in a running cluster through **dynamic registration**, and cluster admins can update custom resources independently of the cluster itself.
-
- Once a custom resource is installed, users can create and access its objects using `kubectl`, just as they do for built-in resources like Pods.
+- A custom resource is an **extension of the Kubernetes API** that is not necessarily available in a default Kubernetes installation. It represents a customization of a particular Kubernetes installation. However, many core Kubernetes functions are now built using custom resources, making Kubernetes more modular.
- The custom resource created is also stored in the etcd cluster with proper replication and lifecycle management.
+- Custom resources can appear and disappear in a running cluster through **dynamic registration**, and cluster admins can update custom resources independently of the cluster itself.
----
-
-
-
----
-
-
-
-
+- Once a custom resource is installed, users can create and access its objects using `kubectl`, just as they do for built-in resources like Pods.
-
\ No newline at end of file
+- The custom resource created is also stored in the `etcd` cluster with proper replication and lifecycle management.
diff --git a/Labs/12-Wordpress-MySQL-PVC/README.md b/Labs/12-Wordpress-MySQL-PVC/README.md
index 4c50fa1..bd9ec64 100644
--- a/Labs/12-Wordpress-MySQL-PVC/README.md
+++ b/Labs/12-Wordpress-MySQL-PVC/README.md
@@ -6,62 +6,46 @@
---
# WordPress, MySQL, PVC
-- In This tutorial you will deploy a WordPress site and a MySQL database.
+- In this tutorial you will deploy a WordPress site and a MySQL database.
- You will use `PersistentVolumes` and `PersistentVolumeClaims` as storage.
---
## Walkthrough
- Patch `minikube` so we can use `Service: LoadBalancer`
- ```sh
- # Sourse:
- # https://github.com/knative/serving/blob/b31d96e03bfa1752031d0bc4ae2a3a00744d6cd5/docs/creating-a-kubernetes-cluster.md#loadbalancer-support-in-minikube
- sudo ip route add \
- $(cat ~/.minikube/profiles/minikube/config.json | \
- jq -r ".KubernetesConfig.ServiceCIDR") \
- via $(minikube ip)
- kubectl run minikube-lb-patch \
- --replicas=1 \
- --image=elsonrodriguez/minikube-lb-patch:0.1 \--namespace=kube-system
- ```
-- Create the desired Namespace
-- Create the MySQL resources
+```sh
+# Source:
+# https://github.com/knative/serving/blob/b31d96e03bfa1752031d0bc4ae2a3a00744d6cd5/docs/creating-a-kubernetes-cluster.md#loadbalancer-support-in-minikube
+
+sudo ip route add \
+ $(cat ~/.minikube/profiles/minikube/config.json | \
+ jq -r ".KubernetesConfig.ServiceCIDR") \
+ via $(minikube ip)
+
+kubectl run minikube-lb-patch \
+ --replicas=1 \
+ --image=elsonrodriguez/minikube-lb-patch:0.1 \--namespace=kube-system
+```
+
+- Create the desired `Namespace`
+- Create the `MySQL` resources:
- Create `Service`
- Create `PersistentVolumeClaims`
- Create `Deployment`
- - Create password file
-- Create the WordPress resources
+ - Create `password file`
+- Create the WordPress resources:
- Create `Service`
- Create `PersistentVolumeClaims`
- Create `Deployment`
-- Create a `kustomization.yaml` with
- - Secret generator
- - MySQL resources
- - WordPress resources
+- Create a `kustomization.yaml` with:
+ - `Secret generator`
+ - `MySQL` resources
+ - `WordPress` resources
- Deploy the stack
- Port forward from the host to the application
- - We use a port forward so we will be able to test and verify if the WordPress is actually running
- ```sh
- kubectl port-forward service/wordpress 8080:32267 -n wp-demo
- ```
-
-
-
----
-
-
-
-
+- We use a port forward so we will be able to test and verify if the WordPress is actually running:
-
\ No newline at end of file
+```sh
+kubectl port-forward service/wordpress 8080:32267 -n wp-demo
+```
diff --git a/Labs/13-HelmChart/README.md b/Labs/13-HelmChart/README.md
index ebfef94..cdece36 100644
--- a/Labs/13-HelmChart/README.md
+++ b/Labs/13-HelmChart/README.md
@@ -1,106 +1,67 @@

-
-# Kubernetes Hands-on Lab: Helm Chart
+# K8S Hands-on

---
-## Overview
+# Helm Chart
-- Welcome to the Helm Chart hands-on lab! In this tutorial, you'll learn the essentials of Helm (version 3), the package manager for Kubernetes.
-- You'll build, package, install, and manage applications using Helm charts, gaining practical experience with real Kubernetes resources.
+- Welcome to the `Helm` Chart hands-on lab! In this tutorial, you'll learn the essentials of `Helm` (version 3), the package manager for Kubernetes.
+- You'll build, package, install, and manage applications using `Helm` charts, gaining practical experience with real Kubernetes resources.
---
+
+## Pre requirements
-## Prerequisites
-
-- [Helm](https://helm.sh/docs/intro/install/) installed
-- Access to a Kubernetes cluster (local or remote)
+- [`Helm`](https://helm.sh/docs/intro/install/) installed
+- K8S cluster - Setting up minikube cluster instruction
+- [**kubectl**](https://kubernetes.io/docs/tasks/tools/) configured to interact with your cluster
----
[](https://console.cloud.google.com/cloudshell/editor?cloudshell_git_repo=https://github.com/nirgeier/KubernetesLabs)
-
### **CTRL + click to open in new window**
-
+
---
-## What You'll Learn
+## What will you learn
-- What Helm is and why it's useful
-- Helm chart structure and key files
-- Common Helm commands for managing releases
-- How to create, package, install, upgrade, and rollback a Helm chart
+- What `Helm` is and why is it useful
+- `Helm` chart structure and key files
+- Common `Helm` commands for managing releases
+- How to create, pack, install, upgrade, and rollback a `Helm` chart
- Troubleshooting and best practices
---
-## Table of Contents
-
-- [Overview](#overview)
-- [Prerequisites](#prerequisites)
-- [What You'll Learn](#what-youll-learn)
-- [Table of Contents](#table-of-contents)
-- [Introduction](#introduction)
- - [Terminology](#terminology)
- - [Chart files and folders](#chart-files-and-folders)
- - [Common Helm Commands](#common-helm-commands)
-- [Lab](#lab)
- - [Step 01. Installing Helm](#step-01-installing-helm)
- - [Verify Installation](#verify-installation)
- - [Step 02 - Creating our Helm chart](#step-02---creating-our-helm-chart)
- - [Create a New Chart](#create-a-new-chart)
- - [Navigate to the Chart Directory](#navigate-to-the-chart-directory)
- - [Write the chart content](#write-the-chart-content)
- - [Step 03 - Pack the chart](#step-03---pack-the-chart)
- - [`helm package`](#helm-package)
- - [Step 04 - Validate the chart content](#step-04---validate-the-chart-content)
- - [`helm template`](#helm-template)
- - [Step 05 - Install the chart](#step-05---install-the-chart)
- - [`helm install`](#helm-install)
- - [Step 06: Verify the installation](#step-06-verify-the-installation)
- - [Step 07: Test the service](#step-07-test-the-service)
- - [Step 08: Upgrade the release to newer version](#step-08-upgrade-the-release-to-newer-version)
- - [Step 09: Check the upgrade](#step-09-check-the-upgrade)
- - [Step 10 - History:](#step-10---history)
- - [`helm history`](#helm-history)
- - [Step 11 - Rollback](#step-11---rollback)
- - [`helm rollback`](#helm-rollback)
-- [Finalize \& Cleanup](#finalize--cleanup)
-- [Troubleshooting](#troubleshooting)
-- [Next Steps](#next-steps)
-
----
-
## Introduction
- `Helm` is the **package manager** for Kubernetes.
- It simplifies the deployment, management, and upgrade of applications on your Kubernetes cluster.
-- Helm helps you manage Kubernetes applications by providing a way to define, install, and upgrade complex Kubernetes applications.
-- When packaging applications as Helm charts, you gain a standardized and reusable approach for deploying and managing your services.
+- `Helm` helps you manage Kubernetes applications by providing a way to define, install, and upgrade complex Kubernetes applications.
+- When packing applications as `Helm` charts, you gain a standardized and reusable approach for deploying and managing your services.
-- A Helm chart consists of a few files that define the Kubernetes resources that will be **created** when the chart is installed.
+- A `Helm` chart consists of a few files that define the Kubernetes resources that will be **created** when the chart is installed.
- These files include the:
- - `Chart.yaml` file, which contains metadata about the chart, such as its name and version, and the
+ - `Chart.yaml` file, which contains metadata about the chart, such as its name and version, and the chart's dependencies and maintainers.
- `values.yaml` file, which contains the configuration values for the chart.
- - The `templates` directory contains the Kubernetes resource templates that will be used to create the actual resources in the cluster.
+ - The `templates` directory which contains the Kubernetes resource templates to be used to create the actual resources in the cluster.
### Terminology
* `Chart`
- - A Helm package is called a **chart**.
- - Charts are versioned, shareable packages that contain all the Kubernetes resources needed to run an application.
+ - A `Helm` package is called a **chart**.
+ - Charts are versioned, shareable packages that contain all the Kubernetes resources needed to run an application.
* `Release`
- - A specific instance of a chart is called a **release**.
- - Each release is a deployed *version of a chart*, with its own configuration, resources, and revision history.
+ - A specific instance of a chart is called a **release**.
+ - Each release is a deployed *version of a chart*, with its own configuration, resources, and revision history.
* `Repository`
- - A collection of charts is stored in a Helm repository.
- - Helm charts can be hosted in public or private repositories for easy sharing and distribution.
+ - A collection of charts is stored in a `Helm` repository.
+ - `Helm` charts can be hosted in public or private repositories for easy sharing and distribution.
### Chart files and folders
@@ -112,8 +73,9 @@
| `charts/` | Directory containing dependencies of the chart. |
| `README.md` | Documentation for the chart, explaining how to use and configure it. |
+##### codewizard-helm-demo Helm Chart tructure
+
```sh
-## codewizard-helm-demo
- Chart.yaml # Defines chart metadata and values schema
- values.yaml # Default configuration values
- templates/ # Deployment templates using Go templating language
@@ -122,233 +84,243 @@
- README.md # Documentation for your chart
```
-### Common Helm Commands
+### Common `Helm` Commands
+
+Here are some of the most common `Helm` commands you’ll use when working with `Helm` charts:
-Here are some of the most common Helm commands you’ll use when working with Helm charts:
| Command | Description |
| ------------------------------------------------ | -------------------------------------------------------------------------------------------------------- |
-| helm **create** `chart-name` | Create a new Helm chart with the specified name. |
-| helm **install** `release-name` `chart-path` | Install a Helm chart to your Kubernetes cluster. |
-| helm **upgrade** `release-name` `chart-path` | Upgrade an installed release with a new version of a chart. |
-| | |
-| helm **uninstall** `release-name` | Uninstall a release from the Kubernetes cluster. |
-| helm **list** | List all installed Helm releases in the cluster. |
-| helm **status** `release-name` | Show the status of a deployed Helm release. |
-| helm **rollback** `release-name` `revision` | Rollback a release to a previous revision. |
-| helm **get all** `release-name` | Retrieve all information about a deployed release (e.g., templates, values). |
-| helm **show values** `chart-name` | Show the default values of a Helm chart. |
-| helm **template** `chart-name` | Generate the output of the Helm chart. |
-| helm **lint** `chart-path` | This command takes a path to a chart and runs a series of tests to verify that the chart is well-formed. |
-| helm **history** `chart-name` | This command takes a path to a chart and runs a series of tests to verify that the chart is well-formed. |
+| `helm` **create** `chart-name` | Create a new `Helm` chart with the specified name. |
+| `helm` **install** `release-name` `chart-path` | Install a `Helm` chart to your Kubernetes cluster. |
+| `helm` **upgrade** `release-name` `chart-path` | Upgrade an installed release with a new version of a chart. |
+| `helm` **uninstall** `release-name` | Uninstall a release from the Kubernetes cluster. |
+| `helm` **list** | List all installed `Helm` releases in the cluster. |
+| `helm` **status** `release-name` | Show the status of a deployed `Helm` release. |
+| `helm` **rollback** `release-name` `revision` | Rollback a release to a previous revision. |
+| `helm` **get all** `release-name` | Retrieve all information about a deployed release (e.g., templates, values). |
+| `helm` **show values** `chart-name` | Show the default values of a `Helm` chart. |
+| `helm` **template** `chart-name` | Generate the output of the `Helm` chart. |
+| `helm` **lint** `chart-path` | This command takes a path to a chart and runs a series of tests to verify that the chart is well-formed. |
+| `helm` **history** `chart-name` | This command takes a path to a chart and runs a series of tests to verify that the chart is well-formed. |
---
-## Lab
+# Lab
+
+### Step 01 - Installing `Helm`
-### Step 01. Installing Helm
+- Before you can use the `codewizard-helm-demo` chart, you'll need to **install** `Helm` on your local machine.
-- Before you can use the `codewizard-helm-demo` chart, you'll need to **install** Helm on your local machine.
+- `Helm` install methods by OS:
-- Steps to Install Helm
- | OS | Command |
- | ------------------------ | ---------------------------------------------------------------------------------- |
- | Linux | `curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 \| bash` |
- | MacOS | `brew install helm` |
- | Windows (via Chocolatey) | `choco install kubernetes-helm` |
+ | OS | Command |
+ | ------------------------ | ---------------------------------------------------------------------------------- |
+ | Linux | `curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 \| bash` |
+ | MacOS | ``brew install helm`` |
+ | Windows (via Chocolatey) | ``choco install kubernetes-helm`` |
#### Verify Installation
- - To confirm that Helm is installed correctly, run:
- ```bash
- $ helm version
- version.BuildInfo{Version:"xx", GitCommit:"xx", GitTreeState:"clean", GoVersion:"xx"}
- ```
+ - To confirm that `Helm` is installed correctly, run:
+
+ ```bash
+ $ helm version
+
+ ## Expected output
+ version.BuildInfo{Version:"xx", GitCommit:"xx", GitTreeState:"clean", GoVersion:"xx"}
+ ```
+
+---
-### Step 02 - Creating our Helm chart
+### Step 02 - Creating our `Helm` chart
-- Creating our custom `codewizard-helm-demo` Helm chart
-- The custom `codewizard-helm-demo` Helm chart is build upon the following K8S resources:
+- Creating our custom `codewizard-helm-demo` `Helm` chart
+- The custom `codewizard-helm-demo` `Helm` chart is build upon the following K8S resources:
- - ConfigMap
- - Deployment
- - Service
+ - ConfigMap
+ - Deployment
+ - Service
-- As mentioned above we will also have the following Helm resources:
- - Chart.yaml
- - values.yaml
- - templates/\_helpers.tpl
+- As mentioned above, we will also have the following `Helm` resources:
+ - Chart.yaml
+ - values.yaml
+ - templates/\_helpers.tpl
#### Create a New Chart
-- First, you need to create a Helm chart using the `helm create` command.
+- First, we need to create a `Helm` chart using the ``helm create`` command.
- This command will generate the necessary file structure for your new chart.
```bash
helm create codewizard-helm-demo
```
-- ?? Question: What is result of this command? Examine the chart structure
+??? Question "What is the result of this command?"
+ Examine the chart structure!
#### Navigate to the Chart Directory
- - Move into the Chart Directory
-
- ```bash
- cd codewizard-helm-demo
- ```
+ ```bash
+ cd codewizard-helm-demo
+ ```
#### Write the chart content
- - Copy the content of the chart folder (in this lab) to the chart directory (overwrite the files)
+ - Copy the content of the chart folder (in this lab) to the chart directory (overwriting the files).
### Step 03 - Pack the chart
-- After you’ve create or customized your chart, you need to pack it as `.tgz` file, which can then be shared or installed.
+- After we have created or customized our chart, we need to pack it as `.tgz` file, which can then be shared or installed.
- #### `helm package`
+#### helm package
-> [!NOTE]
-> `helm package` packages a chart into a **versioned chart archive file**.
-> If a path is given, this will look at that path for a chart which must contain a `Chart.yaml` file and then package that directory.
+!!! warning "Helm Package"
+ `helm package` packages a chart into a **versioned chart archive file**.
+ If a path is given, this will "look" at that path for a chart which must contain a `Chart.yaml` file and then pack that directory.
- ```sh
- helm package codewizard-helm-demo
- ```
+```sh
+helm package codewizard-helm-demo
+```
-- This command will create a file called `codewizard-helm-demo-.tgz` in your current directory.
+- This command will create a file called `codewizard-helm-demo-.tgz` inside your current directory.
### Step 04 - Validate the chart content
-#### `helm template`
+#### ``helm template``
-- Helm allows you to **generate** the Kubernetes manifests based on the templates and values files without actually installing the chart.
-- This is useful to preview what the generated resources will look like.
+- `Helm` allows you to **generate** the Kubernetes manifests based on the templates and values files without actually installing the chart.
+- This is useful to preview what the generated resources will look like:
- ```sh
- helm template codewizard-helm-demo
- ```
-- This will output the rendered Kubernetes manifests to your terminal.
+```sh
+helm template codewizard-helm-demo
+
+## This will output the rendered Kubernetes manifests to your terminal
+```
### Step 05 - Install the chart
- Install the `codewizard-helm-demo` chart into Kubernetes cluster
-#### `helm install`
+
+#### The ``helm install`` command
- This command installs a chart archive.
-- The install argument must be a chart reference, a path to a packaged chart, a path to an unpacked chart directory or a URL.
-- To override values in a chart:
- - `--values` - pass in a file
- - `--set` - pass configuration from the command line
-
- ```
- # Install the packed helm
- helm install codewizard-helm-demo codewizard-helm-demo-0.1.0.tgz
- ```
+- The install argument must be a chart reference, a path to a packed chart, a path to an unpacked chart directory or a URL.
+- To override values in a chart, use:
+ - `--values` - pass in a file
+ - `--set` - pass configuration from the command line
-### Step 06: Verify the installation
-- Examine newly created Helm chart release, and all cluster created resources
+```sh
+# Install the packed helm chart
+helm install codewizard-helm-demo codewizard-helm-demo-0.1.0.tgz
+```
- ```
- # List the installed helms
- helm ls
+### Step 06 - Verify the installation
- # Check the resources
- kubectl get all -n codewizard
- ```
+- Examine newly created `Helm` chart release, and all cluster created resources:
-### Step 07: Test the service
+```sh
+# List the installed helms
+helm ls
-- Perform an HTTP GET request, send it to the newly created cluster service
-- Confirm that the response contains the `CodeWizard Helm Demo` message passed from the `values.yaml` file
+# Check the resources
+kubectl get all -n codewizard
+```
- ```sh
- kubectl run busybox \
- --image=busybox \
- --rm \
- -it \
- --restart=Never \
- -- /bin/sh -c "wget -qO- http://codewizard-helm-demo.codewizard.svc.cluster.local"
+### Step 07 - Test the service
- ### Output:
- `CodeWizard Helm Demo`
- ```
+- Perform an `HTTP GET` request, send it to the newly created cluster service.
+- Confirm that the response contains the `CodeWizard Helm Demo` message passed from the `values.yaml` file.
-### Step 08: Upgrade the release to newer version
+```sh
+kubectl run busybox \
+ --image=busybox \
+ --rm \
+ -it \
+ --restart=Never \
+ -- /bin/sh -c "wget -qO- http://codewizard-helm-demo.codewizard.svc.cluster.local"
+
+### Output:
+CodeWizard Helm Demo
+```
-- Perform a Helm upgrade on the `codewizard-helm-demo` release
+### Step 08 - Upgrade the release to newer version
- ```
- # upgrade and pass different message than the one from the default values
- # Use the --set to pass the desired value
- helm upgrade \
- codewizard-helm-demo \
- codewizard-helm-demo-0.1.0.tgz \
- --set nginx.conf.message="Helm Rocks"
- ```
+- Perform a Helm upgrade on the `codewizard-helm-demo` release:
+
+```sh
+# upgrade and pass a different message than the one from the default values
+# Use the --set to pass the desired value
+helm upgrade \
+ codewizard-helm-demo \
+ codewizard-helm-demo-0.1.0.tgz \
+ --set nginx.conf.message="Helm Rocks"
+```
-### Step 09: Check the upgrade
+### Step 09 - Check the upgrade
-- Perform another HTTP GET request.
-- Confirm that the response now has the updated message `Helm Rocks`
+- Perform another `HTTP GET` request.
+- Confirm that the response now has the updated message `Helm Rocks`:
```sh
- kubectl run busybox \
- --image=busybox \
- --rm \
- -it \
- --restart=Never \
- -- /bin/sh -c "wget -qO- http://codewizard-helm-demo.codewizard.svc.cluster.local"
-
- ### Output:
- `Helm Rocks`
- ```
+kubectl run busybox \
+ --image=busybox \
+ --rm \
+ -it \
+ --restart=Never \
+ -- /bin/sh -c "wget -qO- http://codewizard-helm-demo.codewizard.svc.cluster.local"
+
+### Output:
+Helm Rocks
+```
-### Step 10 - History:
+### Step 10 - History
- Examine the `codewizard-helm-demo` release history
- #### `helm history`
+#### `helm history`
- `helm history` prints historical revisions for a given release.
- - A default maximum of 256 revisions will be returned
+ - A default maximum of 256 revisions will be returned.
- ```
- helm history codewizard-helm-demo
+```sh
+$ helm history codewizard-helm-demo
- ### Sample output
- REVISION UPDATED STATUS CHART APP VERSION DESCRIPTION
- 1 ... superseded codewizard-helm-demo-0.1.0 1.19.7 Install complete
- 2 ... deployed codewizard-helm-demo-0.1.0 1.19.7 Upgrade complete
- ```
+### Sample output
+REVISION UPDATED STATUS CHART APP VERSION DESCRIPTION
+1 ... superseded codewizard-helm-demo-0.1.0 1.19.7 Install complete
+2 ... deployed codewizard-helm-demo-0.1.0 1.19.7 Upgrade complete
+```
### Step 11 - Rollback
- #### `helm rollback`
+#### `helm rollback`
- - Rollback the `codewizard-helm-demo` release to previous version
+ - Rollback the `codewizard-helm-demo` release to previous version:
- ```sh
- helm rollback codewizard-helm-demo
+```sh
+$ helm rollback codewizard-helm-demo
+
+### Output:
+Rollback was a success! Happy Helming!
+```
- ### Output:
- Rollback was a success! Happy Helming!
+- Check again to verify that you get the original message!
- ```
- - Check again to verify that you get the original message
+---
## Finalize & Cleanup
-To remove all resources created by this lab, uninstall the `codewizard-helm-demo` release:
+- To remove all resources created by this lab, uninstall the `codewizard-helm-demo` release:
```sh
helm uninstall codewizard-helm-demo
```
-(Optional) If you created a dedicated namespace for this lab, you can delete it:
+- (Optional) If you have created a dedicated namespace for this lab, you can delete it by runniung:
```sh
kubectl delete namespace codewizard
@@ -360,57 +332,52 @@ kubectl delete namespace codewizard
- **Helm not found:**
- Make sure Helm is installed and available in your `PATH`. Run `helm version` to verify.
+Make sure `Helm` is installed and available in your `PATH`.
+Run the following to verify:
+
+```sh
+helm version
+```
+
+
- **Pods not starting:**
- Check pod status and logs:
+Check pod status and logs by running the following commands:
- ```sh
- kubectl get pods -n codewizard
- kubectl describe pod -n codewizard
- kubectl logs -n codewizard
- ```
+```sh
+kubectl get pods -n codewizard
+kubectl describe pod -n codewizard
+kubectl logs -n codewizard
+```
+
+
- **Service not reachable:**
- Ensure the service and pods are running:
+Ensure the service and pods are running by running the following commands:
- ```sh
- kubectl get svc -n codewizard
- kubectl get pods -n codewizard
- ```
+```sh
+kubectl get svc -n codewizard
+kubectl get pods -n codewizard
+```
+
+
- **Values not updated after upgrade:**
- Double-check your `--set` or `--values` flags and confirm the upgrade with:
+Double-check your `--set` or `--values` flags and confirm the upgrade by running:
- ```sh
- helm get values codewizard-helm-demo
- ```
+```sh
+helm get values codewizard-helm-demo
+```
---
## Next Steps
-- Try creating your own Helm chart for a different application.
-- Explore Helm chart repositories like [Artifact Hub](https://artifacthub.io/).
-- Learn about advanced Helm features: dependencies, hooks, and chart testing.
-- Integrate Helm with CI/CD pipelines for automated deployments.
+- Try creating your own `Helm` chart for a different application.
+- Explore `Helm` chart repositories like [Artifact Hub](https://artifacthub.io/).
+- Learn about advanced `Helm` features, such as: dependencies, hooks, and chart testing.
+- Integrate `Helm` with CI/CD pipelines for automated deployments.
- Read more in the [official Helm documentation](https://helm.sh/docs/).
-
----
-
-12-Wordpress-MySQL-PVC
- || 14-Logging
- :arrow_right:
-
----
-
-
-
-
-
-
diff --git a/Labs/14-Logging/README.md b/Labs/14-Logging/README.md
new file mode 100644
index 0000000..0ddde84
--- /dev/null
+++ b/Labs/14-Logging/README.md
@@ -0,0 +1,185 @@
+
+
+# K8S Hands-on
+
+
+
+---
+
+## Logging
+
+- Welcome to the `Logging` hands-on lab! In this tutorial, we will learn the essentials of `Logging` in Kubernetes clusters.
+- We will deploy a sample application, configure log collection, and explore logs using popular tools like `Fluentd`, `Elasticsearch`, and `Kibana` (EFK stack).
+
+---
+
+## Pre requirements
+
+- Kubernetes cluster - Setting up minikube cluster instruction
+- [**kubectl**](https://kubernetes.io/docs/tasks/tools/) configured to interact with your cluster
+- [Helm](https://helm.sh/docs/intro/install/) installed for easier deployment
+
+[](https://console.cloud.google.com/cloudshell/editor?cloudshell_git_repo=https://github.com/nirgeier/KubernetesLabs)
+
+### **CTRL + click to open in new window**
+
+---
+
+## What will we learn?
+
+- Why `Logging` is important in Kubernetes
+- How to deploy a sample app that generates logs
+- How to collect logs using Fluentd
+- How to store and search logs with `Elasticsearch`
+- How to visualize logs with `Kibana`
+- Troubleshooting and best practices
+
+---
+
+## Introduction
+
+- `Logging` is critical for monitoring, debugging, and auditing applications in Kubernetes.
+- Kubernetes does not provide a builtin, centralized `Logging` solution, but it allows us to integrate with many `Logging` stacks.
+- We will set up the EFK stack (`Elasticsearch`, `Fluentd`, `Kibana`) to collect, store, and visualize logs from our cluster.
+
+---
+
+## Lab
+
+### Step 01 - Deploy a Sample Application
+
+- Deploy a simple `Nginx` application that generates access logs.
+
+```sh
+kubectl create deployment nginx --image=nginx
+kubectl expose deployment nginx --port=80 --type=NodePort
+```
+
+- Check that the pod is running:
+
+```sh
+kubectl get pods
+```
+
+### Step 02 - Deploy `Elasticsearch`
+
+- Deploy `Elasticsearch` using `Helm`:
+
+```sh
+helm repo add elastic https://helm.elastic.co
+helm repo update
+helm install elasticsearch elastic/elasticsearch --set replicas=1 --set minimumMasterNodes=1
+```
+
+- Wait for the pod to be ready and check its status:
+
+```sh
+kubectl get pods
+```
+
+
+### Step 03 - Deploy `Kibana`
+
+- Deploy `Kibana` using `Helm`:
+
+```sh
+helm install kibana elastic/kibana
+```
+
+- Forward the `Kibana` port:
+
+```sh
+kubectl port-forward svc/kibana-kibana 5601:5601 &
+```
+
+!!! warning "If you are running this lab in Google Cloud Shell:"
+ 1. After running the port-forward command above, click the **Web Preview** button in the Cloud Shell toolbar (usually at the top right).
+ 2. Enter port `5601` when prompted.
+ 3. This will open `Kibana` in a new browser tab at a URL like `https://.shell.cloud.google.com/?port=5601`.
+ 4. If you see a warning about an untrusted connection, you can safely proceed.
+
+- Access `Kibana` at [http://localhost:5601](http://localhost:5601) (if running locally) or via the Cloud Shell Web Preview, as explained above.
+
+### Step 04 - Deploy `Fluentd`
+
+- Deploy `Fluentd` as a `DaemonSet` to collect logs from all nodes and forward them to `Elasticsearch`.
+
+```sh
+kubectl apply -f https://raw.githubusercontent.com/fluent/fluentd-kubernetes-daemonset/master/fluentd-daemonset-elasticsearch-rbac.yaml
+```
+
+- Check that `Fluentd` pods are running:
+
+```sh
+kubectl get pods -l app=fluentd
+```
+
+### Step 05 - Generate and View Logs
+
+- Access the `Nginx` service to generate logs:
+
+```sh
+minikube service nginx
+```
+
+
+In `Kibana`, configure an index pattern to view logs:
+
+1. Open Kibana in your browser (using the Cloud Shell Web Preview as described above).
+2. In the left menu, click **Stack Management** > **Kibana** > **Index Patterns**.
+3. Click **Create index pattern**.
+4. In the "Index pattern" field, enter `fluentd-*` (or `logstash-*` if your logs use that prefix).
+5. Click **Next step**.
+6. For the time field, select `@timestamp` and click **Create index pattern**.
+7. Go to **Discover** in the left menu to view and search your logs.
+
+Explore the logs, search, and visualize traffic.
+
+---
+
+## Troubleshooting
+
+##### **Pods not starting:**
+ - Check pod status and logs:
+
+```sh
+kubectl get pods
+kubectl describe pod
+kubectl logs
+```
+
+
+
+##### **Kibana not reachable:**
+
+ - Ensure port-forward is running and no firewall is blocking port 5601.
+
+
+
+##### **No logs in Kibana:**
+
+ - Check Fluentd and Elasticsearch pod logs for errors.
+ - Ensure index pattern is set up correctly in Kibana.
+
+---
+
+## Cleanup
+
+- To remove all resources created by this lab:
+
+```sh
+helm uninstall elasticsearch
+helm uninstall kibana
+kubectl delete deployment nginx
+kubectl delete service nginx
+kubectl delete -f https://raw.githubusercontent.com/fluent/fluentd-kubernetes-daemonset/master/fluentd-daemonset-elasticsearch-rbac.yaml
+```
+
+---
+
+## Next Steps
+
+- Try deploying other logging stacks like `Loki` + `Grafana`.
+- Explore log aggregation, alerting, and retention policies.
+- Integrate logging with monitoring and alerting tools.
+- Read more in the [Kubernetes logging documentation](https://kubernetes.io/docs/concepts/cluster-administration/logging/).
diff --git a/Labs/15-Prometheus-Grafana/README.md b/Labs/15-Prometheus-Grafana/README.md
index 7138c32..88acdba 100644
--- a/Labs/15-Prometheus-Grafana/README.md
+++ b/Labs/15-Prometheus-Grafana/README.md
@@ -7,77 +7,62 @@

---
+# Prometheus and Grafana Monitoring Lab
+- In this lab, we will learn how to set up and configure *`Prometheus` and `Grafana` for monitoring a Kubernetes cluster.
+- You will install `Prometheus` to collect metrics from the cluster and `Grafana` to visualize those metrics.
+- By the end of this lab, you will have a functional monitoring stack that provides insights into the health and performance of your Kubernetes environment.
+
+---
-## PreRequirements
+## Pre requirements
-- [Helm](https://helm.sh/docs/intro/install/)
-- K8S cluster
-- **kubectl** configured to interact with your cluster.
+- [`Helm`](https://helm.sh/docs/intro/install/) installed
+- K8S cluster - Setting up minikube cluster instruction
+- [**kubectl**](https://kubernetes.io/docs/tasks/tools/) configured to interact with your cluster
----
[](https://console.cloud.google.com/cloudshell/editor?cloudshell_git_repo=https://github.com/nirgeier/KubernetesLabs)
-
### **CTRL + click to open in new window**
-
+
---
-# Prometheus and Grafana: Setup and Configuration Guide
+## Prometheus and Grafana Setup and Configuration Guide
-- This guide walks you through the steps to set up **Prometheus** and **Grafana** on your Kubernetes cluster.
-- It includes hands-on steps for installing Prometheus using Helm, configuring Prometheus to collect metrics, setting up Grafana to visualize key metrics, and automating the setup using a bash script.
+- This guide serves as a comprehensive walkthrough of the steps to set up `Prometheus` and `Grafana` on your Kubernetes cluster.
+- It includes hands-on steps for installing `Prometheus` using `Helm`, configuring `Prometheus` to collect metrics, setting up `Grafana` to visualize key metrics, and automating the setup using a bash script.
---
-
-## Table of Contents
-
-- [Introduction to Prometheus and Grafana](#introduction-to-prometheus-and-grafana)
- - [`Prometheus`](#prometheus)
- - [`Grafana`](#grafana)
-- [Part 01 - Installing Prometheus and Grafana](#part-01---installing-prometheus-and-grafana)
- - [Step 01: Add Prometheus and Grafana Helm Repositories](#step-01-add-prometheus-and-grafana-helm-repositories)
- - [Step 02: Install Prometheus Stack](#step-02-install-prometheus-stack)
- - [Step 03: Install Grafana](#step-03-install-grafana)
- - [Step 04: Access Grafana](#step-04-access-grafana)
-- [Part 02 - Configuring Prometheus](#part-02---configuring-prometheus)
- - [Step 01: Verify Prometheus Metrics Collection](#step-01-verify-prometheus-metrics-collection)
-- [Part 03 - Configuring Grafana](#part-03---configuring-grafana)
- - [Step 01: Add Prometheus as a Data Source in Grafana](#step-01-add-prometheus-as-a-data-source-in-grafana)
- - [Step 02: Create a Dashboard to Display Metrics](#step-02-create-a-dashboard-to-display-metrics)
- - [Step 03: Get Number of Pods in the Cluster](#step-03-get-number-of-pods-in-the-cluster)
- - [Step 04: Customize the Panel](#step-04-customize-the-panel)
-
----
-
## Introduction to Prometheus and Grafana
### `Prometheus`
-- **Prometheus** is an open-source systems monitoring and alerting toolkit designed for reliability and scalability.
+- `Prometheus` is an open-source systems monitoring and alerting toolkit designed for reliability and scalability.
- It collects and stores metrics as time-series data, providing powerful querying capabilities.
- It is commonly used in Kubernetes environments for monitoring cluster health, application performance, and infrastructure.
### `Grafana`
-- **Grafana** is a popular open-source data visualization tool that works well with Prometheus.
+- `Grafana` is a popular open-source data visualization tool that works well with `Prometheus`.
- It allows you to create dashboards and visualize metrics in real-time, providing insights into system performance and application health.
-- Grafana supports a wide range of visualization options, including `graphs`, `heatmaps`, `tables`, and more.
-- Together, **Prometheus** and **Grafana** provide a powerful stack for monitoring and alerting in Kubernetes.
+- `Grafana` supports a wide range of visualization options, including `graphs`, `heatmaps`, `tables`, and more.
+- Together, `Prometheus` and `Grafana` provide a powerful stack for monitoring and alerting in Kubernetes.
---
## Part 01 - Installing Prometheus and Grafana
-- To begin, we'll use **Helm** , the package manager for Kubernetes, to deploy Prometheus and Grafana.
+!!! warning "Helm Charts"
+ We will use [Helm](../13-HelmChart/README.md), to deploy Prometheus and Grafana.
-### Step 01: Add Prometheus and Grafana Helm Repositories
-First, add the official Helm charts for Prometheus and Grafana:
+### Step 01 - Add Prometheus and Grafana Helm Repositories
+
+- Let's add the official `Helm` charts for `Prometheus` and `Grafana`:
```bash
@@ -89,9 +74,9 @@ helm repo add grafana https://grafana.github.io/helm-charts
helm repo update
```
-### Step 02: Install Prometheus Stack
+### Step 02 - Install Prometheus Stack
-- `Prometheus` is installed using the `prometheus-stack` Helm chart.
+- `Prometheus` is installed using the `prometheus-stack` `Helm` chart.
```bash
# Install Prometheus
@@ -107,21 +92,22 @@ helm repo update
helm status prometheus -n monitoring
```
-### Step 03: Install Grafana
+### Step 03 - Install Grafana
- - Now, let's install **Grafana** .
- - Grafana will be deployed in the same `monitoring` namespace.
+ - Now, let's install `Grafana`.
+ - `Grafana` will be deployed in the same `monitoring` namespace.
```bash
helm install grafana grafana/grafana --namespace monitoring
+
# Verify the status of the release using the following:
- helm status prometheus -n monitoring
+ helm status grafana -n monitoring
```
-### Step 04: Access Grafana
+### Step 04 - Access Grafana
-- Grafana will expose a service in your Kubernetes cluster.
-- To access it, you need password & use port forwarding
+- `Grafana` will expose a service in your Kubernetes cluster.
+- To access it, you need a password and port forwarding.
```bash
# In order to get the Grafana admin password, run the following command:
@@ -132,7 +118,7 @@ helm repo update
# Set the port forwarding so you can access the service using your browsers
kubectl port-forward \
--namespace monitoring \
- service/grafana 3000:80 &
+ service/grafana 3000:80
```
- Verify that you can access `**Grafana`
@@ -141,67 +127,102 @@ helm repo update
- **Username** : `admin`
- **Password** : (the password you retrieved earlier)
+
+!!! warning "Accessing Grafana on Google Cloud Shell"
+
+ If you are running your cluster in Google Cloud Shell, you cannot use `localhost` for port forwarding. Instead, use the Cloud Shell Web Preview:
+
+ 1. Run the port-forward command as usual:
+ ```bash
+ kubectl port-forward --namespace monitoring service/grafana 3000:80
+ ```
+ 2. In Google Cloud Shell, click the "Web Preview" button (top right) and select "Preview on port 3000".
+ 3. Grafana will open in a new browser tab.
+ - **Username**: `admin`
+ - **Password**: (the password you retrieved earlier)
+
+ **Note:** You can use any available port (e.g., 3000, 3001) in the port-forward command, just match it in the Web Preview.
+
+
---
## Part 02 - Configuring Prometheus
- `Prometheus` can collect various metrics from your Kubernetes cluster **automatically** if the right **exporters** are enabled.
-- The **kube-prometheus-stack** chart that you installed earlier automatically configures Prometheus to scrape a number of Kubernetes components (like `kubelet`, `node-exporter`, and `kube-state-metrics`) for various metrics.
+- The **kube-prometheus-stack** chart that you installed earlier automatically configures `Prometheus` to scrape a number of Kubernetes components (like `kubelet`, `node-exporter`, and `kube-state-metrics`) for various metrics.
-### Step 01: Verify Prometheus Metrics Collection
+### Step 01 - Verify Prometheus Metrics Collection
-- You can check if Prometheus is correctly scraping metrics by navigating to Prometheus' web UI.
+- You can check if `Prometheus` is correctly scraping metrics by navigating to `Prometheus`' web UI.
+
+
+```bash
+# Port-forward the Prometheus service:
+kubectl port-forward \
+ --namespace monitoring \
+ svc/prometheus-operated 9090:9090
+```
- ```bash
- # Port-forward the Prometheus service:
- kubectl port-forward \
- --namespace monitoring \
- svc/prometheus-operated 9090:9090
- ```
- Verify that you can access `Prometheus`
- Open [http://localhost:9090](http://localhost:9090)
- - In the expression filed paste the following:
- ```bash
- # This query will show the current status of the `kube-state-metrics` job.
- up{job="kube-state-metrics"}
- ```
+ - In the expression field paste the following:
+
+ ```bash
+ # This query will show the current status of the `kube-state-metrics` job
+ up{job="kube-state-metrics"}
+ ```
+
+!!! warning "Accessing Prometheus on Google Cloud Shell"
+
+ If you are running your cluster in Google Cloud Shell, you cannot use `localhost` for port forwarding. Instead, use the Cloud Shell Web Preview:
+
+ 1. Run the port-forward command as usual:
+ ```bash
+ kubectl port-forward --namespace monitoring svc/prometheus-operated 9090:9090
+ ```
+ 2. In Google Cloud Shell, click the "Web Preview" button (top right) and select "Preview on port 9090".
+ 3. Prometheus will open in a new browser tab.
+
+ **Note:** You can use any available port (e.g., 9090, 9091) in the port-forward command, just match it in the Web Preview.
---
## Part 03 - Configuring Grafana
-- In this part we will set grafana to display Cluster CPU, Memory, and Requests.
-- Grafana dashboards can be configured to display **real-time metrics** for CPU, memory, and requests.
+- In this part we will set `grafana` to display the Cluster's CPUs, Memory, and Requests.
+- `Grafana` dashboards can be configured to display **real-time metrics** for CPU, memory, and requests.
- `Prometheus` stores these metrics and `Grafana` will query `Prometheus` to display them.
-### Step 01: Add Prometheus as a Data Source in Grafana
-
- 1. Log into Grafana. [http://localhost:3000](http://localhost:3000)
+### Step 01 - Add Prometheus as a Data Source in Grafana
+
+ 1. Log into `Grafana` at: [http://localhost:3000](http://localhost:3000), or use the **Cloud Shell Web Preview**.
2. Click on the hamburger icon on the left sidebar to open the **Configuration** menu.
- 3. Click on **Data Sources** .
- 4. Click **Add data source** and choose **Prometheus** .
- 5. In the **URL** field, enter the Prometheus server URL: `http://prometheus-operated:9090`.
- 6. Click **Save & Test** to confirm that the connection is working.
+ 3. Click on **Data Sources**.
+ 4. Click **Add data source** and choose **Prometheus**.
+ 5. In the **URL** field, enter the Prometheus server URL: `http://prometheus-operated:9090`.
+ 6. Click **Save & Test** to confirm that the connection is working.
-### Step 02: Create a Dashboard to Display Metrics
+### Step 02 - Create a Dashboard to Display Metrics
- - Next step is to create a dashboard and panels to display the desired metrics
- - To create a dashboard in Grafana for CPU, memory, and requests do the following:
+ - Next step is to create a dashboard and panels to display the desired metrics.
+ - To create a dashboard in `Grafana` for CPU, memory, and requests do the following:
- 1. In Grafana, open the left sidebar menu and select **Dashboard**.
+ 1. In `Grafana`, open the left sidebar menu and select **Dashboard**.
2. Click **Add visualization**.
- 3. Choose `Data Source` (we defined it previously)
- 4. In the panel editor, click on the `Code` option (right side of the query builder)
+ 3. Choose `Data Source` (as we defined it previously).
+ 4. In the panel editor, click on the `Code` option (right side of the query builder).
5. Enter the below queries to visualize metric(s):
Note: To add new query click on the `+ Add query`
- 6. Save the dashboard
+ 6. Save the dashboard.
+
+
- **CPU Usage**
- ```plaintext
- sum(rate(container_cpu_usage_seconds_total{namespace="default", container!="", container!="POD"}[5m])) by (pod, namespace)
- ```
+ ```plaintext
+ sum(rate(container_cpu_usage_seconds_total{namespace="default", container!="", container!="POD"}[5m])) by (pod, namespace)
+ ```
- **Memory Usage** :
@@ -215,22 +236,27 @@ helm repo update
sum(rate(http_requests_total{job="kubelet", cluster="", namespace="default"}[5m])) by (pod, namespace)
```
----
-### Step 03: Get Number of Pods in the Cluster
+
+### Step 03 - Get Number of Pods in the Cluster
- To track the number of pods running in the cluster, add new panel with the following query:
-- This query counts the number of pods running in all the namespace.
- ```plaintext
- count(kube_pod_info{}) by (namespace)
- ```
-- Add another query which will count the number of pods under the namespace `monitoring`
- - Tip: We already defined query based upon namespaces before....
-
- ```plaintext
- count(kube_pod_info{}) by (namespace)
- ```
+```sh
+# This query counts the number of pods running in all the namespaces
+count(kube_pod_info{}) by (namespace)
+```
+
+- Add another query which will count the number of pods under the namespace `monitoring`:
+
+
+ ```sh
+ count(kube_pod_info{namespace="monitoring"}) by (namespace)
+ ```
+!!! note "Tip"
+ We have already defined query based upon namespaces before....
+ You can use the same approach to filter by other labels as well.
+
### Step 04: Customize the Panel
- Change the visualization by changing the **Graph Style**
diff --git a/Labs/16-Affinity-Taint-Tolleration/README.md b/Labs/16-Affinity-Taint-Tolleration/README.md
index 871c1d6..5606d9b 100644
--- a/Labs/16-Affinity-Taint-Tolleration/README.md
+++ b/Labs/16-Affinity-Taint-Tolleration/README.md
@@ -1,6 +1,442 @@

+
# K8S Hands-on
+

----
\ No newline at end of file
+---
+
+# Node Affinity, Taints, and Tolerations Lab
+
+- In this lab, we will explore Kubernetes mechanisms for controlling Pod placement on Nodes.
+- We will learn how to use `Node Affinity`, `Taints`, and `Tolerations` to schedule Pods on specific Nodes based on labels, constraints, and preferences.
+- By the end of this lab, you will understand how to control where Pods run in your cluster and how to reserve Nodes for specific workloads.
+
+---
+
+
+## Pre requirements
+
+- K8S cluster - Setting up minikube cluster instruction
+- [**kubectl**](https://kubernetes.io/docs/tasks/tools/) configured to interact with your cluster
+
+[](https://console.cloud.google.com/cloudshell/editor?cloudshell_git_repo=https://github.com/nirgeier/KubernetesLabs)
+
+### **CTRL + click to open in new window**
+
+
+---
+
+## Introduction to Pod Scheduling
+
+Kubernetes provides several mechanisms to control which Nodes your Pods run on:
+
+### Node Affinity
+
+- `Node Affinity` allows us to constrain which Nodes our Pods can be scheduled on based on Node labels.
+- It's a more expressive and flexible version of `nodeSelector`.
+- There are two types:
+ - **`requiredDuringSchedulingIgnoredDuringExecution`**: Hard requirement - Pod will not be scheduled unless the rule is met.
+ - **`preferredDuringSchedulingIgnoredDuringExecution`**: Soft preference - Scheduler will try to enforce but will still schedule the Pod if it can't.
+
+### Taints and Tolerations
+
+- `Taints` are applied to Nodes and allow a Node to repel a set of Pods.
+- `Tolerations` are applied to Pods and allow (but do not require) Pods to schedule onto Nodes with matching `Taints`.
+- `Taints` and `Tolerations` work together to ensure that Pods are not scheduled onto inappropriate Nodes.
+- Use cases include:
+ - Dedicating Nodes to specific workloads
+ - Reserving Nodes with special hardware (GPUs, SSDs)
+ - Isolating problematic Pods
+
+---
+
+## Part 01 - Node Affinity
+
+- In this section, we will learn how to use Node `Affinity` to schedule Pods on specific Nodes.
+
+### Step 01 - Label Your Nodes
+
+- First, let's label some Nodes to use with Node `Affinity`:
+
+```bash
+# Get list of nodes
+kubectl get nodes
+
+# Label a node with environment=production
+kubectl label nodes environment=production
+
+# Label another node with environment=development
+kubectl label nodes environment=development
+
+# Verify the labels
+kubectl get nodes --show-labels
+```
+
+### Step 02 - Create a Pod with Required Node Affinity
+
+- Create a Pod that **must** run on a Node with `environment=production`:
+
+```yaml
+apiVersion: v1
+kind: Pod
+metadata:
+ name: affinity-required-pod
+spec:
+ affinity:
+ nodeAffinity:
+ requiredDuringSchedulingIgnoredDuringExecution:
+ nodeSelectorTerms:
+ - matchExpressions:
+ - key: environment
+ operator: In
+ values:
+ - production
+ containers:
+ - name: nginx
+ image: nginx:latest
+```
+
+- Apply the Pod:
+
+```bash
+# Create the Pod
+kubectl apply -f affinity-required-pod.yaml
+
+# Check which Node the Pod is running on
+kubectl get pod affinity-required-pod -o wide
+
+# Verify it's running on the production Node
+kubectl describe pod affinity-required-pod | grep Node:
+```
+
+### Step 03 - Create a Pod with Preferred Node Affinity
+
+- Create a Pod that **prefers** to run on a Node with `environment=development`:
+
+```yaml
+apiVersion: v1
+kind: Pod
+metadata:
+ name: affinity-preferred-pod
+spec:
+ affinity:
+ nodeAffinity:
+ preferredDuringSchedulingIgnoredDuringExecution:
+ - weight: 1
+ preference:
+ matchExpressions:
+ - key: environment
+ operator: In
+ values:
+ - development
+ containers:
+ - name: nginx
+ image: nginx:latest
+```
+
+- Apply the Pod:
+
+```bash
+# Create the Pod
+kubectl apply -f affinity-preferred-pod.yaml
+
+# Check which Node the Pod is running on
+kubectl get pod affinity-preferred-pod -o wide
+
+# This Pod will prefer the development Node but can run elsewhere if needed
+```
+
+### Step 04 - Experiment with Node Affinity Operators
+
+- `Node Affinity` supports several operators:
+
+ - **`In`**: Label value is in the list of values
+ - **`NotIn`**: Label value is not in the list of values
+ - **`Exists`**: Label key exists (value does not matter)
+ - **`DoesNotExist`**: Label key does not exist
+ - **`Gt`**: Label value is greater than the specified value (numeric comparison)
+ - **`Lt`**: Label value is less than the specified value (numeric comparison)
+
+- Example with multiple conditions:
+
+```yaml
+apiVersion: v1
+kind: Pod
+metadata:
+ name: affinity-multiple-conditions
+spec:
+ affinity:
+ nodeAffinity:
+ requiredDuringSchedulingIgnoredDuringExecution:
+ nodeSelectorTerms:
+ - matchExpressions:
+ - key: environment
+ operator: In
+ values:
+ - production
+ - staging
+ - key: disk-type
+ operator: Exists
+ containers:
+ - name: nginx
+ image: nginx:latest
+```
+
+---
+
+## Part 02 - Taints and Tolerations
+
+- In this section, we will learn how to use `Taints` and `Tolerations` to control Pod scheduling.
+
+### Step 01 - Understanding Taint Effects
+
+- `Taints` have three effects:
+
+ - **`NoSchedule`**: Pods without matching `tolerations` will not be scheduled on the Node.
+ - **`PreferNoSchedule`**: Scheduler will try to avoid placing Pods without `tolerations`, but it's not guaranteed.
+ - **`NoExecute`**: Existing Pods without `tolerations` will be evicted, and new ones won't be scheduled.
+
+### Step 02 - Apply a Taint to a Node
+
+- Let's `taint` a Node to dedicate it for special workloads:
+
+```bash
+# Apply a taint to a Node
+kubectl taint nodes dedicated=special-workload:NoSchedule
+
+# Verify the taint
+kubectl describe node | grep Taints
+```
+
+### Step 03 - Create a Pod Without Toleration
+
+- Let's try to create a Pod without a `toleration`:
+
+```yaml
+apiVersion: v1
+kind: Pod
+metadata:
+ name: pod-without-toleration
+spec:
+ containers:
+ - name: nginx
+ image: nginx:latest
+```
+
+```bash
+# Create the Pod
+kubectl apply -f pod-without-toleration.yaml
+
+# Check the Pod status - it should not be scheduled on the tainted Node
+kubectl get pod pod-without-toleration -o wide
+
+# If all your Nodes are tainted, the Pod will remain Pending
+kubectl describe pod pod-without-toleration
+```
+
+### Step 04 - Create a Pod With Toleration
+
+- Now let's create a Pod that tolerates the `taint`:
+
+```yaml
+apiVersion: v1
+kind: Pod
+metadata:
+ name: pod-with-toleration
+spec:
+ tolerations:
+ - key: "dedicated"
+ operator: "Equal"
+ value: "special-workload"
+ effect: "NoSchedule"
+ containers:
+ - name: nginx
+ image: nginx:latest
+```
+
+```bash
+# Create the Pod
+kubectl apply -f pod-with-toleration.yaml
+
+# This Pod can now be scheduled on the tainted Node
+kubectl get pod pod-with-toleration -o wide
+```
+
+### Step 05 - Understanding Toleration Operators
+
+- Tolerations support two operators:
+
+ - **`Equal`**: Requires exact match of key, value, and effect
+ - **`Exists`**: Only checks for key existence (value is ignored)
+
+- Example with `Exists` operator:
+
+```yaml
+apiVersion: v1
+kind: Pod
+metadata:
+ name: pod-with-exists-toleration
+spec:
+ tolerations:
+ - key: "dedicated"
+ operator: "Exists"
+ effect: "NoSchedule"
+ containers:
+ - name: nginx
+ image: nginx:latest
+```
+
+### Step 06 - NoExecute Effect
+
+- The `NoExecute` effect is special - it evicts running Pods:
+
+```bash
+# Apply a NoExecute taint
+kubectl taint nodes maintenance=true:NoExecute
+
+# Any Pods on this Node without matching toleration will be evicted
+kubectl get pods -o wide --watch
+```
+
+- Let's add a toleration with `tolerationSeconds` to delay eviction:
+
+```yaml
+apiVersion: v1
+kind: Pod
+metadata:
+ name: pod-with-delayed-eviction
+spec:
+ tolerations:
+ - key: "maintenance"
+ operator: "Equal"
+ value: "true"
+ effect: "NoExecute"
+ tolerationSeconds: 300 # Pod will be evicted after 5 minutes
+ containers:
+ - name: nginx
+ image: nginx:latest
+```
+
+---
+
+## Part 03 - Combining Affinity, Taints, and Tolerations
+
+- We can combine `Node Affinity` with `Taints` and `Tolerations` for fine-grained control.
+
+### Step 01 - Create a Dedicated Node Pool
+
+- Let's simulate a dedicated Node pool for GPU workloads:
+
+```bash
+# Label a Node for GPU workload
+kubectl label nodes hardware=gpu
+
+# Taint the Node to prevent non-GPU Pods
+kubectl taint nodes nvidia.com/gpu=true:NoSchedule
+```
+
+### Step 02 - Deploy a GPU Workload
+
+- Let's create a Pod that requires GPU Nodes:
+
+```yaml
+apiVersion: v1
+kind: Pod
+metadata:
+ name: gpu-workload
+spec:
+ affinity:
+ nodeAffinity:
+ requiredDuringSchedulingIgnoredDuringExecution:
+ nodeSelectorTerms:
+ - matchExpressions:
+ - key: hardware
+ operator: In
+ values:
+ - gpu
+ tolerations:
+ - key: "nvidia.com/gpu"
+ operator: "Equal"
+ value: "true"
+ effect: "NoSchedule"
+ containers:
+ - name: gpu-app
+ image: nvidia/cuda:11.0-base
+ command: ["nvidia-smi"]
+```
+
+```bash
+# Apply the Pod
+kubectl apply -f gpu-workload.yaml
+
+# Verify it's scheduled on the GPU Node
+kubectl get pod gpu-workload -o wide
+```
+
+---
+
+## Part 04 - Cleanup and Best Practices
+
+### Step 01 - Remove Taints
+
+```bash
+# Remove a taint from a Node (add a minus sign at the end)
+kubectl taint nodes dedicated=special-workload:NoSchedule-
+
+# Remove all taints with a specific key
+kubectl taint nodes nvidia.com/gpu-
+```
+
+### Step 02 - Remove Labels
+
+```bash
+# Remove a label from a Node (add a minus sign at the end)
+kubectl label nodes environment-
+kubectl label nodes hardware-
+```
+
+### Step 03 - Delete Test Pods
+
+```bash
+# Delete all test Pods
+kubectl delete pod affinity-required-pod affinity-preferred-pod
+kubectl delete pod pod-with-toleration pod-without-toleration
+kubectl delete pod gpu-workload
+```
+
+---
+
+### Best Practices
+
+1. **Use Node Affinity for preferences**, `Taints/Tolerations` for hard requirements.
+2. **Label Nodes consistently** across your cluster (e.g., `node-role`, `hardware-type`, `environment`).
+3. **Document your taints** - team members need to know why Nodes are tainted.
+4. **Use `PreferNoSchedule`** for soft isolation instead of `NoSchedule` when appropriate.
+5. **Combine with Pod Priority** for more sophisticated scheduling strategies.
+6. **Test eviction behavior** before using `NoExecute` in production.
+7. **Use tolerationSeconds** to gracefully handle Node maintenance.
+8. **Monitor unschedulable Pods** - they indicate scheduling constraint conflicts.
+
+---
+
+## Summary
+
+In this lab, you learned:
+
+- How to use **Node Affinity** to schedule Pods on specific Nodes based on labels.
+- The difference between `required` and `preferred` affinity rules.
+- How to use **Taints** to repel Pods from Nodes.
+- How to use **Tolerations** to allow Pods on tainted Nodes.
+- The three taint effects: `NoSchedule`, `PreferNoSchedule`, and `NoExecute`.
+- How to combine affinity, taints, and tolerations for complex scheduling scenarios.
+- Best practices for Pod placement in production clusters.
+
+---
+
+## Additional Resources
+
+- [Kubernetes Node Affinity Documentation](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity)
+- [Kubernetes Taints and Tolerations Documentation](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/)
+- [Pod Priority and Preemption](https://kubernetes.io/docs/concepts/scheduling-eviction/pod-priority-preemption/)
\ No newline at end of file
diff --git a/Labs/17-PodDisruptionBudgets-PDB/README.md b/Labs/17-PodDisruptionBudgets-PDB/README.md
index 86b636c..90bf067 100644
--- a/Labs/17-PodDisruptionBudgets-PDB/README.md
+++ b/Labs/17-PodDisruptionBudgets-PDB/README.md
@@ -5,19 +5,29 @@
---
-# PodDisruptionBudgets(PDB)
+# Pod Disruption Budgets (PDB)
+- In this lab, we will learn about `Pod Disruption Budgets (PDB)` in Kubernetes.
+- We will explore how to define and implement PDBs to ensure application availability during voluntary disruptions, such as node maintenance or cluster upgrades.
+- By the end of this lab, you will understand how to create and manage Pod Disruption Budgets to maintain the desired level of service availability in your Kubernetes cluster.
+
+---
+
+
+## Pre requirements
-### Pre-Requirements
- K8S cluster - Setting up minikube cluster instruction
+- [**kubectl**](https://kubernetes.io/docs/tasks/tools/) configured to interact with your cluster
-[](https://console.cloud.google.com/cloudshell/editor?cloudshell_git_repo=https://github.com/nirgeier/KubernetesLabs)
-**CTRL + click to open in new window**
+[](https://console.cloud.google.com/cloudshell/editor?cloudshell_git_repo=https://github.com/nirgeier/KubernetesLabs)
+### **CTRL + click to open in new window**
+
+
---
## `PodDisruptionBudgets`: Budgeting the Number of Faults to Tolerate
-- A pod disruption budget is an **indicator of the number of disruptions that can be tolerated at a given time for a class of pods** (a budget of faults).
+- A `pod disruption budget` is an **indicator of the number of disruptions that can be tolerated at a given time for a class of pods** (a budget of faults).
- Disruptions may be caused by **deliberate** or **accidental** Pod deletion.
- Whenever a disruption to the pods in a service is calculated to cause the service to **drop below the budget**, the operation is paused until it can maintain the budget. This means that the `drain event` could be temporarily halted while it waits for more pods to become available such that the budget isn’t crossed by evicting the pods.
@@ -29,15 +39,20 @@
- `ReplicaSet`
- `StatefulSet`
-- For this tutorial you should get familier with Kubernetes Eviction Policies since it demonstrates how Pod Disruption Budgets handle evictions.
+- For this tutorial you should get familier with [**Kubernetes Eviction Policies**](https://kubernetes.io/docs/concepts/scheduling-eviction/), as it demonstrates how `Pod Disruption Budgets` handle evictions.
-- As in the Kubernetes Eviction Policies tutorial we start with eviction-hard="memory.available<480M
+- As in the `Kubernetes Eviction Policies` tutorial, we start with
+```sh
+eviction-hard="memory.available<480M"
+```
+---
### Sample
-- In the below sample we will configure a `PodDisruptionBudget` which insure that we will always have **at least** 1 Nginx instance.
+- In the below sample we will configure a `Pod Disruption Budget` which insure that we will always have **at least** 1 Nginx instance.
+
+- First we need an [Nginx Deployment](./resources/Deployment.yaml):
-- First we need an [Nginx Deployment](./resources/Deployment.yaml)
```yaml
apiVersion: apps/v1
kind: Deployment
@@ -48,6 +63,9 @@ metadata:
app: nginx # <- We will use this name below
...
```
+
+- Now we can create the `Pod Disruption Budget`:
+
```yaml
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
@@ -62,18 +80,15 @@ spec:
---
## Walkthrough
+
[01. start minikube with Feature Gates](#01-start-minikube-with-feature-gates)
+
[02. Check Node Pressure(s)](#02-check-node-pressure)
---
-### 01. start minikube with Feature Gates
-- For more details about Feature Gates read:
- https://kubernetes.io/docs/reference/command-line-tools-reference/feature-gates/#feature-stages
-
-- For more details about eviction-signals
- https://kubernetes.io/docs/tasks/administer-cluster/out-of-resource/#eviction-signals
-
+### Step 01 - Start minikube with Feature Gates
+- Run thwe following command to start minikube with the required `Feature Gates` and `Eviction Signals`:
```sh
minikube start \
@@ -82,8 +97,14 @@ minikube start \
--extra-config=kubelet.feature-gates="ExperimentalCriticalPodAnnotation=true"
```
-### 02. Check Node Pressure(s)
-- Check to see the Node conditions, if we have any kind of "Presure"
+- For more details about `Feature Gates`, read [here](https://kubernetes.io/docs/reference/command-line-tools-reference/feature-gates/#feature-stages).
+
+- For more details about `eviction-signals`, read [here](https://kubernetes.io/docs/tasks/administer-cluster/out-of-resource/#eviction-signals).
+
+### Step 02 - Check Node Pressure(s)
+
+- Check to see the Node conditions, if we have any kind of "Pressure", by running the following:
+
```sh
kubectl describe node minikube | grep MemoryPressure
@@ -105,7 +126,10 @@ Allocated resources:
ephemeral-storage 0 (0%) 0 (0%)
```
-### 03. Create 3 Pods using 50 MB each.
+### Step 03 - Create 3 Pods using 50 MB each
+
+- Create a file named `50MB-ram.yaml` with the following content:
+
```yaml
# ./resources/50MB-ram.yaml
...
@@ -123,34 +147,21 @@ resources:
memory: "128Mi"
cpu: "500m"
```
-- Create the pods
+
+- Create the pods with the following command:
+
```sh
kubectl apply -f resources/50MB-ram.yaml
```
-### 04. Check MemoryPressure
+### Step 04 - Check Memory Pressure
+
+- Now let's check the Node conditions again to see if we have `MemoryPressure`:
+
```sh
$ kubectl describe node minikube | grep MemoryPressure
# Output should be similar to
MemoryPressure False ... KubeletHasSufficientMemory kubelet has sufficient memory available
```
-
-
----
-
-
-
-
-
-
\ No newline at end of file
+- As we can see, we still have `sufficient memory available`.
\ No newline at end of file
diff --git a/Labs/18-ArgoCD/README.md b/Labs/18-ArgoCD/README.md
new file mode 100644
index 0000000..a1d13a4
--- /dev/null
+++ b/Labs/18-ArgoCD/README.md
@@ -0,0 +1,877 @@
+
+
+# K8S Hands-on
+
+
+
+---
+
+# ArgoCD
+
+- In this tutorial, we will learn the essentials of `ArgoCD`, a declarative GitOps continuous delivery tool for Kubernetes.
+- We will install `ArgoCD`, deploy applications, sync resources from Git repositories, and gain practical experience with GitOps workflows.
+
+---
+
+## Pre Requirements
+
+- K8S cluster - Setting up minikube cluster instruction
+- [**kubectl**](https://kubernetes.io/docs/tasks/tools/) configured to interact with your cluster
+- A `Git repository` (GitHub, GitLab, or Bitbucket) for storing application manifests
+- Basic understanding of Kubernetes resources (Deployments, Services, etc.)
+
+[](https://console.cloud.google.com/cloudshell/editor?cloudshell_git_repo=https://github.com/nirgeier/KubernetesLabs)
+
+### **CTRL + click to open in new window**
+
+---
+
+## What will we learn?
+
+- What `ArgoCD` is and why it's useful for GitOps.
+- How to install and configure `ArgoCD` on Kubernetes.
+- `ArgoCD` core concepts: Applications, Projects, and Sync.
+- How to deploy applications from `Git repositories`.
+- Application health and sync status monitoring.
+- Rollback and sync strategies.
+- Best practices for GitOps workflows.
+
+---
+
+### What is ArgoCD?
+
+- `ArgoCD` is a declarative, GitOps continuous delivery tool for Kubernetes.
+- It follows the `GitOps` pattern where Git repositories are the source of truth for defining the desired application state.
+- `ArgoCD` automates the deployment of the desired application states in the specified target environments.
+
+### Why ArgoCD?
+
+- **GitOps Workflow**: Uses Git as the single source of truth.
+- **Automated Deployment**: Automatically syncs your Kubernetes cluster with Git repositories.
+- **Application Health Monitoring**: Continuous monitoring of deployed applications.
+- **Rollback Capabilities**: Easy rollback to previous versions.
+- **Multi-Cluster Support**: Manage applications across multiple clusters.
+- **SSO Integration**: Supports various SSO providers for authentication.
+- **RBAC**: Fine-grained access control.
+- **Audit Trail**: Full audit trail of all operations.
+
+### Terminology
+
+* **Application**
+ - An `ArgoCD` **Application** is a Kubernetes resource object representing a deployed application instance in an environment.
+ - It defines the source repository, target cluster, and sync policies.
+
+* **Project**
+ - An `ArgoCD` **Project** provides a logical grouping of applications.
+ - Projects are useful for organizing applications and implementing RBAC.
+
+* **Sync**
+ - **Sync** is the process of making a live cluster state match the desired state defined in Git.
+ - Sync can run manually or automatically.
+
+* **Sync Status**
+ - Indicates whether the live state matches the Git state.
+ - Status can be: **Synced**, **OutOfSync**, or **Unknown**.
+
+* **Health Status**
+ - Indicates the health of the application resources.
+ - Status can be: **Healthy**, **Progressing**, **Degraded**, **Suspended**, or **Missing**.
+
+### ArgoCD Architecture
+
+| Component | Description |
+| ------------------------ | ---------------------------------------------------------------------------------------------- |
+| **API Server** | Exposes the API consumed by Web UI, CLI, and CI/CD systems |
+| **Repository Server** | Maintains a local cache of Git repositories holding application manifests |
+| **Application Controller** | Monitors running applications and compares the current live state against the desired state |
+| **Dex** | Identity service for integrating with external identity providers |
+| **Redis** | Used for caching |
+
+### Common ArgoCD CLI Commands
+
+| Command | Description |
+| ----------------------------------------------------- | -------------------------------------------------------------------- |
+| `argocd login ` | Login to `ArgoCD` server |
+| `argocd app create ` | Create a new application |
+| `argocd app list` | List all applications |
+| `argocd app get ` | Get application details |
+| `argocd app sync ` | Sync (deploy) an application |
+| `argocd app delete ` | Delete an application |
+| `argocd app set ` | Update application parameters |
+| `argocd app diff ` | Show differences between Git and live state |
+| `argocd app history ` | Show application deployment history |
+| `argocd app rollback ` | Rollback to a previous revision |
+
+---
+
+# Lab
+
+## Part 01 - Installing ArgoCD
+
+### Step 01 - Create an ArgoCD Namespace
+
+- Create a dedicated namespace for `ArgoCD`:
+
+```bash
+kubectl create namespace argocd
+```
+
+### Step 02 - Install ArgoCD
+
+- Install `ArgoCD` using the official installation manifest:
+
+```bash
+kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
+```
+
+### Step 03 - Verify Installation
+
+- Check that all `ArgoCD` pods are running:
+
+```bash
+kubectl get pods -n argocd
+```
+
+- Expected output should show all pods in **Running** status:
+
+```plaintext
+NAME READY STATUS RESTARTS AGE
+argocd-application-controller-0 1/1 Running 0 2m
+argocd-dex-server-xxx 1/1 Running 0 2m
+argocd-redis-xxx 1/1 Running 0 2m
+argocd-repo-server-xxx 1/1 Running 0 2m
+argocd-server-xxx 1/1 Running 0 2m
+```
+
+### Step 04 - Expose ArgoCD Server
+
+- By default, the `ArgoCD` API server is not exposed externally.
+- We' will use port-forwarding to access it, by running:
+
+```bash
+kubectl port-forward svc/argocd-server -n argocd 8080:443
+```
+
+- Alternatively, you can change the service type to **LoadBalancer** or create an **Ingress**:
+
+```bash
+# Change to LoadBalancer (for cloud environments)
+kubectl patch svc argocd-server -n argocd -p '{"spec": {"type": "LoadBalancer"}}'
+
+# Or use NodePort (for local/development)
+kubectl patch svc argocd-server -n argocd -p '{"spec": {"type": "NodePort"}}'
+```
+
+### Step 05 - Get Initial Admin Password
+
+- The initial password for the `admin` user is auto-generated and stored as a secret by running the following command:
+
+```bash
+# Get the initial admin password
+kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d; echo
+```
+
+!!! note
+ Save this password - you'll need it to login to the ArgoCD UI later.
+
+### Step 06 - Access ArgoCD UI
+
+- Open your browser and navigate to:
+ - **Port-forward**: `https://localhost:8080`
+ - **LoadBalancer**: Use the external IP
+ - **NodePort**: Use `http://:`
+
+- Login with:
+ - **Username**: `admin`
+ - **Password**: (from Step 05)
+
+---
+
+## Part 02 - Installing ArgoCD CLI
+
+### Step 01 - Download and Install ArgoCD CLI
+
+- Install the `ArgoCD CLI` based on your operating system:
+
+**Linux:**
+```bash
+curl -sSL -o argocd-linux-amd64 https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64
+sudo install -m 555 argocd-linux-amd64 /usr/local/bin/argocd
+rm argocd-linux-amd64
+```
+
+**macOS:**
+```bash
+brew install argocd
+```
+
+**Windows (via Chocolatey):**
+```bash
+choco install argocd-cli
+```
+
+### Step 02 - Verify CLI Installation
+
+```bash
+argocd version --short
+```
+
+### Step 03 - Login via CLI
+
+```bash
+# If using port-forward
+argocd login localhost:8080 --insecure
+
+# You'll be prompted for username and password
+```
+
+### Step 04 - Change Admin Password (optional, but highly recommended)
+
+```bash
+argocd account update-password
+```
+
+---
+
+## Part 03 - Deploying Your First Application
+
+### Step 01 - Prepare a Git Repository
+
+- For this lab, we will use a sample Git repository with Kubernetes manifests.
+- You can use the `ArgoCD` example repository or your own:
+
+```bash
+# Sample repository URL
+https://github.com/argoproj/argocd-example-apps.git
+```
+
+### Step 02 - Create an Application via CLI
+
+- Create an `ArgoCD` application that deploys the guestbook app:
+
+```bash
+argocd app create guestbook \
+ --repo https://github.com/argoproj/argocd-example-apps.git \
+ --path guestbook \
+ --dest-server https://kubernetes.default.svc \
+ --dest-namespace default
+```
+
+??? info "Command Explanation"
+ - `--repo`: The Git repository URL
+ - `--path`: Path within the repository where manifests are located
+ - `--dest-server`: Target Kubernetes cluster (default is the cluster where ArgoCD is installed)
+ - `--dest-namespace`: Target namespace for deployment
+
+### Step 03 - View Application Status
+
+```bash
+# List all applications
+argocd app list
+
+# Get detailed information about the application
+argocd app get guestbook
+```
+
+### Step 04 - Sync the Application
+
+- Initially, the application status will be **OutOfSync**.
+- Sync it to deploy by running:
+
+```bash
+argocd app sync guestbook
+```
+
+### Step 05 - Verify Deployment
+
+```bash
+# Check the deployed resources
+kubectl get all -n default
+
+# You should see the guestbook deployment, service, and pods
+```
+
+### Step 06 - Access the Application
+
+```bash
+# Port-forward to access the guestbook service
+kubectl port-forward svc/guestbook-ui -n default 8081:80
+
+# Open browser to http://localhost:8081
+```
+
+---
+
+## Part 04 - Creating Application via UI
+
+### Step 01 - Access ArgoCD UI
+
+- Navigate to the `ArgoCD` UI at `https://localhost:8080`
+
+### Step 02 - Create a New Application
+
+1. Click on **"+ NEW APP"** button.
+2. Fill in the following details:
+ - **Application Name**: `helm-guestbook`
+ - **Project**: `default`
+ - **Sync Policy**: `Manual` (or `Automatic` for auto-sync)
+ - **Repository URL**: `https://github.com/argoproj/argocd-example-apps.git`
+ - **Revision**: `HEAD`
+ - **Path**: `helm-guestbook`
+ - **Cluster URL**: `https://kubernetes.default.svc`
+ - **Namespace**: `default`
+
+3. Click **"CREATE"**.
+
+### Step 03 - View Application in UI
+
+- You should now be able to see the `helm-guestbook` application tile in the UI.
+- Click on it to see the application topology.
+
+### Step 04 - Sync via UI
+
+- Click the **"SYNC"** button.
+- Select the resources you want to sync (or select all).
+- Click **"SYNCHRONIZE"**.
+
+### Step 05 - Monitor Sync Progress
+
+- Watch the sync progress in real-time.
+- The UI will show each resource being created/updated.
+- Once completed, all resources should show as **Healthy** and **Synced**.
+
+---
+
+## Part 05 - Application Management
+
+### Step 01 - View Application Details
+
+```bash
+# Get full application details
+argocd app get guestbook
+
+# View application parameters
+argocd app get guestbook --show-params
+```
+
+### Step 02 - View Sync History
+
+```bash
+# View deployment history
+argocd app history guestbook
+```
+
+### Step 03 - View Differences
+
+```bash
+# Show differences between Git and live state
+argocd app diff guestbook
+```
+
+### Step 04 - Manual Sync with Options
+
+```bash
+# Sync with prune (removes resources not in Git)
+argocd app sync guestbook --prune
+
+# Sync specific resources
+argocd app sync guestbook --resource Deployment:guestbook-ui
+
+# Dry-run sync
+argocd app sync guestbook --dry-run
+```
+
+---
+
+## Part 06 - Auto-Sync and Self-Healing
+
+### Step 01 - Enable Auto-Sync
+
+- Enable automatic synchronization so `ArgoCD` automatically deploys changes from Git:
+
+```bash
+argocd app set guestbook --sync-policy automated
+```
+
+### Step 02 - Enable Self-Healing
+
+- Enable self-healing to automatically fix out-of-sync resources:
+
+```bash
+argocd app set guestbook --self-heal
+```
+
+### Step 03 - Enable Auto-Prune
+
+- Enable auto-prune to automatically delete resources removed from Git:
+
+```bash
+argocd app set guestbook --auto-prune
+```
+
+### Step 04 - Test Auto-Sync
+
+1. Make a change to your Git repository (e.g., change replica count).
+2. Commit and push the change.
+3. Watch as `ArgoCD` automatically detects and syncs the change:
+
+```bash
+# Watch the application sync status
+watch argocd app get guestbook
+```
+
+### Step 05 - Test Self-Healing
+
+1. Manually modify a deployed resource by running:
+
+```bash
+# Manually scale the deployment
+kubectl scale deployment guestbook-ui --replicas=5
+```
+
+2. Watch as `ArgoCD` detects the drift and automatically restores the desired state:
+
+```bash
+# ArgoCD will revert the replica count to what's in Git
+argocd app get guestbook
+```
+
+---
+
+## Part 07 - Rollback
+
+### Step 01 - View Application History
+
+```bash
+# View all deployment revisions
+argocd app history guestbook
+```
+
+Example output:
+```plaintext
+ID DATE REVISION
+0 2025-11-10 10:15:30 abc123 (HEAD)
+1 2025-11-10 10:20:45 def456
+2 2025-11-10 10:25:30 ghi789
+```
+
+### Step 02 - Rollback to Previous Revision
+
+```bash
+# Rollback to revision ID 1
+argocd app rollback guestbook 1
+```
+
+### Step 03 - Verify Rollback
+
+```bash
+# Verify the application state
+argocd app get guestbook
+
+# Check deployed resources
+kubectl get all -n default
+```
+
+---
+
+## Part 08 - Working with Helm Charts
+
+### Step 01 - Create Helm-Based Application
+
+```bash
+argocd app create nginx-helm \
+ --repo https://charts.bitnami.com/bitnami \
+ --helm-chart nginx \
+ --revision 15.1.0 \
+ --dest-server https://kubernetes.default.svc \
+ --dest-namespace default
+```
+
+### Step 02 - Set Helm Values
+
+```bash
+# Set Helm values
+argocd app set nginx-helm \
+ --helm-set service.type=NodePort \
+ --helm-set replicaCount=3
+```
+
+### Step 03 - Sync Helm Application
+
+```bash
+argocd app sync nginx-helm
+```
+
+### Step 04 - View Helm Parameters
+
+```bash
+# View all Helm parameters
+argocd app get nginx-helm --show-params
+```
+
+---
+
+## Part 09 - Working with Kustomize
+
+### Step 01 - Create Kustomize-Based Application
+
+```bash
+argocd app create kustomize-guestbook \
+ --repo https://github.com/argoproj/argocd-example-apps.git \
+ --path kustomize-guestbook \
+ --dest-server https://kubernetes.default.svc \
+ --dest-namespace default
+```
+
+### Step 02 - Sync Kustomize Application
+
+```bash
+argocd app sync kustomize-guestbook
+```
+
+### Step 03 - Verify Deployment
+
+```bash
+kubectl get all -n default -l app=kustomize-guestbook
+```
+
+---
+
+## Part 10 - Projects and RBAC
+
+### Step 01 - Create a New Project
+
+```bash
+argocd proj create my-project \
+ --description "My demo project" \
+ --src https://github.com/argoproj/argocd-example-apps.git \
+ --dest https://kubernetes.default.svc,default \
+ --dest https://kubernetes.default.svc,my-namespace
+```
+
+### Step 02 - List Projects
+
+```bash
+argocd proj list
+```
+
+### Step 03 - View Project Details
+
+```bash
+argocd proj get my-project
+```
+
+### Step 04 - Create Application in Project
+
+```bash
+argocd app create my-app \
+ --project my-project \
+ --repo https://github.com/argoproj/argocd-example-apps.git \
+ --path guestbook \
+ --dest-server https://kubernetes.default.svc \
+ --dest-namespace default
+```
+
+---
+
+## Part 11 - Multi-Source Applications
+
+### Step 01 - Create Multi-Source Application
+
+- `ArgoCD` supports applications with multiple source repositories:
+
+```yaml
+# Save as multi-source-app.yaml
+apiVersion: argoproj.io/v1alpha1
+kind: Application
+metadata:
+ name: multi-source-app
+ namespace: argocd
+spec:
+ project: default
+ sources:
+ - repoURL: https://github.com/argoproj/argocd-example-apps.git
+ path: guestbook
+ targetRevision: HEAD
+ - repoURL: https://github.com/another-repo/configs.git
+ path: overlays/prod
+ targetRevision: HEAD
+ destination:
+ server: https://kubernetes.default.svc
+ namespace: default
+ syncPolicy:
+ automated:
+ prune: true
+ selfHeal: true
+```
+
+### Step 02 - Apply Multi-Source Application
+
+```bash
+kubectl apply -f multi-source-app.yaml
+```
+
+---
+
+## Part 12 - Sync Windows and Waves
+
+### Step 01 - Configure Sync Windows
+
+- Sync windows allow you to define time periods when syncs are allowed or denied:
+
+```bash
+# Add a sync window to allow syncs only during business hours
+argocd proj windows add my-project \
+ --kind allow \
+ --schedule "0 9 * * 1-5" \
+ --duration 8h \
+ --applications "*"
+```
+
+### Step 02 - Configure Sync Waves
+
+- Use annotations to control the order of resource synchronization:
+
+```yaml
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: database
+ annotations:
+ argocd.argoproj.io/sync-wave: "0" # Deploy first
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: backend
+ annotations:
+ argocd.argoproj.io/sync-wave: "1" # Deploy after database
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: frontend
+ annotations:
+ argocd.argoproj.io/sync-wave: "2" # Deploy last
+```
+
+---
+
+## Part 13 - Health Checks and Hooks
+
+### Step 01 - Custom Health Checks
+
+- Define custom health checks for your resources:
+
+```yaml
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: argocd-cm
+ namespace: argocd
+data:
+ resource.customizations: |
+ cert-manager.io/Certificate:
+ health.lua: |
+ hs = {}
+ if obj.status ~= nil then
+ if obj.status.conditions ~= nil then
+ for i, condition in ipairs(obj.status.conditions) do
+ if condition.type == "Ready" and condition.status == "False" then
+ hs.status = "Degraded"
+ hs.message = condition.message
+ return hs
+ end
+ if condition.type == "Ready" and condition.status == "True" then
+ hs.status = "Healthy"
+ hs.message = condition.message
+ return hs
+ end
+ end
+ end
+ end
+ hs.status = "Progressing"
+ hs.message = "Waiting for certificate"
+ return hs
+```
+
+### Step 02 - Sync Hooks
+
+- Use hooks to execute actions during sync:
+
+```yaml
+apiVersion: batch/v1
+kind: Job
+metadata:
+ name: database-migration
+ annotations:
+ argocd.argoproj.io/hook: PreSync
+ argocd.argoproj.io/hook-delete-policy: HookSucceeded
+spec:
+ template:
+ spec:
+ containers:
+ - name: migration
+ image: myapp/migration:latest
+ command: ["./run-migrations.sh"]
+ restartPolicy: Never
+```
+
+---
+
+## Finalize & Cleanup
+
+### Clean Up Applications
+
+```bash
+# Delete all applications
+argocd app delete guestbook --cascade
+argocd app delete helm-guestbook --cascade
+argocd app delete nginx-helm --cascade
+argocd app delete kustomize-guestbook --cascade
+
+# Or delete via kubectl
+kubectl delete applications -n argocd --all
+```
+
+### Uninstall ArgoCD
+
+```bash
+# Delete ArgoCD installation
+kubectl delete -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
+
+# Delete the namespace
+kubectl delete namespace argocd
+```
+
+---
+
+## Troubleshooting
+
+### ArgoCD Server Not Accessible
+
+- Check if the `ArgoCD` server pod is running:
+
+```bash
+kubectl get pods -n argocd -l app.kubernetes.io/name=argocd-server
+```
+
+- Check the service:
+
+```bash
+kubectl get svc -n argocd argocd-server
+```
+
+### Application Stuck in Progressing State
+
+- Check application details:
+
+```bash
+argocd app get
+kubectl describe application -n argocd
+```
+
+- Check pod logs:
+
+```bash
+kubectl logs -n argocd -l app.kubernetes.io/name=argocd-application-controller
+```
+
+### Sync Fails with Permission Errors
+
+- Verify RBAC settings:
+
+```bash
+argocd proj get
+```
+
+- Check if the destination namespace exists:
+
+```bash
+kubectl get namespace
+```
+
+### Out of Sync Status
+
+- View the differences:
+
+```bash
+argocd app diff
+```
+
+- Force sync:
+
+```bash
+argocd app sync --force
+```
+
+### Repository Connection Issues
+
+- Test repository connectivity:
+
+```bash
+argocd repo add --username --password
+argocd repo list
+```
+
+---
+
+## Best Practices
+
+### GitOps Workflow
+
+1. **Single Source of Truth**: Keep all Kubernetes manifests in Git.
+2. **Branch Strategy**: Use branches for different environments (dev, staging, prod).
+3. **Pull Requests**: Use PRs for all changes with proper reviews.
+4. **Automated Testing**: Validate manifests before merging.
+5. **Rollback Strategy**: Use Git revert for rollbacks.
+
+### Application Organization
+
+1. **Use Projects**: Organize applications by team or environment.
+2. **Naming Conventions**: Use clear, consistent naming.
+3. **Sync Policies**: Choose appropriate sync policies per environment.
+4. **Resource Limits**: Set proper resource limits in manifests.
+5. **Health Checks**: Define custom health checks for complex resources.
+
+### Security
+
+1. **RBAC**: Implement fine-grained access control.
+2. **SSO Integration**: Use SSO for authentication.
+3. **Secret Management**: Use sealed-secrets or external secret managers.
+4. **Network Policies**: Restrict `ArgoCD` network access.
+5. **Audit Logging**: Enable and monitor audit logs.
+
+### Monitoring
+
+1. **Notifications**: Configure notifications for sync failures.
+2. **Metrics**: Monitor `ArgoCD` metrics with `Prometheus`.
+3. **Dashboards**: Create Grafana dashboards for visibility.
+4. **Alerts**: Set up alerts for critical failures.
+5. **Regular Reviews**: Periodically review application health.
+
+---
+
+## Next Steps
+
+- Explore **ApplicationSets** for managing multiple applications.
+- Integrate `ArgoCD` with **CI/CD pipelines**.
+- Set up **notifications** using Slack, email, or webhooks.
+- Implement **Progressive Delivery** with Argo Rollouts.
+- Configure **SSO** integration with your identity provider.
+- Set up **multi-cluster** management.
+- Explore **ArgoCD Image Updater** for automated image updates.
+- Read the [official ArgoCD documentation](https://argo-cd.readthedocs.io/)
+- Join the [ArgoCD community](https://github.com/argoproj/argo-cd)
+
+---
+
+## Additional Resources
+
+- [ArgoCD Official Documentation](https://argo-cd.readthedocs.io/)
+- [ArgoCD GitHub Repository](https://github.com/argoproj/argo-cd)
+- [ArgoCD Best Practices](https://argo-cd.readthedocs.io/en/stable/user-guide/best_practices/)
+- [GitOps Principles](https://www.gitops.tech/)
+- [Argo Rollouts (Progressive Delivery)](https://argoproj.github.io/argo-rollouts/)
+- [ArgoCD ApplicationSet](https://argo-cd.readthedocs.io/en/stable/user-guide/application-set/)
+
diff --git a/Labs/19-CustomScheduler/README.md b/Labs/19-CustomScheduler/README.md
index 298313c..1a476c4 100644
--- a/Labs/19-CustomScheduler/README.md
+++ b/Labs/19-CustomScheduler/README.md
@@ -7,37 +7,55 @@
# Writing custom Scheduler
-### Pre-Requirements
+- `Scheduling` is the process of selecting a node for a pod to run on.
+- In this lab we will write our own pods `scheduler`.
+- It is probably not something that you will ever need to do, but still it's a good practice to understand how scheduling works in K8S and how you can extend it.
+
+
+---
+
+
+## Pre Requirements
+
- K8S cluster - Setting up minikube cluster instruction
+- [**kubectl**](https://kubernetes.io/docs/tasks/tools/) configured to interact with your cluster
+- A `Git repository` (GitHub, GitLab, or Bitbucket) for storing application manifests
+- Basic understanding of Kubernetes resources (Deployments, Services, etc.)
+
+[](https://console.cloud.google.com/cloudshell/editor?cloudshell_git_repo=https://github.com/nirgeier/KubernetesLabs)
-[](https://console.cloud.google.com/cloudshell/editor?cloudshell_git_repo=https://github.com/nirgeier/KubernetesLabs)
-**CTRL + click to open in new window**
+### **CTRL + click to open in new window**
+
---
-# Custom Scheduler
-- Official docs: [Scheduler Configuration](https://kubernetes.io/docs/reference/scheduling/config)
-- In this lab we will write our own pods scheduler.
-- It not something that you will even need to do, but still its a good practice
+## Custom Scheduler
+
+- See further information in the official documentation: [Scheduler Configuration](https://kubernetes.io/docs/reference/scheduling/config)
- To schedule a given pod using a specific scheduler, specify the name of the scheduler in that specification `.spec.schedulerName`.
+
## A bit about scheduler
-- Scheduling happens in a series of **stages* that are exposed through extension points.
+
+- Scheduling happens in a series of **stages** that are exposed through extension points.
- We can define several scheduling Profile. A scheduling Profile allows you to configure the different stages of scheduling in the `kube-scheduler`
+---
+
## Sample `KubeSchedulerConfiguration`
+
```yaml
###
# Sample KubeSchedulerConfiguration
###
#
-# You can configure `kube-scheduler` to run more than one profile.
-# Each profile has an associated scheduler name and can have a different
-# set of plugins configured in its extension points.
+# You can configure `kube-scheduler` to run more than one profile.
+# Each profile has an associated scheduler name and can have a different
+# set of plugins configured in its extension points.
# With the following sample configuration,
# the scheduler will run with two profiles:
# - default plugins
-# - all scoring plugins disabled.
+# - all scoring plugins disabled
apiVersion: kubescheduler.config.k8s.io/v1beta1
kind: KubeSchedulerConfiguration
@@ -52,7 +70,9 @@ profiles:
disabled:
- name: '*'
```
-- Once you have your scheduler code you can use it in your pod scheduler
+
+- Once you have your scheduler code, you can use it in your pod scheduler:
+
```yaml
# In this sample we use deployment but it will apply to any pod
...
@@ -69,8 +89,10 @@ spec:
```
-### Sample bash scheduler.
+### Sample bash scheduler
+
- The "trick" is loop over all the waiting pods and search for the custom scheduler match in `spec.schedulerName`
+
```sh
...
@@ -95,23 +117,3 @@ spec:
fi
...
```
-
-
-
----
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Labs/20-CronJob/README.md b/Labs/20-CronJob/README.md
index a4ed853..2fc1498 100644
--- a/Labs/20-CronJob/README.md
+++ b/Labs/20-CronJob/README.md
@@ -5,14 +5,121 @@
---
-# Writing custom Scheduler
+
+# CronJobs
+
+- In this lab, we will learn how to create and manage `CronJobs` in Kubernetes.
+- A `CronJob` creates `Jobs` on a time-based schedule. It is useful for running periodic and recurring tasks, such as backups or report generation.
+
+---
### Pre-Requirements
- K8S cluster - Setting up minikube cluster instruction
+- [**kubectl**](https://kubernetes.io/docs/tasks/tools/) configured to interact with your cluster
[](https://console.cloud.google.com/cloudshell/editor?cloudshell_git_repo=https://github.com/nirgeier/KubernetesLabs)
**CTRL + click to open in new window**
---
-# CronJobs
+### What is a CronJob?
+- A `CronJob` in Kubernetes runs Jobs on a time-based schedule, similar to Linux cron.
+- Useful for periodic tasks like backups, reports, or cleanup.
+
+---
+
+### Step - 01: Create a CronJob YAML
+- Create a file named `hello-cronjob.yaml` with the following content:
+
+```yaml
+apiVersion: batch/v1
+kind: CronJob
+metadata:
+ name: hello
+ namespace: default
+spec:
+ schedule: "*/1 * * * *" # Every 1 minute
+ jobTemplate:
+ spec:
+ template:
+ spec:
+ containers:
+ - name: hello
+ image: busybox
+ args:
+ - /bin/sh
+ - -c
+ - date; echo Hello from the Kubernetes CronJob!
+ restartPolicy: OnFailure
+```
+
+
+
+### Step - 02: Apply the CronJob
+
+```sh
+kubectl apply -f hello-cronjob.yaml
+```
+
+### Step - 03: Verify CronJob Creation
+
+```sh
+kubectl get cronjob hello
+```
+
+
+### Step - 04: Check CronJob and Jobs
+
+- List CronJobs:
+
+```sh
+kubectl get cronjobs
+```
+
+- List Jobs created by the CronJob:
+
+```sh
+kubectl get jobs
+```
+
+- List Pods created by Jobs:
+
+```sh
+kubectl get pods
+```
+
+
+
+### Step - 05: View Job Output
+
+- Get the name of a pod created by the CronJob, then view its logs:
+
+```sh
+kubectl logs
+```
+
+Example output:
+
+```
+Mon Nov 10 12:00:00 UTC 2025
+Hello from the Kubernetes CronJob!
+```
+
+
+### Step - 06: Clean Up
+
+- Delete the CronJob and its Jobs:
+
+```sh
+kubectl delete cronjob hello
+kubectl delete jobs --all
+```
+
+---
+
+### Questions:
+
+- What happens if the job takes longer than the schedule interval?
+- How would you change the schedule to run every 5 minutes?
+- How can you limit the number of successful or failed jobs to keep?
+
diff --git a/Labs/21-KubeAPI/README.md b/Labs/21-KubeAPI/README.md
index ebfb22c..149f440 100644
--- a/Labs/21-KubeAPI/README.md
+++ b/Labs/21-KubeAPI/README.md
@@ -4,86 +4,93 @@

-### Verify pre-requirements
+---
+# Kube API Access from Pod
-- **`kubectl`** - short for Kubernetes Controller - is the CLI for Kubernetes cluster and is required in order to be able to run the labs.
-- In order to install `kubectl` and if required creating a local cluster, please refer to [Kubernetes - Install Tools](https://kubernetes.io/docs/tasks/tools/)
-
+- In this lab, we will learn how to access the Kubernetes API from within a Pod.
+- We will create a simple Pod that runs a script to query the Kubernetes API server and retrieve information about the cluster.
---
-## Lab Highlights:
- - [01. Build the docker image](#01-Build-the-docker-image)
- - [01.01. The script which will be used for query K8S API](#0101-The-script-which-will-be-used-for-query-K8S-API)
- - [01.02. Build the docker image](#0102-Build-the-docker-image)
- - [02. Deploy the Pod to K8S](#02-Deploy-the-Pod-to-K8S)
- - [02.01. Run kustomization to deploy](#0201-Run-kustomization-to-deploy)
- - [02.02. Query the K8S API](#0202-Query-the-K8S-API)
+
+### Pre-Requirements
+- K8S cluster - Setting up minikube cluster instruction
+- [**kubectl**](https://kubernetes.io/docs/tasks/tools/) configured to interact with your cluster
+
+[](https://console.cloud.google.com/cloudshell/editor?cloudshell_git_repo=https://github.com/nirgeier/KubernetesLabs)
+**CTRL + click to open in new window**
---
-
-### 01. Build the docker image
+### Part 01 - Build the docker image
+
+- In order to demonstrate the API query we will build a custom docker image.
+- It is optional to use the pre-build image and skip this step.
+
+### Step 01 - The script which will be used for query K8S API
+
+- In order to be able to access K8S API from within a pod, we will be using the following script:
-- In order to demonstrate the APi query we will build a custom docker image.
-- You can use the pre-build image and skip this step
-### 01.01. The script which will be used for query K8S API
+```sh
+# `api_query.sh`
+
+#!/bin/sh
+
+#################################
+## Access the internal K8S API ##
+#################################
+# Point to the internal API server hostname
+API_SERVER_URL=https://kubernetes.default.svc
-- In order to be able to access K8S api from within a pod we will be using the following script:
-- `api_query.sh`
+# Path to ServiceAccount token
+# The service account is mapped by the K8S Api server in the pods
+SERVICE_ACCOUNT_FOLDER=/var/run/secrets/kubernetes.io/serviceaccount
- ```sh
- #!/bin/sh
+# Read this Pod's namespace if required
+# NAMESPACE=$(cat ${SERVICE_ACCOUNT_FOLDER}/namespace)
- #################################
- ## Access the internal K8S API ##
- #################################
- # Point to the internal API server hostname
- API_SERVER_URL=https://kubernetes.default.svc
+# Read the ServiceAccount bearer token
+TOKEN=$(cat ${SERVICE_ACCOUNT_FOLDER}/token)
+
+# Reference the internal certificate authority (CA)
+CACERT=${SERVICE_ACCOUNT_FOLDER}/ca.crt
+
+# Explore the API with TOKEN and the Certificate
+curl --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" -X GET ${API_SERVER_URL}/api
+```
- # Path to ServiceAccount token
- # The service account is mapped by the K8S Api server in the pods
- SERVICE_ACCOUNT_FOLDER=/var/run/secrets/kubernetes.io/serviceaccount
+### Step 02 - Build the docker image
- # Read this Pod's namespace if required
- # NAMESPACE=$(cat ${SERVICE_ACCOUNT_FOLDER}/namespace)
+- For the pod image we will use the following Dockerfile:
- # Read the ServiceAccount bearer token
- TOKEN=$(cat ${SERVICE_ACCOUNT_FOLDER}/token)
- # Reference the internal certificate authority (CA)
- CACERT=${SERVICE_ACCOUNT_FOLDER}/ca.crt
- # Explore the API with TOKEN and the Certificate
- curl --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" -X GET ${API_SERVER_URL}/api
- ```
+```Dockerfile
-### 01.02. Build the docker image
+# `Dockerfile`
-- For the pod image we will use the following Dockerfile
-- `Dockerfile`
+FROM alpine
- ```Dockerfile
- FROM alpine
+# Update and install dependencies
+RUN apk add --update nodejs npm curl
- # Update and install dependencies
- RUN apk add --update nodejs npm curl
+# Copy the endpoint script
+COPY api_query.sh .
- # Copy the endpoint script
- COPY api_query.sh .
+# Set the execution bit
+RUN chmod +x api_query.sh .
+```
- # Set the execution bit
- RUN chmod +x api_query.sh .
- ```
+---
-### 02. Deploy the Pod to K8S
+### Part 02 - Deploy the Pod to K8S
-- Once the image is ready we can deploy the image as pod to the cluster
-- The required resources are under the k8s folder
+- Once the image is ready, we can deploy it as a pod to the cluster.
+- The required resources are under the k8s folder.
-### 02.01. Run kustomization to deploy
+### Step 01 - Run kustomization to deploy
- Deploy to the cluster
@@ -95,9 +102,9 @@ kubectl kustomize k8s | kubectl delete -f -
kubectl kustomize k8s | kubectl apply -f -
```
-### 02.02. Query the K8S API
+### Step 02 - Query the K8S API
-- Run the following script to verify that the connection to the API is working
+- Run the following script to verify that the connection to the API is working:
```sh
# Get the deployment pod name
@@ -106,23 +113,3 @@ POD_NAME=$(kubectl get pod -A -l app=monitor-app -o jsonpath="{.items[0].metadat
# Print out the logs to verify that the pods is connected to the API
kubectl exec -it -n codewizard $POD_NAME sh ./api_query.sh
```
-
-
-
----
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Labs/24-HelmOperator/README.md b/Labs/24-HelmOperator/README.md
index 7ed2569..df08e3c 100644
--- a/Labs/24-HelmOperator/README.md
+++ b/Labs/24-HelmOperator/README.md
@@ -1,17 +1,35 @@
-## Helm Operator Tutorial
+## 
+
+# K8S Hands-on
+
+
+
+---
+
+
+
+## Helm Operator
- An in-depth Helm-based operator tutorial.
+- The `Helm Operator` is a Kubernetes operator, allowing one to declaratively manage Helm chart releases.
+
+---
+
- > The Helm Operator is a Kubernetes operator, allowing one to declaratively manage Helm chart releases.
+### Pre-Requirements
+- K8S cluster - Setting up minikube cluster instruction
+- [**kubectl**](https://kubernetes.io/docs/tasks/tools/) configured to interact with your cluster
-### Prerequisites
+[](https://console.cloud.google.com/cloudshell/editor?cloudshell_git_repo=https://github.com/nirgeier/KubernetesLabs)
+**CTRL + click to open in new window**
- Docker
- kubectl
-- operator-sdk [brew install operator-sdk]
-- `cluster-admin` permissions.
+- operator-sdk installed and configured
+- `cluster-admin` permissions
-### Prerequisites - Install `operator-sdk`
+
+### Install `operator-sdk`
```sh
# Grab the ARCH and OS
@@ -28,7 +46,9 @@ curl -LO ${OPERATOR_SDK_DL_URL}/operator-sdk_${OS}_${ARCH}
chmod +x operator-sdk_${OS}_${ARCH} && sudo mv operator-sdk_${OS}_${ARCH} /usr/local/bin/operator-sdk
```
-### Step 01: Create a new project
+---
+
+### Step 01 - Create a new project
- Use the CLI to create a new Helm-based nginx-operator project:
@@ -124,14 +144,17 @@ operator-sdk \
### Step 02 - Customize the operator logic
- For this example the nginx-operator will execute the following reconciliation logic for each Nginx Custom Resource (CR):
- - Create an nginx Deployment if it doesn’t exist
- - Create an nginx Service if it doesn’t exist
- - Create an nginx Ingress if it is enabled and doesn’t exist
- - Ensure that the Deployment, Service, and optional Ingress match the desired configuration (e.g. replica count, image, service type, etc) as specified by the Nginx CR
+ - Create an nginx Deployment, if it doesn’t exist.
+ - Create an nginx Service, if it doesn’t exist.
+ - Create an nginx Ingress, if it is enabled and doesn’t exist.
+ - Update the Deployment, Service, and Ingress, if they already exist but don’t match the desired configuration as specified by the Nginx CR.
+ - Ensure that the Deployment, Service, and optional Ingress all match the desired configuration (e.g. replica count, image, service type, etc) as specified by the Nginx CR.
+
+
#### Watch the Nginx CR
-- By default, the nginx-operator watches Nginx resource events as shown in `watches.yaml` and executes Helm releases using the specified chart:
+- By default, the Nginx-operator watches Nginx resource events as shown in `watches.yaml` and executes Helm releases using the specified chart:
```yaml
# Use the 'create api' subcommand to add watches to this file.
@@ -141,22 +164,26 @@ operator-sdk \
chart: helm-charts/nginx
```
+
+
### Reviewing the Nginx Helm Chart
- When a Helm operator project is created, the SDK creates an example Helm chart that contains a set of templates for a simple Nginx release.
-- For this example, we have templates for deployment, service, and ingress resources, along with a NOTES.txt template, which Helm chart developers use to convey helpful information about a release.
+- For this example, we have templates for deployment, service, and ingress resources, along with a `NOTES.txt` template, which Helm chart developers use to convey helpful information about a release.
+
+
### Understanding the Nginx CR spec
-- Helm uses a concept called values to provide customizations to a Helm chart’s defaults, which are defined in the Helm chart’s values.yaml file.
+- Helm uses a concept called `values` to provide customizations to a Helm chart’s defaults, which are defined in the Helm chart’s `values.yaml` file.
- Overriding these defaults is as simple as setting the desired values in the CR spec.
-- Let’s use the number of replicas as an example.
+- Let’s use the number of replicas value as an example.
-- First, inspecting `helm-charts/nginx/values.yaml`, we see that the chart has a value called `replicaCount` and it is set to `1` by default.
+- First, inspecting `helm-charts/nginx/values.yaml`, we can see that the chart has a value called `replicaCount` and it is set to `1` by default.
-- Lets update the value to 3 `replicaCount: 3`.
+- Let’s update the value to 3 - `replicaCount: 3`.
```yaml
# Update `config/samples/demo_v1alpha1_nginx.yaml` to look like the following:
@@ -169,19 +196,20 @@ operator-sdk \
replicaCount: 3 # <------- Adding our replicas count
```
-- Similarly, we see that the default service port is set to `80`, but we would like to use `8888`, so we’ll again update config/samples/demo_v1alpha1_nginx.yaml by adding the service port override.
+- Similarly, we see that the default service port is set to `80`, but we would like to use `8888`, so we will again update config/samples/demo_v1alpha1_nginx.yaml by adding the service port override.
- ```yaml
- # Update `config/samples/demo_v1alpha1_nginx.yaml` to look like the following:
- apiVersion: demo.codewizard.co.il/v1alpha1
- kind: Nginx
- metadata:
- name: nginx-sample
- spec:
- #... (Around line 36)
- service:
- port: 8888 # <------- Updating our service port
- ```
+
+```yaml
+# Update `config/samples/demo_v1alpha1_nginx.yaml` to look like the following:
+apiVersion: demo.codewizard.co.il/v1alpha1
+kind: Nginx
+metadata:
+ name: nginx-sample
+spec:
+ #... (Around line 36)
+ service:
+ port: 8888 # <------- Updating our service port
+```
### Step 03 - Build the operator’s image
@@ -198,10 +226,11 @@ IMG ?= controller:latest
IMG ?= nirgeier/helm_operator:latest
```
-- Now lets build and push the image
- ```sh
- make docker-build docker-push
- ```
+- Now let's build and push the image:
+
+```sh
+make docker-build docker-push
+```
### Step 04 - Deploy the operator to the cluster
@@ -212,7 +241,6 @@ make deploy
kubectl get deployment -n nginx-operator-system
```
----
### Step 05 - Create the custom Nginx
@@ -247,14 +275,14 @@ replicaCount: 5
38 # type: ClusterIP
```
-- Apply the changes
+- Apply the changes:
```sh
# Apply the changes
kubectl apply -f config/samples/demo_v1alpha1_nginx.yaml
```
-- Check to see that the operator is working as expected
+- Check to see that the operator is working as expected:
```sh
# Ensure that the nginx-operator still running
@@ -272,14 +300,14 @@ kubectl get svc | grep nginx-sample
### Step07 - Logging / Debugging
-- We can view the operator logs using the following command:
+- We can view the operator's logs using the following command:
```sh
# View the operator logs
kubectl logs deployment.apps/nginx-operator-controller-manager -n nginx-operator-system -c manager
```
-- Review the CR status and events
+- Review the CR status and events:
```sh
kubectl describe nginxes.demo.codewizard.co.il
diff --git a/Labs/index.md b/Labs/index.md
new file mode 100644
index 0000000..8987674
--- /dev/null
+++ b/Labs/index.md
@@ -0,0 +1,120 @@
+# Kubernetes Labs
+
+## 📋 Lab Overview
+
+Welcome to the hands-on Kubernetes labs! This comprehensive series of labs will guide you through essential Kubernetes concepts and advanced topics.
+
+## 🗂️ Available Labs
+
+### Foundation Labs
+
+| Lab | Topic | Description |
+|-----|-------|-------------|
+| [00](00-VerifyCluster/README.md) | Verify Cluster | Ensure your Kubernetes cluster is properly configured |
+| [01](01-Namespace/README.md) | Namespace | Learn to organize resources with namespaces |
+| [02](02-Deployments-Imperative/README.md) | Deployments (Imperative) | Create deployments using kubectl commands |
+| [03](03-Deployments-Declarative/README.md) | Deployments (Declarative) | Create deployments using YAML manifests |
+| [04](04-Rollout/README.md) | Rollout | Manage deployment updates and rollbacks |
+| [05](05-Services/README.md) | Services | Expose applications with Kubernetes services |
+
+### Storage & StatefulSets
+
+| Lab | Topic | Description |
+|-----|-------|-------------|
+| [06](06-DataStore/README.md) | DataStore | Work with persistent storage in Kubernetes |
+| [09](09-StatefulSet/README.md) | StatefulSet | Deploy stateful applications |
+| [12](12-Wordpress-MySQL-PVC/README.md) | WordPress MySQL PVC | Complete stateful application with persistent storage |
+
+### Networking & Ingress
+
+| Lab | Topic | Description |
+|-----|-------|-------------|
+| [07](07-nginx-Ingress/README.md) | Nginx Ingress | Configure ingress controllers for external access |
+| [10](10-Istio/README.md) | Istio | Implement service mesh for microservices |
+
+### Configuration Management
+
+| Lab | Topic | Description |
+|-----|-------|-------------|
+| [08](08-Kustomization/README.md) | Kustomization | Manage configurations with Kustomize |
+| [13](13-HelmChart/README.md) | Helm Chart | Package and deploy applications with Helm |
+
+### GitOps & CI/CD
+
+| Lab | Topic | Description |
+|-----|-------|-------------|
+| [18](18-ArgoCD/README.md) | ArgoCD | Implement GitOps with ArgoCD |
+
+### Observability
+
+| Lab | Topic | Description |
+|-----|-------|-------------|
+| [14](14-Logging/README.md) | Logging | Centralized logging with Fluentd |
+| [15](15-Prometheus-Grafana/README.md) | Prometheus & Grafana | Monitoring and visualization |
+| [23](23-MetricServer/README.md) | Metric Server | Resource metrics collection |
+
+### Advanced Topics
+
+| Lab | Topic | Description |
+|-----|-------|-------------|
+| [11](11-CRD-Custom-Resource-Definition/README.md) | Custom Resource Definition | Extend Kubernetes API with CRDs |
+| [16](16-Affinity-Taint-Tolleration/README.md) | Affinity, Taint & Toleration | Control pod scheduling |
+| [17](17-PodDisruptionBudgets-PDB/README.md) | Pod Disruption Budgets | Ensure availability during disruptions |
+| [19](19-CustomScheduler/README.md) | Custom Scheduler | Build custom scheduling logic |
+| [20](20-CronJob/README.md) | CronJob | Schedule recurring tasks |
+| [21](21-Auditing/README.md) | Auditing | Track cluster activities |
+| [21](21-KubeAPI/README.md) | Kube API | Work with Kubernetes API |
+| [24](24-HelmOperator/README.md) | Helm Operator | Manage Helm releases with operators |
+| [25](25-kubebuilder/README.md) | Kubebuilder | Build Kubernetes operators |
+
+### Tools & Utilities
+
+| Lab | Topic | Description |
+|-----|-------|-------------|
+| [22](22-Rancher/README.md) | Rancher | Multi-cluster management platform |
+| [26](26-k9s/README.md) | k9s | Terminal-based Kubernetes UI |
+| [27](27-krew/README.md) | Krew | kubectl plugin manager |
+| [28](28-kubeapps/README.md) | Kubeapps | Application dashboard for Kubernetes |
+| [29](29-kubeadm/README.md) | Kubeadm | Bootstrap Kubernetes clusters |
+| [30](30-k9s/README.md) | k9s (Advanced) | Advanced k9s usage |
+
+## 🎯 Learning Path
+
+### Beginner Track
+Start here if you're new to Kubernetes:
+1. Lab 00: Verify Cluster
+2. Lab 01: Namespace
+3. Lab 02: Deployments (Imperative)
+4. Lab 03: Deployments (Declarative)
+5. Lab 05: Services
+
+### Intermediate Track
+For those with basic Kubernetes knowledge:
+1. Lab 04: Rollout
+2. Lab 06: DataStore
+3. Lab 07: Nginx Ingress
+4. Lab 08: Kustomization
+5. Lab 13: Helm Chart
+
+### Advanced Track
+For experienced Kubernetes users:
+1. Lab 10: Istio
+2. Lab 11: Custom Resource Definition
+3. Lab 18: ArgoCD
+4. Lab 19: Custom Scheduler
+5. Lab 25: Kubebuilder
+
+## 💡 Tips for Success
+
+- **Take your time**: Don't rush through the labs
+- **Practice regularly**: Repetition builds muscle memory
+- **Experiment**: Try variations of the examples
+- **Read the docs**: Kubernetes documentation is excellent
+- **Join the community**: Engage with other learners
+
+## 🚀 Get Started
+
+Ready to begin? Click on any lab on the left menu, or start with [Lab 00: Verify Cluster](00-VerifyCluster/README.md)!
+
+
+
diff --git a/Labs/welcome.md b/Labs/welcome.md
new file mode 100644
index 0000000..7cc6809
--- /dev/null
+++ b/Labs/welcome.md
@@ -0,0 +1,76 @@
+# Welcome to Kubernetes Labs
+
+
+
+
+Welcome to the **Kubernetes Labs** repository! This is a comprehensive collection of hands-on labs designed to help you learn and master Kubernetes concepts, from basic deployments to advanced topics like Istio, ArgoCD and custom schedulers.
+
+## 📚 What You'll Learn
+
+This lab series covers a wide range of `Kubernetes` topics:
+
+- **Basics**: Namespaces, Deployments, Services and Rollouts
+- **Storage**: DataStores, Persistent Volume Claims and StatefulSets
+- **Networking**: Ingress Controllers and Service Mesh (Istio)
+- **Configuration Management**: Kustomization and Helm Charts
+- **GitOps**: ArgoCD for continuous deployment
+- **Observability**: Logging, Prometheus and Grafana
+- **Advanced Topics**: Custom Resource Definitions (CRDs), Custom Schedulers and Pod Disruption Budgets
+- **Tools**: k9s, Krew, Kubeapps, Kubeadm and Rancher
+
+## 🎯 Who Is This For?
+
+These labs are designed for:
+
+- **Beginners** wanting to get started with Kubernetes
+- **Intermediate users** looking to deepen their knowledge
+- **Advanced practitioners** seeking to master complex Kubernetes patterns
+- **DevOps Engineers** preparing for Kubernetes certifications (CKA, CKAD, CKS)
+
+## 🛠️ Prerequisites
+
+Before starting these labs, you should have:
+
+- Basic understanding of containerization (Docker)
+- Command-line (CLI) familiarity
+- A Kubernetes cluster (Minikube, Kind, or cloud-based cluster)
+- kubectl installed and configured
+
+## 📖 How to Use This Repository
+
+1. **Start with the Basics**: Begin with Lab 00 (Verify Cluster) to ensure your environment is set up correctly
+2. **Progress Sequentially**: The labs are numbered in a logical progression
+3. **Hands-On Practice**: Each lab includes practical exercises and examples
+4. **Experiment**: Don't be afraid to modify the examples and see what happens
+
+## 🎓 Lab Structure
+
+Each lab includes:
+
+- **Objectives**: What you'll learn in the lab
+- **Step-by-step instructions**: Detailed walkthrough of concepts
+- **Example YAML files**: Ready-to-use Kubernetes manifests
+- **Verification steps**: How to confirm everything is working
+- **Challenges**: Optional exercises to test your understanding
+
+## 🚦 Getting Started
+
+Ready to begin? Head over to the [Labs](index.md) section and start with:
+
+1. [00 Verify Cluster](00-VerifyCluster/README.md) - Verify your Kubernetes cluster is ready
+2. [01 Namespace](01-Namespace/README.md) - Learn about Kubernetes namespaces
+3. [02 Deployments Imperative](02-Deployments-Imperative/README.md) - Create deployments using kubectl
+
+## 🤝 Contributing
+
+Contributions are welcome! If you find issues or have suggestions for improvements, please feel free to open an issue or submit a pull request.
+
+## 📬 Contact
+
+For questions or feedback, please reach out through the social links located above the header of this page.
+
+---
+
+**Happy Learning! 🎉**
+
+Let's dive into the world of Kubernetes together!
diff --git a/codewizard-helm-demo/.helmignore b/codewizard-helm-demo/.helmignore
new file mode 100644
index 0000000..0e8a0eb
--- /dev/null
+++ b/codewizard-helm-demo/.helmignore
@@ -0,0 +1,23 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*.orig
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
+.vscode/
diff --git a/codewizard-helm-demo/Chart.yaml b/codewizard-helm-demo/Chart.yaml
new file mode 100644
index 0000000..cae3fe3
--- /dev/null
+++ b/codewizard-helm-demo/Chart.yaml
@@ -0,0 +1,24 @@
+apiVersion: v2
+name: codewizard-helm-demo
+description: A Helm chart for Kubernetes
+
+# A chart can be either an 'application' or a 'library' chart.
+#
+# Application charts are a collection of templates that can be packaged into versioned archives
+# to be deployed.
+#
+# Library charts provide useful utilities or functions for the chart developer. They're included as
+# a dependency of application charts to inject those utilities and functions into the rendering
+# pipeline. Library charts do not define any templates and therefore cannot be deployed.
+type: application
+
+# This is the chart version. This version number should be incremented each time you make changes
+# to the chart and its templates, including the app version.
+# Versions are expected to follow Semantic Versioning (https://semver.org/)
+version: 0.1.0
+
+# This is the version number of the application being deployed. This version number should be
+# incremented each time you make changes to the application. Versions are not expected to
+# follow Semantic Versioning. They should reflect the version the application is using.
+# It is recommended to use it with quotes.
+appVersion: "1.16.0"
diff --git a/codewizard-helm-demo/templates/NOTES.txt b/codewizard-helm-demo/templates/NOTES.txt
new file mode 100644
index 0000000..1dc190d
--- /dev/null
+++ b/codewizard-helm-demo/templates/NOTES.txt
@@ -0,0 +1,35 @@
+1. Get the application URL by running these commands:
+{{- if .Values.httpRoute.enabled }}
+{{- if .Values.httpRoute.hostnames }}
+ export APP_HOSTNAME={{ .Values.httpRoute.hostnames | first }}
+{{- else }}
+ export APP_HOSTNAME=$(kubectl get --namespace {{(first .Values.httpRoute.parentRefs).namespace | default .Release.Namespace }} gateway/{{ (first .Values.httpRoute.parentRefs).name }} -o jsonpath="{.spec.listeners[0].hostname}")
+ {{- end }}
+{{- if and .Values.httpRoute.rules (first .Values.httpRoute.rules).matches (first (first .Values.httpRoute.rules).matches).path.value }}
+ echo "Visit http://$APP_HOSTNAME{{ (first (first .Values.httpRoute.rules).matches).path.value }} to use your application"
+
+ NOTE: Your HTTPRoute depends on the listener configuration of your gateway and your HTTPRoute rules.
+ The rules can be set for path, method, header and query parameters.
+ You can check the gateway configuration with 'kubectl get --namespace {{(first .Values.httpRoute.parentRefs).namespace | default .Release.Namespace }} gateway/{{ (first .Values.httpRoute.parentRefs).name }} -o yaml'
+{{- end }}
+{{- else if .Values.ingress.enabled }}
+{{- range $host := .Values.ingress.hosts }}
+ {{- range .paths }}
+ http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }}
+ {{- end }}
+{{- end }}
+{{- else if contains "NodePort" .Values.service.type }}
+ export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "codewizard-helm-demo.fullname" . }})
+ export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
+ echo http://$NODE_IP:$NODE_PORT
+{{- else if contains "LoadBalancer" .Values.service.type }}
+ NOTE: It may take a few minutes for the LoadBalancer IP to be available.
+ You can watch its status by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "codewizard-helm-demo.fullname" . }}'
+ export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "codewizard-helm-demo.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
+ echo http://$SERVICE_IP:{{ .Values.service.port }}
+{{- else if contains "ClusterIP" .Values.service.type }}
+ export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "codewizard-helm-demo.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
+ export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
+ echo "Visit http://127.0.0.1:8080 to use your application"
+ kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
+{{- end }}
diff --git a/codewizard-helm-demo/templates/_helpers.tpl b/codewizard-helm-demo/templates/_helpers.tpl
new file mode 100644
index 0000000..4b4fabb
--- /dev/null
+++ b/codewizard-helm-demo/templates/_helpers.tpl
@@ -0,0 +1,62 @@
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "codewizard-helm-demo.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+If release name contains chart name it will be used as a full name.
+*/}}
+{{- define "codewizard-helm-demo.fullname" -}}
+{{- if .Values.fullnameOverride }}
+{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- $name := default .Chart.Name .Values.nameOverride }}
+{{- if contains $name .Release.Name }}
+{{- .Release.Name | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
+{{- end }}
+{{- end }}
+{{- end }}
+
+{{/*
+Create chart name and version as used by the chart label.
+*/}}
+{{- define "codewizard-helm-demo.chart" -}}
+{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Common labels
+*/}}
+{{- define "codewizard-helm-demo.labels" -}}
+helm.sh/chart: {{ include "codewizard-helm-demo.chart" . }}
+{{ include "codewizard-helm-demo.selectorLabels" . }}
+{{- if .Chart.AppVersion }}
+app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
+{{- end }}
+app.kubernetes.io/managed-by: {{ .Release.Service }}
+{{- end }}
+
+{{/*
+Selector labels
+*/}}
+{{- define "codewizard-helm-demo.selectorLabels" -}}
+app.kubernetes.io/name: {{ include "codewizard-helm-demo.name" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+{{- end }}
+
+{{/*
+Create the name of the service account to use
+*/}}
+{{- define "codewizard-helm-demo.serviceAccountName" -}}
+{{- if .Values.serviceAccount.create }}
+{{- default (include "codewizard-helm-demo.fullname" .) .Values.serviceAccount.name }}
+{{- else }}
+{{- default "default" .Values.serviceAccount.name }}
+{{- end }}
+{{- end }}
diff --git a/codewizard-helm-demo/templates/deployment.yaml b/codewizard-helm-demo/templates/deployment.yaml
new file mode 100644
index 0000000..bb59fbe
--- /dev/null
+++ b/codewizard-helm-demo/templates/deployment.yaml
@@ -0,0 +1,78 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: {{ include "codewizard-helm-demo.fullname" . }}
+ labels:
+ {{- include "codewizard-helm-demo.labels" . | nindent 4 }}
+spec:
+ {{- if not .Values.autoscaling.enabled }}
+ replicas: {{ .Values.replicaCount }}
+ {{- end }}
+ selector:
+ matchLabels:
+ {{- include "codewizard-helm-demo.selectorLabels" . | nindent 6 }}
+ template:
+ metadata:
+ {{- with .Values.podAnnotations }}
+ annotations:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ labels:
+ {{- include "codewizard-helm-demo.labels" . | nindent 8 }}
+ {{- with .Values.podLabels }}
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ spec:
+ {{- with .Values.imagePullSecrets }}
+ imagePullSecrets:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ serviceAccountName: {{ include "codewizard-helm-demo.serviceAccountName" . }}
+ {{- with .Values.podSecurityContext }}
+ securityContext:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ containers:
+ - name: {{ .Chart.Name }}
+ {{- with .Values.securityContext }}
+ securityContext:
+ {{- toYaml . | nindent 12 }}
+ {{- end }}
+ image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
+ imagePullPolicy: {{ .Values.image.pullPolicy }}
+ ports:
+ - name: http
+ containerPort: {{ .Values.service.port }}
+ protocol: TCP
+ {{- with .Values.livenessProbe }}
+ livenessProbe:
+ {{- toYaml . | nindent 12 }}
+ {{- end }}
+ {{- with .Values.readinessProbe }}
+ readinessProbe:
+ {{- toYaml . | nindent 12 }}
+ {{- end }}
+ {{- with .Values.resources }}
+ resources:
+ {{- toYaml . | nindent 12 }}
+ {{- end }}
+ {{- with .Values.volumeMounts }}
+ volumeMounts:
+ {{- toYaml . | nindent 12 }}
+ {{- end }}
+ {{- with .Values.volumes }}
+ volumes:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ {{- with .Values.nodeSelector }}
+ nodeSelector:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ {{- with .Values.affinity }}
+ affinity:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ {{- with .Values.tolerations }}
+ tolerations:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
diff --git a/codewizard-helm-demo/templates/hpa.yaml b/codewizard-helm-demo/templates/hpa.yaml
new file mode 100644
index 0000000..fe5e47b
--- /dev/null
+++ b/codewizard-helm-demo/templates/hpa.yaml
@@ -0,0 +1,32 @@
+{{- if .Values.autoscaling.enabled }}
+apiVersion: autoscaling/v2
+kind: HorizontalPodAutoscaler
+metadata:
+ name: {{ include "codewizard-helm-demo.fullname" . }}
+ labels:
+ {{- include "codewizard-helm-demo.labels" . | nindent 4 }}
+spec:
+ scaleTargetRef:
+ apiVersion: apps/v1
+ kind: Deployment
+ name: {{ include "codewizard-helm-demo.fullname" . }}
+ minReplicas: {{ .Values.autoscaling.minReplicas }}
+ maxReplicas: {{ .Values.autoscaling.maxReplicas }}
+ metrics:
+ {{- if .Values.autoscaling.targetCPUUtilizationPercentage }}
+ - type: Resource
+ resource:
+ name: cpu
+ target:
+ type: Utilization
+ averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
+ {{- end }}
+ {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }}
+ - type: Resource
+ resource:
+ name: memory
+ target:
+ type: Utilization
+ averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }}
+ {{- end }}
+{{- end }}
diff --git a/codewizard-helm-demo/templates/httproute.yaml b/codewizard-helm-demo/templates/httproute.yaml
new file mode 100644
index 0000000..f304111
--- /dev/null
+++ b/codewizard-helm-demo/templates/httproute.yaml
@@ -0,0 +1,38 @@
+{{- if .Values.httpRoute.enabled -}}
+{{- $fullName := include "codewizard-helm-demo.fullname" . -}}
+{{- $svcPort := .Values.service.port -}}
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ name: {{ $fullName }}
+ labels:
+ {{- include "codewizard-helm-demo.labels" . | nindent 4 }}
+ {{- with .Values.httpRoute.annotations }}
+ annotations:
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+spec:
+ parentRefs:
+ {{- with .Values.httpRoute.parentRefs }}
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+ {{- with .Values.httpRoute.hostnames }}
+ hostnames:
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+ rules:
+ {{- range .Values.httpRoute.rules }}
+ {{- with .matches }}
+ - matches:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ {{- with .filters }}
+ filters:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ backendRefs:
+ - name: {{ $fullName }}
+ port: {{ $svcPort }}
+ weight: 1
+ {{- end }}
+{{- end }}
diff --git a/codewizard-helm-demo/templates/ingress.yaml b/codewizard-helm-demo/templates/ingress.yaml
new file mode 100644
index 0000000..93d795e
--- /dev/null
+++ b/codewizard-helm-demo/templates/ingress.yaml
@@ -0,0 +1,43 @@
+{{- if .Values.ingress.enabled -}}
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ name: {{ include "codewizard-helm-demo.fullname" . }}
+ labels:
+ {{- include "codewizard-helm-demo.labels" . | nindent 4 }}
+ {{- with .Values.ingress.annotations }}
+ annotations:
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+spec:
+ {{- with .Values.ingress.className }}
+ ingressClassName: {{ . }}
+ {{- end }}
+ {{- if .Values.ingress.tls }}
+ tls:
+ {{- range .Values.ingress.tls }}
+ - hosts:
+ {{- range .hosts }}
+ - {{ . | quote }}
+ {{- end }}
+ secretName: {{ .secretName }}
+ {{- end }}
+ {{- end }}
+ rules:
+ {{- range .Values.ingress.hosts }}
+ - host: {{ .host | quote }}
+ http:
+ paths:
+ {{- range .paths }}
+ - path: {{ .path }}
+ {{- with .pathType }}
+ pathType: {{ . }}
+ {{- end }}
+ backend:
+ service:
+ name: {{ include "codewizard-helm-demo.fullname" $ }}
+ port:
+ number: {{ $.Values.service.port }}
+ {{- end }}
+ {{- end }}
+{{- end }}
diff --git a/codewizard-helm-demo/templates/service.yaml b/codewizard-helm-demo/templates/service.yaml
new file mode 100644
index 0000000..93e94da
--- /dev/null
+++ b/codewizard-helm-demo/templates/service.yaml
@@ -0,0 +1,15 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: {{ include "codewizard-helm-demo.fullname" . }}
+ labels:
+ {{- include "codewizard-helm-demo.labels" . | nindent 4 }}
+spec:
+ type: {{ .Values.service.type }}
+ ports:
+ - port: {{ .Values.service.port }}
+ targetPort: http
+ protocol: TCP
+ name: http
+ selector:
+ {{- include "codewizard-helm-demo.selectorLabels" . | nindent 4 }}
diff --git a/codewizard-helm-demo/templates/serviceaccount.yaml b/codewizard-helm-demo/templates/serviceaccount.yaml
new file mode 100644
index 0000000..7b99bcc
--- /dev/null
+++ b/codewizard-helm-demo/templates/serviceaccount.yaml
@@ -0,0 +1,13 @@
+{{- if .Values.serviceAccount.create -}}
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: {{ include "codewizard-helm-demo.serviceAccountName" . }}
+ labels:
+ {{- include "codewizard-helm-demo.labels" . | nindent 4 }}
+ {{- with .Values.serviceAccount.annotations }}
+ annotations:
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+automountServiceAccountToken: {{ .Values.serviceAccount.automount }}
+{{- end }}
diff --git a/codewizard-helm-demo/templates/tests/test-connection.yaml b/codewizard-helm-demo/templates/tests/test-connection.yaml
new file mode 100644
index 0000000..d3863ec
--- /dev/null
+++ b/codewizard-helm-demo/templates/tests/test-connection.yaml
@@ -0,0 +1,15 @@
+apiVersion: v1
+kind: Pod
+metadata:
+ name: "{{ include "codewizard-helm-demo.fullname" . }}-test-connection"
+ labels:
+ {{- include "codewizard-helm-demo.labels" . | nindent 4 }}
+ annotations:
+ "helm.sh/hook": test
+spec:
+ containers:
+ - name: wget
+ image: busybox
+ command: ['wget']
+ args: ['{{ include "codewizard-helm-demo.fullname" . }}:{{ .Values.service.port }}']
+ restartPolicy: Never
diff --git a/codewizard-helm-demo/values.yaml b/codewizard-helm-demo/values.yaml
new file mode 100644
index 0000000..bc4eead
--- /dev/null
+++ b/codewizard-helm-demo/values.yaml
@@ -0,0 +1,161 @@
+# Default values for codewizard-helm-demo.
+# This is a YAML-formatted file.
+# Declare variables to be passed into your templates.
+
+# This will set the replicaset count more information can be found here: https://kubernetes.io/docs/concepts/workloads/controllers/replicaset/
+replicaCount: 1
+
+# This sets the container image more information can be found here: https://kubernetes.io/docs/concepts/containers/images/
+image:
+ repository: nginx
+ # This sets the pull policy for images.
+ pullPolicy: IfNotPresent
+ # Overrides the image tag whose default is the chart appVersion.
+ tag: ""
+
+# This is for the secrets for pulling an image from a private repository more information can be found here: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/
+imagePullSecrets: []
+# This is to override the chart name.
+nameOverride: ""
+fullnameOverride: ""
+
+# This section builds out the service account more information can be found here: https://kubernetes.io/docs/concepts/security/service-accounts/
+serviceAccount:
+ # Specifies whether a service account should be created
+ create: true
+ # Automatically mount a ServiceAccount's API credentials?
+ automount: true
+ # Annotations to add to the service account
+ annotations: {}
+ # The name of the service account to use.
+ # If not set and create is true, a name is generated using the fullname template
+ name: ""
+
+# This is for setting Kubernetes Annotations to a Pod.
+# For more information checkout: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/
+podAnnotations: {}
+# This is for setting Kubernetes Labels to a Pod.
+# For more information checkout: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/
+podLabels: {}
+
+podSecurityContext: {}
+ # fsGroup: 2000
+
+securityContext: {}
+ # capabilities:
+ # drop:
+ # - ALL
+ # readOnlyRootFilesystem: true
+ # runAsNonRoot: true
+ # runAsUser: 1000
+
+# This is for setting up a service more information can be found here: https://kubernetes.io/docs/concepts/services-networking/service/
+service:
+ # This sets the service type more information can be found here: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types
+ type: ClusterIP
+ # This sets the ports more information can be found here: https://kubernetes.io/docs/concepts/services-networking/service/#field-spec-ports
+ port: 80
+
+# This block is for setting up the ingress for more information can be found here: https://kubernetes.io/docs/concepts/services-networking/ingress/
+ingress:
+ enabled: false
+ className: ""
+ annotations: {}
+ # kubernetes.io/ingress.class: nginx
+ # kubernetes.io/tls-acme: "true"
+ hosts:
+ - host: chart-example.local
+ paths:
+ - path: /
+ pathType: ImplementationSpecific
+ tls: []
+ # - secretName: chart-example-tls
+ # hosts:
+ # - chart-example.local
+
+# -- Expose the service via gateway-api HTTPRoute
+# Requires Gateway API resources and suitable controller installed within the cluster
+# (see: https://gateway-api.sigs.k8s.io/guides/)
+httpRoute:
+ # HTTPRoute enabled.
+ enabled: false
+ # HTTPRoute annotations.
+ annotations: {}
+ # Which Gateways this Route is attached to.
+ parentRefs:
+ - name: gateway
+ sectionName: http
+ # namespace: default
+ # Hostnames matching HTTP header.
+ hostnames:
+ - chart-example.local
+ # List of rules and filters applied.
+ rules:
+ - matches:
+ - path:
+ type: PathPrefix
+ value: /headers
+ # filters:
+ # - type: RequestHeaderModifier
+ # requestHeaderModifier:
+ # set:
+ # - name: My-Overwrite-Header
+ # value: this-is-the-only-value
+ # remove:
+ # - User-Agent
+ # - matches:
+ # - path:
+ # type: PathPrefix
+ # value: /echo
+ # headers:
+ # - name: version
+ # value: v2
+
+resources: {}
+ # We usually recommend not to specify default resources and to leave this as a conscious
+ # choice for the user. This also increases chances charts run on environments with little
+ # resources, such as Minikube. If you do want to specify resources, uncomment the following
+ # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
+ # limits:
+ # cpu: 100m
+ # memory: 128Mi
+ # requests:
+ # cpu: 100m
+ # memory: 128Mi
+
+# This is to setup the liveness and readiness probes more information can be found here: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/
+livenessProbe:
+ httpGet:
+ path: /
+ port: http
+readinessProbe:
+ httpGet:
+ path: /
+ port: http
+
+# This section is for setting up autoscaling more information can be found here: https://kubernetes.io/docs/concepts/workloads/autoscaling/
+autoscaling:
+ enabled: false
+ minReplicas: 1
+ maxReplicas: 100
+ targetCPUUtilizationPercentage: 80
+ # targetMemoryUtilizationPercentage: 80
+
+# Additional volumes on the output Deployment definition.
+volumes: []
+# - name: foo
+# secret:
+# secretName: mysecret
+# optional: false
+
+# Additional volumeMounts on the output Deployment definition.
+volumeMounts: []
+# - name: foo
+# mountPath: "/etc/foo"
+# readOnly: true
+
+nodeSelector: {}
+
+tolerations: []
+
+affinity: {}
diff --git a/mkdocs-site/00-VerifyCluster/index.html b/mkdocs-site/00-VerifyCluster/index.html
new file mode 100644
index 0000000..f6705af
--- /dev/null
+++ b/mkdocs-site/00-VerifyCluster/index.html
@@ -0,0 +1,126 @@
+ 00 Verify Cluster - KubernetesLabs
# In this sample we have minikube pod running
+$kubectlgetnodes
+NAMESTATUSROLESAGEVERSION
+minikubeReadycontrol-plane,master3m9sv1.20.0
+
\ No newline at end of file
diff --git a/mkdocs-site/01-Namespace/index.html b/mkdocs-site/01-Namespace/index.html
new file mode 100644
index 0000000..374c955
--- /dev/null
+++ b/mkdocs-site/01-Namespace/index.html
@@ -0,0 +1,82 @@
+ 01 Namespace - KubernetesLabs
# In this sample `codewizard` is the desired namespace
+$kubectlcreatenamespacecodewizard
+namespace/codewizardcreated
+
+### !!! Try to create the following namespace (with _ & -), and see what happens:
+$kubectlcreatenamespacemy_namespace-
+
The above deployment contains a container named, multitool.
In order for us to be able to access this multitool container, we need to create a resource of type Service which will “open” the server for incoming traffic.
# "Expose" the desired port for incoming traffic
+# This command is equivalent to declare a `kind: Service` im YAML file
+
+$kubectlexposedeployment-ncodewizardmultitool--port80--typeNodePort
+service/multitoolexposed
+
Verify that the service have been created by running:
$kubectlgetservice-ncodewizard
+
+# The output should be something like
+NAMETYPECLUSTER-IPEXTERNAL-IPPORT(S)AGE
+service/multitoolNodePort10.102.73.248<none>80:31418/TCP3s
+
Find the port & the IP which was assigned to our pod by the cluster.¶
Grab the port from the previous output.
Port: In the above sample its 31418 [80:31418/TCP]
IP: we will need to grab the cluster ip using kubectl cluster-info
# get the IP
+$kubectlcluster-info
+
+# You should get output similar to this one
+Kubernetescontrolplaneisrunningathttps://192.168.49.2:8443
+KubeDNSisrunningathttps://192.168.49.2:8443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
+
+# Programmatically get the port and the IP
+CLUSTER_IP=$(kubectlgetnodes\
+--selector=node-role.kubernetes.io/control-plane\
+-ojsonpath='{$.items[*].status.addresses[?(@.type=="InternalIP")].address}')
+
+NODE_PORT=$(kubectlget-o\
+jsonpath="{.spec.ports[0].nodePort}"\
+servicesmultitool-ncodewizard)
+
Test to see if the deployment worked using the ip address and port number we have retrieved above.
Execute curl with the following parameters: http://${CLUSTER_IP}:${NODE_PORT}
curlhttp://${CLUSTER_IP}:${NODE_PORT}
+
+# Or in the above sample
+curl192.168.49.2:30436
+
+# The output should be similar to this:
+PraqmaNetworkMultiTool(withNGINX)...
+
- If you get the above output, congratulations! You have successfully created a deployment using imperative commands.
\ No newline at end of file
diff --git a/mkdocs-site/03-Deployments-Declarative/index.html b/mkdocs-site/03-Deployments-Declarative/index.html
new file mode 100644
index 0000000..634a25c
--- /dev/null
+++ b/mkdocs-site/03-Deployments-Declarative/index.html
@@ -0,0 +1,150 @@
+ 03 Deployments Declarative - KubernetesLabs
If this is your first k8sYAML file, its advisable that you type it in order to get the feeling of the structure.
Save the file with the following name: nginx.yaml
apiVersion:apps/v1
+kind:Deployment# We use a deployment and not pod !!!!
+metadata:
+name:nginx# Deployment name
+namespace:codewizard
+labels:
+app:nginx# Deployment label
+spec:
+replicas:2
+selector:
+matchLabels:# Labels for the replica selector
+app:nginx
+template:
+metadata:
+labels:
+app:nginx# Labels for the replica selector
+version:"1.17"# Specify specific verion if required
+spec:
+containers:
+-name:nginx# The name of the pod
+image:nginx:1.17# The image which we will deploy
+ports:
+-containerPort:80
+
Create the deployment using the -f flag & --record=true
\ No newline at end of file
diff --git a/mkdocs-site/03-Deployments-Declarative/nginx.yaml b/mkdocs-site/03-Deployments-Declarative/nginx.yaml
new file mode 100644
index 0000000..360e728
--- /dev/null
+++ b/mkdocs-site/03-Deployments-Declarative/nginx.yaml
@@ -0,0 +1,23 @@
+apiVersion: apps/v1
+kind: Deployment # We use a deployment and not pod !!!!
+metadata:
+ name: nginx # Deployment name
+ namespace: codewizard
+ labels:
+ app: nginx # Deployment label
+spec:
+ replicas: 1
+ selector:
+ matchLabels: # Labels for the replica selector
+ app: nginx
+ template:
+ metadata:
+ labels:
+ app: nginx # Labels for the replica selector
+ version: "1.17" # Specify specific verion if required
+ spec:
+ containers:
+ - name: nginx # The name of the pod
+ image: nginx:1.17 # The image which we will deploy
+ ports:
+ - containerPort: 80
diff --git a/mkdocs-site/04-Rollout/index.html b/mkdocs-site/04-Rollout/index.html
new file mode 100644
index 0000000..7020ee1
--- /dev/null
+++ b/mkdocs-site/04-Rollout/index.html
@@ -0,0 +1,168 @@
+ 04 Rollout - KubernetesLabs
save-config If true, the configuration of current object will be saved in its annotation. Otherwise, the annotation will be unchanged. This flag is useful when you want to perform kubectl apply on this object in the future.
Again, note that in case we already have this service we will get an error message as well.
04. Verify that the pods and the service are running¶
$kubectlgetall-ncodewizard
+
+# The output should be similar to this
+NAMEREADYSTATUSRESTARTSAGE
+pod/nginx-db749865c-lmgtv1/1Running066s
+
+NAMETYPECLUSTER-IPEXTERNAL-IPPORT(S)AGE
+service/nginxNodePort10.102.79.9<none>80:31204/TCP30s
+
+NAMEREADYUP-TO-DATEAVAILABLEAGE
+deployment.apps/nginx1/11166s
+
+NAMEDESIREDCURRENTREADYAGE
+replicaset.apps/nginx-db749865c11166s
+
# !!! Get the Ip & port for this service
+$kubectlgetservices-ncodewizard-owide
+
+# Write down the port number
+NAMETYPECLUSTER-IPEXTERNAL-IPPORT(S)AGESELECTOR
+nginxNodePort10.102.79.9<none>80:31204/TCP7m7sapp=nginx
+
+# Get the cluster IP and port
+$kubectlcluster-info
+Kubernetescontrolplaneisrunningathttps://192.168.49.2:8443
+
+# Using the above <host>:<port> test the nginx
+# -I is for getting the headers
+$curl-sI<host>:<port>
+
+# The response should display the nginx version
+example:curl-sI192.168.49.2:31204
+
+HTTP/1.1200OK
+Server:nginx/1.17.10<------------Thisisthepodversion
+Date:Fri,15Jan202120:13:48GMT
+Content-Type:text/html
+Content-Length:612
+Last-Modified:Tue,14Apr202014:19:26GMT
+Connection:keep-alive
+ETag:"5e95c66e-264"
+Accept-Ranges:bytes
+...
+
# Deploy another version of nginx (1.16)
+$kubectlsetimagedeployment-ncodewizardnginxnginx=nginx:1.16--record
+deployment.apps/nginximageupdated
+
+# Check to verify that the new version deployed - same as in previous step
+$curl-sI<host>:<port>
+
+# The response should display the new version
+HTTP/1.1200OK
+Server:nginx/1.16.1<------------Thisisthepodversion(newversion)
+Date:Fri,15Jan202120:16:11GMT
+Content-Type:text/html
+Content-Length:612
+Last-Modified:Tue,13Aug201910:05:00GMT
+Connection:keep-alive
+ETag:"5d528b4c-264"
+Accept-Ranges:bytes
+
10. Let’s see what was changed during the previous updates:¶
Print out the rollout changes:
# replace the X with 1 or 2 or any number revision id
+$kubectlrollouthistorydeploymentnginx-ncodewizard--revision=<X># replace here
+deployment.apps/nginxwithrevision#1
+PodTemplate:
+Labels:app=nginx
+pod-template-hash=db749865c
+Containers:
+nginx:
+Image:nginx:1.17
+Port:<none>
+HostPort:<none>
+Environment:<none>
+Mounts:<none>
+Volumes:<none>
+
11. Undo the version upgrade by rolling back and restoring previous version¶
# Check the current nginx version
+$ curl -sI <host>:<port>
+
+# Undo the last deployment
+$ kubectl rollout undo deployment nginx
+deployment.apps/nginx rolled back
+
+# Verify that we have the previous version
+$ curl -sI <host>:<port>
+
If we deploy using imagePullPolicy: always set in the YAML file, we can use rollout restart to force K8S to grab the latest image.
This is the fastest restart method these days
# Force pods restart
+kubectl rollout restart deployment [deployment_name]
+
\ No newline at end of file
diff --git a/mkdocs-site/05-Services/index.html b/mkdocs-site/05-Services/index.html
new file mode 100644
index 0000000..217006f
--- /dev/null
+++ b/mkdocs-site/05-Services/index.html
@@ -0,0 +1,199 @@
+ 05 Services - KubernetesLabs
Service is a unit of application behavior bound to a unique name in a service registry.
Service consist of multiple network endpoints implemented by workload instances running on pods, containers, VMs etc.
Service allow us to gain access to any given pod or container (e.g., a web service).
A service is (normally) created on top of an existing deployment and exposing it to the “world”, using IP(s) & port(s).
K8S define 3 main ways (+FQDN internally) to define a service, which means that we have 4 different ways to access Pods.
There are several proxy mode which inplements diffrent behaviour, for example in user proxy mode for each Servicekube-proxy opens a port (randomly chosen) on the local node. Any connections to this “proxy port” are proxied to one of the Service’s backend Pods (as reported via Endpoints).
All the service types are assigned with a Cluster-IP.
Every service also creates Endoint(s), which point to the actual pods. Endpoints are usually referred to as back-ends of a particular service.
01. Create namespace and clear previous data if there is any¶
# If the namespace already exists and contains data form previous steps, let's clean it
+kubectldeletenamespacecodewizard
+
+# Create the desired namespace [codewizard]
+$kubectlcreatenamespacecodewizard
+namespace/codewizardcreated
+
02. Create the required resources for this hand-on¶
# Network tools pod
+$kubectlcreatedeployment-ncodewizardmultitool--image=praqma/network-multitool
+deployment.apps/multitoolcreated
+
+# nginx pod
+$kubectlcreatedeployment-ncodewizardnginx--image=nginx
+deployment.apps/nginxcreated
+
+# Verify that the pods running
+$kubectlgetall-ncodewizard
+
+NAMEREADYSTATUSRESTARTSAGE
+pod/multitool-74477484b8-bdrwr1/1Running029s
+pod/nginx-6799fc88d8-p2fjn1/1Running07s
+NAMEREADYUP-TO-DATEAVAILABLEAGE
+deployment.apps/multitool1/11130s
+deployment.apps/nginx1/1118s
+NAMEDESIREDCURRENTREADYAGE
+replicaset.apps/multitool-74477484b811130s
+replicaset.apps/nginx-6799fc88d81118s
+
# Expose the service on port 80
+$kubectlexposedeploymentnginx-ncodewizard--port80--typeClusterIP
+service/nginxexposed
+
+# Check the services and see it's type
+# Grab the ClusterIP - we will use it in the next steps
+$kubectlgetservices-ncodewizard
+
+NAMETYPECLUSTER-IPEXTERNAL-IPPORT(S)
+nginxClusterIP10.109.78.182<none>80/TCP
+
Since the service is a ClusterIP, we will test if we can access the service using the multitool pod.
# Get the name of the multitool pod to be used
+$kubectlgetpods-ncodewizard
+NAME
+multitool-XXXXXX-XXXXX
+
+# Run an interactive shell inside the network-multitool-container (same concept as with Docker)
+$kubectlexec-it<podname>-ncodewizard--sh
+
Connect to the service in any of the following ways:
1. using the IP from the services output. grab the server response:¶
bash-5.0#curl-s<ClusterIP>
+
# Expected output:
+<!DOCTYPE html>
+<html>
+<head>
+<title>Welcome to nginx!</title>
+<style>
+html{color-scheme:lightdark;}
+body{width:35em;margin:0auto;
+font-family:Tahoma,Verdana,Arial,sans-serif;}
+</style>
+</head>
+<body>
+<h1>Welcome to nginx!</h1>
+<p>If you see this page, the nginx web server is successfully installed and
+working. Further configuration is required.</p>
+
+<p>For online documentation and support please refer to
+<ahref="http://nginx.org/">nginx.org</a>.<br/>
+Commercial support is available at
+<ahref="http://nginx.com/">nginx.com</a>.</p>
+
+<p><em>Thank you for using nginx.</em></p>
+</body>
+</html>
+
2. Test the nginx using the deployment name - using the service name since its the DNS name behind the scenes¶
bash-5.0#curl-snginx
+
# Expected output:
+<!DOCTYPE html>
+<html>
+<head>
+<title>Welcome to nginx!</title>
+<style>
+body{
+width:35em;
+margin:0auto;
+font-family:Tahoma,Verdana,Arial,sans-serif;
+}
+</style>
+</head>
+<body>
+<h1>Welcome to nginx!</h1>
+<p>
+ If you see this page, the nginx web server is successfully installed and
+ working. Further configuration is required.
+</p>
+<p>
+ For online documentation and support please refer to
+<ahref="http://nginx.org/">nginx.org</a>.<br/>
+ Commercial support is available at
+<ahref="http://nginx.com/">nginx.com</a>.
+</p>
+<p><em>Thank you for using nginx.</em></p>
+</body>
+</html>
+
3. using the full DNS name - for every service we have a full FQDN (Fully qualified domain name) so we can use it as well¶
# As before but this time the type is a NodePort
+$kubectlexposedeployment-ncodewizardnginx--port80--typeNodePort
+service/nginxexposed
+
+# Verify that the type is set to NodePort.
+# This time you should see ClusterIP and port as well
+$kubectlgetsvc-ncodewizard
+NAMETYPECLUSTER-IPEXTERNAL-IPPORT(S)
+nginxNodePort100.65.29.172<none>80:32593/TCP
+
# As before this time the type is a LoadBalancer
+$kubectlexposedeploymentnginx-ncodewizard--port80--typeLoadBalancer
+service/nginxexposed
+
+# In real cloud we should se an EXTERNAL-IP and we can access the service
+# via the internet
+$kubectlgetsvc
+NAMETYPECLUSTER-IPEXTERNAL-IPPORT(S)
+nginxLoadBalancer100.69.15.8935.205.60.2980:31354/TCP
+
# Testing load balancer only require us to use the EXTERNAL-IP
+$curl-s<EXTERNAL-IP>
+
\ No newline at end of file
diff --git a/mkdocs-site/06-DataStore/index.html b/mkdocs-site/06-DataStore/index.html
new file mode 100644
index 0000000..11ba1dc
--- /dev/null
+++ b/mkdocs-site/06-DataStore/index.html
@@ -0,0 +1,303 @@
+ 06 DataStore - KubernetesLabs
01. Create namespace and clear previous data (if there is any)¶
# If the namespace already exist and contains data from previous steps, lets clean it
+kubectldeletenamespacecodewizard
+
+# Create the desired namespace [codewizard]
+$kubectlcreatenamespacecodewizard
+namespace/codewizardcreated
+
Note
You can skip section number 02. if you don’t wish to build and push your docker container
//
+// server.js
+//
+const
+// Get those values in runtime.
+// The variables will be passed from the Docker file and later on from K8S ConfingMap/ecret
+language=process.env.LANGUAGE,
+token=process.env.TOKEN;
+
+require("http")
+.createServer((request,response)=>{
+response.write(`Language: ${language}`);
+response.write(`Token : ${token}\n`);
+response.end(`\n`);
+})
+// Set the default port to 5000
+.listen(process.env.PORT||5000);
+
If you wish, you can skip this and use the existing docker image: nirgeier/k8s-secrets-sample
In the Dockerfile we will set the ENV for or variables
# Base Image
+FROMnode
+
+# exposed port - same port is defined in the server.js
+EXPOSE5000
+
+# The "configuration" which we pass in runtime
+# The server will "read" those variables at run time and will print them out
+ENVLANGUAGEHebrew
+ENVTOKENHard-To-Guess
+
+# Copy the server to the container
+COPYserver.js.
+
+# start the server
+ENTRYPOINTnodeserver.js
+
# The container name is prefixed with the Dockerhub account
+# !!! You should replace the prefix to your dockerhub account
+# In the sample the username is `nirgeier`
+$dockerbuild.-tnirgeier/k8s-secrets-sample
+
+# The output should be similar to this
+SendingbuildcontexttoDockerdaemon12.8kB
+Step1/6:FROMnode
+latest:Pullingfromlibrary/node
+2587235a7635:Pullcomplete
+953fe5c215cb:Pullcomplete
+d4d3f270c7de:Pullcomplete
+ed36dafe30e3:Pullcomplete
+00e912dd434d:Pullcomplete
+dd25ee3ea38e:Pullcomplete
+7e835b17ced9:Pullcomplete
+79ae84aa9e91:Pullcomplete
+629164f2c016:Pullcomplete
+Digest:sha256:3a9d0636755ebcc8e24148a148b395c1608a94bb1b4a219829c9a3f54378accb
+Status:Downloadednewerimagefornode:latest
+--->d6740064592f
+Step2/6:EXPOSE5000
+--->Runningin060220eaa65b
+Removingintermediatecontainer060220eaa65b
+--->68262a3e6741
+Step3/6:ENVLANGUAGEHebrew
+--->Runninginc404e7e6fa16
+Removingintermediatecontainerc404e7e6fa16
+--->45fcf1fe03aa
+Step4/6:ENVTOKENHard-To-Guess
+--->Runningind3c1491f9de5
+Removingintermediatecontainerd3c1491f9de5
+--->71e8acdbdab2
+Step5/6:COPYserver.js.
+--->42233d2b66a8
+Step6/6:ENTRYPOINTnodeserver.js
+--->Runningin223629e16589
+Removingintermediatecontainer223629e16589
+--->f5cbb1895d66
+Successfullybuiltf5cbb1895d66
+Successfullytaggednirgeier/k8s-secrets-sample:latest
+
# Run the docker container which you build earlier,
+# replace the name if you used your own name
+# and check the response from the server.
+# It should print out the variables which were defined inside the DockerFile
+$dockerrun-d-p5000:5000nirgeier/k8s-secrets-sample--nameserver
+
+# Get the response from the container
+# The port is the one which we exposed inside the DockerFile
+curl127.0.0.1:5000
+
+# Response:
+Language:Hebrew
+Token:Hard-To-Guess
+
Stop the container
# Stop the running container
+# We are using the name which we passed in the `docker run` command --name <container name>
+dockerstopserver
+
Push the container to your docker hub account if you wish
We will need a second container for executing the curl request.
We will use a busyBox image for this purpose.
# grab the name of the pod
+$kubectlgetpods-ncodewizard
+
+# Output
+NAMEREADYSTATUSRESTARTSAGE
+codewizard-secrets-56f556c758-2mknc1/1Running06m27s
+
+# Login to the container and test the reponse
+# kubectl exec -it -n codewizard <pod name> -- sh
+# For the above output we will use
+kubectlexec-it-ncodewizardcodewizard-secrets-56f556c758-2mknc--sh
+
+# Now get the server response (from inside the container)
+$curllocalhost:5000
+
+# Response
+Language:Hebrew
+Token:Hard-To-Guess2
+
1. Create the desired secret and config map for this lab¶
# Create the secret
+# Key = Token
+# Value = Hard-To-Guess3
+$kubectlcreate-ncodewizardsecretgenerictoken--from-literal=TOKEN=Hard-To-Guess3
+secret/tokencreated
+
+# Create the config map
+# Key = LANGUAGE
+# Value = English
+$kubectlcreate-ncodewizardconfigmaplanguage--from-literal=LANGUAGE=English
+configmap/languagecreated
+
+# Verify that the resources have been created:
+$kubectlgetsecrets,cm-ncodewizard
+NAMETYPEDATAAGE
+secret/default-token-8hzhnkubernetes.io/service-account-token314m
+secret/tokenOpaque180s
+NAMEDATAAGE
+configmap/kube-root-ca.crt114m
+configmap/language144s
+
+# Like other resources we can use describe to view the resource
+$kubectldescribesecrettoken-ncodewizard
+Name:token
+Namespace:codewizard
+Labels:<none>
+Annotations:<none>
+
+Type:Opaque<-----ThecontentisstoredasBASE64
+
+Data
+====
+TOKEN:14bytes
+
+# Same way for the ConfigMap
+$kubectldescribecmlanguage-ncodewizard
+Name:language
+Namespace:codewizard
+Labels:<none>
+Annotations:<none>
+Data
+====
+LANGUAGE:
+----
+English
+Events:<none>
+
2. Update the deployment to read the values from Secrets & ConfigMap¶
Change the env section to the following:
env:
+-name:LANGUAGE
+valueFrom:
+configMapKeyRef:# This value will be read from the config map
+name:language# The name of the ConfigMap
+key:LANGUAGE# The key in the config map
+-name:TOKEN
+valueFrom:
+secretKeyRef:# This value will be read from the secret
+name:token# The name of the secret
+key:TOKEN# The key in the secret
+
3. Update the deployment to read values from K8S resources¶
# Login to the server
+# In this sample, the pod name is: codewizard-secrets-76d99bdc54-s66vl
+kubectlexec-itcodewizard-secrets-76d99bdc54-s66vl-ncodewizard--sh
+
+# Test the changes to verify that they are set from the Secret/ConfigMap
+curllocalhost:5000
+
+# Out put should be
+Language:English
+Token:Hard-To-Guess3
+
Note
Pods are not recreated or updated automatically when Secrets or ConfigMaps change, so you will have to restart your pods manually
The certificate is in the same folder as this file
The certificate is for the hostname: ingress.local
# If you wish to create the certificate use this script
+### ---> The common Name fiels is your host for later on
+### Common Name (e.g. server FQDN or YOUR name) []:
+$opensslreq-x509-nodes-days365-newkeyrsa:2048-keyoutcertificate.key-outcertificate.crt
+
+# Create a pem file
+# The purpose of the DH parameters is to exchange secrets
+$openssldhparam-outcertificate.pem2048
+
Store the certificate in secret:
# Store the certificate
+$kubectlcreatesecrettlstls-certificate--keycertificate.key--certcertificate.crt
+secret/tls-certificatecreated
+
+# Store the DH parameters
+$kubectlcreatesecretgenerictls-dhparam--from-file=certificate.pem
+secret/tls-dhparamcreated
+
Kustomization re-orders the Kind for optimization. For this demo, we will need an existing namespace before using it.
The order of the resources is defined in the source code
// An attempt to order things to help k8s, e.g.
+// - Namespace should be first.
+// - Service should come before things that refer to it.
+// In some cases order just specified to provide determinism.
+varorderFirst=[]string{
+"Namespace",
+"ResourceQuota",
+"StorageClass",
+"CustomResourceDefinition",
+"ServiceAccount",
+"PodSecurityPolicy",
+"Role",
+"ClusterRole",
+"RoleBinding",
+"ClusterRoleBinding",
+"ConfigMap",
+"Secret",
+"Endpoints",
+"Service",
+"LimitRange",
+"PriorityClass",
+"PersistentVolume",
+"PersistentVolumeClaim",
+"Deployment",
+"StatefulSet",
+"CronJob",
+"PodDisruptionBudget",
+}
+
+varorderLast=[]string{
+"MutatingWebhookConfiguration",
+"ValidatingWebhookConfiguration",
+}
+
In the following samples we will refer to the following base.yaml file:
# base.yaml
+# This is the base file for all the demos in this folder
+apiVersion:apps/v1
+kind:Deployment
+metadata:
+name:myapp
+spec:
+selector:
+matchLabels:
+app:myapp
+template:
+metadata:
+labels:
+app:myapp
+spec:
+containers:
+-name:myapp
+image:__image__
+
### FileName: kustomization.yaml
+apiVersion:kustomize.config.k8s.io/v1beta1
+kind:Kustomization
+
+# This will add annotation under every metadata entry
+# ex: main metadata, spec.metadata etc
+commonAnnotations:
+author:nirgeier@gmail.com
+
apiVersion:kustomize.config.k8s.io/v1beta1
+kind:Kustomization
+
+# This will add annotation under every metadata entry
+# ex: main metadata, spec.metadata etc
+commonLabels:
+author:nirgeier@gmail.com
+env:codeWizard-cluster
+
+bases:
+-../_base
+
# Similar to configMap but with an additional type field
+secretGenerator:
+# Generate secret from env file
+-name:secretMapFromFile
+env:.env
+type:Opaque
+generatorOptions:
+disableNameSuffixHash:true
+
# kustomization.yaml
+apiVersion:kustomize.config.k8s.io/v1beta1
+kind:Kustomization
+
+resources:
+-./base.yaml
+
+images:
+# The image as its defined in the Deployment file
+-name:__image__
+# The new name to set
+newName:my-registry/my-image
+# optional: image tag
+newTag:v1
+
There is a bug with the replicas entries which return error for some reason.
$kubectlkustomize.
+
+# For some reason we get this error:
+Error:json:unknownfield"replicas"
+
+# Workaround for this error for now is:
+$kustomizebuild.
+
# File: patch-memory.yaml
+# -----------------------
+# Patch limits.memory
+apiVersion:apps/v1
+kind:Deployment
+# Set the desired deployment to patch
+metadata:
+name:myapp
+spec:
+# patch the memory limit
+template:
+spec:
+containers:
+-name:patch-name
+resources:
+limits:
+memory:512Mi
+
# File: patch-replicas.yaml
+# -------------------------
+apiVersion:apps/v1
+kind:Deployment
+# Set the desired deployment to patch
+metadata:
+name:myapp
+spec:
+# This is the patch for this demo
+replicas:3
+
apiVersion:apps/v1
+kind:Deployment
+metadata:
+name:myapp
+spec:
+# This is the first patch
+replicas:3
+selector:
+matchLabels:
+app:myapp
+template:
+metadata:
+labels:
+app:myapp
+spec:
+# This is the second patch
+containers:
+-name:patch-name
+resources:
+limits:
+memory:512Mi
+-image:__image__
+name:myapp
+
# patch-delete.yaml
+apiVersion:apps/v1
+kind:Deployment
+metadata:
+name:myapp
+spec:
+template:
+spec:
+containers:
+# Remove this section, in this demo it will remove the
+# image with the `name: myapp`
+-$patch:delete
+name:myapp
+image:__image__
+
# patch-replace.yaml
+apiVersion:apps/v1
+kind:Deployment
+metadata:
+name:myapp
+spec:
+template:
+spec:
+containers:
+# Remove this section, in this demo it will remove the
+# image with the `name: myapp`
+-$patch:replace
+-name:myapp
+image:nginx:latest
+args:
+-one
+-two
+
A stateless application is one that does not care which network it is using, and it does not need permanent storage and can be scaled up and down without the need to re-use the same network or persistence.
Deployment is the suitable kind for Stateless applications.
The most trivial example of stateless app is a Web Server.
Stateful applications are apps which in order to work properly need to use the same resources, such as network, storage etc.
Usually with Stateful applications you will need to ensure that pods can reach each other through a unique identity that does not change (e.g., hostnames, IP).
The most trivial example of Stateful app is a database of any kind.
Stateful Notes
Like a Deployment, a StatefulSet manages Pods that are based on an identical container spec.
Unlike a Deployment, a StatefulSet maintains a sticky identity for each of their Pods.
These pods are created from the same spec, but are not interchangeable: each has a persistent identifier that it maintains across any rescheduling.
Deleting and/or scaling down a StatefulSet will not delete the volumes associated with the StatefulSet. This is done to ensure data safety.
StatefulSet keeps a unique identity for each Pod and assign the same identity to those pods when they are rescheduled (update, restart etc).
The storage for a given Pod must either be provisioned by a PersistentVolume provisioner, based on the requested storage class, or pre-provisioned by an admin.
StatefulSet manages the deployment and scaling of a set of Pods, and provides guarantees about the ordering and uniqueness of these Pods.
01. Create namespace and clear previous data if there is any¶
# If the namespace already exist and contains data form previous steps, lets clean it
+kubectldeletenamespacecodewizard
+
+# Create the desired namespace [codewizard]
+$kubectlcreatenamespacecodewizard
+namespace/codewizardcreated
+
apiVersion:v1
+kind:ConfigMap
+metadata:
+name:postgres-config
+labels:
+app:postgres
+data:
+# The following names are the one defined in the officail postgres docs
+
+# The name of the database we will use in this demo
+POSTGRES_DB:codewizard
+# the user name for this demo
+POSTGRES_USER:codewizard
+# The password for this demo
+POSTGRES_PASSWORD:admin123
+
apiVersion:v1
+kind:Service
+metadata:
+name:postgres
+labels:
+app:postgres
+spec:
+selector:
+app:postgres
+# Service of type nodeport
+type:NodePort
+# The deafult port for postgres
+ports:
+-port:5432
+
kind:PersistentVolumeClaim
+apiVersion:v1
+metadata:
+name:postgres-pv-claim
+labels:
+app:postgres
+spec:
+# in this demo we use GCP so we are using the 'standard' StorageClass
+# We can of course define our own StorageClass resource
+storageClassName:standard
+
+# The access modes are:
+# ReadWriteOnce - The volume can be mounted as read-write by a single node
+# ReadWriteMany - The volume can be mounted as read-write by a many node
+# ReadOnlyMany - The volume can be mounted as read-only by many nodes
+accessModes:
+-ReadWriteMany
+resources:
+requests:
+storage:1Gi
+
apiVersion:apps/v1
+kind:StatefulSet
+metadata:
+name:postgres
+spec:
+replicas:1
+# StatefulSet must contain a serviceName
+serviceName:postgres
+selector:
+matchLabels:
+app:postgres# has to match .spec.template.metadata.labels
+template:
+metadata:
+labels:
+app:postgres# has to match .spec.selector.matchLabels
+spec:
+containers:
+-name:postgres
+image:postgres:10.4
+imagePullPolicy:"IfNotPresent"
+# The default DB port
+ports:
+-containerPort:5432
+# Load the required configuration env values form the configMap
+envFrom:
+-configMapRef:
+name:postgres-config
+# Use volume for storage
+volumeMounts:
+-mountPath:/var/lib/postgresql/data
+name:postgredb
+# We can use PersistentVolume or PersistentVolumeClaim.
+# In this sample we are useing PersistentVolumeClaim
+volumes:
+-name:postgredb
+persistentVolumeClaim:
+# reference to Pre-Define PVC
+claimName:postgres-pv-claim
+
Note: You can use the kustomization file to create or apply all the above resources
# Generate and apply the required resources using kustomization
+kubectlkustomizePostgreSQL/|kubectlapply-f-
+
Don’t forget to set the execution flag chmod +x testDb.sh if required
### Test to see if the StatefulSet "saves" the state of the pods
+
+# Programmatically get the port and the IP
+exportCLUSTER_IP=$(kubectlgetnodes\
+--selector=node-role.kubernetes.io/control-plane\
+-ojsonpath='{$.items[*].status.addresses[?(@.type=="InternalIP")].address}')
+
+exportNODE_PORT=$(kubectlget\
+servicespostgres\
+-ojsonpath="{.spec.ports[0].nodePort}"\
+-ncodewizard)
+
+exportPOSTGRES_DB=$(kubectlget\
+configmappostgres-config\
+-ojsonpath='{.data.POSTGRES_DB}'\
+-ncodewizard)
+
+exportPOSTGRES_USER=$(kubectlget\
+configmappostgres-config\
+-ojsonpath='{.data.POSTGRES_USER}'\
+-ncodewizard)
+
+exportPGPASSWORD=$(kubectlget\
+configmappostgres-config\
+-ojsonpath='{.data.POSTGRES_PASSWORD}'\
+-ncodewizard)
+
+# Check to see if we have all the required variables
+printenv|grepPOST*
+
+# Connect to postgres and create table if required.
+# Once the table exists - add row into the table
+# you can run this command as amny times as you like
+psql\
+-U${POSTGRES_USER}\
+-h${CLUSTER_IP}\
+-d${POSTGRES_DB}\
+-p${NODE_PORT}\
+-c"CREATE TABLE IF NOT EXISTS stateful (str VARCHAR); INSERT INTO stateful values (1); SELECT count(*) FROM stateful"
+
04. Scale down the StatefulSet and check that its down¶
# Wait until the pods will be terminated
+kubectlgetpods-ncodewizard--watch
+NAMEREADYSTATUSRESTARTSAGE
+postgres-01/1Running032m
+postgres-01/1Terminating032m
+postgres-00/1Terminating032m
+postgres-00/1Terminating033m
+postgres-00/1Terminating033m
+
If the DB is not reachable it mean that all the pods are down
psql\
+-U${POSTGRES_USER}\
+-h${CLUSTER_IP}\
+-d${POSTGRES_DB}\
+-p${NODE_PORT}\
+-c"SELECT count(*) FROM stateful"
+
+# You should get output similar to this one:
+psql:error:couldnotconnecttoserver:Connectionrefused
+Istheserverrunningonhost"192.168.49.2"andaccepting
+TCP/IPconnectionsonport32570?
+
05. Scale up again and verify that we still have the prevoius data¶
05.03. Verify that the pods is using the previous data¶
psql\
+-U${POSTGRES_USER}\
+-h${CLUSTER_IP}\
+-d${POSTGRES_DB}\
+-p${NODE_PORT}\
+-c"SELECT count(*) FROM stateful"
+# The output should be similar to this one
+
+count
+-------
+2
+(1row)
+
\ No newline at end of file
diff --git a/mkdocs-site/10-Istio/01-demo-services/K8S/AuthorizationPolicy.yaml b/mkdocs-site/10-Istio/01-demo-services/K8S/AuthorizationPolicy.yaml
new file mode 100644
index 0000000..d8cc23d
--- /dev/null
+++ b/mkdocs-site/10-Istio/01-demo-services/K8S/AuthorizationPolicy.yaml
@@ -0,0 +1,19 @@
+apiVersion: security.istio.io/v1beta1
+kind: AuthorizationPolicy
+metadata:
+ name: authorization-policy
+spec:
+ selector:
+ matchLabels:
+ app: webserver
+ rules:
+ - from:
+ - source:
+ principals: ["cluster.local/ns/default/sa/sleep"]
+ to:
+ - operation:
+ methods: ["GET"]
+ when:
+ - key: request.headers[version]
+ values: ["v1", "v2"]
+
diff --git a/mkdocs-site/10-Istio/01-demo-services/K8S/Namespace.yaml b/mkdocs-site/10-Istio/01-demo-services/K8S/Namespace.yaml
new file mode 100644
index 0000000..9a4f7be
--- /dev/null
+++ b/mkdocs-site/10-Istio/01-demo-services/K8S/Namespace.yaml
@@ -0,0 +1,6 @@
+apiVersion: v1
+kind: Namespace
+metadata:
+ name: codewizard
+ labels:
+ istio-injection: enabled
\ No newline at end of file
diff --git a/mkdocs-site/10-Istio/01-demo-services/K8S/kiali-service.yaml b/mkdocs-site/10-Istio/01-demo-services/K8S/kiali-service.yaml
new file mode 100644
index 0000000..67525f1
--- /dev/null
+++ b/mkdocs-site/10-Istio/01-demo-services/K8S/kiali-service.yaml
@@ -0,0 +1,30 @@
+apiVersion: v1
+kind: Service
+metadata:
+ annotations:
+ meta.helm.sh/release-name: kiali-server
+ meta.helm.sh/release-namespace: istio-system
+ labels:
+ app: kiali
+ app.kubernetes.io/instance: kiali
+ app.kubernetes.io/managed-by: Helm
+ app.kubernetes.io/name: kiali
+ app.kubernetes.io/part-of: kiali
+ name: kiali
+ namespace: istio-system
+spec:
+ ipFamilyPolicy: SingleStack
+ ports:
+ - name: http
+ port: 20001
+ protocol: TCP
+ targetPort: 20001
+ - name: http-metrics
+ port: 9090
+ protocol: TCP
+ targetPort: 9090
+ selector:
+ app.kubernetes.io/instance: kiali
+ app.kubernetes.io/name: kiali
+ sessionAffinity: None
+ type: ClusterIP
diff --git a/mkdocs-site/10-Istio/01-demo-services/K8S/kustomization.yaml b/mkdocs-site/10-Istio/01-demo-services/K8S/kustomization.yaml
new file mode 100644
index 0000000..ca866cc
--- /dev/null
+++ b/mkdocs-site/10-Istio/01-demo-services/K8S/kustomization.yaml
@@ -0,0 +1,16 @@
+# kustomization.yaml
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+namespace: codewizard
+
+resources:
+ #- AuthorizationPolicy.yaml
+ - kiali-service.yaml
+ - Namespace.yaml
+ - proxy-deployment.yaml
+ - proxy-service.yaml
+ - web-server1-deployment.yaml
+ - web-server1-service.yaml
+ - web-server2-deployment.yaml
+ - web-server2-service.yaml
+ - web-server2-v2-service.yaml
diff --git a/mkdocs-site/10-Istio/01-demo-services/K8S/proxy-deployment.yaml b/mkdocs-site/10-Istio/01-demo-services/K8S/proxy-deployment.yaml
new file mode 100644
index 0000000..331c352
--- /dev/null
+++ b/mkdocs-site/10-Istio/01-demo-services/K8S/proxy-deployment.yaml
@@ -0,0 +1,32 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: proxy-server
+spec:
+ progressDeadlineSeconds: 60
+ replicas: 1
+ selector:
+ matchLabels:
+ app: proxy-server
+ template:
+ metadata:
+ labels:
+ app: proxy-server
+ version: v1
+ spec:
+ containers:
+ - name: proxy-server
+ image: docker.io/nirgeier/istio-proxy-sample
+ imagePullPolicy: Always
+ ports:
+ - name: web
+ protocol: TCP
+ containerPort: 5050
+ env:
+ - name: PROXY_URL_TO_SERVE
+ value: http://webserver
+ resources:
+ limits:
+ cpu: 100m
+ requests:
+ cpu: 100m
diff --git a/mkdocs-site/10-Istio/01-demo-services/K8S/proxy-service.yaml b/mkdocs-site/10-Istio/01-demo-services/K8S/proxy-service.yaml
new file mode 100644
index 0000000..abe7fa0
--- /dev/null
+++ b/mkdocs-site/10-Istio/01-demo-services/K8S/proxy-service.yaml
@@ -0,0 +1,14 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: proxy-service
+spec:
+ ports:
+ # We use the - as istion require
+ - name: http-web
+ port: 80
+ protocol: TCP
+ targetPort: 5050
+ selector:
+ app: proxy-server
+ type: LoadBalancer
diff --git a/mkdocs-site/10-Istio/01-demo-services/K8S/web-server1-deployment.yaml b/mkdocs-site/10-Istio/01-demo-services/K8S/web-server1-deployment.yaml
new file mode 100644
index 0000000..acf06be
--- /dev/null
+++ b/mkdocs-site/10-Istio/01-demo-services/K8S/web-server1-deployment.yaml
@@ -0,0 +1,36 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ labels:
+ app: webserver
+ version: v1
+ name: webserverv1
+spec:
+ selector:
+ matchLabels:
+ app: webserver
+ version: v1
+ strategy:
+ type: Recreate
+ template:
+ metadata:
+ labels:
+ app: webserver
+ version: v1
+ spec:
+ containers:
+ - env:
+ - name: SERVER_NAME
+ value: WebServerV1
+ image: docker.io/nirgeier/istio-web-server-sample
+ imagePullPolicy: Always
+ name: simpleserver
+ ports:
+ - containerPort: 5050
+ name: web
+ protocol: TCP
+ resources:
+ limits:
+ cpu: 100m
+ requests:
+ cpu: 100m
diff --git a/mkdocs-site/10-Istio/01-demo-services/K8S/web-server1-service.yaml b/mkdocs-site/10-Istio/01-demo-services/K8S/web-server1-service.yaml
new file mode 100644
index 0000000..2676639
--- /dev/null
+++ b/mkdocs-site/10-Istio/01-demo-services/K8S/web-server1-service.yaml
@@ -0,0 +1,13 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: webserverv1
+spec:
+ ports:
+ - name: http-web
+ port: 80
+ protocol: TCP
+ targetPort: 5050
+ selector:
+ app: webserver
+ version: v1
diff --git a/mkdocs-site/10-Istio/01-demo-services/K8S/web-server2-deployment.yaml b/mkdocs-site/10-Istio/01-demo-services/K8S/web-server2-deployment.yaml
new file mode 100644
index 0000000..ef1d34e
--- /dev/null
+++ b/mkdocs-site/10-Istio/01-demo-services/K8S/web-server2-deployment.yaml
@@ -0,0 +1,38 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ labels:
+ app: webserver
+ version: v2
+ name: webserverv2
+spec:
+ replicas: 3
+ selector:
+ matchLabels:
+ app: webserver
+ version: v2
+ strategy:
+ type: Recreate
+ template:
+ metadata:
+ labels:
+ app: webserver
+ version: v2
+ spec:
+ affinity: {}
+ containers:
+ - env:
+ - name: SERVER_NAME
+ value: WebServerV2
+ image: docker.io/nirgeier/istio-web-server-sample
+ imagePullPolicy: Always
+ name: simpleserver
+ ports:
+ - name: web
+ protocol: TCP
+ containerPort: 5050
+ resources:
+ limits:
+ cpu: 100m
+ requests:
+ cpu: 100m
diff --git a/mkdocs-site/10-Istio/01-demo-services/K8S/web-server2-service.yaml b/mkdocs-site/10-Istio/01-demo-services/K8S/web-server2-service.yaml
new file mode 100644
index 0000000..e28d561
--- /dev/null
+++ b/mkdocs-site/10-Istio/01-demo-services/K8S/web-server2-service.yaml
@@ -0,0 +1,12 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: webserver
+spec:
+ ports:
+ - name: http-web
+ port: 80
+ protocol: TCP
+ targetPort: 5050
+ selector:
+ app: webserver
diff --git a/mkdocs-site/10-Istio/01-demo-services/K8S/web-server2-v2-service.yaml b/mkdocs-site/10-Istio/01-demo-services/K8S/web-server2-v2-service.yaml
new file mode 100644
index 0000000..5c3011d
--- /dev/null
+++ b/mkdocs-site/10-Istio/01-demo-services/K8S/web-server2-v2-service.yaml
@@ -0,0 +1,13 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: webserverv2
+spec:
+ ports:
+ - name: http-web
+ port: 80
+ protocol: TCP
+ targetPort: 5050
+ selector:
+ app: webserver
+ version: v2
diff --git a/mkdocs-site/10-Istio/01-demo-services/buildImages.sh b/mkdocs-site/10-Istio/01-demo-services/buildImages.sh
new file mode 100644
index 0000000..4a064fe
--- /dev/null
+++ b/mkdocs-site/10-Istio/01-demo-services/buildImages.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+# Login to docker hub in order to push the images
+docker login -u nirgeier
+
+# Build and push the images
+docker-compose build && docker-compose push
\ No newline at end of file
diff --git a/mkdocs-site/10-Istio/01-demo-services/docker-compose.yaml b/mkdocs-site/10-Istio/01-demo-services/docker-compose.yaml
new file mode 100644
index 0000000..6ff7d8c
--- /dev/null
+++ b/mkdocs-site/10-Istio/01-demo-services/docker-compose.yaml
@@ -0,0 +1,16 @@
+version: "3"
+services:
+ proxy:
+ build: istio-proxy
+ image: ${PROXY_IMAGE_NAME}
+ ports:
+ - ${PROXY_PORT}:${PROXY_PORT}
+ environment:
+ port: ${PROXY_PORT}
+ web-server:
+ build: istio-web-server
+ image: ${SERVER_IMAGE_NAME}
+ ports:
+ - ${SERVER_PORT}:${SERVER_PORT}
+ environment:
+ port: ${SERVER_PORT}
diff --git a/mkdocs-site/10-Istio/01-demo-services/mock-data/external-mock1.txt b/mkdocs-site/10-Istio/01-demo-services/mock-data/external-mock1.txt
new file mode 100644
index 0000000..f4902b6
--- /dev/null
+++ b/mkdocs-site/10-Istio/01-demo-services/mock-data/external-mock1.txt
@@ -0,0 +1 @@
+This is the content of external-mock1.txt
\ No newline at end of file
diff --git a/mkdocs-site/10-Istio/01-demo-services/mock-data/external-mock2.txt b/mkdocs-site/10-Istio/01-demo-services/mock-data/external-mock2.txt
new file mode 100644
index 0000000..4786389
--- /dev/null
+++ b/mkdocs-site/10-Istio/01-demo-services/mock-data/external-mock2.txt
@@ -0,0 +1 @@
+This is the content of external-mock2.txt
\ No newline at end of file
diff --git a/mkdocs-site/10-Istio/02-network-fault/K8S/kustomization.yaml b/mkdocs-site/10-Istio/02-network-fault/K8S/kustomization.yaml
new file mode 100644
index 0000000..0d6ce01
--- /dev/null
+++ b/mkdocs-site/10-Istio/02-network-fault/K8S/kustomization.yaml
@@ -0,0 +1,9 @@
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+
+# We assume since we are build upon demo 01 - we already have the namespace
+namespace: codewizard
+
+resources:
+ - resources/web-server1-network-faults.yaml
+ #- resources/web-server1-network-faults.yaml
diff --git a/mkdocs-site/10-Istio/02-network-fault/K8S/resources/web-server1-VirtualService.yaml b/mkdocs-site/10-Istio/02-network-fault/K8S/resources/web-server1-VirtualService.yaml
new file mode 100644
index 0000000..c5dfb58
--- /dev/null
+++ b/mkdocs-site/10-Istio/02-network-fault/K8S/resources/web-server1-VirtualService.yaml
@@ -0,0 +1,19 @@
+#
+# Add network fault to 50% of the traffic
+# We should get error like this one:
+# >> Proxying reply: fault filter abort - Took 3 milliseconds
+#
+apiVersion: networking.istio.io/v1beta1
+kind: VirtualService
+metadata:
+ name: webserver
+ namespace: codewizard
+spec:
+ # hostname of a request that this VirtualService resource will match
+ hosts:
+ - webserver
+ # rules to direct any HTTP traffic that matches the above hostname
+ http:
+ - route:
+ - destination:
+ host: webserverv1
\ No newline at end of file
diff --git a/mkdocs-site/10-Istio/02-network-fault/K8S/resources/web-server1-network-faults.yaml b/mkdocs-site/10-Istio/02-network-fault/K8S/resources/web-server1-network-faults.yaml
new file mode 100644
index 0000000..bd619e0
--- /dev/null
+++ b/mkdocs-site/10-Istio/02-network-fault/K8S/resources/web-server1-network-faults.yaml
@@ -0,0 +1,16 @@
+apiVersion: networking.istio.io/v1alpha3
+kind: VirtualService
+metadata:
+ name: webserver
+spec:
+ hosts:
+ - webserver
+ http:
+ - route:
+ - destination:
+ host: webserverv1
+ fault:
+ abort:
+ percentage:
+ value: 50
+ httpStatus: 400
\ No newline at end of file
diff --git a/mkdocs-site/10-Istio/index.html b/mkdocs-site/10-Istio/index.html
new file mode 100644
index 0000000..e4b6537
--- /dev/null
+++ b/mkdocs-site/10-Istio/index.html
@@ -0,0 +1,125 @@
+ 10 Istio - KubernetesLabs
Istio is an open-source service mesh that provides a way to manage microservices traffic, security, and observability in a Kubernetes cluster.
It acts as a layer of infrastructure that sits between your services, intercepting and controlling the traffic between them.
Istio key features:
Feature
Description
Traffic Management
Istio enables sophisticated traffic control capabilities, such as routing, load balancing, retries, timeouts, and circuit breakers for microservices.
Service Discovery
Automatically discovers services in the mesh, enabling dynamic routing and management of microservices without the need for manual configuration.
Load Balancing
Istio provides various load balancing algorithms (round-robin, weighted, etc.) to distribute traffic between microservices, ensuring optimal performance.
Traffic Shaping
Allows fine-grained control of traffic between services, such as A/B testing, canary releases, or blue/green deployments by defining routing rules.
Fault Injection
Supports fault injection to simulate network failures, latency, or errors in microservices to test resilience and robustness of the application.
Mutual TLS (mTLS)
Istio can automatically encrypt traffic between services using mutual TLS (mTLS) to ensure secure communication and provide strong identity-based access control.
Authentication & Authorization
Provides identity and access management through role-based access control (RBAC) and integration with external identity providers (e.g., OAuth, JWT).
Telemetry & Observability
Istio collects metrics, logs, and traces for monitoring service’s performance and behavior. It integrates with tools like Prometheus, Grafana, and Jaeger.
Distributed Tracing
Istio integrates with tracing systems like Jaeger and Zipkin to provide end-to-end tracing for debugging and monitoring service interactions.
Policy Enforcement
Istio provides fine-grained control over traffic policies, such as rate limiting, quotas, and security policies, using its Policy and Telemetry components.
Resilience & Retries
Istio can retry failed requests, set timeouts, and apply circuit breakers to prevent cascading failures and enhance the reliability of services.
Sidecar Proxy (Envoy)
Istio uses Envoy as a sidecar proxy to intercept and manage network traffic, providing a transparent proxy between microservices.
Automatic Sidecar Injection
Istio automatically injects the Envoy proxy into application pods via Kubernetes annotations, simplifying the management of service communication.
Service Mesh Topology
Visualizes and manages the network of microservices, allowing users to monitor how services interact with each other and troubleshoot issues.
Canary Deployments
Supports canary releases and traffic splitting, which allows gradual rollout of new versions of services for safe deployments and testing.
Multi-cluster Support
Istio supports a multi-cluster environment, allowing you to deploy services across different Kubernetes clusters while maintaining a unified service mesh.
Integration with Existing Tools
Istio integrates seamlessly with other tools such as Prometheus, Grafana, Jaeger, and Kiali for observability, monitoring, and tracing.
Service-Level Agreements (SLAs)
Provides mechanisms to define service-level objectives (SLOs) and monitor them, ensuring services meet expected performance and reliability standards.
Istio core components:
Components
Description
Envoy Proxy
A sidecar proxy that intercepts traffic to and from microservices.
Pilot
Manages configuration and distributes traffic management rules.
Mixer
Provides policy enforcement and telemetry data collection.
Citadel
Handles security-related tasks like identity and certificate management.
Alternatively, use istioctl to install Istio, as follows:
# Install Istio using istioctl
+echo"Installing Istio..."
+curl-Lhttps://istio.io/downloadIstio|sh-
+cdistio-*
+
+# Add bin directory to your $PATH
+exportPATH=$PWD/bin:$PATH
+
+# Install istio with all features enabled (demo profile)
+istioctlinstall--setprofile=demo-y
+
# Add the Kiali Helm chart repository
+helmrepoaddkialihttps://kiali.org/helm-charts
+helmrepoupdate
+
Install Kiali:
# Install Kiali into the `istio-system` namespace
+# this is the default namespace for Istio components
+#
+# Install Kiali with anonymous authentication
+#
+helminstallkiali-server\
+kiali/kiali-server\
+--namespaceistio-system\
+--setauth.strategy="anonymous"
+
To expose the application via Istio's ingress gateway, create an IstioGateway and VirtualService .
# This will expose the 'Bookinfo' application to the external world via Istio ingress gateway.
+kubectlapply-f\
+samples/bookinfo/networking/bookinfo-gateway.yaml
+
You have now successfully installed Istio and Kiali, set up a service mesh, and visualized your network’s behavior.
The combination of Istio's powerful traffic management features and Kiali's intuitive visualization interface makes it easier to manage and monitor microservices in a Kubernetes cluster.
\ No newline at end of file
diff --git a/mkdocs-site/10-Istio/install.sh b/mkdocs-site/10-Istio/install.sh
new file mode 100644
index 0000000..1ca004b
--- /dev/null
+++ b/mkdocs-site/10-Istio/install.sh
@@ -0,0 +1,107 @@
+#!/bin/bash
+
+# Step 1: Install Istio using istioctl
+echo "Installing Istio..."
+curl -L https://istio.io/downloadIstio | sh -
+cd istio-*
+export PATH=$PWD/bin:$PATH
+istioctl install --set profile=demo -y
+
+# Step 2: Install Kiali using Helm
+echo "Installing Kiali..."
+helm repo add kiali https://kiali.org/helm-charts
+helm repo update
+helm install kiali-server kiali/kiali-server --namespace istio-system --set auth.strategy="anonymous"
+helm install \
+ --namespace kiali-operator \
+ --create-namespace \
+ kiali-operator \
+ kiali/kiali-operator
+
+
+# Step 3: Enable Istio sidecar injection for all namespaces
+echo "Enabling Istio sidecar injection for default namespace..."
+kubectl label namespace default istio-injection=enabled
+kubectl label namespace codewizard istio-injection=enabled
+kubectl label namespace monitoring istio-injection=enabled
+
+
+# Step 4: Deploy the Bookinfo demo application
+echo "Deploying Bookinfo sample app..."
+kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml
+
+# Step 5: Expose the Bookinfo app through Istio gateway
+echo "Exposing Bookinfo app through Istio gateway..."
+kubectl apply -f samples/bookinfo/networking/bookinfo-gateway.yaml
+
+# Step 6: Apply VirtualService to route traffic to v2 of ratings
+echo "Creating VirtualService for ratings..."
+cat <11 CRD Custom Resource Definition - KubernetesLabs
A resource is an endpoint in the Kubernetes API that stores a collection of API objects of a certain kind; for example, the builtin pods resource contains a collection of Pod objects.
A custom resource is an extension of the Kubernetes API that is not necessarily available in a default Kubernetes installation. It represents a customization of a particular Kubernetes installation. However, many core Kubernetes functions are now built using custom resources, making Kubernetes more modular.
Custom resources can appear and disappear in a running cluster through dynamic registration, and cluster admins can update custom resources independently of the cluster itself.
Once a custom resource is installed, users can create and access its objects using kubectl, just as they do for built-in resources like Pods.
The custom resource created is also stored in the etcd cluster with proper replication and lifecycle management.
\ No newline at end of file
diff --git a/mkdocs-site/11-CRD-Custom-Resource-Definition/resources/crd-object.yaml b/mkdocs-site/11-CRD-Custom-Resource-Definition/resources/crd-object.yaml
new file mode 100644
index 0000000..b10f5bd
--- /dev/null
+++ b/mkdocs-site/11-CRD-Custom-Resource-Definition/resources/crd-object.yaml
@@ -0,0 +1,10 @@
+# The apiVerison is taken from the crd defintion
+# ./
+apiVersion: "codewizard.co.il/v1"
+kind: "CodeWizardCRD"
+metadata:
+ name: "codewizard-object"
+spec:
+ crdSpec: "--" # String
+ image: "--" # String
+ replicas: 3 # Integer
diff --git a/mkdocs-site/11-CRD-Custom-Resource-Definition/resources/crd.yaml b/mkdocs-site/11-CRD-Custom-Resource-Definition/resources/crd.yaml
new file mode 100644
index 0000000..c32618b
--- /dev/null
+++ b/mkdocs-site/11-CRD-Custom-Resource-Definition/resources/crd.yaml
@@ -0,0 +1,53 @@
+# The required apiVersion for the CRD
+apiVersion: apiextensions.k8s.io/v1
+
+# The Kind is: 'CustomResourceDefinition'
+kind: CustomResourceDefinition
+metadata:
+ # name must match the spec fields below, and be in the form: .
+ # In this sample we define the name & group:
+ # Refer to the `spec.names` below
+ name: custom-crd.codewizard.co.il
+spec:
+ # The CRD can be applied to either Namespaced or Cluster
+ # In this case we set it to Name
+ scope: Namespaced
+
+ # group name to use for REST API: /apis//
+ # same group as defined under `metadata.name`
+ group: codewizard.co.il
+
+ names:
+ # plural name to be used in the URL: /apis///
+ plural: custom-crd
+ # singular name to be used as an alias on the CLI and for display
+ singular: cwcrd
+ # kind is normally the CamelCased singular type. Your resource manifests use this.
+ kind: CodeWizardCRD
+ # shortNames allow shorter string to match your resource on the CLI
+ shortNames:
+ - cwcrd
+
+ # list of versions supported by this CustomResourceDefinition
+ versions:
+ - name: v1
+ # Each version can be enabled/disabled by Served flag.
+ served: true
+ # One and only one version must be marked as the storage version.
+ storage: true
+ schema:
+ openAPIV3Schema:
+ required: ["spec"]
+ type: object
+ # The properties which be defined under the `spec`
+ properties:
+ spec:
+ type: object
+ # The properties which can be defined and their type
+ properties:
+ crdSpec:
+ type: string
+ image:
+ type: string
+ replicas:
+ type: integer
diff --git a/mkdocs-site/12-Wordpress-MySQL-PVC/Namespace.yaml b/mkdocs-site/12-Wordpress-MySQL-PVC/Namespace.yaml
new file mode 100644
index 0000000..3f3c716
--- /dev/null
+++ b/mkdocs-site/12-Wordpress-MySQL-PVC/Namespace.yaml
@@ -0,0 +1,4 @@
+apiVersion: v1
+kind: Namespace
+metadata:
+ name: wp-demo
diff --git a/mkdocs-site/12-Wordpress-MySQL-PVC/all.yaml b/mkdocs-site/12-Wordpress-MySQL-PVC/all.yaml
new file mode 100644
index 0000000..edbdcf0
--- /dev/null
+++ b/mkdocs-site/12-Wordpress-MySQL-PVC/all.yaml
@@ -0,0 +1,156 @@
+apiVersion: v1
+kind: Namespace
+metadata:
+ name: wp-demo
+---
+apiVersion: v1
+data:
+ mysql-password.txt: bm90MkhhcmQyR3Vlc3M=
+kind: Secret
+metadata:
+ name: mysql-pass-8ttc4k2t5f
+ namespace: wp-demo
+type: Opaque
+---
+apiVersion: v1
+kind: Service
+metadata:
+ labels:
+ app: wordpress
+ tier: mysql
+ name: mysql
+ namespace: wp-demo
+spec:
+ clusterIP: None
+ ports:
+ - port: 3306
+ selector:
+ app: wordpress
+ tier: mysql
+---
+apiVersion: v1
+kind: Service
+metadata:
+ labels:
+ app: wordpress
+ name: wordpress
+ namespace: wp-demo
+spec:
+ ports:
+ - port: 8089
+ selector:
+ app: wordpress
+ tier: frontend
+ type: LoadBalancer
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ labels:
+ app: wordpress
+ tier: mysql
+ name: mysql
+ namespace: wp-demo
+spec:
+ selector:
+ matchLabels:
+ app: wordpress
+ tier: mysql
+ strategy:
+ type: Recreate
+ template:
+ metadata:
+ labels:
+ app: wordpress
+ tier: mysql
+ spec:
+ containers:
+ - env:
+ - name: MYSQL_ROOT_PASSWORD
+ valueFrom:
+ secretKeyRef:
+ key: mysql-password.txt
+ name: mysql-pass-8ttc4k2t5f
+ image: mysql:5.6
+ name: mysql
+ ports:
+ - containerPort: 3306
+ name: mysql
+ volumeMounts:
+ - mountPath: /var/lib/mysql
+ name: mysql-persistent-storage
+ volumes:
+ - name: mysql-persistent-storage
+ persistentVolumeClaim:
+ claimName: mysql-wordpress
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ labels:
+ app: wordpress
+ name: wordpress
+ namespace: wp-demo
+spec:
+ selector:
+ matchLabels:
+ app: wordpress
+ tier: frontend
+ strategy:
+ type: Recreate
+ template:
+ metadata:
+ labels:
+ app: wordpress
+ tier: frontend
+ spec:
+ containers:
+ - env:
+ - name: WORDPRESS_DB_HOST
+ value: mysql
+ - name: WORDPRESS_DB_PASSWORD
+ valueFrom:
+ secretKeyRef:
+ key: mysql-password.txt
+ name: mysql-pass-8ttc4k2t5f
+ image: wordpress:4.8-apache
+ name: wordpress
+ ports:
+ - containerPort: 80
+ name: wordpress
+ volumeMounts:
+ - mountPath: /var/www/html
+ name: wordpress-persistent-storage
+ volumes:
+ - name: wordpress-persistent-storage
+ persistentVolumeClaim:
+ claimName: wp-pv-claim
+---
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ labels:
+ app: mysql-wordpress
+ tier: mysql
+ name: mysql-pvc
+ namespace: wp-demo
+spec:
+ accessModes:
+ - ReadWriteOnce
+ resources:
+ requests:
+ storage: 20Gi
+---
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ labels:
+ app: wordpress
+ name: wp-pv-claim
+ namespace: wp-demo
+spec:
+ accessModes:
+ - ReadWriteOnce
+ resources:
+ requests:
+ storage: 20Gi
diff --git a/mkdocs-site/12-Wordpress-MySQL-PVC/index.html b/mkdocs-site/12-Wordpress-MySQL-PVC/index.html
new file mode 100644
index 0000000..64c04f6
--- /dev/null
+++ b/mkdocs-site/12-Wordpress-MySQL-PVC/index.html
@@ -0,0 +1,78 @@
+ 12 Wordpress MySQL PVC - KubernetesLabs
-Chart.yaml# Defines chart metadata and values schema
+-values.yaml# Default configuration values
+-templates/# Deployment templates using Go templating language
+-deployment.yaml# Deployment manifest template
+-service.yaml# Service manifest template
+-README.md# Documentation for your chart
+
helm package packages a chart into a versioned chart archive file. If a path is given, this will “look” at that path for a chart which must contain a Chart.yaml file and then pack that directory.
helmpackagecodewizard-helm-demo
+
This command will create a file called codewizard-helm-demo-<version>.tgz inside your current directory.
Perform a Helm upgrade on the codewizard-helm-demo release:
# upgrade and pass a different message than the one from the default values
+# Use the --set to pass the desired value
+helmupgrade\
+codewizard-helm-demo\
+codewizard-helm-demo-0.1.0.tgz\
+--setnginx.conf.message="Helm Rocks"
+
Welcome to the Logging hands-on lab! In this tutorial, we will learn the essentials of Logging in Kubernetes clusters.
We will deploy a sample application, configure log collection, and explore logs using popular tools like Fluentd, Elasticsearch, and Kibana (EFK stack).
In this lab, we will learn how to set up and configure *Prometheus and Grafana for monitoring a Kubernetes cluster.
You will install Prometheus to collect metrics from the cluster and Grafana to visualize those metrics.
By the end of this lab, you will have a functional monitoring stack that provides insights into the health and performance of your Kubernetes environment.
Prometheus and Grafana Setup and Configuration Guide¶
This guide serves as a comprehensive walkthrough of the steps to set up Prometheus and Grafana on your Kubernetes cluster.
It includes hands-on steps for installing Prometheus using Helm, configuring Prometheus to collect metrics, setting up Grafana to visualize key metrics, and automating the setup using a bash script.
We will use Helm, to deploy Prometheus and Grafana.
Step 01 - Add Prometheus and Grafana Helm Repositories¶
Let’s add the official Helm charts for Prometheus and Grafana:
# Add Prometheus community Helm repository
+helmrepoaddprometheus-communityhttps://prometheus-community.github.io/helm-charts
+# Add Grafana Helm repository
+helmrepoaddgrafanahttps://grafana.github.io/helm-charts
+# Update your Helm repositories to make sure they are up-to-date
+helmrepoupdate
+
Prometheus is installed using the prometheus-stackHelm chart.
# Install Prometheus
+# Alertmanager
+# Node Exporter
+# Create the `monitoring` namespace if it does not exist.
+helminstallprometheus\
+--namespacemonitoring\
+--create-namespace\
+prometheus-community/kube-prometheus-stack
+
+# Verify the status of the release using the following:
+helmstatusprometheus-nmonitoring
+
Grafana will expose a service in your Kubernetes cluster.
To access it, you need a password and port forwarding.
# In order to get the Grafana admin password, run the following command:
+kubectlgetsecretgrafana\
+--namespacemonitoring\
+-ojsonpath='{.data.admin-password}'|base64--decode;echo
+
+# Set the port forwarding so you can access the service using your browsers
+kubectlport-forward\
+--namespacemonitoring\
+service/grafana3000:80
+
Prometheus can collect various metrics from your Kubernetes cluster automatically if the right exporters are enabled.
The kube-prometheus-stack chart that you installed earlier automatically configures Prometheus to scrape a number of Kubernetes components (like kubelet, node-exporter, and kube-state-metrics) for various metrics.
First, let’s label some Nodes to use with Node Affinity:
# Get list of nodes
+kubectlgetnodes
+
+# Label a node with environment=production
+kubectllabelnodes<node-name>environment=production
+
+# Label another node with environment=development
+kubectllabelnodes<node-name>environment=development
+
+# Verify the labels
+kubectlgetnodes--show-labels
+
Step 02 - Create a Pod with Required Node Affinity¶
Create a Pod that must run on a Node with environment=production:
# Create the Pod
+kubectlapply-faffinity-required-pod.yaml
+
+# Check which Node the Pod is running on
+kubectlgetpodaffinity-required-pod-owide
+
+# Verify it's running on the production Node
+kubectldescribepodaffinity-required-pod|grepNode:
+
Step 03 - Create a Pod with Preferred Node Affinity¶
Create a Pod that prefers to run on a Node with environment=development:
# Create the Pod
+kubectlapply-faffinity-preferred-pod.yaml
+
+# Check which Node the Pod is running on
+kubectlgetpodaffinity-preferred-pod-owide
+
+# This Pod will prefer the development Node but can run elsewhere if needed
+
Step 04 - Experiment with Node Affinity Operators¶
Node Affinity supports several operators:
In: Label value is in the list of values
NotIn: Label value is not in the list of values
Exists: Label key exists (value does not matter)
DoesNotExist: Label key does not exist
Gt: Label value is greater than the specified value (numeric comparison)
Lt: Label value is less than the specified value (numeric comparison)
Let’s taint a Node to dedicate it for special workloads:
# Apply a taint to a Node
+kubectltaintnodes<node-name>dedicated=special-workload:NoSchedule
+
+# Verify the taint
+kubectldescribenode<node-name>|grepTaints
+
# Create the Pod
+kubectlapply-fpod-without-toleration.yaml
+
+# Check the Pod status - it should not be scheduled on the tainted Node
+kubectlgetpodpod-without-toleration-owide
+
+# If all your Nodes are tainted, the Pod will remain Pending
+kubectldescribepodpod-without-toleration
+
# Create the Pod
+kubectlapply-fpod-with-toleration.yaml
+
+# This Pod can now be scheduled on the tainted Node
+kubectlgetpodpod-with-toleration-owide
+
The NoExecute effect is special - it evicts running Pods:
# Apply a NoExecute taint
+kubectltaintnodes<node-name>maintenance=true:NoExecute
+
+# Any Pods on this Node without matching toleration will be evicted
+kubectlgetpods-owide--watch
+
Let’s add a toleration with tolerationSeconds to delay eviction:
apiVersion:v1
+kind:Pod
+metadata:
+name:pod-with-delayed-eviction
+spec:
+tolerations:
+-key:"maintenance"
+operator:"Equal"
+value:"true"
+effect:"NoExecute"
+tolerationSeconds:300# Pod will be evicted after 5 minutes
+containers:
+-name:nginx
+image:nginx:latest
+
Part 03 - Combining Affinity, Taints, and Tolerations¶
We can combine Node Affinity with Taints and Tolerations for fine-grained control.
Let’s simulate a dedicated Node pool for GPU workloads:
# Label a Node for GPU workload
+kubectllabelnodes<node-name>hardware=gpu
+
+# Taint the Node to prevent non-GPU Pods
+kubectltaintnodes<node-name>nvidia.com/gpu=true:NoSchedule
+
# Remove a taint from a Node (add a minus sign at the end)
+kubectltaintnodes<node-name>dedicated=special-workload:NoSchedule-
+
+# Remove all taints with a specific key
+kubectltaintnodes<node-name>nvidia.com/gpu-
+
# Delete all test Pods
+kubectldeletepodaffinity-required-podaffinity-preferred-pod
+kubectldeletepodpod-with-tolerationpod-without-toleration
+kubectldeletepodgpu-workload
+
In this lab, we will learn about Pod Disruption Budgets (PDB) in Kubernetes.
We will explore how to define and implement PDBs to ensure application availability during voluntary disruptions, such as node maintenance or cluster upgrades.
By the end of this lab, you will understand how to create and manage Pod Disruption Budgets to maintain the desired level of service availability in your Kubernetes cluster.
PodDisruptionBudgets: Budgeting the Number of Faults to Tolerate¶
A pod disruption budget is an indicator of the number of disruptions that can be tolerated at a given time for a class of pods (a budget of faults).
Disruptions may be caused by deliberate or accidental Pod deletion.
Whenever a disruption to the pods in a service is calculated to cause the service to drop below the budget, the operation is paused until it can maintain the budget. This means that the drain event could be temporarily halted while it waits for more pods to become available such that the budget isn’t crossed by evicting the pods.
You can specify Pod Disruption Budgets for Pods managed by these built-in Kubernetes controllers:
Deployment
ReplicationController
ReplicaSet
StatefulSet
For this tutorial you should get familier with Kubernetes Eviction Policies, as it demonstrates how Pod Disruption Budgets handle evictions.
As in the Kubernetes Eviction Policies tutorial, we start with
apiVersion:apps/v1
+kind:Deployment
+metadata:
+name:nginx-deployment
+namespace:codewizard
+labels:
+app:nginx# <- We will use this name below
+...
+
Now we can create the Pod Disruption Budget:
apiVersion:policy/v1beta1
+kind:PodDisruptionBudget
+metadata:
+name:nginx-pdb
+spec:
+minAvailable:1# <--- This will insure that we will have at least 1
+selector:
+matchLabels:
+app:nginx# <- The deployment app label
+
Now let’s check the Node conditions again to see if we have MemoryPressure:
$kubectldescribenodeminikube|grepMemoryPressure
+
+# Output should be similar to
+MemoryPressureFalse...KubeletHasSufficientMemorykubelethassufficientmemoryavailable
+
- As we can see, we still have sufficient memory available.
\ No newline at end of file
diff --git a/mkdocs-site/17-PodDisruptionBudgets-PDB/resources/50MB-ram.yaml b/mkdocs-site/17-PodDisruptionBudgets-PDB/resources/50MB-ram.yaml
new file mode 100644
index 0000000..a3bddfa
--- /dev/null
+++ b/mkdocs-site/17-PodDisruptionBudgets-PDB/resources/50MB-ram.yaml
@@ -0,0 +1,24 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: busybox
+spec:
+ replicas: 3
+ selector:
+ matchLabels:
+ app: busybox
+ template:
+ metadata:
+ labels:
+ app: busybox
+ spec:
+ containers:
+ - name: busybox
+ image: busybox
+ resources:
+ requests:
+ memory: "50Mi"
+ cpu: "250m"
+ limits:
+ memory: "128Mi"
+ cpu: "500m"
diff --git a/mkdocs-site/17-PodDisruptionBudgets-PDB/resources/Deployment.yaml b/mkdocs-site/17-PodDisruptionBudgets-PDB/resources/Deployment.yaml
new file mode 100644
index 0000000..2babe59
--- /dev/null
+++ b/mkdocs-site/17-PodDisruptionBudgets-PDB/resources/Deployment.yaml
@@ -0,0 +1,26 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: nginx-deployment
+ namespace: codewizard
+ labels:
+ app: nginx
+spec:
+ replicas: 2
+ selector:
+ matchLabels:
+ app: nginx
+ template:
+ metadata:
+ labels:
+ app: nginx
+ spec:
+ containers:
+ - name: nginx
+ image: nginx
+ ports:
+ - containerPort: 80
+ resources:
+ limits:
+ memory: 256Mi
+ cpu: 500ms
diff --git a/mkdocs-site/17-PodDisruptionBudgets-PDB/resources/PodDisruptionBudget.yaml b/mkdocs-site/17-PodDisruptionBudgets-PDB/resources/PodDisruptionBudget.yaml
new file mode 100644
index 0000000..6084849
--- /dev/null
+++ b/mkdocs-site/17-PodDisruptionBudgets-PDB/resources/PodDisruptionBudget.yaml
@@ -0,0 +1,9 @@
+apiVersion: policy/v1beta1
+kind: PodDisruptionBudget
+metadata:
+ name: nginx-pdb
+spec:
+ minAvailable: 1 # <--- This will insure that we will have at least 1
+ selector:
+ matchLabels:
+ app: nginx
\ No newline at end of file
diff --git a/mkdocs-site/17-PodDisruptionBudgets-PDB/startMinikube.sh b/mkdocs-site/17-PodDisruptionBudgets-PDB/startMinikube.sh
new file mode 100644
index 0000000..728cae6
--- /dev/null
+++ b/mkdocs-site/17-PodDisruptionBudgets-PDB/startMinikube.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+# For more details about Feature Gates read:
+# https://kubernetes.io/docs/reference/command-line-tools-reference/feature-gates/#feature-stages
+#
+# For more details about eviction-signals
+# https://kubernetes.io/docs/tasks/administer-cluster/out-of-resource/#eviction-signals
+
+minikube start \
+ --extra-config=kubelet.eviction-hard="memory.available<480M" \
+ --extra-config=kubelet.eviction-pressure-transition-period="30s" \
+ --extra-config=kubelet.feature-gates="ExperimentalCriticalPodAnnotation=true"
+
+kubectl describe node minikube | grep MemoryPressure
\ No newline at end of file
diff --git a/mkdocs-site/18-ArgoCD/ArgoCD.sh b/mkdocs-site/18-ArgoCD/ArgoCD.sh
new file mode 100644
index 0000000..0ad6e26
--- /dev/null
+++ b/mkdocs-site/18-ArgoCD/ArgoCD.sh
@@ -0,0 +1,63 @@
+# Define the desired namespace
+NAMESPACE=argocd
+
+# start the minikube cluster
+# minikube start
+
+# Download ArgoCD CLI
+VERSION=$(curl --silent "https://api.github.com/repos/argoproj/argo-cd/releases/latest" | grep '"tag_name"' | sed -E 's/.*"([^"]+)".*/\1/')
+sudo curl -sSL -o /usr/local/bin/argocd https://github.com/argoproj/argo-cd/releases/download/$VERSION/argocd-linux-amd64
+sudo chmod +x /usr/local/bin/argocd
+
+# Deploy ArgoCD - create namespace
+kubectl create namespace $NAMESPACE
+
+# Set the default namesapce
+kubectl config set-context --current --namespace=$NAMESPACE
+
+# Change the argocd-server service type to LoadBalancer:
+#kubectl patch svc argocd-server -n $NAMESPACE -p '{"spec": {"type": "NodePort"}}'
+
+# Set the new desired Deployment
+cat << EOF > kustomization.yaml
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+namespace: argocd
+resources:
+ - https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
+
+patchesStrategicMerge:
+- patch-replace.yaml
+EOF
+
+# Set the desired patch
+cat << EOF > patch-replace.yaml
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: argocd-server
+spec:
+ selector:
+ matchLabels:
+ app.kubernetes.io/name: argocd-server
+ template:
+ spec:
+ containers:
+ - name: argocd-server
+ command:
+ - argocd-server
+ - --insecure
+ - --staticassets
+ - /shared/app
+EOF
+
+kubectl kustomize . | kubectl apply -f -
+sleep 30
+
+echo '---------------------------------------------------------------'
+echo 'User : admin'
+echo 'Password: ' $(kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d)
+echo '---------------------------------------------------------------'
+
+
+kubectl port-forward svc/argocd-server -n argocd 8085:80
diff --git a/mkdocs-site/18-ArgoCD/index.html b/mkdocs-site/18-ArgoCD/index.html
new file mode 100644
index 0000000..63e5e24
--- /dev/null
+++ b/mkdocs-site/18-ArgoCD/index.html
@@ -0,0 +1,304 @@
+ 18 ArgoCD - KubernetesLabs
Sync windows allow you to define time periods when syncs are allowed or denied:
# Add a sync window to allow syncs only during business hours
+argocdprojwindowsaddmy-project\
+--kindallow\
+--schedule"0 9 * * 1-5"\
+--duration8h\
+--applications"*"
+
\ No newline at end of file
diff --git a/mkdocs-site/18-ArgoCD/install.sh b/mkdocs-site/18-ArgoCD/install.sh
new file mode 100644
index 0000000..0a132f6
--- /dev/null
+++ b/mkdocs-site/18-ArgoCD/install.sh
@@ -0,0 +1,3 @@
+
+
+kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d; echo
diff --git a/mkdocs-site/18-ArgoCD/kustomization.yaml b/mkdocs-site/18-ArgoCD/kustomization.yaml
new file mode 100644
index 0000000..71b4516
--- /dev/null
+++ b/mkdocs-site/18-ArgoCD/kustomization.yaml
@@ -0,0 +1,8 @@
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+namespace: argocd
+resources:
+ - https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
+
+patchesStrategicMerge:
+- patch-replace.yaml
diff --git a/mkdocs-site/18-ArgoCD/patch-replace.yaml b/mkdocs-site/18-ArgoCD/patch-replace.yaml
new file mode 100644
index 0000000..5839054
--- /dev/null
+++ b/mkdocs-site/18-ArgoCD/patch-replace.yaml
@@ -0,0 +1,17 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: argocd-server
+spec:
+ selector:
+ matchLabels:
+ app.kubernetes.io/name: argocd-server
+ template:
+ spec:
+ containers:
+ - name: argocd-server
+ command:
+ - argocd-server
+ - --insecure
+ - --staticassets
+ - /shared/app
diff --git a/mkdocs-site/19-CustomScheduler/codeWizardScheduler.sh b/mkdocs-site/19-CustomScheduler/codeWizardScheduler.sh
new file mode 100644
index 0000000..e544d91
--- /dev/null
+++ b/mkdocs-site/19-CustomScheduler/codeWizardScheduler.sh
@@ -0,0 +1,101 @@
+#!/bin/bash
+# Author: Nir Geier
+
+# `set -o pipefail`
+# When executing the sequence of commands connected to the pipe,
+# as long as any one command returns a non-zero value,
+# the entire pipe returns a non-zero value,
+# even if the last command returns 0.
+#
+# In other words, the chain of command will fail if any of the command fail
+
+set -eo pipefail
+# `set -x`
+# Shell mode, where all executed commands are printed to the terminal
+# Remark if you dont wish to view full log
+set -x
+
+# Start minikube if required
+source ../../scripts/startMinikube.sh
+
+# Deploy our demo pods
+kubectl kustomize ./resources | kubectl apply -f -
+
+# Start the API and listen on port 8081
+kubectl proxy --port=8081 &
+
+# Syntax:
+# ${parameter:-word}
+# If parameter is unset or null,
+# the expansion of word is substituted.
+# Otherwise,
+# the value of parameter is substituted.
+
+# You can set those paramters out side of this script
+# export CLUSTER_URL=
+CLUSTER_URL="${CLUSTER_URL:-127.0.0.1:8081}"
+CUSTOM_SCHEDULER="${CUSTOM_SCHEDULER:-codeWizardScheduler}"
+
+# Scheduler should always run
+while true; do
+ # Get a list of all our pods in pending state
+ for POD in $(kubectl get pods \
+ --server ${CLUSTER_URL} \
+ --output jsonpath='{.items..metadata.name}' \
+ --field-selector=status.phase==Pending);
+ do
+
+ # Get the desired schedulerName if the pod has defined any schedulerName
+ CUSTOM_SCHEDULER_NAME=$(kubectl get pod ${POD} \
+ --output jsonpath='{.spec.schedulerName}')
+
+ # Check if the desired schedulerName is our custome one
+ # If its a match this is where our custom scheduler will "jump in"
+ if [ "${CUSTOM_SCHEDULER_NAME}" == "${CUSTOM_SCHEDULER}" ];
+ then
+ # Get the pod namespace
+ NAMESPACE=$(kubectl get pod ${POD} \
+ --output jsonpath='{.metadata.namespace}')
+
+ # Get an array for of all the nodes
+ NODES=($(kubectl get nodes \
+ --server ${CLUSTER_URL} \
+ --output jsonpath='{.items..metadata.name}'));
+
+ # Store a number for the length of our NODES array
+ NODES_LENGTH=${#NODES[@]}
+
+ # Randomly select a node from the array
+ # $RANDOM % $NODES_LENGTH will be the remainder
+ # of a random number divided by the length of our nodes
+ # In the case of 1 node this is always ${NODES[0]}
+ NODE=${NODES[$[$RANDOM % $NODES_LENGTH]]}
+
+ # Bind the current pod to the node selected above
+ # The "binding" is done using API call to pods/.../binding
+ curl \
+ --request POST \
+ --silent \
+ --fail \
+ --header "Content-Type:application/json" \
+ --data '{"apiVersion":"v1",
+ "kind": "Binding",
+ "metadata": {
+ "name": "'${POD}'"
+ },
+ "target": {
+ "apiVersion": "v1",
+ "kind": "Node",
+ "name": "'${NODE}'"
+ }
+ }' \
+ http://${CLUSTER_URL}/api/v1/namespaces/${NAMESPACE}/pods/${POD}/binding/ >/dev/null \
+ && echo "${POD} was assigned to ${NODE}" \
+ || echo "Failed to assign ${POD} to ${NODE}"
+ fi
+ done
+ # Current scheduling done, sleep and wake up for the next iteration
+ echo "Scheduler ig going to sleep"
+
+ sleep 15s
+done
\ No newline at end of file
diff --git a/mkdocs-site/19-CustomScheduler/index.html b/mkdocs-site/19-CustomScheduler/index.html
new file mode 100644
index 0000000..0653e37
--- /dev/null
+++ b/mkdocs-site/19-CustomScheduler/index.html
@@ -0,0 +1,124 @@
+ 19 CustomScheduler - KubernetesLabs
Scheduling is the process of selecting a node for a pod to run on.
In this lab we will write our own pods scheduler.
It is probably not something that you will ever need to do, but still it’s a good practice to understand how scheduling works in K8S and how you can extend it.
###
+# Sample KubeSchedulerConfiguration
+###
+#
+# You can configure `kube-scheduler` to run more than one profile.
+# Each profile has an associated scheduler name and can have a different
+# set of plugins configured in its extension points.
+
+# With the following sample configuration,
+# the scheduler will run with two profiles:
+# - default plugins
+# - all scoring plugins disabled
+
+apiVersion:kubescheduler.config.k8s.io/v1beta1
+kind:KubeSchedulerConfiguration
+profiles:
+-schedulerName:default-scheduler
+-schedulerName:no-scoring-scheduler
+plugins:
+preScore:
+disabled:
+-name:'*'
+score:
+disabled:
+-name:'*'
+
Once you have your scheduler code, you can use it in your pod scheduler:
# In this sample we use deployment but it will apply to any pod
+...
+apiVersion:apps/v1
+kind:Deployment
+spec:
+spec:
+# This is the import part of this file.
+# Here we define our custom scheduler
+schedulerName:CodeWizardScheduler# <------
+containers:
+-name:nginx
+image:nginx
+
The “trick” is loop over all the waiting pods and search for the custom scheduler match in spec.schedulerName
...
+# Get a list of all our pods in pending state
+forPODin$(kubectlgetpods\
+--server${CLUSTER_URL}\
+--all-namespaces\
+--outputjsonpath='{.items..metadata.name}'\
+--field-selector=status.phase==Pending);
+do
+
+# Get the desired schedulerName if th epod has defined any schedulerName
+CUSTOM_SCHEDULER_NAME=$(kubectlgetpod${POD}\
+--outputjsonpath='{.spec.schedulerName}')
+
+# Check if the desired schedulerName is our custome one
+# If its a match this is where our custom scheduler will "jump in"
+if["${CUSTOM_SCHEDULER_NAME}"=="${CUSTOM_SCHEDULER}"];
+then
+# Do your magic here ......
+# Schedule the PODS as you wish
+fi
+...
+
\ No newline at end of file
diff --git a/mkdocs-site/19-CustomScheduler/resources/Deployment.yaml b/mkdocs-site/19-CustomScheduler/resources/Deployment.yaml
new file mode 100644
index 0000000..ba1bee2
--- /dev/null
+++ b/mkdocs-site/19-CustomScheduler/resources/Deployment.yaml
@@ -0,0 +1,26 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: nginx
+spec:
+ replicas: 3
+ selector:
+ matchLabels:
+ app: nginx
+ template:
+ metadata:
+ labels:
+ app: nginx
+ spec:
+ # This is the import part of this file.
+ # Here we define our custom scheduler
+ schedulerName: codeWizardScheduler
+ containers:
+ - name: nginx
+ image: nginx
+ resources:
+ limits:
+ memory: "64Mi"
+ cpu: "250m"
+ ports:
+ - containerPort: 80
\ No newline at end of file
diff --git a/mkdocs-site/19-CustomScheduler/resources/Namespace.yaml b/mkdocs-site/19-CustomScheduler/resources/Namespace.yaml
new file mode 100644
index 0000000..6e64f11
--- /dev/null
+++ b/mkdocs-site/19-CustomScheduler/resources/Namespace.yaml
@@ -0,0 +1,4 @@
+apiVersion: v1
+kind: Namespace
+metadata:
+ name: codewizard
\ No newline at end of file
diff --git a/mkdocs-site/19-CustomScheduler/resources/_KubeSchedulerConfiguration.yaml b/mkdocs-site/19-CustomScheduler/resources/_KubeSchedulerConfiguration.yaml
new file mode 100644
index 0000000..47d842d
--- /dev/null
+++ b/mkdocs-site/19-CustomScheduler/resources/_KubeSchedulerConfiguration.yaml
@@ -0,0 +1,25 @@
+###
+# Sample KubeSchedulerConfiguration
+###
+#
+# You can configure `kube-scheduler` to run more than one profile.
+# Each profile has an associated scheduler name and can have a different
+# set of plugins configured in its extension points.
+
+# With the following sample configuration,
+# the scheduler will run with two profiles:
+# - default plugins
+# - all scoring plugins disabled.
+
+apiVersion: kubescheduler.config.k8s.io/v1beta1
+kind: KubeSchedulerConfiguration
+profiles:
+ - schedulerName: default-scheduler
+ - schedulerName: no-scoring-scheduler
+ plugins:
+ preScore:
+ disabled:
+ - name: '*'
+ score:
+ disabled:
+ - name: '*'
\ No newline at end of file
diff --git a/mkdocs-site/19-CustomScheduler/resources/kustomization.yaml b/mkdocs-site/19-CustomScheduler/resources/kustomization.yaml
new file mode 100644
index 0000000..00d77fa
--- /dev/null
+++ b/mkdocs-site/19-CustomScheduler/resources/kustomization.yaml
@@ -0,0 +1,8 @@
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+# Set the default namespace for all the resources
+namespace: codewizard
+
+resources:
+ - Namespace.yaml
+ - Deployment.yaml
diff --git a/mkdocs-site/20-CronJob/K8S/ClusterRoleBinding.yaml b/mkdocs-site/20-CronJob/K8S/ClusterRoleBinding.yaml
new file mode 100644
index 0000000..67bbff9
--- /dev/null
+++ b/mkdocs-site/20-CronJob/K8S/ClusterRoleBinding.yaml
@@ -0,0 +1,17 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+ # This name will be used in `subjects.name`
+ name: cronjob-rbac
+ # This name will be used in `subjects.namespace`
+ namespace: test
+subjects:
+ - kind: ServiceAccount
+ # Same value as upper's `metadata.name`
+ name: default
+ # Same value as upper's `metadata.namespace`
+ namespace: test
+roleRef:
+ kind: ClusterRole
+ name: cluster-admin
+ apiGroup: rbac.authorization.k8s.io
\ No newline at end of file
diff --git a/mkdocs-site/20-CronJob/K8S/CronJob.yaml b/mkdocs-site/20-CronJob/K8S/CronJob.yaml
new file mode 100644
index 0000000..3f0d662
--- /dev/null
+++ b/mkdocs-site/20-CronJob/K8S/CronJob.yaml
@@ -0,0 +1,26 @@
+apiVersion: batch/v1beta1
+kind: CronJob
+metadata:
+ name: cron-test
+ namespace: test
+spec:
+ schedule: '*/1 * * * *'
+ jobTemplate:
+ spec:
+ template:
+ spec:
+ containers:
+ - command:
+ - /bin/sh
+ - '-c'
+ - >-
+ apk update \
+ && apk add curl \
+ && curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" \
+ && chmod +x ./kubectl \
+ && ./kubectl get pods -A
+ image: alpine
+ imagePullPolicy: IfNotPresent
+ name: alpine-cronjob
+ dnsPolicy: ClusterFirst
+ restartPolicy: OnFailure
\ No newline at end of file
diff --git a/mkdocs-site/20-CronJob/K8S/Namespace.yaml b/mkdocs-site/20-CronJob/K8S/Namespace.yaml
new file mode 100644
index 0000000..e3834ea
--- /dev/null
+++ b/mkdocs-site/20-CronJob/K8S/Namespace.yaml
@@ -0,0 +1,4 @@
+apiVersion: v1
+kind: Namespace
+metadata:
+ name: codewizard
\ No newline at end of file
diff --git a/mkdocs-site/20-CronJob/K8S/ServiceAccount.yaml b/mkdocs-site/20-CronJob/K8S/ServiceAccount.yaml
new file mode 100644
index 0000000..0511483
--- /dev/null
+++ b/mkdocs-site/20-CronJob/K8S/ServiceAccount.yaml
@@ -0,0 +1,8 @@
+# NOTE:
+# The service account `default:default` already exists in k8s cluster when the cluster is created.
+# In this sample we will create a default user under our namespace
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: default
+ namespace: test
diff --git a/mkdocs-site/20-CronJob/K8S/kustomization.yaml b/mkdocs-site/20-CronJob/K8S/kustomization.yaml
new file mode 100644
index 0000000..2ceedeb
--- /dev/null
+++ b/mkdocs-site/20-CronJob/K8S/kustomization.yaml
@@ -0,0 +1,8 @@
+namespace: codewizard
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+resources:
+ - ClusterRoleBinding.yaml
+ - CronJob.yaml
+ - Namespace.yaml
+ - ServiceAccount.yaml
diff --git a/mkdocs-site/20-CronJob/index.html b/mkdocs-site/20-CronJob/index.html
new file mode 100644
index 0000000..e07584d
--- /dev/null
+++ b/mkdocs-site/20-CronJob/index.html
@@ -0,0 +1,95 @@
+ 20 CronJob - KubernetesLabs
What happens if the job takes longer than the schedule interval?
How would you change the schedule to run every 5 minutes?
How can you limit the number of successful or failed jobs to keep?
\ No newline at end of file
diff --git a/mkdocs-site/21-Auditing/demo.sh b/mkdocs-site/21-Auditing/demo.sh
new file mode 100644
index 0000000..16e4067
--- /dev/null
+++ b/mkdocs-site/21-Auditing/demo.sh
@@ -0,0 +1,44 @@
+#!/bin/sh
+
+# Debug mode
+set -x
+
+# Stop minikube if its running and delet prevoiud data
+minikube stop
+
+# Set the minikube home directory
+export MINIKUBE_HOME=~/.minikube
+
+# The AuditPolicy file
+AUDIT_POLICY_FILE=$MINIKUBE_HOME/files/etc/ssl/certs/Audit-Policy.yaml
+
+# Create the desired folder(s)
+mkdir -p resources
+mkdir -p logs
+
+# Check to see if we have a pre defined Audit Policy file
+if [[ ! -f $AUDIT_POLICY_FILE ]];
+then
+# Create the Policy file if its not exist
+cat < $AUDIT_POLICY_FILE
+# Log all requests at the Metadata level.
+apiVersion: audit.k8s.io/v1
+kind: Policy
+rules:
+- level: Metadata
+EOF
+fi;
+
+# Start minikube with the AuditPolicy
+minikube start \
+ --extra-config=apiserver.audit-policy-file=$AUDIT_POLICY_FILE \
+ --extra-config=apiserver.audit-log-path=${PWD}/logs/audit.log \
+ --extra-config=kubelet.cgroup-driver=systemd \
+ --alsologtostderr \
+ -v=8
+
+# Test the audit policy
+kubectl create ns TestAudit
+
+# Print out the Audit log
+kubectl logs kube-apiserver-minikube -n kube-system | grep audit.k8s.io/v1
\ No newline at end of file
diff --git a/mkdocs-site/21-Auditing/resources/Audit-Policy.yaml b/mkdocs-site/21-Auditing/resources/Audit-Policy.yaml
new file mode 100644
index 0000000..2086bf0
--- /dev/null
+++ b/mkdocs-site/21-Auditing/resources/Audit-Policy.yaml
@@ -0,0 +1,5 @@
+ # Log all requests at the Metadata level....
+ apiVersion: audit.k8s.io/v1
+ kind: Policy
+ rules:
+ - level: Metadata
diff --git a/mkdocs-site/21-KubeAPI/Dockerfile b/mkdocs-site/21-KubeAPI/Dockerfile
new file mode 100644
index 0000000..4052324
--- /dev/null
+++ b/mkdocs-site/21-KubeAPI/Dockerfile
@@ -0,0 +1,10 @@
+FROM alpine
+
+# Update and install dependencies
+RUN apk add --update nodejs npm curl
+
+# Copy the endpoint script
+COPY api_query.sh .
+
+# Set the execution bit
+RUN chmod +x api_query.sh .
\ No newline at end of file
diff --git a/mkdocs-site/21-KubeAPI/api_query.sh b/mkdocs-site/21-KubeAPI/api_query.sh
new file mode 100644
index 0000000..1ece8cf
--- /dev/null
+++ b/mkdocs-site/21-KubeAPI/api_query.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+#################################
+## Access the internal K8S API ##
+#################################
+# Point to the internal API server hostname
+APISERVER=https://kubernetes.default.svc
+
+# Path to ServiceAccount token
+# The service account is mapped by the K8S Api server in the pods
+SERVICE_ACCOUNT_FOLDER=/var/run/secrets/kubernetes.io/serviceaccount
+
+# Read this Pod's namespace if required
+# NAMESPACE=$(cat ${SERVICEACCOUNT}/namespace)
+
+# Read the ServiceAccount bearer token
+TOKEN=$(cat ${SERVICE_ACCOUNT_FOLDER}/token)
+
+# Reference the internal certificate authority (CA)
+CACERT=${SERVICE_ACCOUNT_FOLDER}/ca.crt
+
+# Explore the API with TOKEN
+curl --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" -X GET ${APISERVER}/api
\ No newline at end of file
diff --git a/mkdocs-site/21-KubeAPI/index.html b/mkdocs-site/21-KubeAPI/index.html
new file mode 100644
index 0000000..15f541b
--- /dev/null
+++ b/mkdocs-site/21-KubeAPI/index.html
@@ -0,0 +1,113 @@
+ 21 KubeAPI - KubernetesLabs
In order to demonstrate the API query we will build a custom docker image.
It is optional to use the pre-build image and skip this step.
Step 01 - The script which will be used for query K8S API¶
In order to be able to access K8S API from within a pod, we will be using the following script:
# `api_query.sh`
+
+#!/bin/sh
+
+#################################
+## Access the internal K8S API ##
+#################################
+# Point to the internal API server hostname
+API_SERVER_URL=https://kubernetes.default.svc
+
+# Path to ServiceAccount token
+# The service account is mapped by the K8S Api server in the pods
+SERVICE_ACCOUNT_FOLDER=/var/run/secrets/kubernetes.io/serviceaccount
+
+# Read this Pod's namespace if required
+# NAMESPACE=$(cat ${SERVICE_ACCOUNT_FOLDER}/namespace)
+
+# Read the ServiceAccount bearer token
+TOKEN=$(cat${SERVICE_ACCOUNT_FOLDER}/token)
+
+# Reference the internal certificate authority (CA)
+CACERT=${SERVICE_ACCOUNT_FOLDER}/ca.crt
+
+# Explore the API with TOKEN and the Certificate
+curl--cacert${CACERT}--header"Authorization: Bearer ${TOKEN}"-XGET${API_SERVER_URL}/api
+
Run the following script to verify that the connection to the API is working:
# Get the deployment pod name
+POD_NAME=$(kubectlgetpod-A-lapp=monitor-app-ojsonpath="{.items[0].metadata.name}")
+
+# Print out the logs to verify that the pods is connected to the API
+kubectlexec-it-ncodewizard$POD_NAMEsh./api_query.sh
+
\ No newline at end of file
diff --git a/mkdocs-site/21-KubeAPI/k8s/Deployment.yaml b/mkdocs-site/21-KubeAPI/k8s/Deployment.yaml
new file mode 100644
index 0000000..9cec04b
--- /dev/null
+++ b/mkdocs-site/21-KubeAPI/k8s/Deployment.yaml
@@ -0,0 +1,25 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: monitor-app
+spec:
+ selector:
+ matchLabels:
+ app: monitor-app
+ template:
+ metadata:
+ labels:
+ app: monitor-app
+ spec:
+ containers:
+ - name: monitor-app
+ image: nirgeier/monitor-app
+ args:
+ - sleep
+ - "86400"
+ resources:
+ limits:
+ memory: "128Mi"
+ cpu: "500m"
+ ports:
+ - containerPort: 8080
diff --git a/mkdocs-site/21-KubeAPI/k8s/Namespace.yaml b/mkdocs-site/21-KubeAPI/k8s/Namespace.yaml
new file mode 100644
index 0000000..af12597
--- /dev/null
+++ b/mkdocs-site/21-KubeAPI/k8s/Namespace.yaml
@@ -0,0 +1,4 @@
+apiVersion: v1
+kind: Namespace
+metadata:
+ name: codewizard
\ No newline at end of file
diff --git a/mkdocs-site/21-KubeAPI/k8s/kustomization.yaml b/mkdocs-site/21-KubeAPI/k8s/kustomization.yaml
new file mode 100644
index 0000000..07c789e
--- /dev/null
+++ b/mkdocs-site/21-KubeAPI/k8s/kustomization.yaml
@@ -0,0 +1,11 @@
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+
+namespace: codewizard
+
+commonLabels:
+ app: monitor-app
+
+resources:
+ - Namespace.yaml
+ - Deployment.yaml
\ No newline at end of file
diff --git a/mkdocs-site/21-KubeAPI/script.sh b/mkdocs-site/21-KubeAPI/script.sh
new file mode 100644
index 0000000..25a49eb
--- /dev/null
+++ b/mkdocs-site/21-KubeAPI/script.sh
@@ -0,0 +1,17 @@
+#!/bin/bash -x
+
+# Build
+docker build -t nirgeier/monitor-app .
+
+# push image to docker hub
+docker push nirgeier/monitor-app
+
+# Deploy the pod to the cluster
+kubectl kustomize k8s | kubectl delete -f -
+kubectl kustomize k8s | kubectl apply -f -
+
+# Get the deployment pod name
+POD_NAME=$(kubectl get pod -A -l app=monitor-app -o jsonpath="{.items[0].metadata.name}")
+
+# Print out the logs to verify that the pods is conneted to the API
+kubectl exec -it -n codewizard $POD_NAME sh ./api_query.sh
\ No newline at end of file
diff --git a/mkdocs-site/22-Rancher/01-pre-requirements.sh b/mkdocs-site/22-Rancher/01-pre-requirements.sh
new file mode 100644
index 0000000..11832c1
--- /dev/null
+++ b/mkdocs-site/22-Rancher/01-pre-requirements.sh
@@ -0,0 +1,38 @@
+#!/bin/bash
+
+### Install k3 & other tools on MacOS
+# brew install k3d kubectl helm
+
+# In case you are not using mac -
+curl -sfL https://get.k3s.io | sh -
+
+# Install cmctl
+# cmctl is a CLI tool that can help you to manage cert-manager resources inside your cluster.
+# https://cert-manager.io/docs/usage/cmctl/
+OS=$(go env GOOS);
+ARCH=$(go env GOARCH);
+
+## create forlder for the installation
+mkdir -p cmctl
+cd cmctl
+## Download cmctl
+### -> cmctl is a CLI tool that can help you to manage cert-manager resources inside your cluster.
+curl -sSL -o cmctl.tar.gz https://github.com/cert-manager/cert-manager/releases/download/v1.8.0/cmctl-$OS-$ARCH.tar.gz
+# Extract the xzip file
+tar xzf cmctl.tar.gz
+# Add it to the path
+sudo mv cmctl /usr/local/bin
+
+# Delete the installtion fodler
+cd ..
+rm -rf cmctl
+
+### Install k3s - Will be used later on for Rancher
+wget -q -O - https://raw.githubusercontent.com/k3d-io/k3d/main/install.sh | bash
+
+### Add the required helm charts
+helm repo add rancher https://releases.rancher.com/server-charts/latest
+helm repo add jetstack https://charts.jetstack.io
+
+# Update the charts repository
+helm repo update
diff --git a/mkdocs-site/22-Rancher/02-install.sh b/mkdocs-site/22-Rancher/02-install.sh
new file mode 100644
index 0000000..5ea3c10
--- /dev/null
+++ b/mkdocs-site/22-Rancher/02-install.sh
@@ -0,0 +1,104 @@
+#!/bin/bash
+
+set -x
+### Use this (( for tee the output to a file))
+((
+
+### Variables
+RANCHER_HOST=rancher.k3d.localhost
+CLUSTER_NAME=rancher-cluster
+CERT_MANAGER_RELEASE=v1.8.0
+API_PORT=6555
+SSL_PORT=6443
+
+### Clear prevoius content
+# docker stop $(docker ps -aq)
+# docker rm $(docker ps -aq)
+
+### Remove all docker leftovers (containers, network etc)
+docker system prune -f
+
+kubectl delete namespace cattle-system
+kubectl delete namespace cert-manager
+
+### Remove old cluster in case there are some leftovers
+k3d cluster \
+ delete \
+ $CLUSTER_NAME
+
+### Create a k3d cluster. Use the loadbalancer provided by k3d
+k3d cluster \
+ create \
+ --wait \
+ $CLUSTER_NAME \
+ --servers 1 \
+ --agents 3 \
+ --api-port $API_PORT \
+ --kubeconfig-switch-context \
+ --port $SSL_PORT:443@loadbalancer
+ # --k3s-arg "--disable=traefik@server:*" \
+ # --k3s-arg '--kubelet-arg=eviction-hard=imagefs.available<1%,nodefs.available<1%@agent:*' \
+ # --k3s-arg '--kubelet-arg=eviction-minimum-reclaim=imagefs.available=1%,nodefs.available=1%@agent:*' \
+ # --k3s-arg '--kube-apiserver-arg=feature-gates=EphemeralContainers=false@server:*'
+
+### Verify the installation
+kubectl cluster-info
+k3d cluster list
+
+### Add the k3s to the kubeconfig
+k3d kubeconfig merge \
+ $CLUSTER_NAME \
+ --kubeconfig-switch-context
+
+### Create the namespace(s) for Rancher & cert-manager
+#kubectl create namespace cattle-system
+#kubectl create namespace cert-manager
+
+### Install Cert-manager
+helm install \
+ --wait \
+ --create-namespace \
+ --set installCRDs=true \
+ --namespace cert-manager \
+ --set prometheus.enabled=true \
+ --version $CERT_MANAGER_RELEASE \
+ cert-manager jetstack/cert-manager
+
+### Verify cert-manager installation
+kubectl rollout \
+ status \
+ deploy/cert-manager \
+ --namespace cert-manager
+
+### Install racnher
+helm install \
+ --wait \
+ --create-namespace \
+ rancher rancher/rancher \
+ --namespace cattle-system \
+ --set hostname=$RANCHER_HOST
+
+### Verify rancher installation
+kubectl rollout status \
+ deploy/racnher \
+ -n cattle-system
+
+### Check that the cert-manager API is ready
+### We expect to see the foloowing message: 'The cert-manager API is ready'
+cmctl check api
+
+### Open broswer in: https://rancher.k3d.localhost
+######
+###### Important, once on this page type; thisisunsafe
+######
+
+### Get the rancher password
+kubectl get secret --namespace cattle-system bootstrap-secret -o go-template='{{.data.bootstrapPassword|base64decode}}{{"\n"}}'
+
+### Verify the cluster nodes
+kubectl get nodes
+
+### Get the pods status in the background
+kubectl get pods -A --watch &
+
+) 2>&1 ) | tee install.txt
diff --git a/mkdocs-site/22-Rancher/03-rancher-airgap.sh b/mkdocs-site/22-Rancher/03-rancher-airgap.sh
new file mode 100644
index 0000000..eb7625b
--- /dev/null
+++ b/mkdocs-site/22-Rancher/03-rancher-airgap.sh
@@ -0,0 +1,88 @@
+
+###
+### rke2 relase
+RKE2_RELEASE="https://github.com/rancher/rke2/releases/download/v1.30.3-rc4%2Brke2r1"
+
+
+# # Setup network
+# ip link add dummy0 type dummy
+# ip link set dummy0 up
+# ip addr add 203.0.113.254/31 dev dummy0
+# ip route add default via 203.0.113.255 dev dummy0 metric 1000
+
+# Install helm
+wget https://get.helm.sh/helm-v3.15.3-linux-amd64.tar.gz
+tar -zxvf helm-v3.15.3-linux-amd64.tar.gz
+mv linux-amd64/helm /usr/local/bin/helm
+
+# Download load rke2 binary
+wget $RKE2_RELEASE/rke2.linux-amd64
+chmod +x rke2.linux-amd64
+mv rke2.linux-amd64 /usr/local/bin/rke2
+
+# Get kubectl
+curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
+chmod +x kubectl
+mv kubectl /usr/local/bin/kubectl
+
+# Download load rke2 installation files
+mkdir ~/rke2-artifacts && cd ~/rke2-artifacts/
+curl -OLs $RKE2_RELEASE/rke2-images.linux-amd64.tar.zst
+curl -OLs $RKE2_RELEASE/rke2.linux-amd64.tar.gz
+curl -OLs $RKE2_RELEASE/sha256sum-amd64.txt
+curl -sfL https://get.rke2.io --output install.sh
+
+INSTALL_RKE2_ARTIFACT_PATH=~/rke2-artifacts sh install.sh
+# systemctl enable rke2-server.service
+# systemctl start rke2-server.service
+rke2 server
+
+# Set the kubeconfig
+mkdir -p ~/.kube
+ln -s /etc/rancher/rke2/rke2.yaml ~/.kube/config
+
+# Install k9s
+wget https://github.com/derailed/k9s/releases/download/v0.32.5/k9s_Linux_amd64.tar.gz
+gunzip k9s_Linux_amd64.tar.gz
+tar -xvf k9s_Linux_amd64.tar
+chmod +x k9s
+mv k9s /usr/local/bin/k9s
+
+# Set the kubeconfig
+mkdir -p ~/.kube/
+cp /etc/rancher/rke2/rke2.yaml ~/.kube/config
+
+# Check the server status
+kubectl get pods -A
+
+# Add the rancher helm repository
+helm repo add rancher-latest https://releases.rancher.com/server-charts/latest
+
+# Download the helm
+helm fetch rancher-latest/rancher
+helm repo add jetstack https://charts.jetstack.io
+helm repo update
+helm fetch jetstack/cert-manager
+
+curl -L -o cert-manager-crd.yaml https://github.com/cert-manager/cert-manager/releases/download/v1.11.0/cert-manager.crds.yaml
+kubectl create ns cert-manager
+kubectl apply -n cert-manager -f cert-manager-crd.yaml
+helm --debug install cert-manager --create-namespace -n cert-manager cert-manager-v1.15.2.tgz
+
+
+
+# Setuup the registry for rancher
+# cat << "EOF" > /etc/rancher/rke2/registries.yaml
+# mirrors:
+# docker.io:
+# endpoint:
+# - "https://globalrepo.pe.jfrog.io/remote-docker-hub"
+# EOF
+
+# docker pull quay.io/jetstack/cert-manager-ctl
+# docker pull quay.io/jetstack/cert-manager-acmesolver
+# docker pull quay.io/jetstack/cert-manager-cainjector
+# docker pull quay.io/jetstack/cert-manager-webhook
+
+# helm repo add jetstack https://charts.jetstack.io
+
diff --git a/mkdocs-site/23-MetricServer/components.yaml b/mkdocs-site/23-MetricServer/components.yaml
new file mode 100644
index 0000000..e5fdd91
--- /dev/null
+++ b/mkdocs-site/23-MetricServer/components.yaml
@@ -0,0 +1,197 @@
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ labels:
+ k8s-app: metrics-server
+ name: metrics-server
+ namespace: kube-system
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ labels:
+ k8s-app: metrics-server
+ rbac.authorization.k8s.io/aggregate-to-admin: "true"
+ rbac.authorization.k8s.io/aggregate-to-edit: "true"
+ rbac.authorization.k8s.io/aggregate-to-view: "true"
+ name: system:aggregated-metrics-reader
+rules:
+- apiGroups:
+ - metrics.k8s.io
+ resources:
+ - pods
+ - nodes
+ verbs:
+ - get
+ - list
+ - watch
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ labels:
+ k8s-app: metrics-server
+ name: system:metrics-server
+rules:
+- apiGroups:
+ - ""
+ resources:
+ - nodes/metrics
+ verbs:
+ - get
+- apiGroups:
+ - ""
+ resources:
+ - pods
+ - nodes
+ verbs:
+ - get
+ - list
+ - watch
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+ labels:
+ k8s-app: metrics-server
+ name: metrics-server-auth-reader
+ namespace: kube-system
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: Role
+ name: extension-apiserver-authentication-reader
+subjects:
+- kind: ServiceAccount
+ name: metrics-server
+ namespace: kube-system
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+ labels:
+ k8s-app: metrics-server
+ name: metrics-server:system:auth-delegator
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: system:auth-delegator
+subjects:
+- kind: ServiceAccount
+ name: metrics-server
+ namespace: kube-system
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+ labels:
+ k8s-app: metrics-server
+ name: system:metrics-server
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: system:metrics-server
+subjects:
+- kind: ServiceAccount
+ name: metrics-server
+ namespace: kube-system
+---
+apiVersion: v1
+kind: Service
+metadata:
+ labels:
+ k8s-app: metrics-server
+ name: metrics-server
+ namespace: kube-system
+spec:
+ ports:
+ - name: https
+ port: 443
+ protocol: TCP
+ targetPort: https
+ selector:
+ k8s-app: metrics-server
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ labels:
+ k8s-app: metrics-server
+ name: metrics-server
+ namespace: kube-system
+spec:
+ selector:
+ matchLabels:
+ k8s-app: metrics-server
+ strategy:
+ rollingUpdate:
+ maxUnavailable: 0
+ template:
+ metadata:
+ labels:
+ k8s-app: metrics-server
+ spec:
+ containers:
+ - args:
+ - --cert-dir=/tmp
+ - --secure-port=4443
+ - --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
+ - --kubelet-use-node-status-port
+ - --metric-resolution=15s
+ - --kubelet-insecure-tls
+ image: k8s.gcr.io/metrics-server/metrics-server:v0.6.1
+ imagePullPolicy: IfNotPresent
+ livenessProbe:
+ failureThreshold: 3
+ httpGet:
+ path: /livez
+ port: https
+ scheme: HTTPS
+ periodSeconds: 10
+ name: metrics-server
+ ports:
+ - containerPort: 4443
+ name: https
+ protocol: TCP
+ readinessProbe:
+ failureThreshold: 3
+ httpGet:
+ path: /readyz
+ port: https
+ scheme: HTTPS
+ initialDelaySeconds: 20
+ periodSeconds: 10
+ resources:
+ requests:
+ cpu: 100m
+ memory: 200Mi
+ securityContext:
+ allowPrivilegeEscalation: false
+ readOnlyRootFilesystem: true
+ runAsNonRoot: true
+ runAsUser: 1000
+ volumeMounts:
+ - mountPath: /tmp
+ name: tmp-dir
+ nodeSelector:
+ kubernetes.io/os: linux
+ priorityClassName: system-cluster-critical
+ serviceAccountName: metrics-server
+ volumes:
+ - emptyDir: {}
+ name: tmp-dir
+---
+apiVersion: apiregistration.k8s.io/v1
+kind: APIService
+metadata:
+ labels:
+ k8s-app: metrics-server
+ name: v1beta1.metrics.k8s.io
+spec:
+ group: metrics.k8s.io
+ groupPriorityMinimum: 100
+ insecureSkipTLSVerify: true
+ service:
+ name: metrics-server
+ namespace: kube-system
+ version: v1beta1
+ versionPriority: 100
diff --git a/mkdocs-site/23-MetricServer/kubelet-config-1.23.yaml b/mkdocs-site/23-MetricServer/kubelet-config-1.23.yaml
new file mode 100644
index 0000000..a15c9b5
--- /dev/null
+++ b/mkdocs-site/23-MetricServer/kubelet-config-1.23.yaml
@@ -0,0 +1,57 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: kubelet-config-1.23
+ namespace: kube-system
+data:
+ kubelet: |
+ apiVersion: kubelet.config.k8s.io/v1beta1
+ authentication:
+ anonymous:
+ enabled: false
+ webhook:
+ cacheTTL: 0s
+ enabled: true
+ x509:
+ clientCAFile: /var/lib/minikube/certs/ca.crt
+ authorization:
+ mode: Webhook
+ webhook:
+ cacheAuthorizedTTL: 0s
+ cacheUnauthorizedTTL: 0s
+ cgroupDriver: systemd
+ clusterDNS:
+ - 10.96.0.10
+ clusterDomain: cluster.local
+ cpuManagerReconcilePeriod: 0s
+ evictionHard:
+ imagefs.available: 0%
+ nodefs.available: 0%
+ nodefs.inodesFree: 0%
+ evictionPressureTransitionPeriod: 0s
+ failSwapOn: false
+ fileCheckFrequency: 0s
+ healthzBindAddress: 127.0.0.1
+ healthzPort: 10248
+ httpCheckFrequency: 0s
+ imageGCHighThresholdPercent: 100
+ imageMinimumGCAge: 0s
+ serverTLSBootstrap: true
+ kind: KubeletConfiguration
+ logging:
+ flushFrequency: 0
+ options:
+ json:
+ infoBufferSize: "0"
+ verbosity: 0
+ memorySwap: {}
+ nodeStatusReportFrequency: 0s
+ nodeStatusUpdateFrequency: 0s
+ rotateCertificates: true
+ runtimeRequestTimeout: 0s
+ shutdownGracePeriod: 0s
+ shutdownGracePeriodCriticalPods: 0s
+ staticPodPath: /etc/kubernetes/manifests
+ streamingConnectionIdleTimeout: 0s
+ syncFrequency: 0s
+ volumeStatsAggPeriod: 0s
diff --git a/mkdocs-site/23-MetricServer/runMe.sh b/mkdocs-site/23-MetricServer/runMe.sh
new file mode 100644
index 0000000..36c6dfc
--- /dev/null
+++ b/mkdocs-site/23-MetricServer/runMe.sh
@@ -0,0 +1,60 @@
+#!/bin/bash
+
+# Start minikube
+minikube start
+
+# Download the metric-server resources
+wget https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
+
+# Apply metric-server
+# We know that it will not work under minikube so we will need to fix it
+kubectl apply -f components.yaml
+
+# Check if the metrics-server is working
+# We expect to get the following error
+### >>> Error from server (ServiceUnavailable): the server is currently unable to handle the request (get nodes.metrics.k8s.io)
+kubectl top nodes
+
+kubectl get deployment metrics-server -n kube-system
+# NAME READY UP-TO-DATE AVAILABLE AGE
+# metrics-server 0/1 1 0 71s
+
+# View the error
+## We should see error like this:
+## "Failed to scrape node" err="Get \"https://192.168.49.2:10250/metrics/resource\":
+## x509: cannot validate certificate for 192.168.49.2 because it doesn't contain any IP SANs" node="minikube"
+kubectl logs -n kube-system deploy/metrics-server
+
+###
+### Fixing the error
+###
+# We need to fix the tls before we can install the mertric-server
+
+# Get the kubelet configuration
+KUBELET_CONFIG=$(kubectl get configmap -n kube-system --no-headers -o custom-columns=":metadata.name" | grep kubelet-config)
+kubectl edit configmap $KUBELET_CONFIG -n kube-system
+
+## Add to the following configuration under the `kubelet` ConfigMap
+serverTLSBootstrap: true
+
+# We also need to fix the metric server and add the following line under the metric-server Deploymet
+
+# Edit the deploymnet and add the required lines under the spec
+###
+### vi components.yaml (~line 140)
+###
+### spec:
+### containers:
+### - args
+- --kubelet-insecure-tls
+
+# Stop and start minikube
+minikube stop && minikube start
+
+# Uninstall and re-install the metrics-server
+kubectl delete -f components.yaml
+kubectl apply -f components.yaml
+
+# Verify that now the metric server is working
+kubectl top nodes
+kubectl top pods -A
\ No newline at end of file
diff --git a/mkdocs-site/24-HelmOperator/index.html b/mkdocs-site/24-HelmOperator/index.html
new file mode 100644
index 0000000..ef53215
--- /dev/null
+++ b/mkdocs-site/24-HelmOperator/index.html
@@ -0,0 +1,231 @@
+ 22 HelmOperator - KubernetesLabs
# Grab the ARCH and OS
+exportARCH=$(case$(uname-m)inx86_64)echo-namd64;;aarch64)echo-narm64;;*)echo-n$(uname-m);;esac)
+exportOS=$(uname|awk'{print tolower($0)}')
+
+# Get the desired download URL
+exportOPERATOR_SDK_DL_URL=https://github.com/operator-framework/operator-sdk/releases/download/v1.23.0
+
+# Download the Operator binaries
+curl-LO${OPERATOR_SDK_DL_URL}/operator-sdk_${OS}_${ARCH}
+
+# Install the release binary in your PATH
+chmod+xoperator-sdk_${OS}_${ARCH}&&sudomvoperator-sdk_${OS}_${ARCH}/usr/local/bin/operator-sdk
+
For this example the nginx-operator will execute the following reconciliation logic for each Nginx Custom Resource (CR):
Create an nginx Deployment, if it doesn’t exist.
Create an nginx Service, if it doesn’t exist.
Create an nginx Ingress, if it is enabled and doesn’t exist.
Update the Deployment, Service, and Ingress, if they already exist but don’t match the desired configuration as specified by the Nginx CR.
Ensure that the Deployment, Service, and optional Ingress all match the desired configuration (e.g. replica count, image, service type, etc) as specified by the Nginx CR.
When a Helm operator project is created, the SDK creates an example Helm chart that contains a set of templates for a simple Nginx release.
For this example, we have templates for deployment, service, and ingress resources, along with a NOTES.txt template, which Helm chart developers use to convey helpful information about a release.
Helm uses a concept called values to provide customizations to a Helm chart’s defaults, which are defined in the Helm chart’s values.yaml file.
Overriding these defaults is as simple as setting the desired values in the CR spec.
Let’s use the number of replicas value as an example.
First, inspecting helm-charts/nginx/values.yaml, we can see that the chart has a value called replicaCount and it is set to 1 by default.
Let’s update the value to 3 - replicaCount: 3.
# Update `config/samples/demo_v1alpha1_nginx.yaml` to look like the following:
+apiVersion:demo.codewizard.co.il/v1alpha1
+kind:Nginx
+metadata:
+name:nginx-sample
+spec:
+#... (Around line 33)
+replicaCount:3# <------- Adding our replicas count
+
Similarly, we see that the default service port is set to 80, but we would like to use 8888, so we will again update config/samples/demo_v1alpha1_nginx.yaml by adding the service port override.
# Update `config/samples/demo_v1alpha1_nginx.yaml` to look like the following:
+apiVersion:demo.codewizard.co.il/v1alpha1
+kind:Nginx
+metadata:
+name:nginx-sample
+spec:
+#... (Around line 36)
+service:
+port:8888# <------- Updating our service port
+
# Login to your DockerHub / acr / ecr or any other registry account
+
+# Set the desired image name and tag
+
+# In the Makefile update the following line
+# Image URL to use all building/pushing image targets
+IMG?=controller:latest
+
+# change it to your registry account
+IMG?=nirgeier/helm_operator:latest
+
# Deploy the custom nginx we created earlier
+kubectlapply-fconfig/samples/demo_v1alpha1_nginx.yaml
+
+# Ensure that the nginx-operator created
+kubectlgetdeployment|grepnginx-sample
+
+# Check that we have 3 replicas as defined earlier
+kubectlgetpods|grepnginx-sample
+
+# Check that the port is set to 8888
+kubectlgetsvc|grepnginx-sample
+
# Update the replicaCount and remove the port
+# Once we update the yaml we will check that the operator is working
+# and updating the desired values
+
+# Update the replicaCount in `config/samples/demo_v1alpha1_nginx.yaml`
+replicaCount:5
+
+# Remark the service section in the yaml file
+# We wish to see that the operator will use the default values
+36#service:
+37# port: 8888
+38# type: ClusterIP
+
Apply the changes:
# Apply the changes
+kubectlapply-fconfig/samples/demo_v1alpha1_nginx.yaml
+
Check to see that the operator is working as expected:
# Ensure that the nginx-operator still running
+kubectlgetdeployment|grepnginx-sample
+
+# Deploy the custom nginx we created earlier
+kubectlapply-fconfig/samples/demo_v1alpha1_nginx.yaml
+
+# Check that we have 5 replicas as defined earlier
+kubectlgetpods|grepnginx-sample
+
+# Check that the port is set back to its default (80)
+kubectlgetsvc|grepnginx-sample
+