Conception, développement, déploiement et exploitation de vos plateformes, applications et données.
Contactez-nous !Un client m’a dit récemment : “Nicolas, tu es pénible - jusqu’à présent, je pensais que la solution X était bien, mais quand je vois les mises à jour à faire ici et là, ça devient pénible” et un de ces développeurs de me dire “Mais pourquoi mettre à jour, ça marche !”. Effectivement, ça marche jusqu’au jour où cela ne marche plus.
Souvent, on dépeint les développeurs comme voulant toujours des nouveautés et les ops comme voulant la stabilité et idéalement ne rien changer. Sauf que ne rien changer, ce n’est pas apporter de la stabilité mais juste de l’immobilisme. Darwin sait comment cela se finit, à la fin, le serveur ou l’application, ils meurent. Par ricochet, le projet et l’équipe projet peuvent aussi y passer.
Par ailleurs, nous sommes dans un monde qui évolue. Des nouveautés sont apportées, ainsi que des améliorations de performance mais aussi ne les oublions pas des failles. Dès lors, lorsque l’on fournit un service quelconque, la stabilité prend un autre sens à mon avis : il ne s’agit plus de figer le service dans un état donné mais plutôt de fournir un environnement qui permet au service de s’exécuter avec la même stabilité. Votre service vit dans un écosystème et celui-ci évolue. Dès lors, la stabilité de votre service implique d’être toujours en phase avec votre écosystème. Si votre service s’interface avec le fournisseur Y, vous devez toujours être en mesure de vous interfacer avec plutôt que de dire à votre client : “Le fournisseur Y a changé son API ou je ne sais quoi, vous ne pouvez plus utiliser le service”. Dans ce cas, ce n’est pas de la stabilité que vous apporter à votre client mais une régression. D’ailleurs, la littérature informatique a un terme pour cela “Le Maintien en Conditions Opérationnelles (MCO)” (à ne pas confondre avec “Le Maintien à bout de bras” ou “Le Maintien à flot”).
Et sinon, les projets d’upgrade tous les X ans qui prennent Y mois pendant lesquels on n’apporte aucune valeur au client le temps de faire cette fichue mise à jour, c’est d’un pénible et d’un frustrant. Et faut-il encore que ce budget d’upgrade - qui ne cesse de grossir par nombre de fois où il est reporté - soit un jour accordé.
Donc tout comme il est plus facile de ranger sa chambre chaque jour un petit peu plutôt qu’une fois par mois ou trimestre, la stabilité de votre plateforme, vous l’assurez en mettant à jour régulièrement vos composants. Il y a certes un petit effort régulier à faire. Certains pourront trouver ça pénible - mais vaut mieux ça que le “plus jamais ça” après un chantier d’un an de mise à jour. Les premiers rangements, vous allez les sentir passer car ils sont encore un peu gros. Mais au fur et à mesure, ils vont diminuer et être tout à fait acceptables. En faisant aussi des petites mises à jour incrémentales et pour les bugs que vous allez découvrir, il est aussi plus facile de voir ce qui a pu créer un bug dans le cadre de la mise à jour du fait de sa taille. Dans les grosses mises à jour, il y a tellement de coupables possibles que c’est une véritable partie de Cluedo que vous allez devoir mener pendant des semaines.
De plus, avoir des composants à jour vous permet d’être supporté par les éditeurs ou les communautés d’utilisateurs. Vous éviterez la réponse “Mettez à jour - passer à la version X pour être supporté - Je ferme le ticket”.
Donc oui, je vais fortement encourager mes clients à maintenir leur système à jour car je veux leur apporter la stabilité dont ils ont besoin. Au delà du fait aussi que je ne veux pas me retrouver dans une situation dantesque en cas de pépin majeur, c’est tout autant ma tranquillité que la leur dont je m’assure de façon régulière. Cerise sur le gâteau, ils bénéficient aussi des avancées des composants et peuvent à leur tour fournir un meilleur service à leurs clients et contribuer ainsi à la pérennité de leur entreprise.
img, kaniko, etc. Pour les autres cas, il faudra peut être passer par l’API kubernetes ou trouver les alternatives qui vont bien.dockershim pour permettre à ceux qui ont en besoin de pouvoir continuer à l’utliiser. La limite étant que si vous êtes sur du service managé et que votre provider ne le fournit pas, vous ne pourrez pas l’utiliser…docker logs fonctionne avec tous les drivers de log et non unqiement json & journald et plein d’autres améliorations/harmonisations au niveau de la CLI. Pour ceux sous Fedora qui avaient bidouillé avec firewalld précédemment pour faire fonctionner docker et qui ont un problème lié à l’interface docker0 au démarrage du service docker, allez voir par ici.-mount, les jpbs swarm et une synthèse de l’actualité de l’écosystème docker.network (dis)connect, support des alias avec des noms courts, amélioration des commandes play|generate kube et capacité de monter une image OCI dans un container.influxdb et influxdb2 à passer pour ceux qui étaient déjà en 2.0 et ceci afin d’éviter que des gens en 1.x passent involontairement en 2.x, le “delete with predicate” a été réactivé, améliorations sur le process d’upgrade, des commandes autour des actions en mode V1, mise à jour de flux, et plein d’autres corrections/améliorations.Il ne me reste plus qu’à vous souhaiter de bonnes fêtes de fin d’année et on se retrouve l’année prochaine !
Kubernetes, c’est bien mais parfois on veut faire des choses plus simples et on n’a pas forcément besoin d’avoir un système distribué ou d’une forte disponibilité. Pour autant, il est agréable avec kubernetes de pouvoir interagit à distance avec l’API au travers de kubectl. Je me suis donc mis en quête d’une alternative. Partir sur k3s avec un déploiement mono-node ? Partir sur docker/docker-compose/docker-swarm ?
Je me souvenais que l’on pouvait exposer la socket docker sur le réseau en TCP et authentification par certificat mais cela ne me plaisait pas beaucoup. Je me suis alors rappelé que l’on pouvait interagir avec un hote docker via une connection ssh. En continuant à creuser, je suis tomber sur les contextes dans Docker et ce billet How to deploy on remote Docker hosts with docker-compose. Et là : 🤩
Alors, certes, pas de secrets, configmaps ou cronjobs mais un déploiement remote que je peux automatiser dans gitlab et/ou avec lequel je peux interagir à distance 😍
Création d’un contexte puis utilisation d’un contexte :
# Création du contexte docker
docker context create mon-serveur ‐‐docker “host=ssh://user@monserveur”
# Lister les contextes docker existant
docker context ls
# Utiliser un contexte
docker context use mon-serveur
# Vérifier le bon fonctionnement de la cli docker
docker ps
[liste de conteneurs tournant sur la machine "mon-serveur"]
# Vérifier le bon fonctionnement de la cli docker-compose
cd /path/to/docker/compose/project
docker-compose ps
Pour que cela fonctionne, en plus des versions récentes de docker et docker-compose coté client et serveur. Il vous faut aussi une version récent de paramiko coté client. Celle présente dans Debian 10 n’est pas assez à jour par ex, il a fallu passer par pip3 install paramiko. Il faut aussi avoir une authentification par clé pour se simplifier la vie.
Pour docker-compose, vous avez deux solutions pour utiliser un contexte distant :
# En deux commandes
docker context use <remote context>
docker-compose <command>
# En une commande
docker-compose --context <remote context> <command>
Dans gitlab, dans le fichier .gitlab-ci.yml, on peut alors profiter de cette intégration de la façon suivante ; je prends l’exemple du déploiement du site du Paris Time Series Meetup.
Tout d’abord, dans mon fichier docker-compose.yml, j’ai mis un place holder IMAGE qui sera remplacé par la référence de mon image docker fabriquée par gitlab-ci :
version: '3.8'
services:
web:
image: IMAGE
labels:
[...]
restart: always
Ensuite, dans gitlab-ci :
docker.env. Ce fichier sera sauvegardé en fin de job sous la forme d’un artefact disponible pour le job “docker”.docker.env sous la forme de variable d’environnement. Il remplace ensuite IMAGE par sa vraie valeur. On initialise le contexte docker pour se connecter au serveur cible et on peut alors réaliser les actions habituelles avec docker-compose.---
stages:
- publish
- image
- deploy
publish:
image: $CI_REGISTRY/nsteinmetz/hugo:latest
artifacts:
paths:
- public
expire_in: 1 day
only:
- master
- web
script:
- hugo
stage: publish
tags:
- hugo
kaniko:
stage: image
image:
name: gcr.io/kaniko-project/executor:debug
entrypoint: [""]
variables:
RELEASE_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA-$CI_PIPELINE_ID-$CI_JOB_ID
script:
- echo "IMAGE=${RELEASE_IMAGE}" >> docker.env
- mkdir -p /kaniko/.docker
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
- /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination $RELEASE_IMAGE
only:
- master
- web
when: on_success
tags:
- kaniko
artifacts:
reports:
dotenv: docker.env
docker:
stage: deploy
before_script:
- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
script:
- sed -i -e "s|IMAGE|${IMAGE}|g" docker-compose.yml
- docker context use mon-serveur
- docker-compose pull
- docker-compose up -d
needs:
- job: kaniko
artifacts: true
when: on_success
only:
- master
- web
tags:
- shell
environment:
name: production
url: https://www.ptsm.io/
Et voilà ! 😎
Avec ces contextes (que l’on peut utiliser aussi avec swarm et kubernetes), on a donc un moyen simple et efficace pour déployer des conteneurs à distance et de façon automatisée.
Ce soir, il y a la 8ème édition du Paris Time Series Meetup sur AWS TimeStream.
scp file destination:/path/to/file ? La commande scp est victime de nombreuses failles. Du coup, elle va être dépréciée. Néanmoins une initiative vise à maintenir uen commande scp mais se fondant sur sftp et son modèle de sécurité.Pour ceux sous Fedora et utilisant podman en alternative au binaire docker, pour se connecter à la registry google (via):
gcloud auth print-access-token | podman login -u oauth2accesstoken --password-stdin gcr.io
InfluxDB 0SS 2.0 étant sortie, j’ai testé la mise à jour d’une instance 1.8.3 vers 2.0.1 sur une VM Debian 10 à jour.
La documentation pour une mise à jour 1.x vers 2.x est disponible. La vidéo “Path to InfluxDB 2.0: Seamlessly Migrate 1.x Data” reprend cela et va plus loin en présentant bien tous les points à prendre en compte (y compris pour Telegraf, Chronograf et Kapacitor). Je ne rajouterai donc que mes remarques.
Concernant la commande influxd upgrade :
sudo pour ne pas avoir de problèmes de permisisons.~/.influxdbV2. Or je doute que vous vouliez que vos données soient à cet endroit. Je vous invite donc à regarder la documentation de influxd upgrade pour définir les propriétés --engine-path et --bolt-pathExemple:
mkdir -p /srv/influxdb/influxdb2
influxd upgrade --engine-path /srv/influxdb/influxdb2/engine --bolt-path /srv/influxdb/influxdb2/influxd.bolt
config.toml est généré dans /etc/influxdb/. Il contient quelques valeurs issues de la migration et des valeurs par défaut. Je l’ai personnalisé de la façon suivante pour tenir compte de mes valeurs :bolt-path = "/srv/influx/influxdb2/influxd.bolt"
engine-path = "/srv/influx/influxdb2/engine"
http-bind-address = "127.0.0.1:8086"
storage-series-id-set-cache-size = 100
influxd cherchait à initialiser ses fichiers dans /var/lib/influxdb/.influxdbv2. Ayant noté que le service InfluxDB prennait /etc/default/influxdb comme fichier d’environnement, j’ai ajouté dans ce fichier :# /etc/default/influxdb
INFLUXD_CONFIG_PATH=/etc/influxdb/config.toml
Dès lors, /etc/influxdb/config.toml était bien pris en compte et InfluxDB démarrait bien avec mes données.
Une fois InfluxDB 2 démarré, j’ai pu noter avec plaisir :
Je n’ai donc pas d’urgence à migrer la configuration et le paramétrage de ces derniers. Je vais pouvoir le faire progressivement ces prochains jours.
N’utilisant pas Chronograf et Kapacitor, je n’ai pas eu de données à migrer ou d’ajustements à faire à ce niveau là. La vidéo reprend bien les points d’attention et les éventuelles limitations à prendre en compte dans le cadre de la migration.
Finalement, c’est pas mal qu’ils aient réintégrer les endpoints 1.x dans la version 2.0 à ce niveau là ;-)
La 2.0.2 étant sortie pendant ma mise à jour, j’ai poursuivi la mise à jour. Je suis tombé sur ce bug rendant l’écriture de données impossibles. Cela a mis en évidence un bug sur la migration des “retention policies” et sur le fait que j’avais aussi des très vieilles bases InfluxDB. Je n’aurai a priori pas eu ce bug en faisant la migration 1.8.3 vers 2.0.2. En tous cas, une 2.0.3 devrait donc arriver prochainement avec une amélioration du processus de migration faisant suite à ma séance de troubleshooting.
Elle peut se faire très progressivement - si par ex vous utilisez telegraf pour envoyer vos données et Grafana pour la partie dashboarding :
influxdb à l’output influxdb_v2 sans impacter grafana qui continuera à accéder à vos données en InfluxQLSi vous devez rétablir un accès à vos données via les API 1.x à un bucket nouvellement créé (j’ai profité de la migration pour mettre des buckets clients dans des organisations représentant les clients en question).
# Créer le bucket
influx bucket create --name <BUCKET_NAME> --retention 0 --org <ORGANISATION>
# Récupérer l'ID de bucket via la liste des buckets
influx bucket list
# Créer une DBRP (DataBase Retention Policies) pour le bucket en question - les accès en 1.x se font en mode SELECT * FROM <db_name>.<retention_policies> ...
influx v1 dbrp create --bucket-id=<BUCKET_ID> --db=<BUCKET_NAME> --rp=autogen --default=true
# Créer un utilsateur sans mot de passe pour le moment
influx v1 auth create --username <USER> --read-bucket <BUCKET_ID> --write-bucket <BUCKET_ID> --org <ORGANISATION> --no-password
# Créer un mot de passe au format V1
influx v1 auth set-password --username <USER>
Les utilisateurs migrés depuis la version 1.x sont visibles via influx v1 auth list.
Le support de Flux dans Grafan existe depuis la version 7.1 mais il n’est pas aussi aisé que celui dans InfluxDB 2.0 OSS. Il y a certes de la complétion au niveau du code ou le support des variables mais pas de capacité d’introspection sur la partie données.
Pour le moment, je procède donc de la façon suivante :
Ex coté InfluxDB 2.0 OSS / Flux :
from(bucket: v.bucket)
|> range(start: v.timeRangeStart, stop: v.timeRangeStop)
|> filter(fn: (r) => r["_measurement"] == "net")
|> filter(fn: (r) => r["_field"] == "bytes_recv" or r["_field"] == "bytes_sent")
|> filter(fn: (r) => r["host"] == v.host)
|> derivative(unit: v.windowPeriod, nonNegative: false)
|> yield(name: "derivative")
La version dans Grafana :
from(bucket: "${bucket}")
|> range(start: v.timeRangeStart, stop: v.timeRangeStop)
|> filter(fn: (r) => r["_measurement"] == "net")
|> filter(fn: (r) => r["_field"] == "bytes_recv" or r["_field"] == "bytes_sent")
|> filter(fn: (r) => r["host"] == "${host}")
|> derivative(unit: v.windowPeriod, nonNegative: false)
|> yield(name: "derivative")
La différence portant sur la gestion des variables v.host vs "${host}" et v.bucket vs "${bucket}".
Autre bonne nouvelle, les variables sont supportées dans Grafana ; vous pouvez donc définir les variables comme celles vu juste au-dessus :
Variable bucket de type “Query” en prenant InfluxDB/Flux comme datasource :
buckets()
|> filter(fn: (r) => r.name !~ /^_/)
|> rename(columns: {name: "_value"})
|> keep(columns: ["_value"])
Variable host de type “Query” en prenant InfluxDB/Flux comme datasource :
# Provide list of hosts
import "influxdata/influxdb/schema"
schema.tagValues(bucket: v.bucket, tag: "host")
Si votre requête fonctionne dans un dashboard InfluxDB ou en mode explore mais qu’elle est tronquée dans Grafana, il vous faudra ajuster le “Max Data Points” pour récupérer plus de points pour cette requête (cf grafana/grafana#26484).