Suite aux vidéos de Xavki sur l'installation d'une stack Node-exporter/Prometheus/Grafana que j'ai trouvé très interessantes, j'ai voulu mettre à jour mon monitoring en m'y inspirant. Cependant je préfères la stack TICK (Télégraf, Influxdb, Chronograf et Kapacitor), la stack de influxdata, principalement pour l'intégration complète et puissante des notifications directement dans chronograf. Le fait aussi de n'avoir qu'un seul agent (Télégraf), au lieu de plusieurs exporter (docker-exporter, node-exporter etc ...).
Dernier point, je n'ai pas trouvé beaucoup d'information sur le sujet, donc cela m'a permis de beaucoup monté en compétence, notamment sur la gestion des rôles avec la création d'un rbac pour ça.
Nous allons donc voir ici, comment installer la stack TICK, le tout dans un cluster kubernetes, et sans HELM ou autre outils facilitant l'installation.
Je passerais rapidement sur beaucoup de chose, si vous n'avez pas les bases, je vous invite à regarder les vidéos de xavki.
Organisation
J'ai une certaine organisation, je ne sais pas si c'est une bonne pratique mais c'est la mienne.
Déjà j'ai un namespace par stack, et chaque déploiement de la stack se passera dedans. Pour l'organisation des fichiers, je crée un répertoire par stack/namespace, dedans j'ai mes fichiers qui seront globaux à la stack, et ensuite un répertoire par déploiement avec les fichiers nécessaires à celui là.
Donc en gros nous aurons :
- monitor
- ns.yml
- telegraf
- 10-volumes.yml
- 20-daemonset.yml
 
- kapacitor
- 10-volumes.yml
- 20-deployment.yml
- 30-services.yml
 
- chronograf
- .....
 
- influxdb
- .....
 
 
Architecture
Pour ce tutoriel, j'utilise ma production (pas bien), en gros j'ai un cluster k3s avec un master et 3 workers, tout les fichiers seront stockés sur ma VMs qui sers de fileserver avec un serveur NFS.
J'ai donc 5 VMs :
- k3s-master : 192.168.1.121
- k3s-node1 : 192.168.1.131
- k3s-node2 : 192.168.1.132
- k3s-node3 : 192.168.1.133
- filer : 192.168.1.105
Création du namespace
Pour le namespace, c'est plutôt simple, nous créons notre fichier ns.yml avec ceci dedans :
apiVersion: v1
kind: Namespace
metadata:
  name: monitor
et on applique :
$ kubectl apply -f ns.yml
namespace/monitor created
Déploiement de influxdb
Comme dit j'ai mon organisation, donc dans mon répertoire monitor, je crée un autre répertoire influxdb, dans lequel je vais mettre tout les fichiers suivants qui concernent influxdb.
10-volume.yml
Nous avons besoin d'un volume, comme dit je stock tout sur mon serveur NFS, donc je commence par créer un fichier 10-volumes.yml :
apiVersion: v1
kind: PersistentVolume
metadata:
  name: data-influxdb-pv
  namespace: monitor
spec:
  storageClassName: data-influxdb
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteMany
  nfs:
    server: 192.168.1.105
    path: "/data/influxdb"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: data-influxdb-pvc
  namespace: monitor
spec:
  storageClassName: data-influxdb
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 500Mi
A adapter pour votre besoin
20-deployment.yml
kind: Deployment
apiVersion: apps/v1
metadata:
  namespace: monitor
  name: influxdb
  labels:
    app: influxdb
spec:
  replicas: 1
  selector:
    matchLabels:
      app: influxdb
  template:
    metadata:
      labels:
        app: influxdb
        namespace: monitor
    spec:
      containers:
        - name: influxdb
          image: influxdb:1.8-alpine
          ports:
            - containerPort: 8086
          volumeMounts:
            - mountPath: /var/lib/influxdb
              name: data
          resources:
            requests:
              memory: 300M
              cpu: 0.2
            limits:
              memory: 800M
              cpu: 0.5
      volumes:
      - name: data
        persistentVolumeClaim:
          claimName: data-influxdb-pvc
Rien de particulier, on a juste un volume (NFS) monté dans /var/lib/influxdb.
30-services.yml
apiVersion: v1
kind: Service
metadata:
  name: influxdb-web
  namespace: monitor
spec:
  ports:
    - protocol: TCP
      name: web
      port: 8086
  selector:
    app: influxdb
---
apiVersion: v1
kind: Service
metadata:
  name: influxdb-externe
  namespace: monitor
