Ceci est une ancienne révision du document !


Version - 2025.01

Last update : 2025/01/18 17:02

DOE310 - StatefulSets, Advanced StorageClass, Advanced Helm and Monitoring

Contents

  • DOE310 - StatefulSets, Advanced StorageClass Usage, Creating a Helm Chart and Monitoring
  • Contents
  • StatefulSets
    • Overview
    • LAB #1 - Setting up a simple StatefulSet
      • 1.1 - Service and StatefulSet creation
      • 1.2 - Scaling Up a StatefulSet
      • 1.3 - Scaling Down a StatefulSet
      • 1.4 - Deleting a StatefulSet
  • Advanced StorageClass Usage
    • LAB #2 - Dynamic NFS provisioning
      • 2.1 - NFS Server Configuration
      • 2.2 - NFS Client Configuration
      • 2.3 - Configuring K8s
      • 2.4 - Creating a PersistentVolumeClaim
      • 2.5 - Using the PersistentVolumeClaim with a Pod
      • 2.6 - Creating a Second PersistentVolumeClaim
      • 2.7 - Deleting the PersistentVolumeClaims
  • Creating a Helm Chart
    • Overview
    • LAB #3 - Creating a Simple Helm Package
      • 3.1 - The values.yaml File
      • 3.2 - Templates
      • 3.3 - Installation and Removal
  • Monitoring
    • Overview
    • LAB #4 - Implementing a Prometheus Solution
      • 4.1 - Stack Deployment with Helm
      • 4.2 - Viewing Data with Grafana
      • 4.3 - Viewing Alerts with the Prometheus Web UI

StatefulSets

Overview

A StatefulSet is a Kubernetes component used for Stateful Applications.

Examples of Stateful Applications are :

  • MySQL
  • elasticsearch
  • mongoDB

These applications record client data a one session for use in the next session. The recorded data is called the application's state.

Stateful Applications are deployed using a StatefulSet, while applications without state are deployed using a Deployment.

StatefulSets and Deployments are similar in that both replicate multiple pods based on an identical specification of a container.

The difference between a StatefulSet and a Deployment is that in a StatefulSet the pods are not identical and have what is known as a Pod Identity. As a result, pods :

  • cannot be created or deleted at the same time,
  • cannot be randomly addressed.

Let's take the case of a StatfulSet containing three replicas of a MySQL pod:

  • mysql-0
  • mysql-1
  • mysql-2

Note that :

  • the pod name takes the form $(Name_of_StatefulSet)-$(ordinal) where the ordinal starts at 0.
  • the StatefulSet will not create the next pod until the previous pod is in a Running state
  • When the StatefulSet is deleted, or in the case of a scale down, pods are deleted in the reverse order in which they were created, e.g. mysql-2 > mysql-1 > mysql-0. Each pod must be completely deleted before K8s deletes the next.

In this case of our StatefulSet, the three pods :

  • cannot all accept write requests, as this would result in inconsistent data,
  • can all accept read requests.

As a result, a StatefulSet mechanism chooses a master to accept write requests, for example:

  • mysql-0 - write / read - Master
  • mysql-1 - read only - slave
  • mysql-2 - read only - slave

So there's a clear difference between the Master pod and the two Slave pods.

The difference between the two Slave pods can be explained by the fact that the pods do not use the same persistent, remote physical storage:

  • mysql-0 - /data/vol/pv1
  • mysql-1 - /data/vol/pv2
  • mysql-2 - /data/vol/pv3

To ensure that each pod contains the same data, a continuous replication mechanism must be set up between the two slave pods and the master pod.

If a new pod is added to the MySQL cluster, it must first clone the data from the last pod into the existing cluster, then start replicating data with the Master:

  • mysql-0 - data
  • mysql-1 - data replicated from mysql-0
  • mysql-2 - data replicated from mysql-0
  • mysql-3 - clone of mysql-2 pod data, then mysql-0 replica data.

The state of each pod, including its Pod Identity, is stored in physical storage alongside the data. As a result, when a pod is replaced, and a new pod added, the new pod inherits the old pod's identity.

For example, if we delete the mysql-1 pod, we get :

  • mysql-0 - /data/vol/pv1
  • pod deleted - /data/vol/pv2 = persistent, the remote physical storage is not deleted
  • mysql-2 - /data/vol/pv3
  • mysql-3 - /data/vol/pv4

By adding a replacement pod, we obtain :

  • mysql-0
  • mysql-1 ««««« The /data/vol/pv2 is attached to the pod. The new pod is called mysql-1 and not mysql-4.
  • mysql-2
  • mysql-3

When a ReplicaSet is created, a load balancing service is created. This service assigns a unique DNS Endpoint to each pod. The DNS Endpoint takes the form $(Pod_name).$(Service_name).$(namespace).svc.cluster.local :

  • mysql-0 - mysql-0.mysvc.default.svc.cluster.local
  • mysql-1 - mysql-1.mysvc.default.svc.cluster.local
  • mysql-2 - mysql-2.mysvc.default.svc.cluster.local
  • mysql-3 - mysql-3.mysvc.default.svc.cluster.local

This way, when a pod is restarted, although its IP address will change :

  • its name will not change
  • its DNS endpoint will not change.

To sum up:

  • mysql-0
    • Role: Master
    • Data: write / read
    • Storage: /data/vol/pv1
    • Endpoint DNS: mysql-0..mysvc.default.svc.cluster.local
  • mysql-1
    • Role: Slave
    • Data: read only
    • Storage: /data/vol/pv2
    • DNS endpoint: mysql-1.mysvc.default.svc.cluster.local
  • mysql-2
    • Role: Slave
    • Data: read only
    • Storage: /data/vol/pv3
    • DNS endpoint: mysql-2.mysvc.default.svc.cluster.local
  • mysql-3
    • Role: Slave
    • Data: read only
    • Storage: /data/vol/pv4
    • DNS endpoint: mysql-3.mysvc.default.svc.cluster.local

Lastly, a StatefulSet is a complicated K8s component that is difficult to implement because Kubernetes does not handle certain tasks such as:

  • the configuration of the data cloning process,
  • the configuration of the data replication process,
  • the creation and configuration of persistent and remote physical storage,
  • the configuration and management of data backups.

LAB #1 - Setting up a simple StatefulSet

Create a quarkus namespace and modify the kubernetes-admin@kubernetes context to use it by default:

root@kubemaster:~# kubectl create ns quarkus
namespace/quarkus created

root@kubemaster:~# kubectl config set-context --current --namespace=quarkus
Context “kubernetes-admin@kubernetes” modified.

Important: Quarkus is a complete native Java framework for Kubernetes, designed for Java Virtual Machines (JVMs) and native compilation, which optimizes Java specifically for containers to make it an efficient platform for serverless, cloud and Kubernetes environments.

If you'd like to observe the results of the following commands in real time, open a second terminal and enter the following command:

root@kubemaster:~# watch -n 1 “kubectl get pods -o wide | awk ‘{print \$1 \” \$2 \“ \$3 \” \$5 \” \$7}’ | column -t”

1.1 - Service and StatefulSet creation

Now create the quarkus-service.yaml file:

To do: Copy the content from here and paste it into your file.

