Deux runners en capacity:1 tuent les jobs par famine — et leurs versions divergent (pi1 v0.3.1 / pi3 v0.2.13) #50

Open
opened 2026-07-30 08:51:12 +02:00 by arcodange · 1 comment
Owner

Relevé en mesurant la CI de kadans les 29-30/07. Deux défauts distincts, même cause racine : la flotte de runners est sous-dimensionnée et hétérogène.

1. La famine de créneau tue des jobs, et son rouge ressemble à un bug

Gitea marque une tâche running dès qu'il l'attribue à un runner, avant exécution. Si rien ne remonte pendant ZOMBIE_TASK_TIMEOUT10 minutes, le défaut, car l'app.ini de Gitea 1.25.5 n'a pas de section [actions] — il la tue :

conclusion : failure
journal     : HTTP 500 … log.zst: file does not exist   ← il n'a JAMAIS rien produit
durée       : ~640-655 s                                 ← la signature du délai

Constaté : trois jobs de workflows différents (gates-navigateur, build-and-push-image, build-storybook) tués au même instant (22:37:29 UTC), en 643/648/652 s, zéro journal.

⚠ Et ce n'était pas une surcharge : le journal du runner montre qu'il n'en exécutait qu'un seul à la fois (tâches 3374, 3376, 3378, espacées), capacity: 1 bien respecté — vérifié dans le conteneur. C'est l'attente qui les a tués, pas la charge.

Pourquoi ça arrive facilement

Un push sur main de kadans déclenche trois workflows (ci.yml + storybook.yml + dockerimage.yaml), soit jusqu'à 4 jobs. Avec 2 runners × capacity 1 et des jobs de 5 à 11 minutes, deux merges rapprochés suffisent à faire mourir de faim tout ce qui suit. Les runners sont partagés par toute la forge, donc le problème n'est pas propre à kadans.

2. Les deux runners ne sont pas interchangeables

Machine act_runner Particularité
pi1 v0.3.1 porte le control-plane k3s
pi3 v0.2.13

Trois versions mineures d'écart sur des machines censées être équivalentes. Effets mesurés :

  • le même job, sur la même image, a mis 511 s sur pi1 et 397 s sur pi3114 s d'écart imputables à la machine ;
  • pi3 (v0.2.13) a mal lu la définition d'un job dont il dépendait : 'runs-on' key not defined, puis No steps found.

Conséquence méthodologique : sur cette flotte, un écart de moins de ~100 s n'est pas mesurable en un seul run. Toute optimisation de CI chiffrée sans répéter la mesure et noter la machine est ininterprétable. (J'en ai fait les frais : quatre annonces de gain successivement réduites par la mesure.)

Pistes, par ordre de sûreté décroissante

  1. Un troisième runner. Le plus sûr. Il éloigne la famine sans toucher aux plafonds mémoire.
  2. Aligner les versions d'act_runner (gitea/act_runner:latest est déjà l'image ; pi3 n'a simplement pas été recréé depuis longtemps — un passage de 03_cicd.yml suffirait, mais ⚠ il tue les jobs en vol, cf. plus bas).
  3. Relever ZOMBIE_TASK_TIMEOUT dans app.ini (rôle deploy_gitea, app.ini.j2). Traite le symptôme, pas la cause : les jobs attendraient plus longtemps au lieu de mourir.
  4. capacity: 2 — ⚠ déconseillé : le commentaire du playbook prévient que les hôtes (8 Go, control-plane k3s sur pi1) ne survivent pas à deux builds lourds simultanés, et l'incident du 2026-07-23 le documente (nuxt generate à 3,5 Go RSS → load 150 → ingress et API k3s morts).

⚠ Précaution pour toute intervention

03_cicd.yml recrée les conteneurs de runner (loop: [absent, present]) : il tue les jobs en vol, qui deviennent failure avec journaux perdus. Vérifier qu'aucun run n'est queued ni in_progress juste avant — et se rappeler qu'un merge est un déclencheur, donc que « plus rien ne tourne » se vérifie après le dernier merge, pas avant.