spec:
  ports:
    - protocol: TCP
      name: web
      port: 8086
  type: LoadBalancer
  selector:
    app: influxdb
J'ajoute ici un service de type loadbalancer afin de pouvoir y accéder depuis l'exterieur du cluster, car j'ai d'autres machines à monitorer.
On applique
$ kubectl apply -f .
persistentvolume/config-influxdb-pv created
persistentvolumeclaim/config-influxdb-pvc created
deployment.apps/influxdb created
service/influxdb-web created
service/influxdb-externe created
Et normalement on a bien notre influxdb :
$ kubectl get all -n monitor
NAME                             READY   STATUS    RESTARTS   AGE
pod/influxdb-6d45d54c68-dsm69    1/1     Running   0          69m
pod/svclb-influxdb-externe-bgfz2   1/1     Running   0          85s
pod/svclb-influxdb-externe-bprrk   1/1     Running   0          85s
pod/svclb-influxdb-externe-7rx8h   1/1     Running   0          85s
pod/svclb-influxdb-externe-vdsbc   1/1     Running   0          85s
NAME                     TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
service/influxdb-web     ClusterIP   10.43.62.69     <none>        8086/TCP   3h36m
service/influxdb-externe   LoadBalancer   10.43.211.61    192.168.1.131   8086:30874/TCP   86s
NAME                                    DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
daemonset.apps/svclb-influxdb-externe   4         4         4       4            4           <none>          27m
NAME                         READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/influxdb     1/1     1            1           3h36m
NAME                                   DESIRED   CURRENT   READY   AGE
replicaset.apps/influxdb-6d45d54c68    1         1         1       3h36m
Comme dit je suis sur k3s, qui utilise klipperlb pour la gestion du LoadBalancing.
Telegraf
Là ça va être la partie la plus complexe, et celle dont j'ai le plus galéré.
Toujours dans ma logique, je suis dans un répertoire telegraf dédié à l'application elle-même.
00-rbac.yml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: telegraf
rules:
- apiGroups: [""]
  resources:
  - nodes
  - nodes/proxy
  - services
  - endpoints
  - pods
  - persistentvolumes
  - persistentvolumeclaims
  verbs: ["get", "list"]
- apiGroups:
  - apps
  resources:
  - deployments
  - daemonsets
  - replicasets
  - statefulsets
  verbs: ["get", "list"]
- nonResourceURLs: ["/metrics"]
  verbs: ["get"]
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: telegrafaccount
  namespace: monitor
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: telegraf
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: telegraf
subjects:
- kind: ServiceAccount
  name: telegrafaccount
  namespace: monitor
On commence par les autorisations, le but étant de donner certains droit à telegraf dans le conteneur. Ici nous lui donnons la possibilités de récupérer plusieurs informations, mais en aucun cas de créer quelques choses.
12-configmap.yml
Ici nous avons une configuration static, donc nous allons utiliser un configmap :
apiVersion: v1
kind: ConfigMap
metadata:
  name: telegraf-config
  namespace: monitor
data:
  telegraf.conf: |
    [global_tags]
    [agent]
      interval = "10s"
      round_interval = true
      metric_batch_size = 1000
      metric_buffer_limit = 10000
      collection_jitter = "0s"
      flush_interval = "10s"
      flush_jitter = "0s"
      precision = ""
      hostname = "${NODE_NAME}"
      omit_hostname = false
    
    [[outputs.influxdb]]
      urls = [ "http://influxdb-web:8086" ]
      database = "telegraf"
    
    [[inputs.cpu]]
      percpu = true
      totalcpu = true
      collect_cpu_time = false
      report_active = false
    
    [[inputs.disk]]
      ignore_fs = ["tmpfs", "devtmpfs", "devfs", "iso9660", "overlay", "aufs", "squashfs", "nfs"]
    
    [[inputs.diskio]]
    
    [[inputs.kernel]]
    
    [[inputs.mem]]
    
    [[inputs.processes]]
    
    [[inputs.swap]]
    
    [[inputs.system]]
   
    [[inputs.kubernetes]]
      url = "https://kubernetes.default.svc.cluster.local/api/v1/nodes/$NODE_NAME/proxy"
      insecure_skip_verify = true
    [[inputs.kube_inventory]]
      url = "https://kubernetes.default.svc.cluster.local"
      insecure_skip_verify = true
      namespace= ""
