Vue lecture

Utiliser Varnish pour injecter du contenu sans toucher au backend

Varnish, en voilà une techno qu’on ne voit pas tous les jours sur les plateformes modernes d’hébergement, et pourtant, ça se déploie même dans Kubernetes 😛 Et si je vous dis que j’ai eu à gérer un cas de fourniture de contenu dans un contexte plus que particulier, avec du Varnish en frontal, et que je ne pouvais pas passer par le serveur web ? Allez, je vous explique.

Varnish, un logiciel méconnu, mais très puissant

Varnish est un serveur « cache », qui fonctionne comme un « proxy » entre votre site web et ses visiteurs. Son rôle est de garder en mémoire temporairement le résultat de la construction des pages de votre site. En effet, quand vous visitez un site comme le mien par exemple, le moteur WordPress, qui doit être chargé en premier lieu, doit construire l’intégralité du code HTML de la page, avec les références aux feuilles de style, aux scripts JavaScript des différents plugins, et le contenu de la page en question, ce qui implique des appels à la base de données. Bref, un paquet d’activités à rejouer pour chaque visiteur, chaque page visitée. Si le contenu d’une page n’a pas à bouger pendant un certain temps, on peut se dire que générer une fois la page, et servir le résultat X fois pendant ledit certain temps, est un gain de ressources et donc de performances non négligeable. C’est un des points forts des CDN, allié à l’aspect géographique de ce type de service.

Varnish est reconnu pour son empreinte minimaliste, et ses performances redoutables. Il est aussi connu pour la mentalité des développeurs du projet concernant la sécurité (ils se sont réjoui le jour où ils ont ouvert une section « security advisory », après 10 ans d’existence). Il est aussi devenu un peu pénible à utiliser depuis que le HTTPS s’est généralisé, parce que les développeurs ont pris le parti de ne pas l’implémenter, ce qui a réduit un peu son intérêt. L’usage des CDNs étant une autre raison du désamour croissant.

Le contexte particulier qui nous intéresse

Particulier, c’est bien le mot : le site sur lequel je dois intervenir est déployé avec Docker, un container avec Varnish en frontal, un container avec Apache/PHP derrière. Mais j’ai plusieurs problèmes avec cette plateforme. Les containers sont déployés avec docker-compose, mais je n’ai pas accès au fichier, tout est construit et déployé avec un pipeline Jenkins, jusque là rien que de très commun me direz-vous. Sauf que le serveur Jenkins n’existe plus non plus, je n’ai accès à aucune source ni aucun support de l’agence web d’origine (plus de contrat). Le bonheur de tout sysadmin.

On me demande de mettre à jour le « merchant ID » d’Apple pour le site web (un identifiant qui sert pour Apple Pay), et ça passe par un fichier spécial qui doit être joignable avec une URI de la forme /.well-known/apple-developer-merchantid-domain-association. Pas de bol, le container avec le site web est en lecture seule (comprendre, l’utilisateur du container n’est pas le propriétaire du « DocumentRoot », le dossier qui contient le site web). Mais, le Varnish 5 présent en front, lui, il l’est, d’autant plus qu’on a accès à son compte root !

On est donc tout équipé pour tenter une greffe à chaud sans anesthésie.

Opération à cœur ouvert

J’identifie le fichier « VCL » qui contient la configuration relative au site, et j’attaque la première tentative. On rajoute un test dans la section vcl_recv{} :