Lié

  • arcodange/kadans #187 (où placer un garde-fou e2e — cette contrainte pèse sur l'arbitrage) et #198 (test flaky — à ne pas confondre avec une famine).
  • Doctrine consignée dans arcodange/kadans CLAUDE.md (PR #232).

🤖 Generated with Claude Code

Relevé en mesurant la CI de `kadans` les 29-30/07. Deux défauts distincts, même cause racine : **la flotte de runners est sous-dimensionnée et hétérogène**. ## 1. La famine de créneau tue des jobs, et son rouge ressemble à un bug Gitea marque une tâche `running` **dès qu'il l'attribue** à un runner, *avant* exécution. Si rien ne remonte pendant **`ZOMBIE_TASK_TIMEOUT`** — **10 minutes**, le défaut, car l'`app.ini` de Gitea 1.25.5 **n'a pas de section `[actions]`** — il la tue : ``` conclusion : failure journal : HTTP 500 … log.zst: file does not exist ← il n'a JAMAIS rien produit durée : ~640-655 s ← la signature du délai ``` **Constaté** : trois jobs de workflows **différents** (`gates-navigateur`, `build-and-push-image`, `build-storybook`) tués **au même instant** (22:37:29 UTC), en 643/648/652 s, zéro journal. ⚠ Et ce n'était **pas** une surcharge : le journal du runner montre qu'il n'en exécutait **qu'un seul à la fois** (tâches 3374, 3376, 3378, espacées), `capacity: 1` bien respecté — vérifié *dans* le conteneur. **C'est l'attente qui les a tués, pas la charge.** ### Pourquoi ça arrive facilement Un push sur `main` de `kadans` déclenche **trois workflows** (`ci.yml` + `storybook.yml` + `dockerimage.yaml`), soit jusqu'à **4 jobs**. Avec **2 runners × capacity 1** et des jobs de **5 à 11 minutes**, deux merges rapprochés suffisent à faire mourir de faim tout ce qui suit. Les runners sont **partagés par toute la forge**, donc le problème n'est pas propre à `kadans`. ## 2. Les deux runners ne sont pas interchangeables | Machine | act_runner | Particularité | |---|---|---| | **pi1** | **v0.3.1** | porte le **control-plane k3s** | | **pi3** | **v0.2.13** | — | Trois versions mineures d'écart sur des machines censées être équivalentes. Effets mesurés : - le **même** job, sur la **même** image, a mis **511 s sur pi1** et **397 s sur pi3** — **114 s d'écart imputables à la machine** ; - pi3 (v0.2.13) a mal lu la définition d'un job dont il dépendait : `'runs-on' key not defined`, puis `No steps found`. ⚠ **Conséquence méthodologique** : sur cette flotte, **un écart de moins de ~100 s n'est pas mesurable en un seul run**. Toute optimisation de CI chiffrée sans répéter la mesure *et* noter la machine est ininterprétable. (J'en ai fait les frais : quatre annonces de gain successivement réduites par la mesure.) ## Pistes, par ordre de sûreté décroissante 1. **Un troisième runner.** Le plus sûr. Il éloigne la famine sans toucher aux plafonds mémoire. 2. **Aligner les versions d'act_runner** (`gitea/act_runner:latest` est déjà l'image ; pi3 n'a simplement pas été recréé depuis longtemps — un passage de `03_cicd.yml` suffirait, mais ⚠ il **tue les jobs en vol**, cf. plus bas). 3. **Relever `ZOMBIE_TASK_TIMEOUT`** dans `app.ini` (rôle `deploy_gitea`, `app.ini.j2`). Traite le symptôme, pas la cause : les jobs attendraient plus longtemps au lieu de mourir. 4. **`capacity: 2`** — ⚠ **déconseillé** : le commentaire du playbook prévient que les hôtes (8 Go, control-plane k3s sur pi1) **ne survivent pas à deux builds lourds simultanés**, et l'incident du 2026-07-23 le documente (`nuxt generate` à 3,5 Go RSS → load 150 → ingress et API k3s morts). ## ⚠ Précaution pour toute intervention `03_cicd.yml` recrée les conteneurs de runner (`loop: [absent, present]`) : **il tue les jobs en vol**, qui deviennent `failure` avec journaux perdus. Vérifier qu'aucun run n'est `queued` ni `in_progress` **juste avant** — et se rappeler qu'**un merge est un déclencheur**, donc que « plus rien ne tourne » se vérifie *après* le dernier merge, pas avant. ## Lié - `arcodange/kadans` #187 (où placer un garde-fou e2e — cette contrainte pèse sur l'arbitrage) et #198 (test flaky — à ne pas confondre avec une famine). - Doctrine consignée dans `arcodange/kadans` `CLAUDE.md` (PR #232). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Author
Owner

Moitié « versions divergentes » : réglée — et une hypothèse à corriger

Migration faite le 2026-07-30 (PR #51) : Gitea 1.25.5 → 1.27.1 et les deux runners act_runner v0.3.1 / v0.2.13 → gitea/runner v2.3.0.

L'image du runner avait changé de nom : gitea/act_runner est gelée à 0.6.1, le successeur officiel est gitea/runner (binaire renommé act_runnergitea-runner). Épingler l'ancien nom nous aurait enfermés dans une image morte.

La cause de la divergence était gitea/act_runner:**latest** + pull: **missing** — un tag flottant jamais rafraîchi : chaque hôte gardait ce que « latest » voulait dire à son premier pull. La version est désormais épinglée dans inventory/group_vars/all/gitea.yml.

Chaîne validée de bout en bout : gates 324 s (pi3) et gates-navigateur 553 s (pi1), les deux verts, image précuite et garde anti-dérive comprises.

⚠ Mais l'écart entre machines n'était PAS dû aux versions

C'est la correction qui compte pour la suite de ce ticket. J'avais attribué les 114 s d'écart à la divergence d'act_runner. Faux — mesures de gates-navigateur, toutes avec l'image précuite :

Machine Runner Durée
pi3 v0.2.13 397 s
pi1 v0.3.1 511 s
pi1 v2.3.0 553 s

pi1 reste le lent, versions alignées ou non. L'explication tient à la machine : pi1 porte le control-plane k3s, et ça se paie sur chaque job.

Ce que ça change pour les pistes

  • Piste 2 (aligner les versions) : faite, et elle supprime une source de bruit de diagnostic — mais elle ne rééquilibre pas les machines. Ne pas en attendre de gain de durée.
  • Piste 1 (troisième runner) : reste la meilleure, et l'argument se précise — l'idéal serait un runner qui ne porte pas le control-plane k3s, ou de sortir la CI de pi1.
  • Piste 4 (capacity: 2) : toujours déconseillée, et pi1 encore moins que pi3.

La famine de créneau, elle, est intacte

ZOMBIE_TASK_TIMEOUT reste à 10 min par défaut : vérifié, app.ini n'a toujours pas de section [actions] en 1.27.1. Un push sur main de kadans déclenchait 4 jobs pour 2 créneaux — kadans#234 le ramène à 3 en filtrant Storybook, ce qui soulage sans rien couvrir de moins.

🤖 Generated with Claude Code

## ✅ Moitié « versions divergentes » : réglée — et une hypothèse à corriger Migration faite le 2026-07-30 (PR #51) : **Gitea 1.25.5 → 1.27.1** et les deux runners **`act_runner` v0.3.1 / v0.2.13 → `gitea/runner` v2.3.0**. ⚠ **L'image du runner avait changé de nom** : `gitea/act_runner` est **gelée à 0.6.1**, le successeur officiel est **`gitea/runner`** (binaire renommé `act_runner` → `gitea-runner`). Épingler l'ancien nom nous aurait enfermés dans une image morte. **La cause de la divergence** était `gitea/act_runner:**latest**` + `pull: **missing**` — un tag flottant **jamais rafraîchi** : chaque hôte gardait ce que « latest » voulait dire à son premier pull. La version est désormais **épinglée** dans `inventory/group_vars/all/gitea.yml`. Chaîne validée de bout en bout : `gates` **324 s** (pi3) et `gates-navigateur` **553 s** (pi1), les deux verts, image précuite et garde anti-dérive comprises. ## ⚠ Mais l'écart entre machines n'était PAS dû aux versions C'est la correction qui compte pour la suite de ce ticket. J'avais attribué les **114 s** d'écart à la divergence d'act_runner. **Faux** — mesures de `gates-navigateur`, toutes avec l'image précuite : | Machine | Runner | Durée | |---|---|---:| | **pi3** | v0.2.13 | **397 s** | | **pi1** | v0.3.1 | 511 s | | **pi1** | **v2.3.0** | **553 s** | **pi1 reste le lent, versions alignées ou non.** L'explication tient à la machine : **pi1 porte le control-plane k3s**, et ça se paie sur chaque job. ### Ce que ça change pour les pistes - **Piste 2 (aligner les versions)** : faite, et elle **supprime une source de bruit de diagnostic** — mais elle **ne rééquilibre pas** les machines. Ne pas en attendre de gain de durée. - **Piste 1 (troisième runner)** : reste la meilleure, et l'argument se précise — l'idéal serait un runner **qui ne porte pas le control-plane k3s**, ou de sortir la CI de pi1. - **Piste 4 (`capacity: 2`)** : toujours déconseillée, et pi1 encore moins que pi3. ### La famine de créneau, elle, est intacte `ZOMBIE_TASK_TIMEOUT` reste à **10 min** par défaut : vérifié, `app.ini` n'a **toujours pas** de section `[actions]` en 1.27.1. Un push sur `main` de `kadans` déclenchait 4 jobs pour 2 créneaux — [kadans#234](https://gitea.arcodange.lab/arcodange/kadans/pulls/234) le ramène à 3 en filtrant Storybook, ce qui soulage sans rien couvrir de moins. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: arcodange-org/factory#50