C'est la configuration de telegraf, donc je vous invite à en regarder la docs (cf annexes).
Nous avons ici quelques spécificité, comme le hostname = "${NODE_NAME}, ceci est une variable que l'on récupère au niveau du déploiement (enfin du daemonset, nous verrons ceci).
Autrement nous avons l'url de kubernetes, en gros nous tapons sur le namespace default pour pouvoir accéder à l'API.
20-daemonset.yml
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: telegraf
  namespace: monitor
  labels:
    name: telegraf
spec:
  selector:
    matchLabels:
      name: telegraf
  template:
    metadata:
      labels:
        name: telegraf
    spec:
      hostPID: true
      hostIPC: true
      serviceAccountName: telegrafaccount
      containers:
        - resources:
            requests:
              cpu: 0.15
              memory: 128M
          env:
          - name: NODE_NAME
            valueFrom:
              fieldRef:
                fieldPath: spec.nodeName
          securityContext:
            privileged: true
          image: telegraf:1.16-alpine
          name: telegraf
          volumeMounts:
            - name: dev
              mountPath: /host/dev
            - name: proc
              mountPath: /host/proc
            - name: sys
              mountPath: /host/sys
            - name: rootfs
              mountPath: /rootfs
            - name: config
              mountPath: /etc/telegraf
      volumes:
        - name: config
          configMap:
            name: telegraf-config
        - name: proc
          hostPath:
            path: /proc
        - name: dev
          hostPath:
            path: /dev
        - name: sys
          hostPath:
            path: /sys
        - name: rootfs
          hostPath:
            path: /
Alors là nous créons un daemonset au lui d'un deployment, ceci permets d'avoir une instance par noeud.
En pus de ceci, nous utilisons donc le serviceAccount créer tout à l'heure.
Le but étant de monitorer aussi bien l'hôte que kube lui même, j'ai ajouté hostPID et hostIPC, ainsi que les points de montage /proc, /dev, /sys et /. J'aurais pu également utiliser hostNetwork, mais ceci fait sortir mon conteneur du réseau du cluster, je verrais par la suite pour le mettre ou non, mais cela ajoutera des modifications à la configuration de telegraf.
Ensuite dans le env, j'ajoute une variable NODE_NAME, pour récupérer le nom de l'hôte sur lequel le pod tourne, ce qui me permets d'overwrite le hostname, et au lieu d'avoir un tag host avec le nom du pod, j'ai bien le nom du noeud.
Au lieu de faire ceci, j'aurais pu effectivement installer telegraf sur l'hôte directement, mais je trouvais ça interressant de le faire via kube.
On applique
$ kubectl apply -f .
clusterrole.rbac.authorization.k8s.io/telegraf created
serviceaccount/telegrafaccount created
clusterrolebinding.rbac.authorization.k8s.io/telegraf created
persistentvolume/telegraf-pv created
persistentvolumeclaim/telegraf-pvc created
configmap/telegraf-config created
daemonset.apps/telegraf created
Ce qui donne :
$ kubectl get all -n monitor
NAME                               READY   STATUS    RESTARTS   AGE
pod/influxdb-6d45d54c68-dsm69      1/1     Running   0          99m
pod/svclb-influxdb-externe-bgfz2   1/1     Running   0          27m
pod/svclb-influxdb-externe-bprrk   1/1     Running   0          27m
pod/svclb-influxdb-externe-7rx8h   1/1     Running   0          27m
pod/svclb-influxdb-externe-vdsbc   1/1     Running   0          27m
pod/telegraf-kk8j2                 1/1     Running   0          31s
pod/telegraf-mz47w                 1/1     Running   0          31s
pod/telegraf-lm727                 1/1     Running   0          31s
pod/telegraf-hjnxj                 1/1     Running   0          30s
NAME                       TYPE           CLUSTER-IP     EXTERNAL-IP     PORT(S)          AGE
service/influxdb-web       ClusterIP      10.43.62.69    <none>          8086/TCP         4h5m
service/influxdb-externe   LoadBalancer   10.43.211.61   192.168.1.131   8086:30874/TCP   27m
NAME                                    DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
daemonset.apps/svclb-influxdb-externe   4         4         4       4            4           <none>          27m
daemonset.apps/telegraf                 4         4         4       4            4           <none>          31s
NAME                        READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/influxdb    1/1     1            1           4h5m
NAME                                   DESIRED   CURRENT   READY   AGE
replicaset.apps/influxdb-6d45d54c68    1         1         1       4h5m
Kapacitor
Je passerai très rapidement dessus, avec simplement les fichiers de conf
10-volumes.yml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: data-kapacitor-pv
  namespace: monitor