root@kubemaster:~# vi quarkus-service.yaml
root@kubemaster:~# cat quarkus-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: quarkus 
  labels:
    app: quarkus-statefulset
spec:
  ports:
  - port: 8080
    name: web
  clusterIP: None 
  selector:
    app: quarkus-statefulset

Important: Note the service name - quarkus. The None value of the ClusterIP entry makes the service headless. In this case, the DNS server will return the IP addresses of the individual pods instead of the IP address of the service. The client can then connect to any of them.

Create the service:

root@kubemaster:~# kubectl apply -f quarkus-service.yaml
service/quarkus created

Now create the statefulset.yaml file:

To do: Copy the content from here and paste it into your file.

root@kubemaster:~# vi statefulset.yaml
root@kubemaster:~# cat statefulset.yaml 
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: quarkus-statefulset
  labels:
    app: quarkus-statefulset
spec:
  serviceName: "quarkus" 
  replicas: 2
  template:
    metadata:
      labels:
        app: quarkus-statefulset
    spec:
      containers:
      - name: quarkus-statefulset
        image: quay.io/rhdevelopers/quarkus-demo:v1
        ports:
        - containerPort: 8080
          name: web
  selector:
    matchLabels:
      app: quarkus-statefulset

Important: Note that the value of serviceName is quarkus.

Create the StatefulSet :

root@kubemaster:~# kubectl apply -f statefulset.yaml
statefulset.apps/quarkus-statefulset created

Note the presence of the two pods in Namespace:

Every 1,0s: kubectl get pods -o wide | awk '{print $1 " " $2 " " $3 " " $5 " " $7}' | column -t                                                                kubemaster.ittraining.loc: Tue Dec  6 17:43:50 2022

NAME                   READY  STATUS   AGE    NODE
quarkus-statefulset-0  1/1    Running  2m17s  kubenode2.ittraining.loc
quarkus-statefulset-1  1/1    Running  106s   kubenode1.ittraining.loc

Check the StatefulSet's status:

root@kubemaster:~# kubectl get statefulsets
NAME                  READY   AGE
quarkus-statefulset   2/2     3m35s

as well as the presence of the service:

root@kubemaster:~# kubectl get services
NAME      TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
quarkus   ClusterIP   None         <none>        8080/TCP   12m

1.2 - Scaling Up a StatefulSet

Perform a scale up:

root@kubemaster:~# kubectl scale sts quarkus-statefulset --replicas=3

Important: Note that the short name of a serviceName is sts.

Note the presence of three pods in the Namespace:

Every 1,0s: kubectl get pods -o wide | awk '{print $1 " " $2 " " $3 " " $5 " " $7}' | column -t                                                                kubemaster.ittraining.loc: Tue Dec  6 17:46:42 2022

NAME                   READY  STATUS   AGE    NODE
quarkus-statefulset-0  1/1    Running  5m9s   kubenode2.ittraining.loc
quarkus-statefulset-1  1/1    Running  4m38s  kubenode1.ittraining.loc
quarkus-statefulset-2  1/1    Running  13s    kubenode2.ittraining.loc

Note the pod creation order:

root@kubemaster:~# kubectl get events --sort-by=.metadata.creationTimestamp
LAST SEEN   TYPE     REASON             OBJECT                            MESSAGE
6m35s       Normal   SuccessfulCreate   statefulset/quarkus-statefulset   create Pod quarkus-statefulset-0 in StatefulSet quarkus-statefulset successful
6m35s       Normal   Scheduled          pod/quarkus-statefulset-0         Successfully assigned quarkus/quarkus-statefulset-0 to kubenode2.ittraining.loc
6m34s       Normal   Pulling            pod/quarkus-statefulset-0         Pulling image "quay.io/rhdevelopers/quarkus-demo:v1"
6m5s        Normal   Pulled             pod/quarkus-statefulset-0         Successfully pulled image "quay.io/rhdevelopers/quarkus-demo:v1" in 28.871622372s
6m4s        Normal   Created            pod/quarkus-statefulset-0         Created container quarkus-statefulset
6m4s        Normal   Started            pod/quarkus-statefulset-0         Started container quarkus-statefulset
6m3s        Normal   Scheduled          pod/quarkus-statefulset-1         Successfully assigned quarkus/quarkus-statefulset-1 to kubenode1.ittraining.loc
6m3s        Normal   SuccessfulCreate   statefulset/quarkus-statefulset   create Pod quarkus-statefulset-1 in StatefulSet quarkus-statefulset successful
5m58s       Normal   Pulling            pod/quarkus-statefulset-1         Pulling image "quay.io/rhdevelopers/quarkus-demo:v1"
5m22s       Normal   Pulled             pod/quarkus-statefulset-1         Successfully pulled image "quay.io/rhdevelopers/quarkus-demo:v1" in 35.551473165s
5m21s       Normal   Created            pod/quarkus-statefulset-1         Created container quarkus-statefulset
5m21s       Normal   Started            pod/quarkus-statefulset-1         Started container quarkus-statefulset
99s         Normal   Scheduled          pod/quarkus-statefulset-2         Successfully assigned quarkus/quarkus-statefulset-2 to kubenode2.ittraining.loc
99s         Normal   SuccessfulCreate   statefulset/quarkus-statefulset   create Pod quarkus-statefulset-2 in StatefulSet quarkus-statefulset successful
98s         Normal   Pulled             pod/quarkus-statefulset-2         Container image "quay.io/rhdevelopers/quarkus-demo:v1" already present on machine
97s         Normal   Created            pod/quarkus-statefulset-2         Created container quarkus-statefulset
97s         Normal   Started            pod/quarkus-statefulset-2         Started container quarkus-statefulset

Now create a pod to query the K8s DNS:

root@kubemaster:~# kubectl run -it --restart=Never --rm --image busybox:1.28 dns-test
If you don't see a command prompt, try pressing enter.
/ # nslookup quarkus-statefulset-0.quarkus
Server:    10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local

Name:      quarkus-statefulset-0.quarkus
Address 1: 192.168.150.2 quarkus-statefulset-0.quarkus.quarkus.svc.cluster.local
/ # exit
pod "dns-test" deleted
root@kubemaster:~#

1.3 - Scaling Down a StatefulSet

Now perform a scale down:

root@kubemaster:~# kubectl scale sts quarkus-statefulset --replicas=2
statefulset.apps/quarkus-statefulset scaled

Note the presence of two pods in Namespace :

Every 1,0s: kubectl get pods -o wide | awk '{print $1 " " $2 " " $3 " " $5 " " $7}' | column -t                                                                kubemaster.ittraining.loc: Tue Dec  6 18:02:27 2022

NAME                   READY  STATUS   AGE  NODE
quarkus-statefulset-0  1/1    Running  20m  kubenode2.ittraining.loc
quarkus-statefulset-1  1/1    Running  20m  kubenode1.ittraining.loc

1.4 - Deleting the StatefulSet

Finally, delete the StatefulSet, the service and the Namespace :

root@kubemaster:~# kubectl delete -f statefulset.yaml 
statefulset.apps “quarkus-statefulset” deleted

root@kubemaster:~# kubectl delete -f quarkus-service.yaml 
service “quarkus-statefulset-2” deleted

root@kubemaster:~# kubectl config set-context --current --namespace=default
Context “kubernetes-admin@kubernetes” modified.

