Kubernetes Cluster Setup¶
Basic Requirement¶
You have to configuration your OpenStack CLI has described on dedicated section. The example are based on this configuration, if you have some specific configuration you have to adapt.
You will also have to install magnum client. Magnum is the OpenStack compoment that manage kubernetes provisioning.
$ source ~/.venv/openstack-cli/bin/activate
(openstack-cli) $ pip install python-magnumclient
Danger
Provisioning k8s cluster on cloud@vd require some other privileges not available by normal users. You have to contact us for more details.
Danger
magnum use a specific API to store trust, unfortunately, trust cannot be
managed by application credential
by default. To deploy a k8s cluster through
magnum, you have to add --unrestricted
option while creating
application credential
.
Cluster Configuration¶
Templates¶
Magnum use templates to define the kind of k8s infrastructure you want to start.
By default, VirtualData provide a set of public template. You can, of course,
tweak those templates, but it will not be documented on page. Our template follow
our own naming convention k8s-<version>-<node-os-version>[-lb]
where :
version
is the k8s versionnode-os-version
is the base os of k8s infrastructure-lb
is present if a load-balancer is configured in front of k8s
To list availables template, you have to launch the following command
$ openstack coe cluster template list
+--------------------------------------+----------------------+------+
| uuid | name | tags |
+--------------------------------------+----------------------+------+
[...]
| 4cad1f92-dc10-434a-966a-7166fb6db3e4 | k8s-1.30-coreos40-lb | None |
+--------------------------------------+----------------------+------+
Auto-scaling¶
Magnum support k8s auto-scaling. This mean that magnum will monitor k8s usage and start (or stop) a node if more resources are required. When started a cluster, you have to specify the minimum and the maximum number of node of this cluster.
Note
Each node take resource from your project quota when a node is started. You need to take care of having enough resources available or the scaling will fail.
Multi-master¶
Kubernetes support multiple master configuration. This is mandatory to have 2 masters to allow rolling upgrade of kubernetes infrastructure.
Our first kubernetes infrastructure¶
Starting our cluster¶
openstack coe cluster create my-k8s-${USERNAME} \
--cluster-template k8s-1.30-coreos40-lb \
--key vd-key \
--master-count 2 \
--node-count 2 \
--time 30 \
--labels min_node_count=2 \
--labels max_node_count=5 \
--merge-labels
Get your k8s status¶
OpenStack will provide you two monitor value:
status
which is the status of kubernetes infrastructure in OpenStack point-of-view. If it'sCREATE_COMPLETE
this mean that all virtual machine, all cinder volumes and load balancer are up and running.health_status
which is the status in k8s point-of-view. If it'sHEALTHY
then all pods are running and a user can use the cluster.
$ openstack coe cluster show my-k8s-${USERNAME} -c status
+---------------+-----------------+
| Field | Value |
+---------------+-----------------+
| status | CREATE_COMPLETE |
| health_status | HEALTHY |
+---------------+-----------------+
Retrieve k8s credentials¶
The credential to access k8s are generated by OpenStack during cluster creation and are independent to your cloud@vd credential. After your cluster is running, you have to get those credentials to access to your cluster.
$ mkdir -p ~/.k8s/my-k8s-${USERNAME}
$ openstack coe cluster config my-k8s-${USERNAME} --dir ~/.k8s/my-k8s/
$ export KUBECONFIG=~/.k8s/my-k8s-${USERNAME}/config
$
Get k8s cluster informations¶
To check your k8s cluster status, you just have to launch
$ kubectl cluster-info
Kubernetes control plane is running at https://<ip-add>:6443
CoreDNS is running at https://<ip-add>:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
OpenStack ressources for k8s¶
Of course, all k8s ressources are runs on OpenStack. You can list all OpenStack ressources like the loadbalancer and servers.
Note
To use loadbalancer openstack command you have to install octavia client
with the command pip install python-octaviaclient
$ openstack loadbalancer list
+---------+-------------+------------+----------+---------+---------+---------+
| id | name | project_id | vip[...] | pr[...] | op[...] | pr[...] |
+---------+-------------+------------+----------+---------+---------+---------+
| 68[...] | my-k8s[...] | 5e426[...] | 10.[...] | ACTIVE | ONLINE | amphora |
| 80[...] | my-k8s[...] | 5e426[...] | 10.[...] | ACTIVE | ONLINE | amphora |
+---------+-------------+------------+----------+---------+---------+---------+
$ openstack server list
+-------+-----------------------+-+-----------------------------+---------+-+
| ID | Name | | Networks | Image | |
+-------+-----------------------+-+-----------------------------+---------+-+
| [...] | my-k8s[...]-node-0 | | my-k8s=10.[...], 157.[...] | fe[...] | |
| [...] | my-k8s-[...]-node-1 | | my-k8s=10.[...], 157.[...] | fe[...] | |
| [...] | my-k8s-[...]-master-1 | | my-k8s=10.[...], 157.[...] | fe[...] | |
| [...] | my-k8s-[...]-master-0 | | my-k8s=10.[...], 157.[...] | fe[...] | |
+-------+-----------------------+-+-----------------------------+---------+-+
Deploy application¶
For the documentation, we will consider to start a basic web service with a nginx frontend and a mysql database. A set of templates are available in gitlab repository you can clone it and take it as example.
The templates are been created to work out-of-the-box with cloud@vd but can be
used on every OpenStack infrastructure. Some part of .yml
are OpenStack
specific as k8s need to provision volume and ingress control through OpenStack
services.
Nginx¶
nginx installation¶
In this section, we will install Nginx in your Kubernetes cluster using a Deployment. The Deployment configuration defines the number of replicas, container image, ports, and resource limits for the nginx containers.
nginx deployment¶
Below is the content of the nginx/deployment.yml
file which we will use to create
the nginx deployment:
$ kubectl apply -f nginx/deployment.yml
$ kubectl get deployment
NAME READY UP-TO-DATE AVAILABLE AGE
nginx 2/2 2 2 5m17s
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-785d6689b-2twqw 1/1 Running 0 15m
nginx-785d6689b-j4j88 1/1 Running 0 15m
Deployment will start two nginx pods based on nginx:latest
docker image. But the
service will not be available outside k8s cluster. The next step of our tutorial
will be to expose the http port (80).
Expose nginx service¶
After creating the deployment, apply the service configuration file to expose nginx to the network.
$ kubectl apply -f nginx/service.yml
$ kubernetes % kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
[...]
nginx LoadBalancer 10.254.108.111 <pending> 80:30966/TCP 5s
If you get service status just after the apply
, you will have a <pending>
external IP. You have to wait some minutes to have the external IP available
$ kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
[...]
nginx LoadBalancer 10.254.108.111 157.136.xxx.yyy 80:30966/TCP 2m33s
By following these steps, you will have a fully functional nginx deployment in your Kubernetes cluster. The deployment ensures that two replicas of the nginx container are running, and the service exposes nginx to handle incoming traffic.
You can test it with curl command
$ curl http://157.136.xxx.yyy > /dev/null
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 615 100 615 0 0 31185 0 --:--:-- --:--:-- --:--:-- 32368
Custom html pages¶
We will create a ConfigMap to hold the HTML content and mount it to the nginx
pod. This will overwrite the default index.html
file provide by nginx:latest
docker image.
$ kubectl apply -f nginx/configmap.yml
$ kubectl get ConfigMap
NAME DATA AGE
kube-root-ca.crt 1 11d
nginx-index 1 50s
To link the ConfigMap containing the HTML content to the nginx pods,
you need to modify the deployment. To do that, you can use
nginx/deployment-with-config-map.yml
which is based on nginx/deployment.yml
.
$ diff -u nginx/deployment.yml nginx/deployment-with-config-map.yml
--- nginx/deployment.yml 2024-09-28 19:21:27
+++ nginx/deployment-with-config-map.yml 2024-09-28 19:24:54
@@ -22,3 +22,10 @@
requests:
memory: "64Mi"
cpu: "250m"
+ volumeMounts:
+ - name: nginx-index
+ mountPath: /usr/share/nginx/html
+ volumes:
+ - name: nginx-index
+ configMap:
+ name: nginx-index
$ kubectl apply -f nginx/deployment-with-config-map.yml
deployment.apps/nginx configured
Now, nginx service will provide your custom index.html
file.
$ curl http://157.136.xxx.yyy
Hi VirtualData !
$
OpenStack ressources for a deployment¶
Of course, compute ressource are provide by my-k8s-[...]-node-x
virtual
machine, but the loadbalancer service for nginx has been started by k8s has a
OpenStack octavia service
$ openstack loadbalancer list
+---------+-------------------------------------+-+-------------+-+-+----------+
| id | name | | vip_address | | | provider |
+---------+-------------------------------------+-+-------------+-+-+----------+
| 79[...] | kube_service_[...]_default_nginx | | 10.[...] | | | amphora |
+---------+-------------------------------------+-+-------------+-+-+----------+
Mysql¶
Danger
This part and beyond are not tested
Installation¶
In this section, we will install MySQL in your Kubernetes cluster using a Deployment. The Deployment configuration defines the container image, ports, and resource limits for the MySQL containers. Additionally, it will include persistent storage to ensure data durability.
To create the MySQL Deployment, apply the following YAML configuration file:
$ kubectl apply -f mysql/deployment.yml
$
Finally, apply the Service configuration to expose MySQL:
kubectl apply -f mysql/service.yml
MYSQL Configuration¶
After applying the deployment, you can verify that the MySQL pods are running by executing:
kubectl get deployments
kubectl get pods
To check if the MySQL service is running correctly and exposed, you can use the following command:
kubectl get services
Ingress Controller Installation¶
Problematic¶
Currently, in our example, our NGINX server uses a single IP. If we multiply the number of web sites, we will end up using multiple IPs, resulting in fewer available IPs. To address this, we will set up a reverse proxy using the Octavia Ingress Controller to direct users to the correct application based on the URL used.
Installation Octavia Ingress Controller¶
exposur with reverse-proxy¶
We will install the Octavia Ingress Controller in the cluster and configure it to handle HTTP(S) traffic, routing requests to the appropriate services based on the URL.
Compatibility matrix¶
For this Kubernetes cluster, we need specific requirements to use the Octavia Ingress Controller:
- Communication between Octavia Ingress Controller and Octavia is needed.
- Octavia stable/queens or higher version is required because of features such as bulk pool members operation.
- OpenStack Key Manager (Barbican) service is required for TLS Ingress, otherwise Ingress creation will fail.
Install Octavia Ingress Controller¶
Follow the instructions provided in the Octavia Ingress Controller documentation to install the controller.
Apply the service account configuration defined in serviceaccount.yaml for the Octavia Ingress Controller.
kubectl apply -f octavia-ingress-controller/serviceaccount.yaml
Note
Apply the configuration settings specified in config.yaml for the Octavia Ingress Controller.
$ kubectl apply -f octavia-ingress-controller/config.yaml
$
Deploy the Octavia Ingress Controller using the settings defined in deployment.yaml.
$ kubectl apply -f octavia-ingress-controller/deployment.yaml
$
Apply the Ingress resource configuration defined in ingress.yaml to configure traffic routing for the Octavia Ingress Controller. In this file you have to define the url for the service access
$ kubectl apply -f octavia-ingress-controller/ingress.yaml
$
loadbalancer¶
Load Balancer Configuration
When deploying the reverse proxy, it will dynamically fetch the load balancer's IP address from OpenStack. During the initial deployment of the reverse proxy pods, it is recommended to avoid specifying a fixed IP address for the load balancer in the configuration. Ensure that the IP address assigned by OpenStack is accessible and meets your requirements.
To find the IP address, use the following command:
kubectl get ingress
Add this IP address to the "octavia-ingress-controller/ingress.yaml" file.
Alternatively, you can directly access your nginx web server without using the octavia-ingress-controller. When launching the nginx service, a load balancer is automatically configured for nginx.
To retrieve the IP address of the nginx service, use:
$ kubectl get service
$
You can then make an HTTP request using the IP address of the nginx service.
In our tutorial we will use the ingress controller to limit the use of IPs We are going to delete the LB on the nginx-service
$ kubectl patch service nginx-service -p '{"spec": {"type": "NodePort"}}'
$
DNS¶
Now an IP is allocated to the pods of ingress-controller, to have a link beetween the IP and the ingress routes created we will have to associate CNAMEs with the IP that Openstack created for the ingress-controller
$ kubectl get ingress
$
TLS¶
kubectl create secret tls tls-secret \
--cert nginx-yh-server.ijclab.in2p3.fr.crt \
--key nginx-yh-server.ijclab.in2p3.fr.key
kubectl apply -f octavia-ingress-controller/default-backend.yaml
kubectl apply -f octavia-ingress-controller/ingress-update.yaml
Now we can access to http://nginx-yh-server.ijclab.in2p3.fr and http://nginx-yh-server.ijclab.in2p3.fr
Install nginx-ingress & Let's Encrypt¶
This tutorial on the Cert-Manager website guides you through the process of installing and configuring Cert-Manager with NGINX Ingress to automate the acquisition and renewal of SSL/TLS certificates using the ACME protocol. Ideal for securing communications in Kubernetes applications, this guide is a valuable resource for developers and systems administrators looking to implement HTTPS certificates efficiently and automatically.
Volume persistent¶
Introduction¶
This section introduces the necessary configuration for automatic storage management in Kubernetes through a specific StorageClass This creates the storage class needed to automatically provision volumes.
kubectl apply -f volume/storageclass-cinder.yaml
Persistant Volume¶
A Persistent Volume (PV) in Kubernetes is a storage unit in the cluster that has been provisioned manually by an administrator or dynamically by Kubernetes via the StorageClass. The PV is crucial for applications that require durable data retention, independent of the lifespan of individual pods.
Persistent Volume Claim¶
The Persistent Volume Claim (PVC) is a storage request issued by a user, specifying the required size and access mode. The PVC binds to the most suitable available PV to ensure a secure and isolated environment for the data.
Cas de test¶
Cas 1 Removal of the Deployment¶
Objective: To confirm that the deletion of the Deployment does not lead to loss of data stored in the database.
Creation of namespace
kubectl create namespace test-pv-pvc
Creation of PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-pvc
namespace: test-pv-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
Creation of deployement
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql
namespace: test-pv-pvc
spec:
selector:
matchLabels:
app: mysql
strategy:
type: Recreate
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql
resources:
requests:
memory: "1Gi"
cpu: 2
limits:
memory: "2Gi"
cpu: 3
ports:
- containerPort: 3306
env:
- name: MYSQL_ROOT_PASSWORD
value: "password" # Remplacez par votre mot de passe sécurisé
volumeMounts:
- name: mysql-storage
mountPath: /var/lib/mysql
volumes:
- name: mysql-storage
persistentVolumeClaim:
claimName: mysql-pvc
Connection to the database
kubectl exec -it [mysql-pod-name] --namespace=test-pv-pvc -- mysql -u root -p
Addition of elements
CREATE DATABASE testdb;
USE testdb;
CREATE TABLE example (
id INT AUTO_INCREMENT PRIMARY KEY,
data VARCHAR(100)
);
INSERT INTO example (data) VALUES ('Test data');
We then remove the pods
kubectl delete pod [mysql-pod-name] --namespace=test-pv-pvc
With the deployment set up, the pod is automatically restarted We will check that the created elements are always present in the mysql pods Deleting the deployment and then recreating it does not impact the database.
Reconnection to the database
kubectl exec -it [mysql-pod-name] --namespace=test-pv-pvc -- mysql -u root -p
Check that items are present
USE testdb;
SELECT * FROM example;
mysql> USE testdb;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> SELECT * FROM example;
+----+-----------+
| id | data |
+----+-----------+
| 1 | Test data |
+----+-----------+
1 row in set (0.00 sec)
mysql>
Cas 2 Removal of the Namespace & Cluster¶
Objectif: Test the effect of deleting the namespace
kubectl delete namespace test-pv-pvc
All the elements present in the namespace are removed Data from the database can no longer be retrieved. To retrieve them after deleting the namespace we will add the persistent volume (PV)
To create the PV we must first create the volume on openstack
openstack volume create --size <size> --description "<Description>" <Name>
Volume observation created with
openstack volume list
Creation of namespace
kubectl create namespace test-pv-pvc
Creation of Persistant Volume
apiVersion: v1
kind: PersistentVolume
metadata:
name: cinder-pv
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteOnce
storageClassName: cinder
cinder:
fsType: ext4
volumeID: "ID" #METTRE ID DU VOLUME
persistentVolumeReclaimPolicy: Retain
Creation of Persistant Volume Claim
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: cinder-pvc
namespace: test-pv-pvc
spec:
accessModes:
- ReadWriteOnce
storageClassName: cinder
resources:
requests:
storage: 5Gi
volumeName: cinder-pv
Creation of deployement
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql
namespace: test-pv-pvc
spec:
selector:
matchLabels:
app: mysql
strategy:
type: Recreate
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql
resources:
requests:
memory: "1Gi"
cpu: 2
limits:
memory: "2Gi"
cpu: 3
ports:
- containerPort: 3306
env:
- name: MYSQL_ROOT_PASSWORD
value: "password" # Remplacez par votre mot de passe sécurisé
volumeMounts:
- name: mysql-storage
mountPath: /var/lib/mysql
volumes:
- name: mysql-storage
persistentVolumeClaim:
claimName: cinder-pvc
I add the data in the database as in the first example Now I destroy the namespace (or is present the pvc and the deployment) They are removed if I restart it (namespace->PVC->Deployment) The pvc tells me that the "volume "cinder-pv" already bound to a different claim." I therefore delete all resources, namely the namespace (pvc ,deployment ) and PV To restart them in the order: NS and PV -> PVC -> deploy
I connect to the databases and observe the data created previously are in. NOTE: I am getting the same error when removing the PVC and deployment I have to repeat the same procedure
Removal of the cluster¶
If the cluster and delete data can still be retrieved with the same procedure.
Authentication with Webhook in Kubernetes using Keystone¶
This documentation outlines the steps to set up and configure a Webhook for authentication in a Kubernetes cluster using Keystone from OpenStack. The test environment assumes a cluster without floating IP or master load balancers (LBAs), with the Kubernetes API server located on the master node.
Prerequisites¶
- My tests were done on a Kubernetes cluster without a floating IP or master LB.
- OpenStack environment with Keystone configured.
- The project used for the Kubernetes cluster in OpenStack:
bash
& openstack project list
NUMBER | ijclab-ing-inf-exploit-container-test
- Roles used in Keystone:
admin
,users
.
Removing Existing Webhook Configurations¶
Before starting, remove any unused or non-functional Webhook configurations (Daemonstat, Pods, ConfigMaps, Clusterrole {systeme:k8s-keystone-auth}, Clusterrolebindings {systeme:k8s-keystone-auth} , ServiceAccount, in the cluster to avoid potential issues. A webhook is pre-installed in the cluster because in the cluster-template we have labels: 'k8s_keystone_auth_tag': 'v1.XX.0'
Creating the Authorization Policy ConfigMap¶
This ConfigMap gives the permissions to allow users with the "user" role in keystone on any resource in any namespace. But the authorization part is not functional during my tests Create a ConfigMap for user authorization policies:
kubectl apply -f webhook/1config-map.yaml
Apply this ConfigMap using kubectl
.
Generating and Configuring Certificates¶
Generate a self-signed certificate (not recommended for production):
cd PATH/cert
# Generate a self-signed certificate
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem \
-days 365 -nodes -subj /CN=k8s-keystone-auth.kube-system/
# Create a secret kubernetes using the certificate
kubectl --namespace kube-system create secret tls \
keystone-auth-certs --cert=cert.pem --key=key.pem
Setting Up ServiceAccount and RBAC¶
Create a ServiceAccount and ClusterRole for the Webhook:
kubectl apply -f webhook/3Service-Account-RBAC.yaml
Deploying the Webhook¶
Create the Deployment for the Webhook:
kubectl apply -f webhook/4Deployment.yaml
Configuring the Service¶
Initially, configure the service as ClusterIP
:
kubectl apply -f webhook/5.1Service-ClusterIP.yaml
Now you can check if the pods is accessible with the service
Testing the Webhook¶
Authentification¶
Retrieve the OpenStack token:
token=$(openstack token issue -f value -c id)
Test the Webhook with curl
:
kubectl run curl --rm -it --restart=Never --image curlimages/curl -- \
-k -XPOST https://k8s-keystone-auth-service.kube-system:8443/webhook -d '
{
"apiVersion": "authentication.k8s.io/v1beta1",
"kind": "TokenReview",
"metadata": {
"creationTimestamp": null
},
"spec": {
"token": "'$token'"
}
}'
You should see the response from k8s-keystone-auth service.
For debugging, change the service type to NodePort
to access it via the node's IP and port:
kubectl apply -f webhook/5Service-NodePort.yaml
Retrieve the OpenStack token:
token=$(openstack token issue -f value -c id)
Test the Webhook with curl
:
#Get IP of node
kubectl get node -o wide
curl -k -XPOST https://{ip-node-master}:30000/webhook -d '{
"apiVersion": "authentication.k8s.io/v1beta1",
"kind": "TokenReview",
"metadata": {
"creationTimestamp": null
},
"spec": {
"token": "'$token'"
}
}'
Authorization¶
For my tests the permission with the webhook is not working Pay attention to the current permissions set up gives total access if for you the permission works
Exemple :
kubectl run curl --rm -it --restart=Never --image curlimages/curl -- \
-k -XPOST https://k8s-keystone-auth-service.kube-system:8443/webhook -d '
{
"apiVersion": "authorization.k8s.io/v1",
"kind": "SubjectAccessReview",
"spec": {
"resourceAttributes": {
"namespace": "default",
"verb": "get",
"groups": "",
"resource": "pods",
"name": "pod1"
},
"user": "demo",
"groups": ["ID-OF-THE-PROJECT"],
"extra": {
"alpha.kubernetes.io/identity/project/id": ["ID-OF-THE-PROJECT"],
"alpha.kubernetes.io/identity/project/name": ["demo"],
"alpha.kubernetes.io/identity/roles": ["load-balancer_member","users"]
}
}
}'
To try with the master node IP:
curl -k -XPOST https://{ip-node-master}:30000/webhook -d '
-k -XPOST https://k8s-keystone-auth-service.kube-system:8443/webhook -d '
{
"apiVersion": "authorization.k8s.io/v1",
"kind": "SubjectAccessReview",
"spec": {
"resourceAttributes": {
"namespace": "default",
"verb": "get",
"groups": "",
"resource": "pods",
"name": "pod1"
},
"user": "demo",
"groups": ["ID-OF-THE-PROJECT"],
"extra": {
"alpha.kubernetes.io/identity/project/id": ["ID-OF-THE-PROJECT"],
"alpha.kubernetes.io/identity/project/name": ["demo"],
"alpha.kubernetes.io/identity/roles": ["load-balancer_member","users"]
}
}
}'
Configuration on K8S master for authentication and/or authorization¶
We will connect to the master in order to make changes to remove the management of permissions from the webhook.
# ssh on the master (you need to have the ssh-key)
ssh core@ip-node-master
# Root privileges
sudo su
# List files
ls /etc/kubernetes
# Modification du fichier de configuration de l'api server
nano /etc/kubernetes/apiserver
# check the "--authorization-mode=Node,Webhook,RBAC" is in the file
# remove :
# --authorization-webhook-config-file=/etc/kubernetes/keystone_webhook_config.yaml
# and remove the Webhook posibility on the --authorization-mode
# --authorization-mode=Node,RBAC
We will now change the use of the configuration file in the master to define
the ip of the node or the ip of the ClusterIP if it works and the port
to be used nano /etc/kubernetes/keystone_webhook_config.yaml
kubectl apply -f webhook/6Config.yaml
Danger
Security Warning : The port being open it will just have to allow in local and block attempts external to the network (via the security groups of openstack)
Add Security Groups¶
In Openstack security groups you find the one that matches the machine
Then Manage the rules
Then add a rule
Adds a Description
(optional)
Direction: Entrance
Open Port
Port: Webhook port
Remote CIDR
CIDR Networks
Client Configuration¶
Download the client-keystone-auth binary
This link automatically downloads the client
Download Manually client-keystone-auth
Find a release : client-keystone-auth
At the bottom of the web page
Click on assets
And now click on the client-keystone-auth
Retrieve the cluster certificate from the admin's config file.
Run kubectl config set-credentials openstackuser
,
this command creates the following entry in the ~/.kube/config file.
- name: openstackuser
user: {}
Run kubectl config set-credentials openstackuser
,
this command creates the following entry in the ~/.kube/config file.
Config kubectl to use client-keystone-auth binary for the user openstackuser.
Replace my-k8s-${USERNAME} with your own cluster name.
Replace [DATA] with the certificate of the cluster.
Replace [PATH] with the path to acces to the client-keystone-auth.
Replace [IP] with the IP address of the master ip.
Here’s what your ./kube/config
should look like
$ cat .kube/config
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: [DATA]
server: https://[IP]:6443
name: my-k8s-${USERNAME}
contexts:
- context:
cluster: my-k8s-${USERNAME}
user: openstack
name: openstack@my-k8s-${USERNAME}
current-context: default
kind: Config
preferences: {}
users:
- name: openstack
user:
exec:
command: "/[PATH]/client-keystone-auth"
apiVersion: "client.authentication.k8s.io/v1beta1"
Connection¶
If the authorization webhook work test that everything works as expected try:
Put the variable in the RC file before trying a connection with the kubectl
add this line export OS_DOMAIN_NAME="ijclab"
in the file
Source your environment file (RC file)
kubectl --context openstackuser@my-k8s-${USERNAME} get pods
In case you are using this Webhook just for the authentication, you should get an authorization error
## Output
Error from server (Forbidden): pods is forbidden:
User "username" cannot list pods in the namespace "default"
Configuring RBAC Authorization¶
If the Webhook fails, fallback to using Kubernetes RBAC for authorization. Create a RoleBinding for user access: Change 'username' to the real user name
kubectl create rolebinding username-view --clusterrole view\
--user username --namespace default
Retry :
kubectl --context openstackuser@my-k8s-${USERNAME} get pods
Notes and Recommendations¶
- Use a trusted CA-signed certificate for production environments.
- Secure the node port with OpenStack security groups to prevent unauthorized access.
- Simplify Kubernetes user management by syncing Keystone and Kubernetes roles.
Additional Resources¶
Monitoring the cluster¶
Introduction Prometheus¶
In this section, I will install two prometheus, one outside the cluster to have a centralized prometheus that retrieves all metrics of the different services we want to monitor with prometheus
Prometheus¶
Installation Prometheus centralized¶
Download link Configuration link
Also, make sure that the port used by prometheus is open. Port 9090, you can test its connectivity from another machine of the prometheus server with
curl http://IP-Monitor:9090
If the port is not open, add a rule in the Security Groups that is used by the VM hosting the Central Prometheus
Installation Prometheus on kubernetes with helm¶
Installation
helm install my-prometheus -f values.yaml prometheus-community/prometheus
If we modify after the installation the values.yaml file, we apply these modifications to the cluster with:
helm upgrade my-prometheus -f values.yaml prometheus-community/prometheus
Le fichier values.yaml est récupérable directement depuis le site
La partie modifier du fichier values.yaml est la séction :
remoteWrite:
## https://prometheus.io/docs/prometheus/latest/configuration/configuration/#remote_read
- url: http://IP-Monitor:9090/api/v1/write
# The URL of the external Prometheus server where the metrics will be sent.
remote_timeout: 5s
# Sets the timeout duration for the remote write requests.
# If a request takes longer than 300 seconds, it will time out.
write_relabel_configs:
# This section is used to apply transformations to the metrics
# before sending them to the external Prometheus.
- source_labels: [__name__]
# Specifies the metric name as the source label.
regex: ".*"
# Matches all metric names.
action: keep
# Retains all metrics. No filtering is done at this stage.
# keep only specific labels for the kept metrics
- regex: '.*'
action: 'labelkeep'
# This rule adds or replaces a label called 'source'
# with the value 'kubernetes-prometheus'.
# It helps identify that the metrics are coming from the Kubernetes Prometheus.
- action: 'replace'
target_label: 'source'
replacement: 'kubernetes-prometheus-1.30'
Configuration in the centralized prometheus¶
To make the central prometheus capable of receiving metrics from the Kubernetes cluster, we need to adapt its configuration.
In the configuration file: /etc/systemd/system/prometheus.service
ExecStart=/usr/share/prometheus/prometheus \
--config.file=/usr/share/prometheus/prometheus.yml \
--storage.tsdb.path=/var/lib/prometheus \
--web.enable-remote-write-receiver \
--web.listen-address="0.0.0.0:9090"
The option --web.enable-remote-write-receiver
must be set.
The prometheus service is relaunched to take changes into account
sudo systemctl daemon-reload
sudo systemctl restart prometheus.service
sudo systemctl status prometheus.service
Test¶
- Go to the url : IP-Monitor:9090
- Type in "Expression": "up" then "Execute"
- Note: you should have multiple instances with the label:
source="kubernetes-prometheus-1.30"
Additional Resources Prometheus¶
Grafana¶
Introduction Grafana¶
We will install Grafana to have a graphical interface to read prometheus metrics.
Installation Grafana¶
Installation After installation we access the interface of grafana with port 3000 in http so with this URL: IP:3000/ Ensure that the server is only accessible inside the lab with Security Groups in Openstack
Configuration¶
All of the above we will add the local prometheus Datasource (because metrics are stored on the centralize prometheus) IP-Monitor:3000/connections/datasources/new
Home > Connections > Data sources > Add data source > Prometheus
- Add a name in the name section:
- Add the url to the connection: localhost:9090,
- Validate the information with a "Save&test"
Dashboard¶
In order to display these metrics we will retrieve dashboards pre-created by the community
- For example this dashboard
- Copy dashboard ID: 10000
- Go to the url to add dashboard: IP-Monitor:3000/dashboard/import
- Choose your {name, UID(if desired), Prometheus(datasource to use)}
- Valid with import
- It is made editable in settings
For some analysis one has "No data" it means that the promQL query is not good is that you must modify it in order to have the value of the search desired.
Let us analyze this request:
sum (container_memory_working_set_bytes{image!="",
name=~"^k8s*",kubernetes_io_hostname=~"^$Node$"}) by (pod_name)
- It contains a metrics: ‘container_memory_working_set_bytes’
- In Prometheus I enter the metrics we can see an example of the mysql pod:
container_memory_working_set_bytes{beta_kubernetes_io_arch="amd64",
beta_kubernetes_io_instance_type="vd.4", beta_kubernetes_io_os="linux",
draino_enabled="true", failure_domain_beta_kubernetes_io_region="lal",
failure_domain_beta_kubernetes_io_zone="nova",
id="/kubepods/burstable/pod3f392a81-6e42-436e-aebc-62b95a665190",
instance="k8s-1-30-coreos40-lb-yh-public-2i3uldrmsozm-node-0",
job="kubernetes-nodes-cadvisor", kubernetes_io_arch="amd64",
kubernetes_io_hostname="k8s-1-30-coreos40-lb-yh-public-2i3uldrmsozm-node-0",
kubernetes_io_os="linux", magnum_openstack_org_nodegroup="default-worker",
magnum_openstack_org_role="worker", namespace="default",
node_kubernetes_io_instance_type="vd.4", pod="mysql-68d8d6696d-xnp4p",
source="kubernetes-prometheus-1.30",
topology_cinder_csi_openstack_org_zone="nova", topology_kubernetes_io_region="lal",
topology_kubernetes_io_zone="nova"}
= 463773696
- No key named "name"
- no key named "pod_name" it must match "pod"
- Do not forget to add a value key
source="kubernetes-prometheus-1.30"
if one day there are several metrics it may be difficult to find
I change the query to:
sum (container_memory_working_set_bytes{image!="",
source="kubernetes-prometheus-1.30",kubernetes_io_hostname=~"^$Node$"}) by (pod)
The value $Node$
is linked to a variable that can be modified and observed
in the section "Variables" in the Settings, in our case it is well configured
AlertManager¶
Introduction AlertManager¶
We will install AlertManager to receive alerts.
Installation Alertmanager¶
Creation of a user
useradd --no-create-home --shell /bin/false alertmanager
Creation of a configuration directory and data
mkdir /etc/alertmanager
mkdir -p /var/lib//alertmanager/data
chown alertmanager:alertmanager /var/lib/alertmanager/data
Download and installation: have the latest version
wget https://github.com/prometheus/alertmanager/releases/download/v0.27.0/alertmanager-0.27.0.linux-amd64.tar.gz
Place the binaries in the path
cd alertmanager-0.27.0.linux-amd64
cp alertmanager /usr/local/bin/
cp amtool /usr/local/bin/
Change of rights
chown alertmanager:alertmanager /usr/local/bin/alertmanager
chown alertmanager:alertmanager /usr/local/bin/amtool
Configuration alertmanager¶
In the file that will be created: ‘/etc/systemd/system/alertmanager.service’
[Unit]
Description=Alertmanager
Wants=network-online.target
After=network-online.target
[Service]
User=alertmanager
Group=alertmanager
Type=simple
WorkingDirectory=/etc/alertmanager/
ExecStart=/usr/local/bin/alertmanager --config.file=/etc/alertmanager/alertmanager.yml\
--web.external-url http://IP-Monitor:9093 --cluster.advertise-address="IP-Monitor:9093"
[Install]
WantedBy=multi-user.target
The file /etc/alertmanager/alertmanager.yml
I created a Telegram bot to receive notifications
creates it in parallel
For general configuration
There are different ways to send notifications you can find them here
global:
resolve_timeout: 2m
smtp_require_tls: false
route:
group_by: ['kubernetes_io_hostname', 'severity']
# Send all notifications to me.
group_wait: 5s # Temps d'atente avant notification
group_interval: 1m # Delai par rapport aux alertes du meme groupe
repeat_interval: 5s # Attente avant repetition
receiver: 'telegram'
receivers:
- name: 'telegram'
telegram_configs:
- bot_token: ##############################################
api_url: https://api.telegram.org
chat_id: ###########
Configuration prometheus¶
In the prometheus file /usr/share/prometheus/prometheus.yml
we add or remove comments,
the alerting part with alertmanagers
# Alertmanager configuration
alerting:
alertmanagers:
- static_configs:
- targets: ["localhost:9093"]
systemctl daemon-reload
systemctl restart prometheus
systemctl restart alertmanager
First Alert¶
Configuration Alert the file/etc/prometheus/alert_rules.yml
## The backslash (\) at the end of the line indicates
## that the expression should continue on the same line in this example.
groups:
- name: cpu_alerts
rules:
- alert: HighCpuLoad
expr: sum by (kubernetes_io_hostname)\
(rate(container_cpu_usage_seconds_total{id="/", kubernetes_io_hostname=~".+"}[5m]))\
/ sum by (kubernetes_io_hostname) (machine_cpu_cores{kubernetes_io_hostname=~".+"})\
* 100 > 85
for: 5m
labels:
severity: warning
annotations:
summary: "High CPU Load on {{ $labels.kubernetes_io_hostname }}"
description: "Node {{ $labels.kubernetes_io_hostname }}\
has a CPU load higher than 85% for more than 5 minutes\
(current value: {{ $value }}%)."
This alert monitors the CPU usage of Kubernetes nodes and triggers if the load exceeds 85% on a node. It checks this condition over a period of 5 minutes to avoid temporary peaks. If the alert is triggered, a "warning" severity notification is sent with details about the affected node and the CPU load value.
Changes must be applied
systemctl restart alertmanager
Other¶
Create a bot telegram¶
Use a webhook to integrate rocketchats on alertmanager¶
Medium link Github project 1 Github project 2
Additional Resources understanding¶
Save Database on kubernetes¶
Introduction database save¶
First one see if the storageclass is created.
kubectl get storageclasses.storage.k8s.io
NAME PROVISIONER
cinder (default) cinder.csi.openstack.org
if is not created apply this yaml configuration
kubectl apply -f volume/storageclass-cinder.yaml
Now created Persistant Volume Claim.
kubectl apply -f backup/pvc-mysql.yaml
Installation Database¶
Creation of database
kubectl apply -f backup/deployment-mysql.yaml
Creation of Service
kubectl apply -f backup/service-mysql.yaml
Input data in Database¶
Now we have to fill this database with information. First you will have to launch a terminal of the pods with the correct suffix auto-generate by kubernetes
kubectl get pods
kubectl exec -it mysql-SUFFIXE -- /bin/bash
Inside the pod we will launch this command
mysql -u testuser -ptestpassword
SHOW DATABASES;
USE testdb;
CREATE TABLE personnes (
id INT AUTO_INCREMENT PRIMARY KEY,
nom VARCHAR(100),
prenom VARCHAR(100),
annee_naissance INT
);
INSERT INTO personnes (nom, prenom, annee_naissance)
VALUES ('Doe', 'John', 1990),
('Smith', 'Jane', 1985);
#/
SELECT * FROM personnes;
Image Dockerfile for the CronJob¶
For extract the database from the job
i need a ubuntu with ssh and mysql packages installed.
You can see my image here backup/dockerfile
Export Database to server¶
First creates the secret in kubernetes with the ssh priver key. For more security generate a ssh key exclusively for this use (The public key (.pub) must be in the file authorized_keys (/root/. ssh/authorized_keys) of the server that will receive the dump))
kubectl create secret generic ssh-key-secret --from-file=ssh-privatekey=/home/USER/.ssh/KEY.pem
The CronJob
kubectl apply -f backup/cronjob-mysql.yaml
Display of the database dump on the server¶
After the job is executed in kubernetes we can observe the dump on our server. Docker must be installed on the server to import it in order to validate that the database is well presented I will use a docker image with the same version as used in our test.
sudo docker run --name mysql-container -e MYSQL_ROOT_PASSWORD=testpassword -d mysql:9.0.1
sudo docker cp DATABASE/dump-2024-09-19.sql mysql-container:/dump.sql
sudo docker exec -it mysql-container bash
````
Into the container
```sh
mysql -u root -ptestpassword
SOURCE /dump.sql;
SHOW DATABASES;
USE testdb;
SHOW TABLES;
SELECT * FROM personnes;
Dynamically manage our cluster with k9s¶
Installation 1¶
Installation with snap¶
snap install k9s --devmode
Installation 2¶
Download the binary¶
For example, if you are using a 64-bit Linux system
wget https://github.com/derailed/k9s/releases/download/v0.28.2/k9s_Linux_amd64.tar.gz
echo 'Extract the binary'
tar -xzf k9s_Linux_amd64.tar.gz
echo 'Make it executable'
chmod +x k9s
echo 'Move it to a bin directory'
sudo mv k9s /usr/local/bin/