spec:
  storageClassName: data-kapacitor
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteMany
  nfs:
    server: 192.168.1.105
    path: "/data/kapacitor"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: data-kapacitor-pvc
  namespace: monitor
spec:
  storageClassName: data-kapacitor
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 500Mi
12-config.yml
apiVersion: v1
kind: ConfigMap
metadata:
  name: kapacitor-config
  namespace: monitor
data:
  kapacitor.conf: |
    data_dir = "/var/lib/kapacitor"
    [replay]
      dir = "/var/lib/kapacitor/replay"
    [storage]
      boltdb = "/var/lib/kapacitor/kapacitor.db"
20-deployment.yml
kind: Deployment
apiVersion: apps/v1
metadata:
  namespace: monitor
  name: kapacitor
  labels:
    app: kapacitor
spec:
  replicas: 1
  selector:
    matchLabels:
      app: kapacitor
  template:
    metadata:
      labels:
        app: kapacitor
        namespace: monitor
    spec:
      containers:
        - name: kapacitor
          image: kapacitor:1.5-alpine
          ports:
            - containerPort: 9092
          env:
            - name: KAPACITOR_INFLUXDB_0_URLS_0
              value: "http://influxdb-web:8086"
          volumeMounts:
            - mountPath: /etc/kapacitor/
              name: config
            - mountPath: /var/lib/kapacitor/
              name: data
          resources:
            requests:
              memory: 150M
              cpu: 0.2
            limits:
              memory: 500M
              cpu: 0.5
      volumes:
      - name: config
        configMap:
          name: kapacitor-config
      - name: data
        persistentVolumeClaim:
          claimName: data-kapacitor-pvc
30-service.yml
apiVersion: v1
kind: Service
metadata:
  name: kapacitor-web
  namespace: monitor
spec:
  ports:
    - protocol: TCP
      name: web
      port: 9092
  selector:
    app: kapacitor
On applique
$ kubectl apply -f .
persistentvolume/data-kapacitor-pv created
persistentvolumeclaim/data-kapacitor-pvc created
configmap/kapacitor-config created
deployment.apps/kapacitor created
service/kapacitor-web created
$ kubectl get all -n monitor
NAME                               READY   STATUS    RESTARTS   AGE
pod/influxdb-6d45d54c68-dsm69      1/1     Running   0          107m
pod/svclb-influxdb-externe-bgfz2   1/1     Running   0          35m
pod/svclb-influxdb-externe-bprrk   1/1     Running   0          35m
pod/svclb-influxdb-externe-7rx8h   1/1     Running   0          35m
pod/svclb-influxdb-externe-vdsbc   1/1     Running   0          35m
pod/telegraf-kk8j2                 1/1     Running   0          9m12s
pod/telegraf-mz47w                 1/1     Running   0          9m12s
pod/telegraf-lm727                 1/1     Running   0          9m12s
pod/telegraf-hjnxj                 1/1     Running   0          9m11s
pod/kapacitor-f6c7dfcb9-pvnwr      1/1     Running   0          37s
NAME                       TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)          AGE
service/influxdb-web       ClusterIP      10.43.62.69     <none>          8086/TCP         4h14m
service/influxdb-externe   LoadBalancer   10.43.211.61    192.168.1.131   8086:30874/TCP   35m
service/kapacitor-web      ClusterIP      10.43.167.162   <none>          9092/TCP         38s
NAME                                    DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
daemonset.apps/svclb-influxdb-externe   4         4         4       4            4           <none>          35m
daemonset.apps/telegraf                 4         4         4       4            4           <none>          9m12s
NAME                        READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/influxdb    1/1     1            1           4h14m
deployment.apps/kapacitor   1/1     1            1           38s
NAME                                  DESIRED   CURRENT   READY   AGE
replicaset.apps/influxdb-6d45d54c68   1         1         1       4h14m
replicaset.apps/kapacitor-f6c7dfcb9   1         1         1       38s
Chronograf
Pareil, je montre juste les fichiers de déploiement, car ça reste un service plutôt basique.
10-volumes.yml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: data-chronograf-pv
  namespace: monitor
spec:
  storageClassName: data-chronograf
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteMany
  nfs:
    server: 192.168.1.105
    path: "/data/config/chronograf"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: data-chronograf-pvc
  namespace: monitor
spec:
  storageClassName: data-chronograf
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 500Mi
20-deployment.yml
kind: Deployment
apiVersion: apps/v1
metadata:
  namespace: monitor
  name: chronograf
  labels:
    app: chronograf