Advanced StorageClass Usage

LAB #2 - Dynamic NFS provisioning

2.1 - NFS Server Configuration

Connect to the CentOS8 VM as a trainee at 10.0.2.45.

Become root and create the /srv/nfs/kubedata directory:

[root@centos8 ~]# mkdir -p /srv/nfs/kubedata

Now continue by activating and starting the nfs-server service:

[root@centos8 ~]# systemctl status nfs-server
● nfs-server.service - NFS server and services
   Loaded: loaded (/usr/lib/systemd/system/nfs-server.service; disabled; vendor prese>
   Active: inactive (dead)

[root@centos8 ~]# systemctl enable nfs-server.service
Created symlink /etc/systemd/system/multi-user.target.wants/nfs-server.service → /usr/lib/systemd/system/nfs-server.service.

[root@centos8 ~]# systemctl start nfs-server.service

[root@centos8 ~]# systemctl status nfs-server.service
● nfs-server.service - NFS server and services
   Loaded: loaded (/usr/lib/systemd/system/nfs-server.service; enabled; vendor preset: disabled)
   Active: active (exited) since Mon 2022-11-21 11:02:13 CET; 9s ago
  Process: 3276 ExecStart=/bin/sh -c if systemctl -q is-active gssproxy; then systemctl reload gssproxy ; fi (code=exited, >
  Process: 3263 ExecStart=/usr/sbin/rpc.nfsd (code=exited, status=0/SUCCESS)
  Process: 3261 ExecStartPre=/usr/sbin/exportfs -r (code=exited, status=0/SUCCESS)
 Main PID: 3276 (code=exited, status=0/SUCCESS)

Nov 21 11:02:12 centos8.ittraining.loc systemd[1]: Starting NFS server and services...
Nov 21 11:02:13 centos8.ittraining.loc systemd[1]: Started NFS server and services.

Edit the /etc/exports file:

[root@centos8 ~]# vi /etc/exports
[root@centos8 ~]# cat /etc/exports
/srv/nfs/kubedata       *(rw,sync,no_subtree_check,no_root_squash,no_all_squash,insecure)

Important: In this case, we've shared the /srv/nfs/kubedata directory with the world.

Apply the export :

[root@centos8 ~]# exportfs -rav
exporting *:/srv/nfs/kubedata

[root@centos8 ~]# exportfs -v
/srv/nfs/kubedata
                <world>(sync,wdelay,hide,no_subtree_check,sec=sys,rw,insecure,no_root_squash,no_all_squash)

Set SELinux to permissive mode:

[root@centos8 ~]# getenforce
Enforcing

[root@centos8 ~]# setenforce permissive

Next, configure the firewall:

 
[root@centos8 ~]# firewall-cmd --permanent --add-service=nfs
success
[root@centos8 ~]# firewall-cmd --permanent --add-service=rpc-bind
success
[root@centos8 ~]# firewall-cmd --permanent --add-service=mountd
success
[root@centos8 ~]# firewall-cmd --reload
success

2.2 - NFS Client Configuration

Return to your gateway and connect as user trainee to kubenode2 at 192.168.56.4. Then become the root user:

trainee@kubenode2:~$ su -
Password: fenestros
root@kubenode2:~#

Install the nfs-common package:

root@kubenode2:~# apt update
...

root@kubenode2:~# apt install nfs-common
...

Check that you can see the directory exported by 10.0.2.45 :

root@kubenode2:~# showmount --exports 10.0.2.45
Export list for 10.0.2.45:
/srv/nfs/kubedata *

Check that you can mount the directory exported by 10.0.2.45 :

root@kubenode2:~# mount -t nfs 10.0.2.45:/srv/nfs/kubedata /mnt
root@kubenode2:~# mount | grep kubedata
10.0.2.45:/srv/nfs/kubedata on /mnt type nfs4 (rw,relatime,vers=4.2,rsize=524288,wsize=524288,namlen=255,hard,proto=tcp,port=0,timeo=600,retrans=2,sec=sys,clientaddr=10.0.2.67,local_lock=none,addr=10.0.2.45)

Then unmount 10.0.2.45:/srv/nfs/kubedata :

root@kubenode2:~# umount /mnt

root@kubenode2:~# mount | grep kubedata

Connect to kubenode1 at 192.168.56.3 :

root@kubenode2:~# ssh -l trainee 192.168.56.3
The authenticity of host '192.168.56.3 (192.168.56.3)' can't be established.
ECDSA key fingerprint is SHA256:sEfHBv9azmK60cjqF/aJgUc9jg56slNaZQdAUcvBOvE.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '192.168.56.3' (ECDSA) to the list of known hosts.
trainee@192.168.56.3's password: trainee
Linux kubenode1.ittraining.loc 4.9.0-19-amd64 #1 SMP Debian 4.9.320-2 (2022-06-30) x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Wed Sep 28 09:54:21 2022 from 192.168.56.2

trainee@kubenode1:~$ su -
Password: fenestros
root@kubenode1:~# 

Then install the nfs-common package:

root@kubenode1:~# apt update
...

root@kubenode1:~# apt install nfs-common
...

Return to your gateway :

root@kubenode1:~# exit
logout

trainee@kubenode1:~$ exit
logout
Connection to 192.168.56.3 closed.

root@kubenode2:~# exit
logout

trainee@kubenode2:~$ exit
logout
Connection to 192.168.56.4 closed.

2.3 - Configuring K8s

Connect to your kubemaster at 192.168.56.2.

Then install the nfs-common package:

root@kubemaster:~# apt update
...

root@kubemaster:~# apt install nfs-common
...

Add the nfs-subdir-external-provisioner repository to helm :

root@kubemaster:~# helm repo add nfs-subdir-external-provisioner https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner/
“nfs-subdir-external-provisioner” has been added to your repositories

Install the helm nfs-subdir-external-provisioner Chart:

root@kubemaster:~# helm install nfs-subdir-external-provisioner nfs-subdir-external-provisioner/nfs-subdir-external-provisioner --set nfs.server=10.0.2.45 --set nfs.path=/srv/nfs/kubedata
NAME: nfs-subdir-external-provisioner
LAST DEPLOYED: Wed Dec 7 11:12:23 2022
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None

Check the status of the created pod:

root@kubemaster:~# kubectl get pods -o wide
NAME                                               READY   STATUS              RESTARTS      AGE   IP               NODE                       NOMINATED NODE   READINESS GATES
netshoot                                           1/1     Running             3 (25h ago)   70d   192.168.239.58   kubenode1.ittraining.loc   <none>           <none>
nfs-subdir-external-provisioner-59b4b5c476-wxkp4   0/1     ContainerCreating   0             19m   <none>           kubenode1.ittraining.loc   <none>           <none>
nginx-netshoot                                     1/1     Running             3 (25h ago)   70d   192.168.239.59   kubenode1.ittraining.loc   <none>           <none>
postgresql-6f885d8957-tnlbb                        1/1     Running             3 (25h ago)   70d   192.168.239.62   kubenode1.ittraining.loc   <none>           <none>
sharedvolume                                       2/2     Running             6 (25h ago)   78d   192.168.150.60   kubenode2.ittraining.loc   <none>           <none>
troubleshooting                                    1/1     Running             3 (25h ago)   70d   192.168.239.60   kubenode1.ittraining.loc   <none>           <none>
volumepod                                          0/1     Completed           0             78d   192.168.150.41   kubenode2.ittraining.loc   <none>           <none>

Important: If the nfs-subdir-external-provisioner-yyyyyyyy-xxxxx pod remains in a ContainerCreating state for more than 5 minutes, remove the three calico-node-xxxxx pods from the kube-system namespace and wait for them to be recreated.

Once recreated, you can see that the nfs-subdir-external-provisioner-yyyyyyyy-xxxxx pod is in a Running state:

root@kubemaster:~# kubectl get pods -o wide
NAMESPACE          NAME                                                READY   STATUS      RESTARTS         AGE     IP               NODE                        NOMINATED NODE   READINESS GATES
default            netshoot                                            1/1     Running     3 (25h ago)      70d     192.168.239.58   kubenode1.ittraining.loc    <none>           <none>
default            nfs-subdir-external-provisioner-59b4b5c476-wxkp4    1/1     Running     1 (3m18s ago)    36m     192.168.239.63   kubenode1.ittraining.loc    <none>           <none>
default            nginx-netshoot                                      1/1     Running     3 (25h ago)      70d     192.168.239.59   kubenode1.ittraining.loc    <none>           <none>
default            postgresql-6f885d8957-tnlbb                         1/1     Running     3 (25h ago)      70d     192.168.239.62   kubenode1.ittraining.loc    <none>           <none>
default            sharedvolume                                        2/2     Running     6 (25h ago)      78d     192.168.150.60   kubenode2.ittraining.loc    <none>           <none>
default            troubleshooting                                     1/1     Running     3 (25h ago)      70d     192.168.239.60   kubenode1.ittraining.loc    <none>           <none>
default            volumepod                                           0/1     Completed   0                78d     192.168.150.41   kubenode2.ittraining.loc    <none>           <none>

Examination of the pod log nfs-subdir-external-provisioner-yyyyyyyy-xxxxx shows that everything works:

root@kubemaster:~# kubectl logs nfs-subdir-external-provisioner-59b4b5c476-wxkp4
I1207 10:45:38.321263       1 leaderelection.go:242] attempting to acquire leader lease  default/cluster.local-nfs-subdir-external-provisioner...
I1207 10:45:59.097918       1 leaderelection.go:252] successfully acquired lease default/cluster.local-nfs-subdir-external-provisioner
I1207 10:45:59.097979       1 event.go:278] Event(v1.ObjectReference{Kind:"Endpoints", Namespace:"default", Name:"cluster.local-nfs-subdir-external-provisioner", UID:"986e4938-a054-4bf9-bfdd-903749c7f63f", APIVersion:"v1", ResourceVersion:"6690493", FieldPath:""}): type: 'Normal' reason: 'LeaderElection' nfs-subdir-external-provisioner-59b4b5c476-wxkp4_1d17de3a-ac5b-442c-aa63-8253d33c2857 became leader
I1207 10:45:59.098098       1 controller.go:820] Starting provisioner controller cluster.local/nfs-subdir-external-provisioner_nfs-subdir-external-provisioner-59b4b5c476-wxkp4_1d17de3a-ac5b-442c-aa63-8253d33c2857!
I1207 10:45:59.198332       1 controller.go:869] Started provisioner controller cluster.local/nfs-subdir-external-provisioner_nfs-subdir-external-provisioner-59b4b5c476-wxkp4_1d17de3a-ac5b-442c-aa63-8253d33c2857!

