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 VM qui sert 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 lieu d'un deployment
, ceci permets d'avoir une instance par noeud.
En plus de ceci, nous utilisons donc le serviceAccount
créé tout à l'heure.
Le but étant de monitorer aussi bien l'hôte que kubernetes lui même, j'ai donc 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 kubernetes.
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-web: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!