80/TCP 19m
+```
+
+```bash +.
+ curl IP_HTTP_SERVICE
+```
+
+``` {.html}
+It works!
+
+ curl IP_NGINX_SERVICE
+
+
+
+
+Welcome to nginx!
+
+
+
+Welcome to nginx!
+If you see this page, the nginx web server is successfully installed and
+working. Further configuration is required.
+
+For online documentation and support please refer to
+nginx.org.
+Commercial support is available at
+nginx.com.
+
+Thank you for using nginx.
+
+
+```
+
+10. Essayons maintenant de nous connecter aux deux pods via cet ingress :
+
+```bash +.
+ curl IP_INGRESS/httpd
+```
+
+``` {.html}
+It works!
+
+ curl IP_INGRESS/nginx
+
+
+
+
+Welcome to nginx!
+
+
+
+Welcome to nginx!
+If you see this page, the nginx web server is successfully installed and
+working. Further configuration is required.
+
+For online documentation and support please refer to
+nginx.org.
+Commercial support is available at
+nginx.com.
+
+Thank you for using nginx.
+
+
+```
+
+11. Nous allons maintenant définir un ingress, mais cette fois-ci, nos redirection se feront selon l'host que nous utilisons pour nous connecter :
+
+```bash +.
+ touch ingress-with-hosts.yaml
+```
+
+Avec le contenu yaml suivant :
+
+```yaml +.
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ annotations:
+ kubernetes.io/ingress.class: nginx
+ name: ingress-with-hosts
+ namespace: ingress
+spec:
+ rules:
+ - host: nginx.example.com
+ http:
+ paths:
+ - backend:
+ service:
+ name: ingress-nginx-service
+ port:
+ number: 80
+ path: /
+ pathType: Prefix
+ - host: httpd.example.com
+ http:
+ paths:
+ - backend:
+ service:
+ name: ingress-httpd-service
+ port:
+ number: 80
+ path: /
+ pathType: Prefix
+```
+
+12. Créons cet ingress :
+
+```bash +.
+ kubectl apply -f ingress-with-hosts.yaml
+```
+```bash +.
+ingress.networking.k8s.io/ingress-with-hosts created
+```
+
+13. Ajoutons ces deux entrées dans le fichier /etc/hosts :
+
+```bash +.
+IP_INGRESS nginx.example.com
+IP_INGRESS httpd.example.com
+```
+
+14. Essayons maintenant de nous connecter à nos deux pods via l'ingress :
+
+```bash +.
+ curl httpd.example.com
+```
+
+``` {.html}
+It works!
+
+ curl nginx.example.com
+
+
+
+
+Welcome to nginx!
+
+
+
+Welcome to nginx!
+If you see this page, the nginx web server is successfully installed and
+working. Further configuration is required.
+
+For online documentation and support please refer to
+nginx.org.
+Commercial support is available at
+nginx.com.
+
+Thank you for using nginx.
+
+
+```
+
+
+
+
+Machine : **master**
+
+
+
+### Canary Deployment
+
+Les déploiements Canary permettent le déploiement progressif de nouvelles versions d'applications sans aucune interruption de service.
+
+Le **NGINX Ingress Controller** prend en charge les politiques de répartition du trafic basées sur les en-têtes (header) , le cookie et le poids. Alors que les politiques basées sur les en-têtes et les cookies servent à fournir une nouvelle version de service à un sous-ensemble d'utilisateurs, les politiques basées sur le poids servent à détourner un pourcentage du trafic vers une nouvelle version de service.
+
+Le **NGINX Ingress Controller** utilise les annotations suivantes pour activer les déploiements Canary :
+
+```
+- nginx.ingress.kubernetes.io/canary-by-header
+
+- nginx.ingress.kubernetes.io/canary-by-header-value
+
+- nginx.ingress.kubernetes.io/canary-by-header-pattern
+
+- nginx.ingress.kubernetes.io/canary-by-cookie
+
+- nginx.ingress.kubernetes.io/canary-weight
+```
+
+Les règles s'appliquent dans cet ordre :
+
+- canary-by-header
+
+- canary-by-cookie
+
+- canary-weight
+
+Les déploiements Canary nécessitent que vous créiez deux entrées : une pour le trafic normal et une pour le trafic alternatif. Sachez que vous ne pouvez appliquer qu'une seule entrée Canary.
+
+Vous activez une règle de répartition du trafic particulière en définissant l'annotation Canary associée sur true dans la ressource Kubernetes Ingress, comme dans l'exemple suivant :
+
+- `nginx.ingress.kubernetes.io/canary-by-header : "true"`
+
+
+Exemple :
+
+1. Déployer les applications et services suivants
+
+- Application V1 :
+
+```yaml +.
+
+apiVersion: v1
+kind: Service
+metadata:
+ name: echo-v1
+spec:
+ type: ClusterIP
+ ports:
+ - port: 80
+ protocol: TCP
+ name: http
+ selector:
+ app: echo
+ version: v1
+
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: echo-v1
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: echo
+ version: v1
+ template:
+ metadata:
+ labels:
+ app: echo
+ version: v1
+ spec:
+ containers:
+ - name: echo
+ image: "hashicorp/http-echo"
+ args:
+ - -listen=:80
+ - --text="echo-v1"
+ ports:
+ - name: http
+ protocol: TCP
+ containerPort: 80
+
+```
+
+- Application V2 :
+
+```yaml +.
+apiVersion: v1
+kind: Service
+metadata:
+ name: echo-v2
+spec:
+ type: ClusterIP
+ ports:
+ - port: 80
+ protocol: TCP
+ name: http
+ selector:
+ app: echo
+ version: v2
+
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: echo-v2
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: echo
+ version: v2
+ template:
+ metadata:
+ labels:
+ app: echo
+ version: v2
+ spec:
+ containers:
+ - name: echo
+ image: "hashicorp/http-echo"
+ args:
+ - -listen=:80
+ - --text="echo-v2"
+ ports:
+ - name: http
+ protocol: TCP
+ containerPort: 80
+
+```
+
+2. Déployer l'ingress de l'application v1
+
+```yaml +.
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ annotations:
+ ingress.kubernetes.io/rewrite-target: /
+ kubernetes.io/ingress.class: nginx
+ name: ingress-echo
+spec:
+ #ingressClassName: nginx
+ rules:
+ - host: canary.example.com
+ http:
+ paths:
+ - path: /echo
+ pathType: Exact
+ backend:
+ service:
+ name: echo-v1
+ port:
+ number: 80
+
+```
+
+3. Vérifiez qu'il fonctionne
+
+```bash +.
+curl -H "Host: canary.example.com" http://:/echo
+
+```
+4. Vous devriez avoir la réponse suivante
+
+**echo-v2**
+
+5. Test : Par Header
+
+Deployez l'ingress suivant :
+
+```yaml +.
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ annotations:
+ ingress.kubernetes.io/rewrite-target: /
+ nginx.ingress.kubernetes.io/canary: "true"
+ nginx.ingress.kubernetes.io/canary-by-header: "Region"
+ nginx.ingress.kubernetes.io/canary-by-header-pattern: "fr|us"
+ kubernetes.io/ingress.class: nginx
+ name: ingress-echo-canary-header
+spec:
+ #ingressClassName: nginx
+ rules:
+ - host: canary.example.com
+ http:
+ paths:
+ - path: /echo
+ pathType: Exact
+ backend:
+ service:
+ name: echo-v2
+ port:
+ number: 80
+
+```
+
+6. Faire les test suivants
+
+```bash +.
+curl -H "Host: canary.example.com" -H "Region: us" http://:/echo
+curl -H "Host: canary.example.com" -H "Region: de" http://:/echo
+curl -H "Host: canary.example.com" http://:/echo
+```
+
+7. Résultats
+
+**echo-v2**
+
+**echo-v1**
+
+**echo-v1**
+
+8. Supprimer l'ingress ingress-echo-canary-header
+
+```bash +.
+kubectl delete ingress ingress-echo-canary-header
+```
+
+9. Test : Par cookie
+
+Déployez l'ingress suivant :
+
+```yaml +.
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ annotations:
+ ingress.kubernetes.io/rewrite-target: /
+ nginx.ingress.kubernetes.io/canary: "true"
+ nginx.ingress.kubernetes.io/canary-by-cookie: "my-cookie"
+ kubernetes.io/ingress.class: nginx
+ name: ingress-echo-canary-cookie
+spec:
+ #ingressClassName: nginx
+ rules:
+ - host: canary.example.com
+ http:
+ paths:
+ - path: /echo
+ pathType: Exact
+ backend:
+ service:
+ name: echo-v2
+ port:
+ number: 80
+
+```
+
+10. Faire les test suivants
+
+```bash +.
+curl -s --cookie "my-cookie=always" -H "Host: canary.example.com" http://:/echo
+curl -s --cookie "other-cookie=always" -H "Host: canary.example.com" http://:/echo
+curl -H "Host: canary.example.com" http://:/echo
+```
+
+11. Résultats
+
+**echo-v2**
+
+**echo-v1**
+
+**echo-v1**
+
+
+12. Supprimer l'ingress ingress-echo-canary-cookie
+
+```bash +.
+kubectl delete ingress ingress-echo-canary-cookie
+```
+
+
+13. Test : Par poids
+
+Déployez l'ingress suivant :
+
+```yaml +.
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ annotations:
+ ingress.kubernetes.io/rewrite-target: /
+ nginx.ingress.kubernetes.io/canary: "true"
+ nginx.ingress.kubernetes.io/canary-by-header: "X-Canary"
+ nginx.ingress.kubernetes.io/canary-weight: "50"
+ kubernetes.io/ingress.class: nginx
+ name: ingress-echo-canary-weight
+spec:
+ #ingressClassName: nginx
+ rules:
+ - host: canary.example.com
+ http:
+ paths:
+ - path: /echo
+ pathType: Exact
+ backend:
+ service:
+ name: echo-v2
+ port:
+ number: 80
+
+```
+
+14. faire les tests suivants
+
+```bash +.
+
+# 6 fois :
+curl -H "Host: canary.example.com" http://:/echo
+
+```
+
+15. Vérifiez bien que vous avez une répartition de 50% entre echo-v1 et echo-v2
+
+16. Installer k6: https://k6.io/docs/getting-started/installation/
+
+
+Utilisez en l'adaptant (url) le fichier: script.js
+
+Modifiez `http://localhost/echo` par `http://ip-pub-loadbalancer/echo`
+
+``` javascript
+
+import http from 'k6/http';
+import {check, sleep} from 'k6';
+import {Rate} from 'k6/metrics';
+import {parseHTML} from "k6/html";
+
+const reqRate = new Rate('http_req_rate');
+
+export const options = {
+ stages: [
+ {target: 20, duration: '20s'},
+ {target: 20, duration: '20s'},
+ {target: 0, duration: '20s'},
+ ],
+ thresholds: {
+ 'checks': ['rate>0.9'],
+ 'http_req_duration': ['p(95)<1000'],
+ 'http_req_rate{deployment:echo-v1}': ['rate>=0'],
+ 'http_req_rate{deployment:echo-v2}': ['rate>=0'],
+ },
+};
+
+export default function () {
+ const params = {
+ headers: {
+ 'Host': 'canary.example.com',
+ 'Content-Type': 'text/plain',
+ },
+ };
+
+ const res = http.get(`http://localhost/echo`, params);
+ check(res, {
+ 'status code is 200': (r) => r.status === 200,
+ });
+
+
+ var body = res.body.replace(/[\r\n]/gm, '');
+
+ switch (body) {
+ case '"echo-v1"':
+ reqRate.add(true, { deployment: 'echo-v1' });
+ reqRate.add(false, { deployment: 'echo-v2' });
+ break;
+ case '"echo-v2"':
+ reqRate.add(false, { deployment: 'echo-v1' });
+ reqRate.add(true, { deployment: 'echo-v2' });
+ break;
+ }
+
+ sleep(1);
+}
+
+
+```
+
+et lancez le de la manière suivante
+
+`k6 run script.js`
+
+vérifiez la répartition des requetes
+
+### Clean Up
+
+Nous pouvons supprimer les ressources générées par cet exercices de la façon suivante :
+
+```bash +.
+ kubectl delete -f .
+```
+
+```bash +.
+pod "ingress-httpd-pod" deleted
+service "ingress-httpd-service" deleted
+pod "ingress-nginx-pod" deleted
+service "ingress-nginx-service" deleted
+Warning: networking.k8s.io/v1beta1 Ingress is deprecated in v1.19+, unavailable in v1.22+; use networking.k8s.io/v1 Ingress
+ingress.networking.k8s.io "ingress-with-hosts" deleted
+ingress.networking.k8s.io "ingress-with-paths" deleted
+```
+
+
+
+
+## Monitoring
+
+
+
+Machine : **master**
+
+
+
+```bash +.
+mkdir monitoring
+cd monitoring
+kubectl create namespace monitoring
+```
+
+### Metric Server
+
+1. Nous allons essayer d'obtenir les métriques pour les noeuds de notre cluster :
+
+```bash +.
+kubectl top node
+```
+```bash +.
+error: Metrics API not available
+```
+
+... Sans succès.
+
+2. De même, si nous souhaitons récupérer les métriques de nos pods, nous obtenons une erreur :
+
+```bash +.
+kubectl top pod
+```
+```bash +.
+error: Metrics API not available
+```
+
+Nous avons besoin d'installer un metrics server.
+
+3. Nous allons créer un fichier metrics-server.yaml :
+
+```bash +.
+touch metrics-server.yaml
+```
+
+Avec le contenu yaml suivant :
+
+```yaml +.
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ name: system:aggregated-metrics-reader
+ labels:
+ rbac.authorization.k8s.io/aggregate-to-view: "true"
+ rbac.authorization.k8s.io/aggregate-to-edit: "true"
+ rbac.authorization.k8s.io/aggregate-to-admin: "true"
+rules:
+- apiGroups: ["metrics.k8s.io"]
+ resources: ["pods", "nodes"]
+ verbs: ["get", "list", "watch"]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+ name: metrics-server:system:auth-delegator
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: system:auth-delegator
+subjects:
+- kind: ServiceAccount
+ name: metrics-server
+ namespace: kube-system
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+ name: metrics-server-auth-reader
+ namespace: kube-system
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: Role
+ name: extension-apiserver-authentication-reader
+subjects:
+- kind: ServiceAccount
+ name: metrics-server
+ namespace: kube-system
+---
+apiVersion: apiregistration.k8s.io/v1
+kind: APIService
+metadata:
+ name: v1beta1.metrics.k8s.io
+spec:
+ service:
+ name: metrics-server
+ namespace: kube-system
+ group: metrics.k8s.io
+ version: v1beta1
+ insecureSkipTLSVerify: true
+ groupPriorityMinimum: 100
+ versionPriority: 100
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: metrics-server
+ namespace: kube-system
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: metrics-server
+ namespace: kube-system
+ labels:
+ k8s-app: metrics-server
+spec:
+ selector:
+ matchLabels:
+ k8s-app: metrics-server
+ template:
+ metadata:
+ name: metrics-server
+ labels:
+ k8s-app: metrics-server
+ spec:
+ serviceAccountName: metrics-server
+ volumes:
+ # mount in tmp so we can safely use from-scratch images and/or read-only containers
+ - name: tmp-dir
+ emptyDir: {}
+ containers:
+ - name: metrics-server
+ image: k8s.gcr.io/metrics-server/metrics-server:v0.3.7
+ imagePullPolicy: IfNotPresent
+ args:
+ - --cert-dir=/tmp
+ - --secure-port=4443
+ - --kubelet-insecure-tls
+ - --kubelet-preferred-address-types=InternalIP
+ ports:
+ - name: main-port
+ containerPort: 4443
+ protocol: TCP
+ securityContext:
+ readOnlyRootFilesystem: true
+ runAsNonRoot: true
+ runAsUser: 1000
+ volumeMounts:
+ - name: tmp-dir
+ mountPath: /tmp
+ nodeSelector:
+ kubernetes.io/os: linux
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: metrics-server
+ namespace: kube-system
+ labels:
+ kubernetes.io/name: "Metrics-server"
+ kubernetes.io/cluster-service: "true"
+spec:
+ selector:
+ k8s-app: metrics-server
+ ports:
+ - port: 443
+ protocol: TCP
+ targetPort: main-port
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ name: system:metrics-server
+rules:
+- apiGroups:
+ - ""
+ resources:
+ - pods
+ - nodes
+ - nodes/stats
+ - namespaces
+ - configmaps
+ verbs:
+ - get
+ - list
+ - watch
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+ name: system:metrics-server
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: system:metrics-server
+subjects:
+- kind: ServiceAccount
+ name: metrics-server
+ namespace: kube-system
+```
+
+4. Nous pouvons donc déployer notre metrics-server :
+
+```bash +.
+kubectl apply -f metrics-server.yaml
+```
+```bash +.
+clusterrole.rbac.authorization.k8s.io/system:aggregated-metrics-reader created
+clusterrolebinding.rbac.authorization.k8s.io/metrics-server:system:auth-delegator created
+rolebinding.rbac.authorization.k8s.io/metrics-server-auth-reader created
+Warning: apiregistration.k8s.io/v1 APIService is deprecated in v1.19+, unavailable in v1.22+; use apiregistration.k8s.io/v1 APIService
+apiservice.apiregistration.k8s.io/v1.metrics.k8s.io created
+serviceaccount/metrics-server created
+deployment.apps/metrics-server created
+service/metrics-server created
+clusterrole.rbac.authorization.k8s.io/system:metrics-server created
+clusterrolebinding.rbac.authorization.k8s.io/system:metrics-server created
+```
+
+5. Après environ 1 minute, nous pouvons refaire notre top node :
+
+```bash +.
+kubectl top node
+```
+```bash +.
+NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
+master 180m 9% 1249Mi 15%
+worker 47m 2% 818Mi 10%
+```
+
+6. Nous obtenons bien les consommations CPU/RAM pour chaque noeud. Voyons voir maintenant pour les consommations de ressources par nos pods :
+
+```bash +.
+kubectl top pod -A
+```
+```bash +.
+NAMESPACE NAME CPU(cores) MEMORY(bytes)
+kube-system coredns-f9fd979d6-9kb87 4m 12Mi
+kube-system coredns-f9fd979d6-tl95z 3m 12Mi
+kube-system etcd-master 20m 41Mi
+kube-system kube-apiserver-master 48m 294Mi
+kube-system kube-controller-manager-master 16m 47Mi
+kube-system kube-proxy-8dvrj 1m 15Mi
+kube-system kube-proxy-ll8tb 1m 15Mi
+kube-system kube-scheduler-master 4m 21Mi
+kube-system metrics-server-75f98fdbd5-2lp87 1m 12Mi
+kube-system weave-net-c4b7d 2m 58Mi
+kube-system weave-net-zfqt6 2m 62Mi
+```
+
+Parfait !
+
+
+
+### Prometheus/Grafana
+
+
+
+Nous allons déployer une stack de monitoring basée sur Prometheus et Grafana via Helm.
+
+1. Commençons par créer le fichier values.yaml pour le chart :
+
+```bash +.
+touch kube-prometheus-stack.yaml
+```
+
+Avec le contenu yaml suivant :
+
+```yaml +.
+grafana:
+ adminPassword: prom-passw0rd
+```
+
+2. Nous pouvons donc installer la stack prometheus via Helm :
+
+```bash +.
+helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
+helm upgrade --install prometheus prometheus-community/kube-prometheus-stack --values kube-prometheus-stack.yaml --namespace monitoring --create-namespace
+```
+
+
+3. Nous pouvons voir les ressources créées de la façon suivante :
+
+```bash +.
+kubectl get all -n monitoring
+```
+
+
+4. Nous allons faire un port-forward pour se connecter à notre serveur Prometheus :
+
+```bash +.
+kubectl --namespace monitoring port-forward --address 0.0.0.0 service/prometheus-kube-prometheus-prometheus 8080:80
+```
+```bash +.
+Forwarding from 0.0.0.0:8080 -> 9090
+```
+
+5. De même pour Grafana :
+
+```bash +.
+kubectl --namespace monitoring port-forward --address 0.0.0.0 service/prometheus-grafana 8081:80
+```
+```bash +.
+Forwarding from 0.0.0.0:8081 -> 80
+```
+
+6. Enjoy :)
+
+
+
+
+## Helm
+
+
+
+Machine : **master**
+
+
+
+```bash +.
+mkdir helm
+cd helm
+kubectl create namespace helm
+```
+
+### Installation d'un echo-server avec Helm
+
+1. Commençons par l'installation de Helm :
+
+```bash +.
+curl -Lo helm.tar.gz https://get.helm.sh/helm-v3.3.4-linux-amd64.tar.gz
+tar xvf helm.tar.gz
+sudo mv linux-amd64/helm /usr/local/bin
+rm -rf helm.tar.gz linux-amd64
+```
+
+2. Nous pouvons tester l'installation de la façon suivante :
+
+```bash +.
+helm version
+```
+
+```bash +.
+version.BuildInfo{Version:"v3.3.4", GitCommit:"a61ce5633af99708171414353ed49547cf05013d", GitTreeState:"clean", GoVersion:"go1.14.9"}
+```
+
+3. Les artefacts Helm sont stockes sur des repositories. Il est nécessaire d'ajouter. Nous allons ajouter le repo **ealenn** contenant le chart **echo-server** :
+
+```bash +.
+helm repo add ealenn https://ealenn.github.io/charts
+```
+
+```bash +.
+
+"ealenn" has been added to your repositories
+```
+
+4. Une fois notre repository ajouté, nous pouvons installer n'importe quel chart se trouvant dans ce repository. Installons donc notre echo-server :
+
+```bash +.
+helm install echo-server ealenn/echo-server
+```bash +.
+
+```bash +.
+NAME: echo-server
+LAST DEPLOYED: Tue Oct 27 10:21:27 2020
+NAMESPACE: default
+STATUS: deployed
+REVISION: 1
+```
+
+5. Nous pouvons lister les charts installés de la façon suivante :
+
+```bash +.
+helm list
+```
+
+```bash +.
+NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
+echo-server default 1 2020-10-27 10:21:27.307028704 +0000 UTC deployed echo-server-0.3.0 0.4.0
+```
+
+6. Nous pouvons voir les pods générés par l'installation :
+
+```bash +.
+kubectl get pods
+```
+
+```bash +.
+NAME READY STATUS RESTARTS AGE
+echo-server-79cc9789cb-hqmlt 1/1 Running 0 2m10s
+```
+
+7. Quant à la désinstallation, elle se fait de la façon suivante :
+
+```bash +.
+helm uninstall echo-server
+```
+
+```bash +.
+release "echo-server" uninstalled
+```
+
+### Installation avec un fichier values.yaml
+
+1. Commençons par créer un fichier values.yaml :
+
+```bash +.
+touch values.yaml
+```
+
+Avec le contenu yaml suivant :
+
+```yaml +.
+replicaCount: 3
+
+image:
+ tag: "0.4.1"
+```
+
+2. Nous allons cette fois ci installer notre echo-server dans le namespace **helm**, configure à l'aide du fichier values.yaml ci dessus :
+
+```bash +.
+helm install echo-server ealenn/echo-server --values values.yaml --namespace helm
+```
+```bash +.
+NAME: echo-server
+LAST DEPLOYED: Sat Oct 31 17:57:50 2020
+NAMESPACE: helm
+STATUS: deployed
+REVISION: 1
+```
+
+3. Nous allons maintenant voir si notre chart bien génère 3 pods :
+
+```bash +.
+kubectl get pods -n helm
+```
+```bash +.
+NAME READY STATUS RESTARTS AGE
+echo-server-66d9c454b5-8crn7 1/1 Running 0 32s
+echo-server-66d9c454b5-wdr7p 1/1 Running 0 32s
+echo-server-66d9c454b5-z6cwt 1/1 Running 0 32s
+```
+
+4. Nous pouvons maintenant désinstaller notre echo-server :
+
+```bash +.
+helm uninstall echo-server -n helm
+```
+```bash +.
+release "echo-server" uninstalled
+```
+
+
+
+
+## Kustomize
+
+
+
+Machine : **master**
+
+
+
+```bash +.
+mkdir -p kustomize/k8s/base
+cd kustomize/k8s/base
+kubectl create namespace kustomize
+```
+
+
+#### Installation de kustomize
+
+
+
+Choisissez !
+
+#### Structure à Créer
+
+```bash +.
+.
+├── base
+│ ├── deployment.yaml
+│ ├── kustomization.yaml
+│ └── service.yaml
+└── overlays
+ └── prod
+ ├── custom-env.yaml
+ ├── database-secret.yaml
+ ├── kustomization.yaml
+ └── replica-and-rollout-strategy.yaml
+```
+
+
+##### Contenu de `base/deployment.yaml`
+
+```yaml +.
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: sl-demo-app
+spec:
+ selector:
+ matchLabels:
+ app: sl-demo-app
+ template:
+ metadata:
+ labels:
+ app: sl-demo-app
+ spec:
+ containers:
+ - name: app
+ image: nginx:1.19.9
+ ports:
+ - name: http
+ containerPort: 80
+ protocol: TCP
+```
+
+##### Contenu de `base/service.yaml`
+
+```yaml +.
+apiVersion: v1
+kind: Service
+metadata:
+ name: sl-demo-app
+spec:
+ ports:
+ - name: http
+ port: 8080
+ targetPort: 80
+ selector:
+ app: sl-demo-app
+```
+
+
+
+##### Contenu de `base/kustomization.yaml`
+
+```yaml +.
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+
+resources:
+ - service.yaml
+ - deployment.yaml
+```
+
+
+#### Lancement initial
+
+```bash +.
+kubectl apply -k k8s/base
+
+kubectl get all -l app=sl-demo-app
+kubectl get deploy
+```
+
+```bash +.
+NAME READY STATUS RESTARTS AGE
+pod/sl-demo-app-bb6494cc6-sd6k7 1/1 Running 0 6m42s
+
+NAME READY UP-TO-DATE AVAILABLE AGE
+deployment.apps/sl-demo-app 1/1 1 1 6m42s
+
+NAME DESIRED CURRENT READY AGE
+replicaset.apps/sl-demo-app-bb6494cc6 1 1 1 6m42s
+```
+
+
+#### Création de l'overlay de production (prod)
+
+##### Contenu du fichier `overlays/prod/replica-and-rollout-strategy.yaml`
+
+```yaml +.
+
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: sl-demo-app
+spec:
+ replicas: 10
+ strategy:
+ rollingUpdate:
+ maxSurge: 1
+ maxUnavailable: 1
+ type: RollingUpdate
+```
+
+##### Contenu du fichier `overlays/prod/database-secret.yaml`
+
+```yaml +.
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: sl-demo-app
+spec:
+ template:
+ spec:
+ containers:
+ - name: app
+ env:
+ - name: "DB_PASSWORD"
+ valueFrom:
+ secretKeyRef:
+ name: sl-demo-app
+ key: db-password
+```
+
+
+##### Contenu du fichier `overlays/prod/custom-env.yaml`
+
+```yaml +.
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: sl-demo-app
+spec:
+ template:
+ spec:
+ containers:
+ - name: app # (1)
+ env:
+ - name: CUSTOM_ENV_VARIABLE
+ value: Value defined by Kustomize ❤️
+```
+
+##### Contenu du fichier `overlays/prod/kustomization.yaml`
+
+```yaml +.
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+commonLabels:
+ caas.fr/environment: "prod"
+bases:
+- ../../base
+
+patchesStrategicMerge:
+- custom-env.yaml
+- replica-and-rollout-strategy.yaml
+- database-secret.yaml
+
+secretGenerator:
+- literals:
+ - db-password=12345
+ name: sl-demo-app
+ type: Opaque
+
+images:
+- name: nginx
+ newName: nginx
+ newTag: 1.21.0
+```
+
+#### Lancement overlay prod
+
+```bash +.
+kubectl delete -k k8s/base
+kubectl apply -k k8s/overlays/prod
+
+kubectl get all -l caas.fr/environment=prod
+```
+
+
+
+
+
+
+## Logging
+
+
+
+
+Machine : **master**
+
+
+
+### Simple
+
+Avec la CLI kubectl, nous pouvons d'ores et déjà récupérer plusieurs logs concernant notre cluster Kubernetes.
+
+1. Tout d'abord, nous pouvons récupérer des informations sur le cluster, ainsi que de certains composants de ce cluster de la façon suivante :
+
+```bash +.
+kubectl cluster-info
+```
+```bash +.
+Kubernetes master is running at https://10.156.0.3:6443
+KubeDNS is running at https://10.156.0.3:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
+Metrics-server is running at https://10.156.0.3:6443/api/v1/namespaces/kube-system/services/https:metrics-server:/proxy
+
+To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
+```
+
+2. Nous pouvons également voir tout les évènements qui ont eu lieu dans le cluster. Un évènement peut désigner le rescheduling d'un pod, la mise à jour d'un deployment, la création d'un PV ou binding d'un PVC à un PV. Nous pouvons avoir toute ces infos de la façon suivante :
+
+```bash +.
+kubectl get events -A
+```
+```bash +.
+LAST SEEN TYPE REASON OBJECT MESSAGE
+81s Normal ExternalProvisioning persistentvolumeclaim/postgres-openebs-pvc waiting for a volume to be created, either by external provisioner "openebs.io/provisioner-iscsi" or manually created by system administrator
+89s Normal Provisioning persistentvolumeclaim/postgres-openebs-pvc External provisioner is provisioning volume for claim "default/postgres-openebs-pvc"
+```
+
+3. Nous allons maintenant voir comment récupérer les logs générés par les conteneurs d'un pod. Commençons par créer un pod avec l'image nginx à titre d'exemple :
+
+```bash +.
+kubectl run --image nginx test-logs
+```
+```bash +.
+pod/test-logs created
+```
+
+4. Nous pouvons récupérer les logs de ce pod de la façon suivante :
+
+```bash +.
+kubectl logs test-logs
+```
+```bash +.
+/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
+/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
+/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
+10-listen-on-ipv6-by-default.sh: Getting the checksum of /etc/nginx/conf.d/default.conf
+10-listen-on-ipv6-by-default.sh: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
+/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
+/docker-entrypoint.sh: Configuration complete; ready for start up
+```
+
+5. Notez également que les pods system stockent également des logs dans les dossiers /var/log/containers et /var/log/pods, on peut donc les voir de la façon suivante :
+
+```bash +.
+sudo cat /var/log/containers/*
+```
+```bash +.
+...
+{"log":"I1027 12:51:51.629401 1 client.go:360] parsed scheme: \"passthrough\"\n","stream":"stderr","time":"2020-10-27T12:51:51.629623287Z"}
+{"log":"I1027 12:51:51.629456 1 passthrough.go:48] ccResolverWrapper: sending update to cc: {[{https://127.0.0.1:2379 \u003cnil\u003e 0 \u003cnil\u003e}] \u003cnil\u003e \u003cnil\u003e}\n","stream":"stderr","time":"2020-10-27T12:51:51.629671282Z"}
+{"log":"I1027 12:51:51.629471 1 clientconn.go:948] ClientConn switching balancer to \"pick_first\"\n","stream":"stderr","time":"2020-10-27T12:51:51.62968064Z"}
+```
+
+6. Enfin une dernière façon de regarder les logs des différents conteneurs peuplant notre cluster kubernetes est d'utiliser tout simplement Docker :
+
+```bash +.
+docker ps -a
+docker logs ID_CONTENEUR
+```
+
+7. Voila, nous allons maintenant supprimer notre pod de test :
+
+```bash +.
+kubectl delete pod test-logs
+```
+```bash +.
+pod "test-logs" deleted
+```
+
+
+
+### Stack Elastic
+
+
+machine : **master**
+
+
+```bash +.
+mkdir eck
+cd eck
+```
+
+1. Commençons par installer les composants essentiels de ECK, notamment elastic-operator :
+
+```bash +.
+
+# CRDs
+kubectl apply -f https://download.elastic.co/downloads/eck/1.9.1/crds.yaml
+
+# Operateur
+kubectl apply -f https://download.elastic.co/downloads/eck/1.9.1/operator.yaml
+
+
+```
+
+2. Nous pouvons monitorer le déploiement d'elastic-operator de la façon suivante :
+
+```bash +.
+kubectl -n elastic-system logs -f statefulset.apps/elastic-operator
+
+```
+
+
+```bash +.
+...
+{"log.level":"info","@timestamp":"2020-11-01T17:01:06.426Z","log.logger":"controller-runtime.controller","message":"Starting workers","service.version":"1.2.1-b5316231","service.type":"eck","ecs.version":"1.4.0","controller":"enterprisesearch-controller","worker count":3}
+{"log.level":"info","@timestamp":"2020-11-01T17:01:06.426Z","log.logger":"controller-runtime.controller","message":"Starting workers","service.version":"1.2.1-b5316231","service.type":"eck","ecs.version":"1.4.0","controller":"elasticsearch-controller","worker count":3}
+```
+
+3. Nous allons maintenant déployer un elasticsearch. Avec ECK, de nouvelles CustomeDefinitions sont ajoutées au cluster. L'une parmi elles permet notamment de définir un serveur Elasticsearch via yaml. Nous allons donc créer un fichier elasticsearch.yaml :
+
+```bash +.
+touch elasticsearch.yaml
+```
+
+Avec le contenu yaml suivant :
+
+```yaml +.
+apiVersion: elasticsearch.k8s.elastic.co/v1
+kind: Elasticsearch
+metadata:
+ name: elasticsearch
+spec:
+ version: 7.9.3
+ nodeSets:
+ - name: default
+ count: 1
+ volumeClaimTemplates:
+ - metadata:
+ name: elasticsearch-data
+ spec:
+ accessModes:
+ - ReadWriteOnce
+ resources:
+ requests:
+ storage: 5Gi
+ storageClassName: longhorn
+ config:
+ node.master: true
+ node.data: true
+ node.ingest: true
+ node.store.allow_mmap: false
+```
+
+4. Nous pouvons donc créer notre serveur Elasticsearch :
+
+```bash +.
+kubectl apply -f elasticsearch.yaml
+```
+```bash +.
+elasticsearch.elasticsearch.k8s.elastic.co/elasticsearch created
+```
+
+5. Nous pouvons voir le déroulement de son déploiement de la façon suivante :
+
+```bash +.
+kubectl get elasticsearch elasticsearch
+```
+```bash +.
+NAME HEALTH NODES VERSION PHASE AGE
+elasticsearch green 1 7.9.3 Ready 106s
+```
+
+6. Un service exposant notre elasticsearch est créé lors du déploiement, nous pouvons le voir de la façon suivante :
+
+```bash +.
+kubectl get service elasticsearch-es-http
+```
+```bash +.
+NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
+elasticsearch-es-http ClusterIP 10.99.41.114 9200/TCP 2m24s
+```
+
+7. Testons la connexion a notre elasticsearch :
+
+```bash +.
+PASSWORD=$(kubectl get secret elasticsearch-es-elastic-user -o go-template='{{.data.elastic | base64decode}}')
+curl -u "elastic:$PASSWORD" -k "https://CLUSTER_IP_ELASTICSEARCH:9200"
+```
+```bash +.
+{
+ "name" : "elasticsearch-es-default-0",
+ "cluster_name" : "elasticsearch",
+ "cluster_uuid" : "76FfZR4ARxO78QBQw_kBhg",
+ "version" : {
+ "number" : "7.9.3",
+ "build_flavor" : "default",
+ "build_type" : "docker",
+ "build_hash" : "c4138e51121ef06a6404866cddc601906fe5c868",
+ "build_date" : "2020-10-16T10:36:16.141335Z",
+ "build_snapshot" : false,
+ "lucene_version" : "8.6.2",
+ "minimum_wire_compatibility_version" : "6.8.0",
+ "minimum_index_compatibility_version" : "6.0.0-beta1"
+ },
+ "tagline" : "You Know, for Search"
+}
+```
+
+Parfait !
+
+8. Nous allons maintenant passer à l'installation de Kibana. De la même manière, nous allons définir un fichier kibana.yaml permettant de déployer notre kibana :
+
+```bash +.
+touch kibana.yaml
+```
+
+Avec le contenu yaml suivant :
+
+```yaml +.
+apiVersion: kibana.k8s.elastic.co/v1
+kind: Kibana
+metadata:
+ name: kibana
+spec:
+ version: 7.9.3
+ count: 1
+ elasticsearchRef:
+ name: elasticsearch
+```
+
+9. Nous pouvons donc créer notre serveur Kibana :
+
+```bash +.
+kubectl apply -f kibana.yaml
+```
+```bash +.
+kibana.kibana.k8s.elastic.co/kibana created
+```
+
+10. De la même manière qu'elasticsearch, nous pouvons voir l'état de notre Kibana de la façon suivante :
+
+```bash +.
+kubectl get kibana kibana
+```
+```bash +.
+NAME HEALTH NODES VERSION AGE
+kibana green 1 7.9.3 2m23s
+```
+
+1. De même, un service pour Kibana est créé, nous pouvons le voir de la façon suivante :
+
+```bash +.
+kubectl get service kibana-kb-http
+```
+```bash +.
+NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
+kibana-kb-http ClusterIP 10.106.23.116 5601/TCP 2m45s
+```
+
+1. Nous allons récupérer le mot de passe de Kibana :
+
+```bash +.
+kubectl get secret elasticsearch-es-elastic-user -o=jsonpath='{.data.elastic}' | base64 --decode; echo
+```
+```bash +.
+pb809RTC51EVCd3f19i9UVW5
+```
+
+13. Nous allons faire un port-forward de notre service pour pouvoir se connecter dessus via un navigateur :
+
+```bash +.
+kubectl port-forward --address 0.0.0.0 service/kibana-kb-http 5601
+```
+
+Notre Kibana est donc installé ! Vous pouvez y'accéder à l'aide de l'URL suivante : https://MASTER_EXTERNAL_IP:5601
+
+Page d'authentification :
+
+
+
+Page d'accueil :
+
+
+
+14. Nous allons maintenant collecter des logs . Nous allons installé un filebeat et récupérer les logs se trouvant dans /var/log/containers, /var/lib/docker/containers et /var/log/pods/. On va donc créer le fichier filebeat.yaml suivant :
+
+```bash +.
+touch filebeat.yaml
+```
+
+Avec le contenu yaml suivant :
+
+```yaml +.
+apiVersion: beat.k8s.elastic.co/v1beta1
+kind: Beat
+metadata:
+ name: filebeat
+spec:
+ type: filebeat
+ version: 7.9.3
+ elasticsearchRef:
+ name: elasticsearch
+ config:
+ filebeat.inputs:
+ - type: container
+ paths:
+ - /var/log/containers/*.log
+ daemonSet:
+ podTemplate:
+ spec:
+ dnsPolicy: ClusterFirstWithHostNet
+ hostNetwork: true
+ securityContext:
+ runAsUser: 0
+ containers:
+ - name: filebeat
+ volumeMounts:
+ - name: varlogcontainers
+ mountPath: /var/log/containers
+ - name: varlogpods
+ mountPath: /var/log/pods
+ - name: varlibdockercontainers
+ mountPath: /var/lib/docker/containers
+ volumes:
+ - name: varlogcontainers
+ hostPath:
+ path: /var/log/containers
+ - name: varlogpods
+ hostPath:
+ path: /var/log/pods
+ - name: varlibdockercontainers
+ hostPath:
+ path: /var/lib/docker/containers
+```
+
+15. Nous pouvons donc créer notre filebeat :
+
+```bash +.
+kubectl apply -f filebeat.yaml
+```
+```bash +.
+beat.beat.k8s.elastic.co/filebeat created
+```
+
+16. Nous pouvons voir l'état de notre filebeat de la façon suivante :
+
+```bash +.
+kubectl get beat
+```
+```bash +.
+NAME HEALTH AVAILABLE EXPECTED TYPE VERSION AGE
+filebeat green 2 2 filebeat 7.9.2 94s
+```
+
+17. Nous pouvons créer un index pattern en allant sur Discover -> Create index pattern -> Mettre "filebeat-*" en index pattern name -> Mettre @timestamp en time field :
+
+Création de l'index pattern :
+
+
+
+Nom de l'index pattern :
+
+
+
+Time Field :
+
+
+
+18. Nous pouvons voir les logs ou bien sur *Discover* ou bien sur *Logs* :
+
+Discover :
+
+
+
+Logs :
+
+
+
+### Clean Up
+
+Pour désinstaller notre stack ELK via ECK :
+
+```bash +.
+kubectl delete -f .
+```
+```bash +.
+elasticsearch.elasticsearch.k8s.elastic.co "elasticsearch" deleted
+beat.beat.k8s.elastic.co "filebeat" deleted
+kibana.kibana.k8s.elastic.co "kibana" deleted
+```
+
+
+
+
+
+## Mise à jour d’un cluster
+
+
+Machine : **master**, **worker-0**, **worker-1**
+
+
+
+### Mise à jour kubeadm
+
+Pour commencer, il faut mettre à jour kubeadm :
+
+```bash +.
+sudo apt-mark unhold kubeadm
+sudo apt-get install kubeadm=1.24.10-00
+sudo apt-mark hold kubeadm
+```
+
+Vérifions la version de kubeadm :
+
+```bash +.
+kubeadm version
+
+kubeadm version: &version.Info{Major:"1", Minor:"23", GitVersion:"v1.23.16", GitCommit:"60e5135f758b6e43d0523b3277e8d34b4ab3801f", GitTreeState:"clean", BuildDate:"2023-01-18T15:59:57Z", GoVersion:"go1.19.5", Compiler:"gc", Platform:"linux/amd64"}
+
+```
+
+### Drain master
+
+Nous devons maintenant drain le noeud master afin de pouvoir faire l’upgrade dessus :
+
+```bash +.
+
+kubectl drain master --ignore-daemonsets
+
+```
+
+### Upgrade Plan
+
+Nous pouvons avoir un aperçu de l’upgrade de la façon suivante :
+
+```bash +.
+sudo kubeadm upgrade plan
+
+
+[upgrade/config] Making sure the configuration is correct:
+[upgrade/config] Reading configuration from the cluster...
+[upgrade/config] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -o yaml'
+W0206 09:34:54.193329 4187 initconfiguration.go:120] Usage of CRI endpoints without URL scheme is deprecated and can cause kubelet errors in the future. Automatically prepending scheme "unix" to the "criSocket" with value "/run/containerd/containerd.sock". Please update your configuration!
+[preflight] Running pre-flight checks.
+[upgrade] Running cluster health checks
+[upgrade] Fetching available versions to upgrade to
+[upgrade/versions] Cluster version: v1.23.16
+[upgrade/versions] kubeadm version: v1.24.10
+I0206 09:34:58.968509 4187 version.go:256] remote version is much newer: v1.26.1; falling back to: stable-1.24
+[upgrade/versions] Target version: v1.24.10
+[upgrade/versions] Latest version in the v1.23 series: v1.23.16
+
+Components that must be upgraded manually after you have upgraded the control plane with 'kubeadm upgrade apply':
+COMPONENT CURRENT TARGET
+kubelet 3 x v1.23.16 v1.24.10
+
+Upgrade to the latest stable version:
+
+COMPONENT CURRENT TARGET
+kube-apiserver v1.23.16 v1.24.10
+kube-controller-manager v1.23.16 v1.24.10
+kube-scheduler v1.23.16 v1.24.10
+kube-proxy v1.23.16 v1.24.10
+CoreDNS v1.8.6 v1.8.6
+etcd 3.5.6-0 3.5.6-0
+
+You can now apply the upgrade by executing the following command:
+
+ kubeadm upgrade apply v1.24.10
+
+_____________________________________________________________________
+
+
+The table below shows the current state of component configs as understood by this version of kubeadm.
+Configs that have a "yes" mark in the "MANUAL UPGRADE REQUIRED" column require manual config upgrade or
+resetting to kubeadm defaults before a successful upgrade can be performed. The version to manually
+upgrade to is denoted in the "PREFERRED VERSION" column.
+
+API GROUP CURRENT VERSION PREFERRED VERSION MANUAL UPGRADE REQUIRED
+kubeproxy.config.k8s.io v1alpha1 v1alpha1 no
+kubelet.config.k8s.io v1beta1 v1beta1 no
+_____________________________________________________________________
+
+
+
+
+```
+
+
+### Upgrade composants
+
+Nous pouvons maintenant upgrade les composants du cluster :
+
+
+```bash +.
+
+sudo kubeadm upgrade apply v1.24.10
+
+```
+
+
+
+```bash +.
+
+[upgrade/config] Making sure the configuration is correct:
+[upgrade/config] Reading configuration from the cluster...
+[upgrade/config] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -o yaml'
+W0206 09:37:16.226531 4245 initconfiguration.go:120] Usage of CRI endpoints without URL scheme is deprecated and can cause kubelet errors in the future. Automatically prepending scheme "unix" to the "criSocket" with value "/run/containerd/containerd.sock". Please update your configuration!
+[preflight] Running pre-flight checks.
+[upgrade] Running cluster health checks
+[upgrade/version] You have chosen to change the cluster version to "v1.24.10"
+[upgrade/versions] Cluster version: v1.23.16
+[upgrade/versions] kubeadm version: v1.24.10
+[upgrade/confirm] Are you sure you want to proceed with the upgrade? [y/N]: y
+
+
+
+[upgrade/prepull] Pulling images required for setting up a Kubernetes cluster
+[upgrade/prepull] This might take a minute or two, depending on the speed of your internet connection
+[upgrade/prepull] You can also perform this action in beforehand using 'kubeadm config images pull'
+[upgrade/apply] Upgrading your Static Pod-hosted control plane to version "v1.24.10" (timeout: 5m0s)...
+[upgrade/etcd] Upgrading to TLS for etcd
+[upgrade/staticpods] Preparing for "etcd" upgrade
+[upgrade/staticpods] Current and new manifests of etcd are equal, skipping upgrade
+[upgrade/etcd] Waiting for etcd to become available
+[upgrade/staticpods] Writing new Static Pod manifests to "/etc/kubernetes/tmp/kubeadm-upgraded-manifests1021044454"
+[upgrade/staticpods] Preparing for "kube-apiserver" upgrade
+[upgrade/staticpods] Renewing apiserver certificate
+[upgrade/staticpods] Renewing apiserver-kubelet-client certificate
+[upgrade/staticpods] Renewing front-proxy-client certificate
+[upgrade/staticpods] Renewing apiserver-etcd-client certificate
+[upgrade/staticpods] Moved new manifest to "/etc/kubernetes/manifests/kube-apiserver.yaml" and backed up old manifest to "/etc/kubernetes/tmp/kubeadm-backup-manifests-2023-02-06-09-38-09/kube-apiserver.yaml"
+[upgrade/staticpods] Waiting for the kubelet to restart the component
+[upgrade/staticpods] This might take a minute or longer depending on the component/version gap (timeout 5m0s)
+
+
+.....
+.....
+
+
+[apiclient] Found 1 Pods for label selector component=kube-apiserver
+[upgrade/staticpods] Component "kube-apiserver" upgraded successfully!
+[upgrade/staticpods] Preparing for "kube-controller-manager" upgrade
+[upgrade/staticpods] Renewing controller-manager.conf certificate
+[upgrade/staticpods] Moved new manifest to "/etc/kubernetes/manifests/kube-controller-manager.yaml" and backed up old manifest to "/etc/kubernetes/tmp/kubeadm-backup-manifests-2023-02-06-09-38-09/kube-controller-manager.yaml"
+[upgrade/staticpods] Waiting for the kubelet to restart the component
+[upgrade/staticpods] This might take a minute or longer depending on the component/version gap (timeout 5m0s)
+[apiclient] Found 1 Pods for label selector component=kube-controller-manager
+[upgrade/staticpods] Component "kube-controller-manager" upgraded successfully!
+[upgrade/staticpods] Preparing for "kube-scheduler" upgrade
+[upgrade/staticpods] Renewing scheduler.conf certificate
+[upgrade/staticpods] Moved new manifest to "/etc/kubernetes/manifests/kube-scheduler.yaml" and backed up old manifest to "/etc/kubernetes/tmp/kubeadm-backup-manifests-2023-02-06-09-38-09/kube-scheduler.yaml"
+[upgrade/staticpods] Waiting for the kubelet to restart the component
+[upgrade/staticpods] This might take a minute or longer depending on the component/version gap (timeout 5m0s)
+[apiclient] Found 1 Pods for label selector component=kube-scheduler
+[upgrade/staticpods] Component "kube-scheduler" upgraded successfully!
+[upgrade/postupgrade] Removing the deprecated label node-role.kubernetes.io/master='' from all control plane Nodes. After this step only the label node-role.kubernetes.io/control-plane='' will be present on control plane Nodes.
+[upgrade/postupgrade] Adding the new taint &Taint{Key:node-role.kubernetes.io/control-plane,Value:,Effect:NoSchedule,TimeAdded:,} to all control plane Nodes. After this step both taints &Taint{Key:node-role.kubernetes.io/control-plane,Value:,Effect:NoSchedule,TimeAdded:,} and &Taint{Key:node-role.kubernetes.io/master,Value:,Effect:NoSchedule,TimeAdded:,} should be present on control plane Nodes.
+[upload-config] Storing the configuration used in ConfigMap "kubeadm-config" in the "kube-system" Namespace
+[kubelet] Creating a ConfigMap "kubelet-config" in namespace kube-system with the configuration for the kubelets in the cluster
+[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
+[bootstrap-token] Configured RBAC rules to allow Node Bootstrap tokens to get nodes
+[bootstrap-token] Configured RBAC rules to allow Node Bootstrap tokens to post CSRs in order for nodes to get long term certificate credentials
+[bootstrap-token] Configured RBAC rules to allow the csrapprover controller automatically approve CSRs from a Node Bootstrap Token
+[bootstrap-token] Configured RBAC rules to allow certificate rotation for all node client certificates in the cluster
+[addons] Applied essential addon: CoreDNS
+[addons] Applied essential addon: kube-proxy
+
+[upgrade/successful] SUCCESS! Your cluster was upgraded to "v1.24.10". Enjoy!
+
+[upgrade/kubelet] Now that your control plane is upgraded, please proceed with upgrading your kubelets if you haven't already done so.
+
+
+
+```
+
+### uncordon master
+
+
+Nous pouvons remettre le noeud master en marche :
+
+
+```bash +.
+kubectl uncordon master
+```
+
+
+
+**node/master uncordoned**
+
+
+
+### Mise à jour kubelet
+
+Nous devons maintenant mettre à jour la kubelet et kubectl :
+
+```bash +.
+sudo apt-mark unhold kubectl kubelet
+sudo apt-get install kubectl=1.24.10-00 kubelet=1.24.10-00
+sudo apt-mark hold kubectl kubelet
+```
+
+Enfin nous devons redémarrer la kubelet :
+
+```bash +.
+sudo systemctl daemon-reload
+sudo systemctl restart kubelet
+```
+
+
+Vérification de la mise à jour du **master**
+
+```bash +.
+kubectl get nodes
+
+NAME STATUS ROLES AGE VERSION
+master Ready,SchedulingDisabled control-plane 16m v1.24.10
+worker-0 Ready 15m v1.23.16
+worker-1 Ready 15m v1.23.16
+```
+
+
+### Mise à jour worker
+
+Nous devons maintenant mettre à jour les workers :
+
+A faire sur les noeud 1 et 2
+
+```bash +.
+training@worker-0$ sudo apt-mark unhold kubeadm
+training@worker-0$ sudo apt-get install kubeadm=1.24.10-00
+training@worker-0$ sudo apt-mark hold kubeadm
+```
+
+Comme pour le master, nous devons drain les noeuds workers :
+
+Répéter les actions pour le noeud 2 noeud par noeud (pas en // )
+
+
+Sur le master
+
+
+```bash +.
+kubectl drain worker-0 --ignore-daemonsets
+```
+
+Nous devons maintenant mettre à jour la configuration de notre worker-0 :
+
+```bash +.
+training@worker-0$ sudo kubeadm upgrade node
+```
+
+```bash +.
+[upgrade] Reading configuration from the cluster...
+[upgrade] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -oyaml'
+[preflight] Running pre-flight checks
+[preflight] Skipping prepull. Not a control plane node.
+[upgrade] Skipping phase. Not a control plane node.
+[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
+[upgrade] The configuration for this node was successfully updated!
+[upgrade] Now you should go ahead and upgrade the kubelet package using your package manager.
+```
+
+Enfin, comme pour le master nous devons mettre a jour la kubelet et kubectl :
+
+```bash +.
+training@worker-0$ sudo apt-mark unhold kubectl kubelet
+training@worker-0$ sudo apt-get install kubectl=1.24.10-00 kubelet=1.24.10-00
+training@worker-0$ sudo apt-mark hold kubectl kubelet
+```
+
+En prenant soin de redémarrer la kubelet :
+
+```bash +.
+training@worker-0$ sudo systemctl daemon-reload
+training@worker-0$ sudo systemctl restart kubelet
+```
+
+Sans oublier de remettre le noeud en marche :
+
+```bash +.
+kubectl uncordon worker-0
+```
+
+Nous pouvons maintenant lister les noeuds :
+
+```bash +.
+kubectl get nodes
+
+NAME STATUS ROLES AGE VERSION
+master Ready,SchedulingDisabled control-plane 16m v1.24.10
+worker-0 Ready 15m v1.24.10
+worker-1 Ready 15m v1.23.16
+```
+
+Passez à la mise à jour du noeud 2
+
+Et lister les pods pour vérifier que tout est fonctionnel :
+
+```bash +.
+kubectl get pods -A
+
+NAMESPACE NAME READY STATUS RESTARTS AGE
+kube-system coredns-f9fd979d6-jhcg9 1/1 Running 0 7m44s
+kube-system coredns-f9fd979d6-mjfzf 1/1 Running 0 7m44s
+kube-system etcd-master 1/1 Running 1 11m
+kube-system kube-apiserver-master 1/1 Running 0 11m
+kube-system kube-controller-manager-master 1/1 Running 0 11m
+kube-system kube-proxy-4mvtr 1/1 Running 0 14m
+kube-system kube-proxy-lkvxn 1/1 Running 0 13m
+kube-system kube-scheduler-master 1/1 Running 0 11m
+kube-system weave-net-t2h8r 2/2 Running 0 24m
+kube-system weave-net-zxg6p 2/2 Running 1 23m
+```
+
+
+
+**Note** : le CNI doit être mis à jour indépendamment
+
+
+
+
+## Backup and restaure ETCD
+
+### Sauvegarde de la base de données ETCD
+
+1. Sauvegarde de la base de données ETCD:
+
+```bash +.
+ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 \
+ --cacert= --cert= --key= \
+ snapshot save
+```
+
+où trust-ca-file, cert-file et key-file peuvent être obtenus à partir de la description du Pod etcd en mettant sur le master la commande:
+
+```bash +.
+sudo cat /etc/kubernetes/manifests/etcd.yaml
+```
+
+```bash +.
+ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 \
+ --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key \
+ snapshot save etcd-bkp
+```
+
+Le snapshot a créé un fichier nommé: etcd-bkp. Vérifier le status du snapshot:
+
+```bash +.
+ETCDCTL_API=3 \
+etcdctl --write-out=table snapshot status etcd-bkp
+```
+
+2. Créez un pod nommé after-backup avec l'image nginx
+
+```bash +.
+kubectl run after-backup --image=nginx
+```
+
+3. Restaurer maintenant la base de données ETCD:
+
+```bash +.
+ETCDCTL_API=3 etcdctl --endpoints 10.2.0.9:2379 snapshot restore etcd-bkp
+```
+
+Remarquez que l'opération de restauration a généré un repertoire default.etcd
+
+4. Utiliser la base de données restaurée
+
+Avant de remplacer l'ETCD par la nouvelle restauration:
+
+- Arrêtons les composants du control plane en déplaçant les fichiers manifest
+
+```bash +.
+sudo mv /etc/kubernetes/manifests/*.yaml /tmp/
+```
+
+- sécurisons l'état actuel de l'ETCD
+
+```bash +.
+sudo mv /var/lib/etcd/member /var/lib/etcd/member.bak
+```
+
+- Remplaçons les données de l'ETCD par les fichiers de backup:
+
+```bash +.
+sudo mv default.etcd/member /var/lib/etcd/
+```
+
+- Redémarrons les composants du control plane:
+
+```bash +.
+sudo mv /tmp/*.yaml /etc/kubernetes/manifests/
+```
+
+5. Vérifier que la base de données a été restaurée à l'état désiré
+
+Le pod créé après la sauvegarde ne doit plus exister après la restauration
+
+```bash +.
+kubectl get pods
+```
+
+
+
+
+
+
+## Lab final
+
+
+
+### A vous de jouer !
+
+
+
+1. Créer un namespace nommé **kubeops**
+
+
+2. Créer un pod avec les caractéristiques suivantes :
+
+| | |
+|-------------------------------------|-----------------------------|
+| Nom | : webserver |
+| Nom du conteneur | : webserver |
+| Image | : dernière version de nginx |
+| Namespace | : kubeops |
+
+
+ - Sur quel nœud se trouve votre pod ?
+
+ - Connectez-vous au conteneur du pod et vérifiez son OS avec la commande cat /etc/os-release
+
+ - Vérifiez les logs du pod
+
+
+3. Ajoutez un nouveau conteneur du nom de webwatcher au pod créé précédemment, avec une image : afakharany/watcher:latest.
+
+ - Vérifiez que les deux conteneurs sont « running » dans le pod.
+
+ - Connectez-vous au conteneur webwatcher et affichez le contenu du fichier /etc/hosts
+
+
+4. Lancez un Deployment nommé « nginx-deployment » avec 2 réplicas comprenant un conteneur nommé "nginxcont" dans le namespace "kubeops" avec l'image Nginx version 1.17.10 et définissez le port 80 comme port d'exposition.
+
+
+
+5. Augmentez le nombre de réplicas du déploiement à 4 avec la commande kubectl scale
+
+
+
+6. Mettez à jour l’image de votre application à une nouvelle version nginx :1.9.1 avec la commande kubectl set image et observez l’évolution de la mise à jour de l’application
+
+
+
+7. Faites un Rollback de votre mise à jour du déploiement
+
+
+
+8. Exposez votre application avec un service de type Nodeport sur le port 30000 des workers
+
+
+
+9. Créez un Daemonset nommé prometheus-daemonset conteneur nommé prometheus dans le namespace "kubeops" avec l'image prom/node-exporter et définissez le port 9100 comme port d'exposition.
+
+
+
+10. Le Daemonset précédemment créé est présent sur tous les nœuds du cluster. Nous ne souhaitons plus qu’il tourne sur le nœud worker-1. Trouvez la bonne stratégie pour que prometheus-daemonset ne soit présent que sur le worker-0 et le master
+
+
+
+11. Cet exercice vise à montrer l’utilisation d’un secret et d’un configmap.
+
+ - Création du secret :
+ - Générez un certificat en exécutant le script certs.sh. Créez un secret de type tls avec les fichiers générés avec la commande `kubectl create secret tls nginx-certs --cert=tls.crt --key=tls.key`
+
+
+ - Faites un kubectl describe pour vérifier le secret créé
+
+
+ - Création du configmap
+ - Les ConfigMaps peuvent être créés de la même manière que les secrets. Il peut être créer à l’aide d’un fichier YAML, ou via la commande kubectl create configmap pour le créer à partir de la ligne de commande.
+
+ - Afin de personnaliser la configuration de votre serveur nginx, créez un configmap avec le fichier de configuration nginx-custom.conf avec la commande suivante : `kubectl create configmap nginx-config --from-file nginx-custom.conf`
+
+
+ - Création d’un déploiement
+ - Créez un déploiement du nom de nginx d’un réplica avec une image nginx :1.9.1. Créez un volume avec le secret et un deuxième volume avec le configmap dans les spec de votre application. Définir les ports 80 et 443 comme ports d’exposition, puis montez les volumes dans le conteneur avec comme mountPath « /certs » pour le certificat et « /etc/nginx/conf.d » pour la configuration nginx
+
+
+
+ - Testez votre application en l’exposant via un service
+
+12. Nous souhaitons déployer une base de données mysql sur le cluster. Créez un déploiement « mysql-database » avec 1 réplica comprenant un conteneur nommé « database » avec la dernière version de Mysql.
+
+
+
+ - Votre application doit afficher une erreur car il lui manque le mot de passe root de mysql.
+
+ - Redéployez l’application en passant le mot de passe root de mysql en variable d’environnement avec les valeurs comme suit : MYSQL_ROOT_PASSWORD=test.
+
+
+
+13. Utilisateurs et droits dans Kubernetes
+
+ - Générez un certificat pour un utilisateur du nom de dev. Ajoutez les informations d'identification de l'utilisateur dev à notre fichier kubeconfig. Puis vérifiez si dev a le droit de lister les pods en mettant la commande : `kubectl --user=dev get pods`
+
+ - Créez un rôle qui autorise à lister les pods puis liez le rôle à l’utilisateur dev. Vérifiez à présent si dev peut lister les pods.
+
+ - Vous remarquerez que dev est limité au namespace dans lequel le rôle a été créé. Vous décidez de lui permettre de lister les pods de tous les namespaces. Mettez en place une solution appropriée.
+
+
+14. Créez un pod statique avec une image redis. Rajoutez un request de 100Mi RAM et 100m CPU puis une limite à 200Mi RAM et 150m CPU
+
+
+
+15. Installez le helm chart de wordress disponible sur ce lien. Modifier le type de service par défaut définit dans le fichier values.yaml en NodePort.
+
+
+
+16. TroubleShooting : Faire en sorte que l'application fonctionne correctement et puisse afficher une page web avec le calcul de Pi. Corrigez toutes les erreurs dans le `deployment`et les `service`
+
+```yaml
+
+# BUT : faire fonctionner l'application sur curl http://QuelqueChose:8020
+
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: pi-web
+ labels:
+ k8s.alterwaylabs.fr: troubleshooting
+spec:
+ replicas: 0
+ selector:
+ matchLabels:
+ app: pi-web
+ template:
+ metadata:
+ labels:
+ app: pi-web-app
+ spec:
+ containers:
+ - image: kiamol/ch05-pi-app
+ command: ["donet", "Pi.Web.dll", "-m", "web"]
+ name: web
+ ports:
+ - containerPort: 80
+ name: http
+ resources:
+ limits:
+ cpu: "32"
+ memory: "128Gi"
+ readinessProbe:
+ tcpSocket:
+ port: 8020
+ periodSeconds: 5
+ livenessProbe:
+ httpGet:
+ path: /healthy
+ port: 80
+ periodSeconds: 30
+ failureThreshold: 1
+
+
+---
+
+apiVersion: v1
+kind: Service
+metadata:
+ name: pi-np
+ labels:
+ k8s.alterwaylabs.fr: troubleshooting
+spec:
+ selector:
+ app: pi-web-pod
+ ports:
+ - name: http
+ port: 8020
+ targetPort: app
+ nodePort: 8020
+ type: NodePort
+
+---
+
+apiVersion: v1
+kind: Service
+metadata:
+ name: pi-lb
+ labels:
+ k8s.alterwaylabs.fr: troubleshooting
+spec:
+ selector:
+ app: pi-web-pod
+ ports:
+ - name: http
+ port: 8020
+ targetPort: app
+ type: ClusterIP
+```
+
+
+
+
+
+## Solutions pour le lab
+
+
+
+### Solution / Tips
+
+
+Troubleshooting Deployments
+
+Fix:
+
+**Deployments**
+
+- Labels dans les spec du pod pour le rattachement au deploy
+
+- Replicas à 0 >> 1
+
+- Limits / Requests trop élévée
+
+- Nom de l'image >> aller voir sur le hub docker
+
+- Command au niveau de conteneur avec typo
+
+- Readiness probe mauvais port
+
+- Liveness /healthy >> /health
+
+
+**Services**
+
+- target port 8020 invalide
+
+- Service pod selector invalide
+
+- Service port name invalide
+
+
+
+```yaml +.
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: pi-web
+ labels:
+ k8s.alterwaylabs.fr: troubleshooting
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: pi-web
+ template:
+ metadata:
+ labels:
+ app: pi-web
+ spec:
+ containers:
+ - image: kiamol/ch05-pi
+ command: ["dotnet", "Pi.Web.dll", "-m", "web"]
+ name: web
+ ports:
+ - containerPort: 80
+ name: http
+ resources:
+ limits:
+ cpu: "0.5"
+ memory: "1Gi"
+ readinessProbe:
+ tcpSocket:
+ port: 80
+ periodSeconds: 5
+ livenessProbe:
+ httpGet:
+ path: /
+ port: 80
+ periodSeconds: 30
+ failureThreshold: 1
+```
+
+```yaml +.
+
+apiVersion: v1
+kind: Service
+metadata:
+ name: pi-np
+ labels:
+ k8s.alterwaylabs.fr: troubleshooting
+spec:
+ selector:
+ app: pi-web
+ ports:
+ - name: http
+ port: 8020
+ targetPort: http
+ nodePort: 30020
+ type: NodePort
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: pi-lb
+ labels:
+ k8s.alterwaylabs.fr: troubleshooting
+spec:
+ selector:
+ app: pi-web
+ ports:
+ - name: http
+ port: 8020
+ targetPort: http
+ type: LoadBalancer
+
+```
\ No newline at end of file