Now consult the list of StorageClasses available:

root@kubemaster:~# kubectl get storageclass
NAME         PROVISIONER                                     RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
localdisk    kubernetes.io/no-provisioner                    Delete          Immediate           true                   77d
nfs-client   cluster.local/nfs-subdir-external-provisioner   Delete          Immediate           true                   52m

2.4 - Creating a PersistentVolumeClaim

Now create the file pvc.yaml:

To do: Copy the content from here and paste it into your file.

root@kubemaster:~# vi pvc.yaml
root@kubemaster:~# cat pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc1
spec:
  storageClassName: nfs-client 
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 500Mi

Apply the pvc.yaml file:

root@kubemaster:~# kubectl apply -f pvc.yaml 
persistentvolumeclaim/pvc1 created

Now look at the list of PersistentVolumes and PersistentVolumeClaims:

root@kubemaster:~# kubectl get pv,pvc
NAME                                                        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM          STORAGECLASS   REASON   AGE
persistentvolume/mypv                                       1Gi        RWO            Recycle          Available                  localdisk               77d
persistentvolume/pvc-721f5ed3-88b1-41bb-82c2-9eab3b4464da   500Mi      RWX            Delete           Bound       default/pvc1   nfs-client              66s

NAME                         STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/pvc1   Bound    pvc-721f5ed3-88b1-41bb-82c2-9eab3b4464da   500Mi      RWX            nfs-client     67s

Important: Note that the PersistentVolume persistentvolume/pvc-721f5ed3-88b1-41bb-82c2-9eab3b4464da has been created automatically.

Connect to the NFS server and view the contents of the /srv/nfs/kubedata directory:

root@kubemaster:~# ssh -l trainee 10.0.2.45
The authenticity of host '10.0.2.45 (10.0.2.45)' can't be established.
ECDSA key fingerprint is SHA256:Q7T/CP0SLiMbMAIgVzTuEHegYS/spPE5zzQchCHD5Vw.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '10.0.2.45' (ECDSA) to the list of known hosts.
trainee@10.0.2.45's password: trainee
Activate the web console with: systemctl enable --now cockpit.socket

Last login: Wed Dec 7 10:34:25 2022 from 10.0.2.65

[trainee@centos8 ~]$ ls -l /srv/nfs/kubedata/
total 0
drwxrwxrwx. 2 root root 6 Dec 7 12:32 default-pvc1-pvc-721f5ed3-88b1-41bb-82c2-9eab3b4464da

[trainee@centos8 ~]$ exit
logout
Connection to 10.0.2.45 closed.

2.5 - Using the PersistentVolumeClaim with a Pod

Now create the file nfs-busybox.yaml:

To do: Copy the content from here and paste it into your file.

root@kubemaster:~# vi nfs-busybox.yaml
root@kubemaster:~# cat nfs-busybox.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nfs-pv-pod
spec:
  restartPolicy: Never
  containers:
  - name: busybox
    image: busybox
    command: ['sh', '-c', 'while true; do sleep 3600; done']
    volumeMounts:
    - name: pv-storage
      mountPath: /pv-pod-storage
  volumes:
  - name: pv-storage
    persistentVolumeClaim:
      claimName: pvc1

Apply the nfs-busybox.yaml file:

root@kubemaster:~# kubectl apply -f nfs-busybox.yaml
pod/nfs-pv-pod created

Check that pod status nfs-pv-pod is Running :

root@kubemaster:~# kubectl get pods
NAME                                               READY   STATUS      RESTARTS      AGE
netshoot                                           1/1     Running     3 (26h ago)   70d
nfs-pv-pod                                         1/1     Running     0             2m9s
nfs-subdir-external-provisioner-59b4b5c476-wxkp4   1/1     Running     1 (80m ago)   113m
nginx-netshoot                                     1/1     Running     3 (26h ago)   70d
postgresql-6f885d8957-tnlbb                        1/1     Running     3 (26h ago)   70d
sharedvolume                                       2/2     Running     6 (26h ago)   78d
troubleshooting                                    1/1     Running     3 (26h ago)   70d
volumepod                                          0/1     Completed   0             78d

