feat(cicd) — rétention du registre de paquets Gitea : 935 images kadans avaient rempli la carte SD de pi2 #60
@@ -45,6 +45,14 @@ gitea:
|
||||
GITEA__server__SSH_PORT: 2222
|
||||
GITEA__server__SSH_DOMAIN: "{{ hostvars[groups.gitea[0]]['preferred_ip'] }}"
|
||||
GITEA__server__SSH_LISTEN_PORT: 22
|
||||
# Rétention des artefacts de CI (défaut Gitea : 90 jours). Les
|
||||
# artefacts pesaient 3,3 Go sur la carte SD de pi2 le 2026-09-19.
|
||||
GITEA__actions__ARTIFACT_RETENTION_DAYS: 14
|
||||
# Déjà en service, mais posé À LA MAIN dans data/gitea/conf/app.ini :
|
||||
# un job ATTRIBUÉ à un runner mais coincé derrière sa file (capacity 1)
|
||||
# était tué au défaut de 10m — failure sans journal, ~650 s (mesuré les
|
||||
# 30/07 et 03/08). 60m couvre une file de plusieurs jobs longs.
|
||||
GITEA__actions__ZOMBIE_TASK_TIMEOUT: 60m
|
||||
GITEA_server__DOMAIN: localhost
|
||||
GITEA_server__HTTP_PORT: 3000
|
||||
GITEA_server__ROOT_URL: https://gitea.arcodange.lab/
|
||||
@@ -70,3 +78,26 @@ gitea:
|
||||
limits:
|
||||
cpus: "1.5"
|
||||
memory: 1536M
|
||||
# Rétention du registre de paquets — appliquée par le rôle
|
||||
# arcodange.factory.gitea_packages_retention (playbook 03_cicd.yml), exécutée
|
||||
# par Gitea chaque nuit (tâche `cleanup_packages`).
|
||||
#
|
||||
# Incident 2026-09-18 → 19 : la carte SD de pi2 (117 Go) à 94 %, DiskPressure,
|
||||
# pgbouncer expulsé et non replanifiable (épinglé sur pi2), la LAPI CrowdSec en
|
||||
# CrashLoop faute de base, le bouncer Traefik en erreur → kadans.arcodange.fr et
|
||||
# lien.arcodange.fr en 404. Cause : le registre gardait 935 versions de l'image
|
||||
# `arcodange/kadans` (61 Go), une par push de CI depuis le 14/07, ~0,9 Go/jour.
|
||||
#
|
||||
# ⚠ On ne vise QUE les empreintes `sha256:` sans étiquette des images kadans :
|
||||
# jamais un nom d'étiquette. `postgres-postgis`, `*-build-cache`, `cms-deps`…
|
||||
# portent des étiquettes qui servent de BASE à d'autres builds — une règle large
|
||||
# les emporterait au premier passage. `latest` et les manifestes encore
|
||||
# référencés par un index étiqueté sont sautés par Gitea lui-même.
|
||||
# 7 jours = de quoi revenir à l'image d'avant (ArgoCD épingle un digest).
|
||||
gitea_packages_retention_rules:
|
||||
- owner: arcodange
|
||||
type: container
|
||||
keep_count: 20
|
||||
remove_days: 7
|
||||
remove_pattern: '^kadans(-[a-z-]+)?/sha256:[0-9a-f]{64}$'
|
||||
match_full_name: true
|
||||
|
||||
@@ -208,3 +208,12 @@
|
||||
loop: ["absent", "present"]
|
||||
loop_control:
|
||||
loop_var: docker_compose_down_then_up
|
||||
|
||||
# Rétention du registre de paquets (règles dans group_vars/gitea/gitea.yml).
|
||||
# Sur l'hôte de Gitea, où vit aussi sa base : le rôle ne fait que DÉCLARER les
|
||||
# règles, Gitea les applique chaque nuit — aucun redémarrage de la forge.
|
||||
- name: Déclarer la rétention du registre de paquets Gitea
|
||||
hosts: gitea
|
||||
gather_facts: false
|
||||
roles:
|
||||
- arcodange.factory.gitea_packages_retention
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
---
|
||||
# Règles de nettoyage du registre de paquets de Gitea, une par (propriétaire, type).
|
||||
# Gitea les APPLIQUE lui-même, chaque nuit, dans sa tâche `cleanup_packages`
|
||||
# (active par défaut, @midnight) — ce rôle ne supprime rien : il les DÉCLARE.
|
||||
#
|
||||
# Champs (mêmes noms que la table `package_cleanup_rule` et que le formulaire
|
||||
# Réglages → Paquets → Règles de nettoyage) :
|
||||
# owner : utilisateur ou organisation
|
||||
# type : container, npm, generic…
|
||||
# keep_count : les N versions les plus récentes de CHAQUE paquet sont gardées
|
||||
# keep_pattern : regex (RE2, minuscules) — une version qui la vérifie est gardée
|
||||
# remove_days : seules les versions plus vieilles que N jours partent
|
||||
# remove_pattern : regex — SEULES les versions qui la vérifient peuvent partir
|
||||
# match_full_name: les regex voient « <paquet>/<version> » au lieu de « <version> »
|
||||
# Le moteur de Gitea saute de lui-même, pour les conteneurs, l'étiquette `latest`
|
||||
# et tout manifeste encore RÉFÉRENCÉ par un autre (les enfants d'un index étiqueté).
|
||||
gitea_packages_retention_rules: []
|
||||
|
||||
# Conteneur PostgreSQL qui porte la base de Gitea (docker compose, même hôte).
|
||||
gitea_packages_retention_postgres_container: postgres
|
||||
gitea_packages_retention_postgres_user: postgres
|
||||
gitea_packages_retention_database: gitea
|
||||
@@ -0,0 +1,35 @@
|
||||
-- Déclare (ou met à jour) UNE règle de nettoyage. Variables psql :
|
||||
-- owner type keep_count keep_pattern remove_days remove_pattern match_full_name
|
||||
-- Rend « inchangee », « ecrite », ou « proprietaire-absent ».
|
||||
\set ON_ERROR_STOP 1
|
||||
with proprietaire as (
|
||||
select id from "user" where lower_name = lower(:'owner')
|
||||
), voulue as (
|
||||
select p.id as owner_id, :'type'::varchar as type, true as enabled,
|
||||
:'keep_count'::int as keep_count, :'keep_pattern'::varchar as keep_pattern,
|
||||
:'remove_days'::int as remove_days, :'remove_pattern'::varchar as remove_pattern,
|
||||
:'match_full_name'::boolean as match_full_name
|
||||
from proprietaire p
|
||||
), deja as (
|
||||
select 1 from package_cleanup_rule r join voulue v using (owner_id, type)
|
||||
where r.enabled and r.keep_count = v.keep_count and r.keep_pattern = v.keep_pattern
|
||||
and r.remove_days = v.remove_days and r.remove_pattern = v.remove_pattern
|
||||
and r.match_full_name = v.match_full_name
|
||||
), ecrite as (
|
||||
insert into package_cleanup_rule
|
||||
(enabled, owner_id, type, keep_count, keep_pattern, remove_days, remove_pattern,
|
||||
match_full_name, created_unix, updated_unix)
|
||||
select enabled, owner_id, type, keep_count, keep_pattern, remove_days, remove_pattern,
|
||||
match_full_name, extract(epoch from now())::bigint, extract(epoch from now())::bigint
|
||||
from voulue where not exists (select 1 from deja)
|
||||
on conflict (owner_id, type) do update set
|
||||
enabled = excluded.enabled, keep_count = excluded.keep_count,
|
||||
keep_pattern = excluded.keep_pattern, remove_days = excluded.remove_days,
|
||||
remove_pattern = excluded.remove_pattern, match_full_name = excluded.match_full_name,
|
||||
updated_unix = excluded.updated_unix
|
||||
returning 1
|
||||
)
|
||||
select case
|
||||
when not exists (select 1 from proprietaire) then 'proprietaire-absent'
|
||||
when exists (select 1 from ecrite) then 'ecrite'
|
||||
else 'inchangee' end;
|
||||
@@ -0,0 +1,72 @@
|
||||
---
|
||||
# Pourquoi la BASE et pas l'API : Gitea 1.27 n'expose AUCUNE route pour les règles
|
||||
# de nettoyage (le swagger n'a que /packages/{owner}/…). Le seul autre chemin est
|
||||
# le formulaire web (session + CSRF). La table est petite et stable ; on vérifie
|
||||
# quand même son schéma avant d'écrire, pour qu'une montée de Gitea qui la change
|
||||
# fasse ÉCHOUER ce rôle au lieu d'écrire à côté.
|
||||
- name: Vérifier le schéma de package_cleanup_rule
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- docker
|
||||
- exec
|
||||
- "{{ gitea_packages_retention_postgres_container }}"
|
||||
- psql
|
||||
- -U
|
||||
- "{{ gitea_packages_retention_postgres_user }}"
|
||||
- -d
|
||||
- "{{ gitea_packages_retention_database }}"
|
||||
- -At
|
||||
- -c
|
||||
- >-
|
||||
select string_agg(column_name, ',' order by column_name)
|
||||
from information_schema.columns where table_name = 'package_cleanup_rule'
|
||||
register: gitea_packages_retention_schema
|
||||
changed_when: false
|
||||
|
||||
- name: Exiger les colonnes attendues
|
||||
ansible.builtin.assert:
|
||||
that: >-
|
||||
gitea_packages_retention_schema.stdout.split(',') | sort ==
|
||||
['created_unix', 'enabled', 'id', 'keep_count', 'keep_pattern', 'match_full_name',
|
||||
'owner_id', 'remove_days', 'remove_pattern', 'type', 'updated_unix']
|
||||
fail_msg: >-
|
||||
package_cleanup_rule a changé de forme ({{ gitea_packages_retention_schema.stdout }}) —
|
||||
relire le modèle de Gitea avant de réécrire files/regle.sql.
|
||||
|
||||
- name: Déclarer les règles de nettoyage
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- docker
|
||||
- exec
|
||||
- -i
|
||||
- "{{ gitea_packages_retention_postgres_container }}"
|
||||
- psql
|
||||
- -U
|
||||
- "{{ gitea_packages_retention_postgres_user }}"
|
||||
- -d
|
||||
- "{{ gitea_packages_retention_database }}"
|
||||
- -At
|
||||
- -v
|
||||
- "owner={{ regle.owner }}"
|
||||
- -v
|
||||
- "type={{ regle.type }}"
|
||||
- -v
|
||||
- "keep_count={{ regle.keep_count | default(0) | int }}"
|
||||
- -v
|
||||
- "keep_pattern={{ regle.keep_pattern | default('') }}"
|
||||
- -v
|
||||
- "remove_days={{ regle.remove_days | default(0) | int }}"
|
||||
- -v
|
||||
- "remove_pattern={{ regle.remove_pattern | default('') }}"
|
||||
- -v
|
||||
- "match_full_name={{ regle.match_full_name | default(false) | bool | lower }}"
|
||||
stdin: "{{ lookup('ansible.builtin.file', 'regle.sql') }}"
|
||||
loop: "{{ gitea_packages_retention_rules }}"
|
||||
loop_control:
|
||||
loop_var: regle
|
||||
label: "{{ regle.owner }}/{{ regle.type }}"
|
||||
register: gitea_packages_retention_ecriture
|
||||
changed_when: gitea_packages_retention_ecriture.stdout == 'ecrite'
|
||||
failed_when: >-
|
||||
gitea_packages_retention_ecriture.rc != 0
|
||||
or gitea_packages_retention_ecriture.stdout not in ['ecrite', 'inchangee']
|
||||
Reference in New Issue
Block a user