sub vcl_recv {
if (req.url ~ "^/.well-known/apple-developer-merchantid-domain-association") {
return (synth(200,"7C227073704964223B2236304337424..."));
}

return (synth(() est la méthode à utiliser si on veut renvoyer du contenu qui ne vient pas du backend. Il faut par contre définir quelques propriétés dans la section vcl_synth{} pour qu’il ne renvoie pas du HTML par défaut mais du texte brut :

sub vcl_synth {
set resp.http.Content-Type = "text/plain; charset=utf-8";
set resp.body = resp.reason;
return(deliver);
}

Vient ensuite l’injection à chaud de la configuration. C’est un truc que j’aime bien avec Varnish, on peut lui demander de charger plusieurs configurations en parallèle et basculer entres celles-ci en fonction des besoins. En gros, voilà ce que je fais :

bash-5.0# varnishadm
200
-----------------------------
Varnish Cache CLI 1.0
-----------------------------
Linux,3.10.0-1160.83.1.el7.x86_64,x86_64,-junix,-smalloc,-sdefault,-hcritbit
varnish-6.2.1 revision 9f8588e4ab785244e06c3446fe09bf9db5dd8753

Type 'help' for command list.
Type 'quit' to close CLI session.

varnish> vcl.list
200
active warm warm 2 boot

varnish> vcl.load apple /var/varnish-docker/dockerized.apple.vcl
200
VCL compiled.

varnish> vcl.list
200
active warm warm 2 boot
available auto warm 0 apple

varnish> vcl.use apple
200
VCL 'apple' now active
varnish> quit
500
Closing CLI connection
bash-5.0# curl -s http://127.0.0.1:6081/.well-known/apple-developer-merchantid-domain-association
7C227073704964223B2236304337424(...)

Le détail du fonctionnement des commandes Varnish se trouve dans la documentation.

Hourra ! Enfin presque, parce qu’après avoir informé le client, ce dernier me dit que c’est toujours pas bon. Et là, le gaillard qui travaille sur l’intégration Apple Pay m’indique qu’il y a un souci avec le checksum (première fois que c’est mentionné dans nos échanges). La documentation que l’on ne m’avait évidemment pas fourni indiquait bien de livrer le fichier et de ne pas copier son contenu. Je vous le donne en mille, il manque un retour à la ligne à la fin.

J’ai donc bossé sur une alternative. La doc de Varnish me renvoie vers un std.fileread(). Je copie donc le fichier dans le même répertoire que le VCL, et j’en profite pour faire une syntaxe un poil plus propre au niveau de ce dernier, la voici.

sub vcl_recv {
    if (req.url ~ "^/.well-known/apple-developer-merchantid-domain-association") {
        return (synth(200,"apple"));
    }
(...)
}

sub vcl_synth {
(...)
if (resp.reason == "apple") {
    set resp.http.Content-Type = "text/plain; charset=utf-8";
    set resp.body = std.fileread("/var/varnish-docker/apple-developer-merchantid-domain-association");
    return(deliver);
  }
}

Et là, j’ai bien le bon checksum (franchement, pour un retour à la ligne…). À noter qu’il faut bien faire attention aux permissions du fichier qu’il doit lire – même si dans le cas présent on est root-, et que le contenu n’est pas lu dynamiquement, mais mis en cache au chargement à l’instar du reste de la configuration, il faut donc jouer avec use/discard/load/use de varnishadm pour jouer avec les configs (ou charger avec un nom différent à chaque fois, mais c’est un peu plus dégueu).

Dans mon cas, vu le peu de persistance nécessaire je me suis arrêté là. Le site était en cours de refonte et c’est d’ailleurs ce qui a motivé le choix de l’ajout du fichier en premier lieu sur cette plateforme, vu qu’elle répondait déjà au domaine. Je n’avais pas le temps de tenter un reverse des images pour la jouer au niveau du serveur web, et c’était cool au final de bosser avec Varnish, que je vous recommande de découvrir rien que pour comprendre comment fonctionnent certains CDNs.

Amusez-vous bien 🙂

Gitlab-CI, clé SSH avec passphrase, petit casse-tête

C’est un cas peu commun et qui m’a donné du fil à retordre (on parle de 3h d’essais/erreurs), c’est suffisamment barbu pour que je prenne la peine de partager l’info, en redonnant du contexte et un peu plus d’explications quand même.

Le contexte, donc

Nous sommes dans une migration de jobs qui s’exécutaient sur Jenkins à la main et qui doivent être refaits dans Gitlab-CI. Une grande partie de la facilité vient du fait que tout est basé sur Ansible, ce qui fait qu’on a finalement qu’à gérer « l’autour » de la logique des jobs, à savoir le setup de l’environnement d’exécution pour Ansible. Certains de ces jobs ciblent des serveurs sous Windows, d’autres sous Linux (Ansible fait ça très bien, via WinRM/Powershell pour Windows, SSH/Python pour Linux). Pour les Windows, un compte technique dans l’annuaire utilisé pour les environnements de préproduction fera le taf à la place de… variables utilisateurs/mots de passe fournies aux jobs à l’exécution. Pour les Linux, ça repose sur un compte dédié avec une clé SSH exploitée par Jenkins.

En temps normal personne se poserait de question, sauf que là, mon problème est que la clé est protégée par une phrase de passe, en anglais passphrase, et c’est particulièrement compliqué de travailler avec, au point que la plupart des gens recommandent de virer la phrase de la clé. Sauf que là, c’est hors de question, j’ai pas le droit d’y toucher. Au moins je peux la récupérer depuis un Vault Hashicorp privé. Et donc, mon premier symptôme, c’est ça :

debug1: Trying private key: /home/appuser/.ssh/id_rsa
debug1: read_passphrase: can\'t open /dev/tty: No such device or address

SSH, ça s’automatise bien normalement, et puis…

Le fameux tty qu’on voit, c’est l’interface qui doit permettre à l’utilisateur d’interagir, et donc de saisir la passphrase au moment souhaité, à savoir quand il doit la présenter pour la connexion. On comprend donc le problème avec un pipeline Gitlab-CI: point de TTY. Et pas la peine d’y penser, le client SSH n’a aucune autre méthode directe pour accepter ladite phrase (c’est pas comme l’installation du SDK Android où on peux suffixer < yes pour répondre automatiquement aux questions d’acceptation des licences).

À force de chercher et de torturer mes termes de recherche, je finis par tomber sur cet article. Fait intéressant, il mentionne des options/variables d’Ansible pour manipuler justement les clés et éviter de fournir la phrase à chaque fois. Mais le problème, c’est que pour ansible_ssh_prompt, on doit indiquer l’intitulé de la question à laquelle on doit répondre par la passphrase et qu’il va devoir « capturer ». Question qui n’est jamais posée puisque pas de TTY pour ça. Ce n’est donc toujours pas exploitable, mais une réponse suivante sur la même page me mets sur la voie.

Cette ligne m’intrigue particulièrement:

- echo "$SSH_PRIVATE_KEY" |tr -d '\r' | DISPLAY=None SSH_ASKPASS=~/.ssh/tmp ssh-add -

La documentation d’ssh-add va répondre à mes questions:

DISPLAY, SSH_ASKPASS et SSH_ASKPASS_REQUIRE
    Si ssh-add a besoin d'une phrase secrète, il la lira sur le terminal actif s'il a été lancé dans un terminal. Si ssh-add n'a aucun terminal associé alors que DISPLAY et SSH_ASKPASS sont défi‐
    nies, il exécutera le programme spécifié par SSH_ASKPASS (par défaut « ssh-askpass ») et ouvrira une fenêtre X11 pour lire la phrase secrète. Cela s’avère particulièrement utile lors de l'appel
    de ssh-add depuis un fichier .xsession ou un script similaire.

Je tente donc la solution, mais fait face à un nouveau message d’erreur:

ssh_askpass: exec(/home/appuser/.ssh/.print_ssh_password): Exec format error

Là, ça m’a pris beaucoup moins de temps, et la solution finale était tout près, il manque le shebang au script que je fournis (.print_ssh_password). Au passage, et contrairement à la majorité des posts que j’ai pu lire sur le sujet, j’utilise une base python-alpine pour le job et pas Debian/Ubuntu, ce n’est donc pas bash qui est aux commandes, et donc il faut adapter un chouia la séquence, mais ça a donné ça :

script:
  - echo "[SSH]Initialisation Clé"
  - |
    mkdir ~/.ssh && chmod 700 ~/.ssh
    echo -ne '#!/bin/sh\necho $passphrase' > ~/.ssh/.print_ssh_password
    chmod 700 ~/.ssh/.print_ssh_password
    eval $(ssh-agent)
    echo "$key" | tr -d '\r' | DISPLAY="None" SSH_ASKPASS=~/.ssh/.print_ssh_password ssh-add -
  - echo "[INFO] Ping du serveur"
  - ansible linux_server -m ping -i inventories/$ANSIBLE_INVENTORY -vvvvvv

Ouais, la brochette de v à la fin permet d’avoir le moindre bout de message d’erreur, franchement abusez-en pendant une telle opération c’est super pratique.

Donc, qu’est-ce qu’on a fait ? En amont, via Vault, on récupère la clé privée dans la variable key et sa passphrase dans la variable passphrase, rien de très original là-dedans. En passant, comme cette récupération se fait pendant l’exécution du job, job qui se lance dans un runner Kubernetes, il n’y a pas de stockage persistant de ces variables. On crée le dossier .ssh avec les bonnes permissions à la racine du dossier utilisateur de l’image, et on y place un petit script dont le seul rôle est d’afficher le contenu de la variable passphrase. On démarre le ssh-agent, puis on traite le contenu de la variable key (pour virer d’éventuels retours chariot à la Windows), avant de l’envoyer à ssh-add (le tiret à la fin permet de dire qu’on lui envoie la clé directement, sans passer par un fichier), à qui on précise les variables DISPLAY et SSH_ASKPASS pour éviter de chercher à poser la question à un humain, la réponse étant directement fournie par le script.

Le résultat est sans appel:

linux_server | SUCCESS => {
    "ansible_facts": {
        "discovered_python_interpreter": "/usr/bin/python"
    },
    "changed": false,
    "invocation": {
        "module_args": {
            "data": "pong"
        }
    },
    "ping": "pong"
}

« Ça serait pas arrivé avec Kubernetes »

C’est vrai quoi, une API finalement c’est mieux pour faire tout ça non ?

J’avais envie de troller un peu, mais plus sérieusement, étant donné qu’il y a aussi un annuaire pour gérer les connexions utilisateurs sous Linux, je suis surpris que la méthode repose sur ce genre de mécanisme avec un compte local et une clé SSH et pas un compte technique de la même nature que pour les Windows, ce qui aurait simplifié le processus tout en harmonisant les configurations.

En tout cas, j’étais soulagé et heureux d’avoir pu trouver la solution et continuer mes migrations. Et vous, vos connexions SSH dans Gitlab-CI, vous les gérez comment ?

DNS: faisez gaffe aux wildcard !

Encore un déterrage de brouillon, sur un problème qui doit être toujours d’actualité dans son concept. Un Problème surprenant (pour les pros du DNS peut-être pas), mais voilà, on a eu droit à un incident inattendu, que je voulais partager pour sa bizarrerie apparente. Parce que si j’ai trouvé la solution assez rapidement, il m’a fallu du temps pour comprendre, et je suis pas encore certain d’avoir tout intégré, même deux ans et demi après.

Pour fournir une résolution souple aux clients sur leur plateforme Kubernetes (parce que pourquoi pas), on leur a fourni un alias à configurer sur leur propre zone, histoire de ne pas avoir à dépendre d’eux pour d’éventuelles migrations/reconfigurations déménagements. Cet alias repose sur une fonctionnalité du DNS, les wildcard. Ça donne quelque chose dans ce genre-là :

#notredomaine.fr

prd    A    1.2.3.4
sta    A    5.6.7.8
*.prd CNAME prd
*.sta CNAME sta

On l’aura compris, « prd » c’est le cluster de prod, « sta » le cluster de staging. De leur côté, les clients déclarent l’alias dans leur propre zone :

www.domainX.com    CNAME    www.domainX.com.prd.notredomaine.fr

Dans une situation simple, classique, c’est l’alias *.prd qui prend en charge et fournit la réponse aux clients qui cherchent à joindre un des domaines concernés par ce dispositif. Ah, et en passant, on ne peut pas appliquer un CNAME à l’apex (autrement appelé la racine) d’un domaine (RFC je sais plus, demandez à votre moteur de recherche). Et si vous vous demandez pourquoi un tel setup, c’est qu’on se sert aussi d’un tel « domaine technique » (comprendre un domaine différent de celui du vrai site et géré par nos soins) pour pouvoir tester le site dans différentes situations sans reposer sur le nom de domaine du client, notamment pendant certaines phases de validation, et pour pouvoir gérer ça au niveau des Ingress Rules.

Déplacement d’Ingress Controller, arrivent les problèmes

Eh oui, parce que la vie est ainsi faite, on nous demande de déployer un Ingress Controller particulier avec des paramètres bien précis, donc une nouvelle adresse IP, et de déplacer certains des sites derrière celui-ci. Je vais pas détailler la partie « kube » de ce dispositif, c’est assez trivial en principe (cherchez ingressClass si vraiment vous voyez toujours pas), mais voilà comment on prépare côté DNS :

prd-private    A    4.3.2.1
*.prd-private    CNAME    prd-private

Et quand on doit bouger www.domain2.com, avec notre dispositif, avec le Go du client, j’applique la modif côté cluster et je déplace de manière explicite côté DNS de mon côté:

www.domain2.com.prd    CNAME    prd-private

Et là, patatras. Quand un collègue est venu me voir au bout d’un quart d’heure, en me disant qu’un autre domaine sans rapport avec ma modification était tombé, et ayant l’historique prouvant que j’étais le dernier à avoir modifié notre zone notredomaine.fr, il est clair que l’effet de bord n’avait pas été correctement anticipé. Mais quel effet me direz-vous, sachant qu’on avait testé en staging avant sans avoir détecté ce genre de comportement ?

Le premier indice, c’est que tous les domaines ne sont pas concernés. Sur les 4 alertes qui sont apparues, le point commun est que toutes les URLs ont des domaines en .com. Le second, c’est que ma modif fonctionne pour mon propre domaine. Au bout de cinq minutes, je dis à Camille : « je suis pas sûr mais je tente un truc ». J’ai rajouté une ligne :

*.com.prd    CNAME    prd

Et ça répond à nouveau. J’ai eu du pif, mais j’avoue que sur le moment, c’est plus de l’instinct qu’autre chose, et j’aurais été bien en peine d’expliquer pourquoi, mais au niveau du comportement, on voit maintenant ce qui se passe. Je vais essayer de formuler du mieux possible ce que j’en ai compris.

Explication pas forcément claire

Pour rappel, dans le DNS les domaines fonctionnent par niveau, chaque niveau est séparé par un point. On parle souvent de « sous-domaine », mais c’est un abus de langage, en pratique, chaque niveau dispose des mêmes fonctionnalités et propriétés, ils héritent de certaines caractéristiques, le système étant hiérarchique (com, domain.com, www.domain.com, etc). Dans le cas qui nous occupe, tant qu’on avait qu’un seul wildcard pour tout ce qui concernait les niveaux au dessus de prd.mondomaine.fr, le résolveur ne se posait pas trop de questions et répondait à tout. Mais quand j’ai déclaré www.domain2.com.prd dans mondomaine.fr, j’ai donc déclaré au moins une entrée sur .com.prd. À partir de ce moment-là, cette déclaration explicite exclut toutes les autres réponses par le wildcard. La seule solution de contournement implique de déclarer un wilcard supplémentaire pour ce niveau spécifiquement afin de retrouver un comportement initial.

Pourquoi ce comportement ? Eh bien, comme je l’ai dit, les domaines sont gérés par niveau, et il faut savoir que chaque niveau peut être délégué / géré par un résolveur différent. Dès lors, déclarer une entrée spécifique pour un niveau implique que le résolveur arrête d’appliquer les propriétés des niveaux précédents aux niveaux suivants, ce que permet justement le wildcard. Et c’est logique, si un niveau contient des informations (ici au moins un CNAME), si jamais il est délégué, on n’est pas censé répondre à la place dudit délégué. Certes ici c’est le même, et donc il aurait pu continuer de répondre. Il faut se souvenir que le système de DNS remonte à 1983, et même s’il a été mis à jour à la marge, la majorité du fonctionnement intrinsèque n’a pas bougé, et cette gestion en niveaux en fait partie.

On évite les wildcard alors ?

Certains diront oui, mais dans le monde réel, parfois c’est plus pratique/évident de faire avec (tout le monde ne peut pas composer avec de la résolution dynamique comme le fait Kubernetes en interne). Ici, la solution la plus pratique aurait été de supprimer l’utilisation des .com/.fr/.it/.whatever des alias, et ne garder que le nom sans le TLD, ce qui aurait rendu chaque entrée plus unique et donc limité l’impact de l’exclusion du wildcard global du « calcul » de la réponse.

Dans certaines situations, déléguer le niveau de l’alias est une solution plus adaptée, notamment dans le cadre de l’usage certains outils comme les CDN Cloudflare ou Akamai, ainsi ils se chargent notamment de la mise à jour dynamique de la ou les adresses IP par lesquelles vous passez. Certains utilisent aussi du DNS « géographique », à savoir donner une réponse différente en fonction de là où vous vous trouvez sur le réseau (plus ou moins corrélé à votre emplacement physique), réponse qui doit pointer sur un serveur plus proche et donc plus « performant ». Bref, vous l’aurez compris, le wildcard dans le DNS n’est pas toujours la solution la plus adaptée, et ce problème rencontré en production est là pour le rappeler.

Ah, et pour ceux qui pensent qu’on aurait pu juste tout déclarer manuellement, quand on pense une plateforme pour de l’hébergement massif, semi-dynamique, et qu’on a pas les API / accès pour gérer la zone programmatiquement, et qu’on a passé les 50 sites, ben utilise les wildcards DNS « épicétou » 😛

Helm : erreur au déploiement suite à la suppression d’API

On a beau concevoir des chaînes d’intégration et de déploiement continu pour que les mises à jour fréquentes de sites web soient facilitées, il arrive tout de même de temps en temps que des projets « zombies » traînent sur une plateforme hébergeant de nombreuses applications gérées par des équipes différentes. Et si vous faites correctement votre travail de maintenance d’infrastructure de votre côté,  ben parfois, on tombe sur un gros os.

Si vous pensez que l’article ne sert à rien ou presque, allez jusqu’au post-scriptum 😉

Sur cette plateforme, les livraisons se font avec Helm. Depuis Helm 3, l’historique de livraison est stocké dans des secrets Kubernetes dans le namespace cible. Il faut savoir que lors d’une livraison, das les grandes lignes voilà ce que fait Helm :

  1. « Compiler » les manifestes à partir des templates et des variables fournies (fichiers values, --set)
  2. Comparer le résultat à celui de la précédente livraison
  3. Interroger l’API Server pour vérifier la présence des objets de cette précédente livraison
  4. Générer/supprimer/patcher les ressources concernées par des modifications, ce qui correspond de fait à la livraison
  5. Stocker le résultat dans un nouveau secret quand la livraison s’est déroulée correctement.

C’est à l’étape de l’interrogation qu’on peut se retrouver dans un sacré blocage avec ce genre d’erreur :

Error: UPGRADE FAILED: unable to build kubernetes objects from current release manifest: resource mapping not found for name: "ing-pwapriv" namespace: "" from "": no matches for kind "Ingress" in version "extensions/v1beta1"
ensure CRDs are installed first
helm.go:84: [debug] resource mapping not found for name: "ing-pwapriv" namespace: "" from "": no matches for kind "Ingress" in version "extensions/v1beta1"

Au début j’ai pas trop compris, parce que je pensais que c’était les manifestes de livraison de l’application qui posaient problème, mais à l’évidence, la vérité était ailleurs. Et de fait, on a découvert que si les manifestes avaient bien été rafraîchis via un script un an auparavant, le commit contenait la mention magique [skip-ci]. Ce qui ne serait pas plus choquant que ça si dans le même temps l’agence web n’avait pas pris plus d’un an pour mettre à jour le site web… Ce qui veut dire que la dernière livraison contient des manifestes dont l’apiGroup de l’ingressRule n’existent plus.

Et là, ça a été l’aventure pour trouver une solution. On pourrait simplement « désinstaller » le site, et le réinstaller. Mais je suis moyennement fan, parce que ça va créer une indisponibilité alors qu’on pourrait s’en passer, même si on est confiant sur la sécurité des données (le volume NFS est en retain et la BDD ne bougera pas), je cherche une alternative.

Déjà quand on découvre le format du secret. Par défaut, quand on tente de récupérer le YAML ou le JSON du secret, on est face à un contenu encodé en bas64; jusque là, c’est normal. Mais il fait une taille conséquente, et quand on le décode… Ben c’est toujours illisible, et pour cause : c’est encore du base64. Mais si on le décode une deuxième fois, on est face à un truc illisible, en tout cas pour l’instant.

La gueule du contenu…

Car le résultat, si on le met dans un fichier, est au format GZIP. Une fois décodé, on est face… à un JSON qui contient, de manière sérialisée, les manifestes au format YAML, y compris le coupable. Oui, sérialisée. Après avoir épongée la grosse goutte de sueur qui venait de perler, j’entame la marche pour corriger :

  1. Identifier la section de la loooongue chaîne de caractères correspondante au manifeste fautif,
  2. La copier/coller dans un éditeur
  3. Rétablir le formattage YAML (remplacer les \n par de vrais retours à la ligne)
  4. Modifier le manifeste (non seulement la ligne de l’apiGroup, mais toute la définition de la partie service)
  5. Reconvertir les retours à la ligne en \n pour retrouver la ligne unique
  6. Remplacer la section fautive par la version corrigée dans le JSON.

Il ne reste ensuite qu’à recompresser et double/base64-iser le contenu dans une copie corrigée du manifeste du secret, et donc écraser l’historique. Le plus gros challenge dans ce foutoir est de ne pas se planter sur l’indentation. Une fois qu’on a modifié le secret, si on se rate on est bon pour supprimer/recréer le secret.

Avant de retenter la livraison, on peut justement vérifier avec helm get manifest, s’il n’y a pas d’erreur on peut dès lors retenter la livraison qui devrait passer (en tout cas ça a été bon pour moi), même si techniquement on vient de réécrire l’histoire. Et comme ça on évite le gros downtime de la désinstallation/réinstallation du site.

L’intérêt de se faire « mal »

Déjà, j’en ai appris beaucoup sur le process de livraison de Helm, parce que j’avoue que depuis que je m’en sers, je m’étais plutôt concentré sur les problèmes liés au templating, le reste c’était un peu « ta gueule c’est magique ». Surtout, les deux heures qu’ils m’aura fallu pour trouver cette solution sans interruption de service (le site était toujours debout, rien n’avait été modifié), elles auront été rentabilisées car trois autres sites ont depuis été pris du même mal, et là, on parle d’une dizaine de minutes grand max pour chaque nouvelle occurrence rencontrée. Et la méthode fonctionne quelque soit le type d’objet problématique (parce qu’entre temps, y’a les CronJobs et les PDB qui se sont mis à râler aussi, sinon c’est pas drôle).

Il y a plein de réflexions à avoir pour mieux se prémunir de ce genre d’erreurs, mais elles sont pour beaucoup spécifiques aux choix de design fait par le client, notamment la séparation du code du site de la gestion de la livraison (build docker & helm). La difficulté de devoir gérer quasiment 200 projets sur cette plateforme fait qu’on ne peut pas non plus éviter complètement les erreurs, notamment sur les quelques projets qui ne respectent pas les modèles qu’on a mis en place.

Je n’ai pas non plus complètement sorti la méthode de nulle part. Mais l’article qui m’a guidé vers la solution finale est avare en détail, parle du networking.k8s.io/v1beta1 (donc moins chiant à remplacer), ce qui fait qu’il a juste scripté le bousin sans plus de cérémonie. Au moins là on a les explications du pourquoi du comment 🙂

Post Scriptum

Ouais, vu ce que j’ai à dire encore après tout ça, ça tient pas sur une seule ligne. Dès le titre, certains auront pensé « mais en fait c’est dans la doc », « mais y’a un plugin ». Il s’avère que oui, sur la même page, on a la méthode manuelle complète, et la mention d’un plugin « kubemapapis » qu’ont d’ailleurs découvert les collègues vietnamiens quelques semaines plus tard, se facilitant dès lors très fortement la vie. Mais il se trouve que j’ai démarré l’écriture de cet article il y a pratiquement deux ans (j’ai même changé d’entreprise entre temps, c’est dire), la documentation n’était pas nécessairement aussi explicite à l’époque, et je pense que malgré tout, ma recherche de la solution manuelle permettant de se pencher sur le format des informations manipulées par Helm reste pertinente 😉

Pu… 11 ans !

Simili vulgaire, ouais, ne serait-ce que parce que j’ai raté l’étape symbolique des 10 ans. Mais c’est un fait : le première article du blog date de Septembre 2013 (le 21 pour être précis, pile au début de l’automne); techniquement on peut même dire que j’ai raté les 11 ans. Il en aura vu passer des vertes et des pas mûres, de mes expérimentations d’alors, de mes changements de vie aussi, et même s’il est moins qu’actif ces dernières années, je n’ai pas non plus prévu de « débrancher la prise ».

Le calme plat de la technique

Il a beau avoir changé de tête et d’activité au fil des ans, une chose n’a pas changé : WordPress. Le vénérable moteur de blog écrit en PHP a vu passer de la version du langage de 5.4 à 7.4 actuellement, avec un futur assez proche à 8.1/8.2 en fonction de mon niveau de confiance (vu la gueule du dernier live – ah oui, j’en parle juste après-, c’est pas pour tout de suite le temps de tout régler, pas merci SELinux). Et je ne vais pas me risquer à lister les versions même de WordPress. Par contre, exit le vieillissant Debian 8, dont ce n’était même pas la première maison. Je ne me souviens plus bien si c’était la même version depuis le début, à savoir de Debian 5 à Debian 8, en passant par 6 et 7; dire que dans le billet des 5 ans je parlais de réinstaller une Debian 10… Passé d’Apache à Nginx, de PHP « module » à PHP-FPM, ajout de Redis pour calmer les ardeurs des entrées/sorties du vieillissant matériel… Toutes ces choses sont désormais sur un RockyLinux 9 parti pour durer. Bon ça c’était pour la technique.

Le blog a pour l’instant eu trois « visages », Responsive, Simone, Momentous, sachant que le deuxième a été l’occasion pour moi de commencer à expérimenter les thèmes enfants, méthode privilégiée pour apporter ses propres modifications à un thème déjà fourni, bien pratique quand mes propres compétences dans le développement sont plus que limitées. Bon par contre Momentous n’a pas été mis à jour depuis deux ans semble-t-il (ou alors c’est un effet de bord du problème de version de WordPress et/ou de prérequis, comme pour les plugins), donc même en ayant eu la flemme de changer de thème y’a trois ans il semble que je ne vais pas y couper. Reste à savoir quelle sera la nouvelle base. Déjà en 2013 j’avais visé une lecture agréable sur mobile en plus du PC, ça n’est pas près de changer. L’édition est plus problématique, mais pour la sécurité je ne peux plus utiliser l’application mobile, ceci étant dit, vu ma productivité quasi nulle on va dire que ce n’est pas très grave hein 🙂

En fait en parlant de base, plus ça va et plus je me pose la question de quitter WordPress. Je ne publie plus assez souvent pour le justifier, même sur les derniers articles les commentaires ne se sont pas bousculés, donc il y a réflexion à changer de moteur, et d’alléger le tout pour passer à… (roulement de tambours) un SSG, en français un générateur de site statique. Original, non ? Nouvelle méthode de construction, nouvelle méthode de production de contenus, une légèreté à toute épreuve (surtout que le serveur Hetzner est back par un SSD), y’a de quoi s’amuser très fortement en préparant une telle migration.

Une activité dans les chaussettes, mais ce n’est pas très grave non plus

11 ans… À l’époque j’avais utilisé une métaphore scolaire pour parler des 5 ans, on approcherait donc du collège. Et malgré un ralentissement déjà visible à l’époque, de plus en plus important, la diversité des sujets est resté un moteur de mon écriture, ce qui n’est pas pour me déplaire. Même si certains articles ont bien mieux vieillis que d’autres. Linux évidemment, administration système, streaming, auto-hébergement, hacking, hardware, jeu vidéo, cinéma, et j’en passe, je ne me suis pratiquement rien interdit comme sujets à partir du moment où j’avais, selon moi, un éclairage suffisamment rare ou intéressant à partager. Et s’il fallait avoir quelques regrets, c’est d’avoir perdu l’envie du partage des petites astuces du quotidien, et évidemment les gros articles de vulgarisation. Vu le niveau toujours abyssal de la population générale sur les questions numériques au sens large, il est toujours plus que nécessaire de tenter de les rattraper au vol.

Sauf qu’en ce moment, il n’y a pas plus de motivation que ces deux dernières années (trois/quatre même ?) pour marquer le coup, ni faire évoluer le contenu en profondeur. Mais le paysage de la publication a bien changé aussi. Même si j’ai toujours à cœur de partager ce que j’apprends, j’essaie quand même d’y mettre un intérêt : il y a dix ans, je ne trouvais pas beaucoup de ressources notamment en Français sur les sujets qui m’intéressaient, sans parler d’un environnement de vie plus que propice à la vulgarisation. Sur ce dernier point en particulier j’ai déjà eu l’occasion de m’exprimer sur mon abandon, qui a les mêmes motivations que pour le reste du contenu : en 10 ans, la donne a bien changé, je continue d’apprendre toujours plus en particulier dans ce que j’appelle la Galaxie Kubernetes (certains diront la Jungle, je valide aussi :D) mais je ne trouve aucun angle particulier qui justifierais de partager ce que j’ai déjà appris car d’autres s’en chargent généralement avant moi, et en Français qui plus est; si c’est pour faire le perroquet, donc du bruit pour pas grand chose de plus… Ce n’est pas comme si je courais après les statistiques, les avoir est toujours intéressant, mais ce n’est pas un critère déterminant. D’autant plus que Google continue de référencer correctement le blog malgré son activité plus que limitée, sans que je comprenne trop pourquoi ou comment vu que je n’ai jamais cherché à optimiser quoi que ce soit à ce sujet. Aussi mon expertise récente est basée sur des environnements d’hébergement que je n’ai pas forcément à cœur de partager avec vous, parce que j’estime que ce n’est pas nécessaire (Google Cloud, Microsoft Azure), car si je m’en contente au niveau professionnel, je n’ai aucunement envie de leur faire de la publicité auprès du « grand public ».

Activité du blog, allégorie

Un intérêt qui se porte ailleurs

Quand je dis que je ne trouve plus rien à partager, c’est presque faux. Je ne trouve plus d’intérêt pour l’instant à partager à l’écrit sur ce blog. Après un constat sur l’absence de réelles expérimentations dans le cadre de ma veille technique (ce qui peut être une grosse erreur sur certains points complexes), et devant la tristesse de bricoler tout seul dans mon coin, j’ai décidé de tenter l’aventure « Streaming », histoire d’amener un peu d’interactivité dans ces sessions de découvertes. Certains épisodes relèvent plus de la reprise de maintenance, un exercice que finalement on voit peu en termes de contenu. Il est facile de trouver un milliards de tuto pour déployer une techno, une plateforme, un service, mais rarement pour parler maintenance, mises à jour, réparation, migration.

Sujet intéressant que le streaming, que ce soit en termes techniques en local (setup, qualité de la composition de l’image), mais surtout en termes réseau. Après un week-end de préparation sans accroc sur YouTube (parce que j’avais déjà le compte), la première session un vendredi soir s’est avérée catastrophique en terme de stabilité réseau. Et j’aurai persisté deux autres sessions de plus avant de lâcher l’affaire pour créer un compte Twitch. C’est là qu’on voit la différence en termes d’investissement sur la performance réseau, en particulier en Europe, sans parler des pratiques de nos propres opérateurs en matière de routage et d’interconnexions. Le problème de Twitch, qui l’est un peu moins pour du contenu comme le mien, peu nerveux, c’est le retard sur les technologies d’encodage, d’autant plus en étant conservateur sur le débit max (ce qui n’est pas forcément une mauvaise chose dans l’absolu, notez bien). Je peux comprendre l’impasse faite sur H265 à cause du coût des licences pour les encodeurs et la diffusion, mais AV1 bordel ! Ceci dit, vu le retard sur les encodeurs matériels qui permettent un meilleur traitement temps réel, en partie lié à la pandémie, possible que ça ait pris du retard. Mais quand on voit que Google le supporte désormais, un tel immobilisme fait un peu chier de la part d’un géant comme Amazon (car oui, pour ceux qui débarqueraient, Twitch appartient à Amazon).

Le setup du soir où j’ai démonté mon laptop 🙂

Peertube ? J’expérimenterai certainement, forcément en auto-hébergement, pas forcément chez moi, parce que j’ai pas une bande passante de malade, mais pas forcément sur mon serveur allemand non plus, le volume réseau est pas illimité là-bas. Depuis que j’ai découvert ce formidable logiciel, je verses ma petite dîme mensuelle à Framasoft pour continuer de soutenir son développement. Et je réfléchis régulièrement à rapatrier les vidéos que j’avais produite pour le blog ailleurs que sur Youtube. Cette externalisation à l’époque permettait de palier aux ressources problématiques du serveur OVH de l’époque, sans parler de la conso de bande passante, ce dernier point reste toujours valide, mon serveur n’a « que » 20To de « data » (et 1 Gbps de bande passante). À la limite avec une instance non fédérée dédiée à l’hébergement des vidéos pour le blog, mais un autre problème survient très vite car rien n’est gratuit : le stockage ! La vidéo ça pompe pas du réseau par hasard, ça prend masse de place. En un an et demi, ça prend 300Go, le serveur au total n’a que 512Go.

TL;DR : tout va bien

En gros : j’ai mis un an et demi pour écrire cet article (oui, ça aurait du sortir pour les 10 ans), j’ai fait du chemin, je suis plus actif sur Twitch qu’ici, je ne fermerai pas le blog, par contre il ne restera pas éternellement dans cet état, à maintenir la mécanique en dessous sans que ça n’apporte au final grand chose pour tout le monde, moi en premier. Sa nécessaire mutation me redonnera peut-être goût à l’écriture, sait-on jamais. Comme d’habitude, il faudra que ça apporte un point de vue intéressant que je n’ai pas déjà vu, écrit ou entendu dix fois ailleurs 😉

❌