Connect to the nfs-pv-pod pod container:

root@kubemaster:~# kubectl exec -it nfs-pv-pod -- sh
/ # 

Create the hello file in the pv-pod-storage directory:

root@kubemaster:~# kubectl exec -it nfs-pv-pod -- sh
/ # ls
bin             dev             etc             home            lib             lib64           proc            pv-pod-storage  root            sys             tmp             usr             var
/ # touch /pv-pod-storage/hello
/ # ls /pv-pod-storage/
hello
/ # exit

Connect to the NFS server and check the contents of the /srv/nfs/kubedata directory:

root@kubemaster:~# ssh -l trainee 10.0.2.45
trainee@10.0.2.45's password: trainee
Activate the web console with: systemctl enable --now cockpit.socket

Last login: Wed Dec 7 12:37:00 2022 from 10.0.2.65

[trainee@centos8 ~]$ ls -lR /srv/nfs/kubedata/
/srv/nfs/kubedata/:
total 0
drwxrwxrwx. 2 root root 19 Dec 7 13:13 default-pvc1-pvc-721f5ed3-88b1-41bb-82c2-9eab3b4464da

/srv/nfs/kubedata/default-pvc1-pvc-721f5ed3-88b1-41bb-82c2-9eab3b4464da:
total 0
-rw-r--r--. 1 root root 0 Dec 7 13:13 hello

[trainee@centos8 ~]$ exit
logout
Connection to 10.0.2.45 closed.

Important: Note the presence of the hello file.

2.6 - Creating a Second PersistentVolumeClaim

Create the pvc2.yaml file:

To do: Copy the content from here and paste it into your file.

root@kubemaster:~# vi pvc2.yaml
root@kubemaster:~# cat pvc2.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc2
spec:
  storageClassName: nfs-client 
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 100Mi

Apply the pvc2.yaml file:

root@kubemaster:~# kubectl apply -f pvc2.yaml 
persistentvolumeclaim/pvc2 created

Now consult the list of PersistentVolumes and PersistentVolumeClaims:

root@kubemaster:~# kubectl get pv,pvc
NAME                                                        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM          STORAGECLASS   REASON   AGE
persistentvolume/mypv                                       1Gi        RWO            Recycle          Available                  localdisk               77d
persistentvolume/pvc-6dbce6de-e473-4e4c-99be-0fbea26576de   100Mi      RWO            Delete           Bound       default/pvc2   nfs-client              58s
persistentvolume/pvc-721f5ed3-88b1-41bb-82c2-9eab3b4464da   500Mi      RWX            Delete           Bound       default/pvc1   nfs-client              53m

NAME                         STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/pvc1   Bound    pvc-721f5ed3-88b1-41bb-82c2-9eab3b4464da   500Mi      RWX            nfs-client     53m
persistentvolumeclaim/pvc2   Bound    pvc-6dbce6de-e473-4e4c-99be-0fbea26576de   100Mi      RWO            nfs-client     58s

Important: Note that the PersistentVolume persistentvolume/pvc-6dbce6de-e473-4e4c-99be-0fbea26576de has been created automatically.

2.7 - Deleting the PersistentVolumeClaims

Start by deleting the nfs-pv-pod pod:

root@kubemaster:~# kubectl delete pod nfs-pv-pod
pod “nfs-pv-pod” deleted

Note that the pod has been deleted:

root@kubemaster:~# kubectl get pods
NAME                                               READY   STATUS      RESTARTS       AGE
netshoot                                           1/1     Running     3 (27h ago)    70d
nfs-subdir-external-provisioner-59b4b5c476-wxkp4   1/1     Running     1 (126m ago)   159m
nginx-netshoot                                     1/1     Running     3 (27h ago)    70d
postgresql-6f885d8957-tnlbb                        1/1     Running     3 (27h ago)    70d
sharedvolume                                       2/2     Running     6 (27h ago)    78d
troubleshooting                                    1/1     Running     3 (27h ago)    70d
volumepod                                          0/1     Completed   0              78d

Now check the list of PersistentVolumes and PersistentVolumeClaims:

root@kubemaster:~# kubectl get pv,pvc
NAME                                                        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM          STORAGECLASS   REASON   AGE
persistentvolume/mypv                                       1Gi        RWO            Recycle          Available                  localdisk               77d
persistentvolume/pvc-6dbce6de-e473-4e4c-99be-0fbea26576de   100Mi      RWO            Delete           Bound       default/pvc2   nfs-client              27m
persistentvolume/pvc-721f5ed3-88b1-41bb-82c2-9eab3b4464da   500Mi      RWX            Delete           Bound       default/pvc1   nfs-client              79m

NAME                         STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/pvc1   Bound    pvc-721f5ed3-88b1-41bb-82c2-9eab3b4464da   500Mi      RWX            nfs-client     79m
persistentvolumeclaim/pvc2   Bound    pvc-6dbce6de-e473-4e4c-99be-0fbea26576de   100Mi      RWO            nfs-client     27m

Important: Note that the PersistentVolumes and the PersistentVolumeClaims are still present.

Delete both PersistentVolumeClaims:

root@kubemaster:~# kubectl delete pvc --all
persistentvolumeclaim “pvc1” deleted
persistentvolumeclaim “pvc2” deleted

Now you can see that both PersistentVolumes have been deleted automatically:

root@kubemaster:~# kubectl get pv,pvc
NAME                    CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
persistentvolume/mypv   1Gi        RWO            Recycle          Available           localdisk               77d

Connect to the NFS server and view the contents of the /srv/nfs/kubedata directory:

root@kubemaster:~# ssh -l trainee 10.0.2.45
trainee@10.0.2.45's password: trainee
Activate the web console with: systemctl enable --now cockpit.socket

Last login: Wed Dec 7 13:15:49 2022 from 10.0.2.65

[trainee@centos8 ~]$ ls -lR /srv/nfs/kubedata/
/srv/nfs/kubedata/:
total 0
drwxrwxrwx. 2 root root 19 Dec 7 13:13 archived-default-pvc1-pvc-721f5ed3-88b1-41bb-82c2-9eab3b4464da
drwxrwxrwx. 2 root root 6 Dec 7 13:24 archived-default-pvc2-pvc-6dbce6de-e473-4e4c-99be-0fbea26576de

/srv/nfs/kubedata/archived-default-pvc1-pvc-721f5ed3-88b1-41bb-82c2-9eab3b4464da:
total 0
-rw-r--r--. 1 root root 0 Dec 7 13:13 hello

/srv/nfs/kubedata/archived-default-pvc2-pvc-6dbce6de-e473-4e4c-99be-0fbea26576de:
total 0

[trainee@centos8 ~]$ exit
logout
Connection to 10.0.2.45 closed.

Important: Note that directories have a archived- prefix.

Creating a Helm Chart

Overview

A chart is a collection of files and directories that take the following form:

MyChart/
  Chart.yaml
  LICENSE
  README.md
  values.yaml
  values.schema.json
  charts/
  crds/
  templates/
  templates/NOTES.txt