spec:
  replicas: 1
  selector:
    matchLabels:
      app: chronograf
  template:
    metadata:
      labels:
        app: chronograf
        namespace: monitor
    spec:
      containers:
        - name: chronograf
          image: chronograf:1.8-alpine
          ports:
            - containerPort: 8888
          env:
            - name: INFLUXDB_URL
              value: http://influxdb-web:8086
            - name: KAPACITOR_URL
              value: http://kapacitor:9092
          volumeMounts:
            - mountPath: /var/lib/chronograf/
              name: data
          resources:
            requests:
              memory: 150M
              cpu: 0.2
            limits:
              memory: 500M
              cpu: 0.5
      volumes:
      - name: data
        persistentVolumeClaim:
          claimName: data-chronograf-pvc
30-service.yml
apiVersion: v1
kind: Service
metadata:
  name: chronograf-web
  namespace: monitor
spec:
  ports:
    - protocol: TCP
      name: web
      port: 8888
  selector:
    app: chronograf
---
apiVersion: v1
kind: Service
metadata:
  name: chronograf-externe
  namespace: monitor
spec:
  ports:
    - protocol: TCP
      name: web
      port: 8888
  type: LoadBalancer
  selector:
    app: chronograf
On applique
$ kubectl apply -f .
persistentvolume/data-chronograf-pv created
persistentvolumeclaim/data-chronograf-pvc created
deployment.apps/chronograf created
service/chronograf-web created
service/chronograf-externe created
$ kubectl get all -n monitor
NAME                                 READY   STATUS    RESTARTS   AGE
pod/influxdb-6d45d54c68-dsm69        1/1     Running   0          114m
pod/svclb-influxdb-externe-bgfz2     1/1     Running   0          42m
pod/svclb-influxdb-externe-bprrk     1/1     Running   0          42m
pod/svclb-influxdb-externe-7rx8h     1/1     Running   0          42m
pod/svclb-influxdb-externe-vdsbc     1/1     Running   0          42m
pod/telegraf-kk8j2                   1/1     Running   0          15m
pod/telegraf-mz47w                   1/1     Running   0          15m
pod/telegraf-lm727                   1/1     Running   0          15m
pod/telegraf-hjnxj                   1/1     Running   0          15m
pod/kapacitor-f6c7dfcb9-pvnwr        1/1     Running   0          7m
pod/chronograf-85567dd664-s2xdd      1/1     Running   0          34s
pod/svclb-chronograf-externe-2gfsk   1/1     Running   0          33s
pod/svclb-chronograf-externe-kg4jq   1/1     Running   0          33s
pod/svclb-chronograf-externe-jxmzf   1/1     Running   0          33s
pod/svclb-chronograf-externe-hcvz7   1/1     Running   0          33s
NAME                         TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)          AGE
service/influxdb-web         ClusterIP      10.43.62.69     <none>          8086/TCP         4h21m
service/influxdb-externe     LoadBalancer   10.43.211.61    192.168.1.131   8086:30874/TCP   42m
service/kapacitor-web        ClusterIP      10.43.167.162   <none>          9092/TCP         7m1s
service/chronograf-web       ClusterIP      10.43.144.238   <none>          8888/TCP         34s
service/chronograf-externe   LoadBalancer   10.43.185.201   192.168.1.133   8888:31980/TCP   33s
NAME                                      DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
daemonset.apps/svclb-influxdb-externe     4         4         4       4            4           <none>          42m
daemonset.apps/telegraf                   4         4         4       4            4           <none>          15m
daemonset.apps/svclb-chronograf-externe   4         4         4       4            4           <none>          33s
NAME                         READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/influxdb     1/1     1            1           4h21m
deployment.apps/kapacitor    1/1     1            1           7m1s
deployment.apps/chronograf   1/1     1            1           34s
NAME                                    DESIRED   CURRENT   READY   AGE
replicaset.apps/influxdb-6d45d54c68     1         1         1       4h21m
replicaset.apps/kapacitor-f6c7dfcb9     1         1         1       7m1s
replicaset.apps/chronograf-85567dd664   1         1         1       34s
Normalement ça fonctionne
Normalement vous devriez pouvoir y accéder via http://ipexterne:8888, et vous devriez avoir la possibilité de créer vos jolies dashboard.
Ici la partie interressant est vraiment l'installation de telegraf, qui est plus complexe que le reste :


Annexe
- Xavki : Il vous faudra peux être être membre de la chaine (0,99€ minimum).
- Telegraf docs
- Plugins telegraf
 
        
    
Comments
No comments yet. Be the first to react!