The helm template language is based on the GO language.

In the following LAB, you'll take the following two manifests, ghost.yaml and ghost-service.yaml and create a chart helm to install Ghost, a free blogging platform licensed under open source:

To do: Copy the content from here and paste it into your file.

root@kubemaster:~# vi ghost.yaml
root@kubemaster:~# cat ghost.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: blog
  labels:
    app: blog
spec:
  replicas: 1
  selector:
    matchLabels:
      app: blog
  template:
    metadata:
      labels:
        app: blog
    spec:
      containers:
      - name: blog
        image: ghost:2.6-alpine
        imagePullPolicy: Always
        ports:
        - containerPort: 2368
        env:
        - name: url
          value: http://exampleblog.com

To do: Copy the content from here and paste it into your file.

root@kubemaster:~# vi ghost-service.yaml
root@kubemaster:~# cat ghost-service.yaml 
apiVersion: v1
kind: Service
metadata:
  name: blog
spec:
  type: NodePort
  selector:
    app: blog
  ports:
  - protocol: TCP
    port: 80
    targetPort: 2368

LAB #3 - Creating a Simple Helm Package

Start by creating the ~/ghost directory and cd into it:

root@kubemaster:~# mkdir ghost

root@kubemaster:~# cd ghost

A chart requires a file named Chart.yaml to describe the chart in question. Create this file :

root@kubemaster:~/ghost# touch Chart.yaml

3.1 - The values.yaml File

A chart also needs a file called values.yaml which contains configuration values for the chart in question. Create a values.yaml file with the following contents:

To do: Copy the content from here and paste it into your file.

root@kubemaster:~/ghost# vi values.yaml
root@kubemaster:~/ghost# cat values.yaml
service:
  name: blog
  type: NodePort
  app: blog
  protocol: TCP
  port: 80
  targetPort: 2368

3.2 - Templates

Create the templates subdirectory in ghost :

root@kubemaster:~/ghost# mkdir templates

Copy the contents of ~/ghost-service.yaml and paste it into ~/ghost/templates/service.yaml:

To do: Copy the content from here and paste it into your file.

root@kubemaster:~/ghost# vi templates/service.yaml
root@kubemaster:~/ghost# cat templates/service.yaml 
apiVersion: v1
kind: Service
metadata:
  name: blog
spec:
  type: NodePort
  selector:
    app: blog
  ports:
  - protocol: TCP
    port: 80
    targetPort: 2368

Then modify this file to read the values from the values.yaml file:

root@kubemaster:~/ghost# vi templates/service.yaml
root@kubemaster:~/ghost# cat templates/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: {{ .Values.service.name }}
spec:
  type: {{ .Values.service.type }}
  selector:
    app: {{ .Values.service.app }}
  ports:
  - protocol: {{ .Values.service.protocol }}
    port: {{ .Values.service.port }}
    targetPort: {{ .Values.service.targetPort }}

Navigate to the parent directory of the ghost directory:

root@kubemaster:~/ghost# cd ..

Check that helm can read the list of values in the values.yaml file:

root@kubemaster:~# helm show values ghost
Error: validation: chart.metadata.name is required

The error refers to the Chart.yaml file, which is currently empty. Edit this file:

root@kubemaster:~# vi ghost/Chart.yaml
root@kubemaster:~# cat ghost/Chart.yaml
name: ghost
version: 1

Now check that helm can read the list of values in the values.yaml file:

root@kubemaster:~# helm show values ghost
service:
  name: blog
  type: NodePort
  app: blog
  protocol: TCP
  port: 80
  targetPort: 2368

Now check that the service.yaml manifest created by Helm is correct:

root@kubemaster:~# helm install check ghost --dry-run
NAME: check
LAST DEPLOYED: Thu Dec  8 15:54:13 2022
NAMESPACE: default
STATUS: pending-install
REVISION: 1
TEST SUITE: None
HOOKS:
MANIFEST:
---
# Source: ghost/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: blog
spec:
  type: NodePort
  selector:
    app: blog
  ports:
  - protocol: TCP
    port: 80
    targetPort: 2368

Now copy the contents of the ~/ghost.yaml file and paste it into the ~/ghost/templates/ghost.yaml file:

root@kubemaster:~# vi ghost/templates/ghost.yaml
root@kubemaster:~# cat ghost/templates/ghost.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: blog
  labels:
    app: blog
spec:
  replicas: 1
  selector:
    matchLabels:
      app: blog
  template:
    metadata:
      labels:
        app: blog
    spec:
      containers:
      - name: blog
        image: ghost:2.6-alpine
        imagePullPolicy: Always
        ports:
        - containerPort: 2368
        env:
        - name: url
          value: http://exampleblog.com

Then modify this file to read the values from the values.yaml file:

root@kubemaster:~# vi ghost/templates/ghost.yaml
root@kubemaster:~# cat ghost/templates/ghost.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Values.blog.name }}
  labels:
    app: {{ .Values.blog.label }}
spec:
  replicas: {{ .Values.blog.replicas }}
  selector:
    matchLabels:
      app: {{ .Values.blog.name }}
  template:
    metadata:
      labels:
        app: {{ .Values.blog.name }}
    spec:
      containers:
      - name: {{ .Values.blog.name }}
        image: {{ .Values.blog.image }}
        imagePullPolicy: {{ .Values.blog.imagePullPolicy }}
        ports:
        - containerPort: {{ .Values.blog.containerPort }}
        env:
        - name: {{ .Values.blog.url }}
          value: {{ .Values.blog.urlValue }}

Now complete the contents of the values.yaml file:

To do: Copy the content from here and paste it into your file.

root@kubemaster:~# vi ghost/values.yaml
root@kubemaster:~# cat ghost/values.yaml
service:
  name: blog
  type: NodePort
  app: blog
  protocol: TCP
  port: 80
  targetPort: 2368
blog:
  name: blog
  label: blog
  replicas: 1
  image: ghost:2.6-alpine
  imagePullPolicy: Always
  containerPort: 2368
  url: url
  urlValue: http://exampleblog.com

Now check that the ghost.yaml manifest created by Helm is correct:

root@kubemaster:~# helm install check ghost --dry-run
NAME: check
LAST DEPLOYED: Thu Dec  8 16:12:29 2022
NAMESPACE: default
STATUS: pending-install
REVISION: 1
TEST SUITE: None
HOOKS:
MANIFEST:
---
# Source: ghost/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: blog
spec:
  type: NodePort
  selector:
    app: blog
  ports:
  - protocol: TCP
    port: 80
    targetPort: 2368
---
# Source: ghost/templates/ghost.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: blog
  labels:
    app: blog
spec:
  replicas: 1
  selector:
    matchLabels:
      app: blog
  template:
    metadata:
      labels:
        app: blog
    spec:
      containers:
      - name: blog
        image: ghost:2.6-alpine
        imagePullPolicy: Always
        ports:
        - containerPort: 2368
        env:
        - name: url
          value: http://exampleblog.com

See now the organization of the ghost chart:

root@kubemaster:~# tree ghost
ghost
├── Chart.yaml
├── templates
│ ├── ghost.yaml
│ └── service.yaml
└── values.yaml

1 directory, 4 files

3.3 - Installation and Removal

Install the ghost chart:

root@kubemaster:~# helm install live ghost
NAME: live
LAST DEPLOYED: Thu Dec 8 16:14:13 2022
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None

Check the status of the service in the cluster:

root@kubemaster:~# kubectl get svc
NAME               TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
blog               NodePort    10.106.215.169   <none>        80:32070/TCP   52s
kubernetes         ClusterIP   10.96.0.1        <none>        443/TCP        95d
service-netshoot   ClusterIP   10.107.115.28    <none>        80/TCP         71d

Check the presence of the pod in the cluster:

root@kubemaster:~# kubectl get po
NAME                                               READY   STATUS      RESTARTS       AGE
blog-8545df8764-hk8rc                              1/1     Running     0              105s
netshoot                                           1/1     Running     3 (2d6h ago)   71d
nfs-subdir-external-provisioner-59b4b5c476-wxkp4   1/1     Running     1 (28h ago)    29h
nginx-netshoot                                     1/1     Running     3 (2d6h ago)   71d
postgresql-6f885d8957-tnlbb                        1/1     Running     3 (2d6h ago)   71d
sharedvolume                                       2/2     Running     6 (2d6h ago)   79d
troubleshooting                                    1/1     Running     3 (2d6h ago)   71d
volumepod                                          0/1     Completed   0

Check the Chart status :

root@kubemaster:~# helm status live
NAME: live
LAST DEPLOYED: Thu Dec 8 16:14:13 2022
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None

Lastly, delete the chart :

root@kubemaster:~# helm delete live
release “live” uninstalled

Monitoring

Overview

The Prometheus server consists of three modules:

  • Data Retrieval Worker, which retrieves metrics
  • Time Series Database for metrics storage
  • HTTP Server which accepts PromQL requests and provides a Web UI for data consultation

Important : PromQL, short for Prometheus Querying Language, is the main way of querying metrics in Prometheus. You can display the return of an expression as a graph, or export it using the HTTP API. PromQL uses three types of data: scalars, range vectors and snapshot vectors. It also uses strings, but only as literals.

Alerts are then passed to the Alertmanager, which informs the people in charge of the configuration.

LAB #4 - Implementing a Prometheus Solution

Connect to the VM Gateway_10.0.2.40_VNC.

4.1 - Stack Deployment with Helm

Add the prometheus-community repository:

trainee@gateway:~$ helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
“prometheus-community” has been added to your repositories

trainee@gateway:~$ helm repo update

Then install the kube-prometheus-stack Chart:

trainee@gateway:~$ helm install prometheus prometheus-community/kube-prometheus-stack
NAME: prometheus
LAST DEPLOYED: Thu Dec  8 17:04:17 2022
NAMESPACE: default
STATUS: deployed
REVISION: 1
NOTES:
kube-prometheus-stack has been installed. Check its status by running:
  kubectl --namespace default get pods -l "release=prometheus"

Visit https://github.com/prometheus-operator/kube-prometheus for instructions on how to create & configure Alertmanager and Prometheus instances using the Operator.

Wait until all pods are in a Running state:

trainee@gateway:~$ kubectl --namespace default get pods -l "release=prometheus"
NAME                                                   READY   STATUS    RESTARTS   AGE
prometheus-kube-prometheus-operator-689dd6679c-2th6f   1/1     Running   0          4m12s
prometheus-kube-state-metrics-6cfd96f4c8-wrw2n         1/1     Running   0          4m12s
prometheus-prometheus-node-exporter-8cb4s              1/1     Running   0          4m13s
prometheus-prometheus-node-exporter-ll4qp              1/1     Running   0          4m13s
prometheus-prometheus-node-exporter-x87f7              1/1     Running   0          4m13s

Now look at all the Prometheus objects created by the installation:

trainee@gateway:~$ kubectl get all -l "release=prometheus"
NAME                                                       READY   STATUS    RESTARTS   AGE
pod/prometheus-kube-prometheus-operator-689dd6679c-2th6f   1/1     Running   0          13h
pod/prometheus-kube-state-metrics-6cfd96f4c8-wrw2n         1/1     Running   0          13h
pod/prometheus-prometheus-node-exporter-8cb4s              1/1     Running   0          13h
pod/prometheus-prometheus-node-exporter-ll4qp              1/1     Running   0          13h
pod/prometheus-prometheus-node-exporter-x87f7              1/1     Running   0          13h

NAME                                              TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
service/prometheus-kube-prometheus-alertmanager   ClusterIP   10.103.114.236   <none>        9093/TCP   13h
service/prometheus-kube-prometheus-operator       ClusterIP   10.107.174.218   <none>        443/TCP    13h
service/prometheus-kube-prometheus-prometheus     ClusterIP   10.108.124.100   <none>        9090/TCP   13h
service/prometheus-kube-state-metrics             ClusterIP   10.109.13.26     <none>        8080/TCP   13h
service/prometheus-prometheus-node-exporter       ClusterIP   10.103.100.124   <none>        9100/TCP   13h

NAME                                                 DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
daemonset.apps/prometheus-prometheus-node-exporter   3         3         3       3            3           <none>          13h

NAME                                                  READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/prometheus-kube-prometheus-operator   1/1     1            1           13h
deployment.apps/prometheus-kube-state-metrics         1/1     1            1           13h

NAME                                                             DESIRED   CURRENT   READY   AGE
replicaset.apps/prometheus-kube-prometheus-operator-689dd6679c   1         1         1       13h
replicaset.apps/prometheus-kube-state-metrics-6cfd96f4c8         1         1         1       13h

NAME                                                                    READY   AGE
statefulset.apps/alertmanager-prometheus-kube-prometheus-alertmanager   1/1     13h
statefulset.apps/prometheus-prometheus-kube-prometheus-prometheus       1/1     13h

In this output we see:

  • 2 StatefulSets including:
    • the Prometheus server statefulset.apps/prometheus-prometheus-kube-prometheus-prometheus
    • the Alertmanager statefulset.apps/alertmanager-prometheus-kube-prometheus-alertmanager.
  • 2 Deployments including:
    • the deployment.apps/prometheus-kube-prometheus-operator which created the two StatefulSets
    • the kube-state-metrics deployment.apps/prometheus-kube-state-metrics which is a dependency of Prometheus and therefore a Subchart of the latter
  • 2 ReplicaSets created by Deployments:
    • replicaset.apps/prometheus-kube-prometheus-operator-689dd6679c
    • replicaset.apps/prometheus-kube-state-metrics-6cfd96f4c8
  • 1 DaemonSet daemonset.apps/prometheus-prometheus-node-exporter :
    • the pods in this DaemonSet are responsible for transforming node metrics into Prometheus metrics.

The installation also created a large number of ConfigMaps :

trainee@gateway:~$ kubectl get configmap -l "release=prometheus"
NAME                                                           DATA   AGE
prometheus-kube-prometheus-alertmanager-overview               1      13h
prometheus-kube-prometheus-apiserver                           1      13h
prometheus-kube-prometheus-cluster-total                       1      13h
prometheus-kube-prometheus-controller-manager                  1      13h
prometheus-kube-prometheus-etcd                                1      13h
prometheus-kube-prometheus-grafana-datasource                  1      13h
prometheus-kube-prometheus-grafana-overview                    1      13h
prometheus-kube-prometheus-k8s-coredns                         1      13h
prometheus-kube-prometheus-k8s-resources-cluster               1      13h
prometheus-kube-prometheus-k8s-resources-namespace             1      13h
prometheus-kube-prometheus-k8s-resources-node                  1      13h
prometheus-kube-prometheus-k8s-resources-pod                   1      13h
prometheus-kube-prometheus-k8s-resources-workload              1      13h
prometheus-kube-prometheus-k8s-resources-workloads-namespace   1      13h
prometheus-kube-prometheus-kubelet                             1      13h
prometheus-kube-prometheus-namespace-by-pod                    1      13h
prometheus-kube-prometheus-namespace-by-workload               1      13h
prometheus-kube-prometheus-node-cluster-rsrc-use               1      13h
prometheus-kube-prometheus-node-rsrc-use                       1      13h
prometheus-kube-prometheus-nodes                               1      13h
prometheus-kube-prometheus-nodes-darwin                        1      13h
prometheus-kube-prometheus-persistentvolumesusage              1      13h
prometheus-kube-prometheus-pod-total                           1      13h
prometheus-kube-prometheus-prometheus                          1      13h
prometheus-kube-prometheus-proxy                               1      13h
prometheus-kube-prometheus-scheduler                           1      13h
prometheus-kube-prometheus-workload-total                      1      13h

as well as Secrets :

trainee@gateway:~$ kubectl get secrets 
NAME                                                                TYPE                 DATA   AGE
alertmanager-prometheus-kube-prometheus-alertmanager                Opaque               1      13h
alertmanager-prometheus-kube-prometheus-alertmanager-generated      Opaque               1      13h
alertmanager-prometheus-kube-prometheus-alertmanager-tls-assets-0   Opaque               0      13h
alertmanager-prometheus-kube-prometheus-alertmanager-web-config     Opaque               1      13h
my-secret                                                           Opaque               2      88d
prometheus-grafana                                                  Opaque               3      13h
prometheus-kube-prometheus-admission                                Opaque               3      13h
prometheus-prometheus-kube-prometheus-prometheus                    Opaque               1      13h
prometheus-prometheus-kube-prometheus-prometheus-tls-assets-0       Opaque               1      13h
prometheus-prometheus-kube-prometheus-prometheus-web-config         Opaque               1      13h
sh.helm.release.v1.nfs-subdir-external-provisioner.v1               helm.sh/release.v1   1      43h
sh.helm.release.v1.prometheus.v1                                    helm.sh/release.v1   1      13h

and some Custom Resource Definitions or crd: <code> trainee@gateway:~$ kubectl get crd NAME CREATED AT alertmanagerconfigs.monitoring.coreos.com 2022-12-08T16:04:14Z alertmanagers.monitoring.coreos.com 2022-12-08T16:04:14Z bgpconfigurations.crd.projectcalico.org 2022-09-04T07:38:47Z bgppeers.crd.projectcalico.org 2022-09-04T07:38:47Z blockaffinities.crd.projectcalico.org 2022-09-04T07:38:48Z caliconodestatuses.crd.projectcalico.org 2022-09-04T07:38:48Z clusterinformations.crd.projectcalico.org 2022-09-04T07:38:48Z felixconfigurations.crd.projectcalico.org 2022-09-04T07:38:48Z globalnetworkpolicies.crd.projectcalico.org 2022-09-04T07:38:48Z globalnetworksets.crd.projectcalico.org 2022-09-04T07:38:49Z hostendpoints.crd.projectcalico.org 2022-09-04T07:38:49Z ipamblocks.crd.projectcalico.org 2022-09-04T07:38:49Z ipamconfigs.crd.projectcalico.org 2022-09-04T07:38:49Z ipamhandles.crd.projectcalico.org 2022-09-04T07:38:50Z ippools.crd.projectcalico.org 2022-09-04T07:38:50Z ipreservations.crd.projectcalico.org 2022-09-04T07:38:50Z kubecontrollersconfigurations.crd.projectcalico.org 2022-09-04T07:38:50Z networkpolicies.crd.projectcalico.org 2022-09-04T07:38:50Z networksets.crd.projectcalico.org 2022-09-04T07:38:50Z podmonitors.monitoring.coreos.com 2022-12-08T16:04:14Z probes.monitoring.coreos.com 2022-12-08T16:04:14Z prometheuses.monitoring.coreos.com 2022-12-08T16:04:14Z prometheusrules.monitoring.coreos.com 2022-12-08T16:04:14Z servicemonitors.monitoring.coreos.com 2022-12-08T16:04:15Z thanosrulers.monitoring.coreos.com 2022-12-08T16:04:15Z </code> ===4.2 - Viewing Data with Grafana=== The previous installation also installed Grafana. Grafana is an open source interactive data visualization platform, developed by Grafana Labs, that allows users to view their data via charts and graphs that are unified into a dashboard (or multiple dashboards) for easier interpretation and understanding. Consult the list of Grafana objects: <code> trainee@gateway:~$ kubectl get all | grep grafana pod/prometheus-grafana-5d9f5d6499-f4x6t 3/3 Running 1 (13h ago) 14h service/prometheus-grafana ClusterIP 10.109.207.199 <none> 80/TCP 14h deployment.apps/prometheus-grafana 1/1 1 1 14h replicaset.apps/prometheus-grafana-5d9f5d6499 1 1 1 14h </code> Check the port used by Grafana: <code> trainee@gateway:~$ kubectl logs prometheus-grafana-5d9f5d6499-f4x6t -c grafana | grep HTTP logger=http.server t=2022-12-08T16:16:51.215644746Z level=info msg=“HTTP Server Listen” address=[::]:3000 protocol=http subUrl= socket= </code> and the user name to connect to Grafana : <code> trainee@gateway:~$ kubectl logs prometheus-grafana-5d9f5d6499-f4x6t -c grafana | grep “user=” logger=sqlstore t=2022-12-08T16:16:50.536980031Z level=info msg=“Created default admin” user=admin </code> The default password for the admin user can be obtained by consulting the contents of the values.yaml file.

Important: Note that the password is prom-operator.

Set up port forwarding: <code> trainee@gateway:~$ kubectl port-forward deployment/prometheus-grafana 3000 Forwarding from 127.0.0.1:3000 → 3000 Forwarding from [::1]:3000 → 3000 </code> Now go to the VM Gateway_10.0.2.40_VNC and launch the web browser. Enter the URL http://127.0.0.1:3000 and connect to Grafana: Image Then click on Dashboards > Browse > Kubernetes / Compute Resources / Node (Pods) : Finally, click on Dashboards > Browse > Node Exporter / Nodes : ===4.3 - Viewing Alerts with the Prometheus Web UI=== To consult the Prometheus Web UI, set up a port redirection : <code> trainee@gateway:~$ kubectl port-forward prometheus-prometheus-kube-prometheus-prometheus-0 9090 Forwarding from 127.0.0.1:9090 → 9090 Forwarding from [::1]:9090 → 9090 </code> Return to the VM GUI Gateway_10.0.2.40_VNC and enter the URL http://127.0.0.1:9090** :

To consult the list of alerts, click on the Alerts link in the menu at the top of the page:


Copyright © 2025 Hugh Norris

Menu