CI/CD : Forgejo Actions (pas GitLab - le dépôt vit sur Forgejo)
- .forgejo/workflows/ci.yml : build + test cargo (workspace), build npm (app/) sur toute PR/push ; déploiement (main uniquement) : build release, scp + redémarrage systemd sur tripoli-edge et tripoli-api. - Runner Forgejo Actions déployé en systemd (aucun n'existait avant) : socket Docker de l'hôte partagé avec les conteneurs de job plutôt qu'un docker:dind imbriqué et privilégié - plus léger sur une machine déjà partagée. - Déploiement via un compte système "deploy" sans privilège : peut seulement déposer un binaire dans /opt/deploy-staging et lancer un script sudo dédié qui n'accepte que "tripoli-edge" ou "tripoli-api" comme argument. Aucune clé root dans le pipeline. Clé SSH de déploiement générée sur le serveur, jamais passée par cette conversation - stockée directement comme secret Forgejo Actions par l'utilisateur. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
7802c59931
commit
c6aa75ba39
4 changed files with 143 additions and 0 deletions
73
.forgejo/workflows/ci.yml
Normal file
73
.forgejo/workflows/ci.yml
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-and-test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
# rustup plutôt que le paquet apt de l'image : l'image du job
|
||||||
|
# (node:20-bookworm, Debian stable) embarque un rustc trop ancien
|
||||||
|
# pour ce code (async fn dans les traits, édition 2021 récente).
|
||||||
|
- name: Installer Rust
|
||||||
|
run: |
|
||||||
|
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||||
|
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
|
||||||
|
|
||||||
|
- name: Dépendances système (sqlx/rustls)
|
||||||
|
run: apt-get update && apt-get install -y pkg-config libssl-dev build-essential
|
||||||
|
|
||||||
|
- name: cargo build (workspace)
|
||||||
|
run: cargo build --workspace
|
||||||
|
|
||||||
|
- name: cargo test (workspace)
|
||||||
|
run: cargo test --workspace
|
||||||
|
|
||||||
|
- name: npm build (app/)
|
||||||
|
working-directory: app
|
||||||
|
run: |
|
||||||
|
npm ci
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
deploy:
|
||||||
|
needs: build-and-test
|
||||||
|
if: github.ref == 'refs/heads/main'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Installer Rust
|
||||||
|
run: |
|
||||||
|
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||||
|
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
|
||||||
|
|
||||||
|
- name: Dépendances système
|
||||||
|
run: apt-get update && apt-get install -y pkg-config libssl-dev build-essential openssh-client rsync
|
||||||
|
|
||||||
|
- name: Build release (workspace)
|
||||||
|
run: cargo build --release --workspace
|
||||||
|
|
||||||
|
# Identité de déploiement dédiée, sans privilège : `deploy` ne peut
|
||||||
|
# que déposer un fichier dans /opt/deploy-staging et lancer le
|
||||||
|
# script sudo tripoli-deploy.sh, qui installe le binaire et
|
||||||
|
# redémarre EXACTEMENT le service nommé - rien de plus. Pas de clé
|
||||||
|
# root dans ce pipeline.
|
||||||
|
- name: Déployer tripoli-edge et tripoli-api
|
||||||
|
env:
|
||||||
|
DEPLOY_SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
|
||||||
|
DEPLOY_HOST: 65.109.142.188
|
||||||
|
run: |
|
||||||
|
mkdir -p ~/.ssh
|
||||||
|
echo "$DEPLOY_SSH_KEY" > ~/.ssh/deploy_key
|
||||||
|
chmod 600 ~/.ssh/deploy_key
|
||||||
|
ssh-keyscan -H "$DEPLOY_HOST" >> ~/.ssh/known_hosts 2>/dev/null
|
||||||
|
|
||||||
|
for service in tripoli-edge tripoli-api; do
|
||||||
|
scp -i ~/.ssh/deploy_key "target/release/$service" "deploy@$DEPLOY_HOST:/opt/deploy-staging/$service"
|
||||||
|
ssh -i ~/.ssh/deploy_key "deploy@$DEPLOY_HOST" sudo /usr/local/sbin/tripoli-deploy.sh "$service"
|
||||||
|
done
|
||||||
|
|
@ -34,6 +34,7 @@ tenté, non simulé.
|
||||||
| `tripoli-netctl` (Rust, Phase 4) | Fait — `tripoli-netctl/`, labo pédagogique (netns + veth via `rtnetlink`), testé sur le serveur (création, ping traversant la veth, destruction propre), **volontairement pas branché sur le trafic de prod** |
|
| `tripoli-netctl` (Rust, Phase 4) | Fait — `tripoli-netctl/`, labo pédagogique (netns + veth via `rtnetlink`), testé sur le serveur (création, ping traversant la veth, destruction propre), **volontairement pas branché sur le trafic de prod** |
|
||||||
| Observabilité (Phase 5) | Fait — pas de stack dédiée (réutilise le Prometheus/Grafana partagé existant), scrape tripoli-edge + alertes (down, disque) + dashboard versionné (`monitoring/`), vérifié via l'API Prometheus/Grafana |
|
| Observabilité (Phase 5) | Fait — pas de stack dédiée (réutilise le Prometheus/Grafana partagé existant), scrape tripoli-edge + alertes (down, disque) + dashboard versionné (`monitoring/`), vérifié via l'API Prometheus/Grafana |
|
||||||
| Bird/BGP (Phase 6, bonus) | Fait — `bird-lab/`, BGP+BFD entre 2 namespaces via la racine en transit (AS 65000/65010/65011), vérifié (sessions Established, BFD Up, propagation de route, détection de panne sur coupure de lien), puis proprement démonté |
|
| Bird/BGP (Phase 6, bonus) | Fait — `bird-lab/`, BGP+BFD entre 2 namespaces via la racine en transit (AS 65000/65010/65011), vérifié (sessions Established, BFD Up, propagation de route, détection de panne sur coupure de lien), puis proprement démonté |
|
||||||
|
| CI/CD | Fait — `.forgejo/workflows/ci.yml` (pas `.gitlab-ci.yml` : le dépôt vit sur Forgejo, pas GitLab). Runner Forgejo Actions déployé en systemd, déploiement via un compte `deploy` sans privilège (`ci/README.md`) |
|
||||||
|
|
||||||
## Dépôt
|
## Dépôt
|
||||||
|
|
||||||
|
|
|
||||||
45
ci/README.md
Normal file
45
ci/README.md
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
# CI/CD
|
||||||
|
|
||||||
|
Le brief demandait un `.gitlab-ci.yml`, mais ce dépôt vit sur **Forgejo**,
|
||||||
|
pas GitLab (voir le README racine) : l'équivalent réel est un workflow
|
||||||
|
**Forgejo Actions** (`.forgejo/workflows/ci.yml`), syntaxe compatible
|
||||||
|
GitHub Actions.
|
||||||
|
|
||||||
|
## Runner
|
||||||
|
|
||||||
|
Aucun runner Forgejo Actions n'existait sur ce serveur avant cette
|
||||||
|
phase. Déployé en systemd (`forgejo-runner.service`), conteneur
|
||||||
|
`code.forgejo.org/forgejo/runner:13.1`, socket Docker de l'hôte partagé
|
||||||
|
avec les conteneurs de job (`docker_host: "automount"` dans sa config)
|
||||||
|
plutôt qu'un `docker:dind` imbriqué et privilégié - plus léger sur une
|
||||||
|
machine à 3.7 Go déjà partagée, au prix d'une isolation plus faible
|
||||||
|
(un job de CI a accès au docker de l'hôte). Acceptable ici : instance
|
||||||
|
Forgejo mono-utilisateur, pas un service ouvert à des tiers.
|
||||||
|
|
||||||
|
## Déploiement : un compte dédié, pas root
|
||||||
|
|
||||||
|
Le pipeline ne se connecte jamais en root. Un utilisateur système
|
||||||
|
`deploy` existe uniquement pour :
|
||||||
|
1. recevoir le binaire compilé par `scp` dans `/opt/deploy-staging/` ;
|
||||||
|
2. lancer `sudo /usr/local/sbin/tripoli-deploy.sh <service>` - un
|
||||||
|
script sudo dédié (voir `/etc/sudoers.d/deploy` sur le serveur) qui
|
||||||
|
n'accepte que `tripoli-edge` ou `tripoli-api` comme argument, installe
|
||||||
|
le binaire à l'emplacement attendu et redémarre exactement ce
|
||||||
|
service. Rien d'autre n'est accessible via ce compte.
|
||||||
|
|
||||||
|
La clé privée correspondante est stockée comme secret Forgejo Actions
|
||||||
|
(`DEPLOY_SSH_KEY`), jamais dans ce dépôt.
|
||||||
|
|
||||||
|
## Workflow (`.forgejo/workflows/ci.yml`)
|
||||||
|
|
||||||
|
- `build-and-test` (toute PR et push) : installe Rust via rustup
|
||||||
|
(l'image du job, `node:20-bookworm`, a un rustc système trop ancien
|
||||||
|
pour ce code), `cargo build --workspace`, `cargo test --workspace`,
|
||||||
|
puis `npm ci && npm run build` pour le frontend.
|
||||||
|
- `deploy` (uniquement sur `main`, après succès du job précédent) :
|
||||||
|
build release, puis dépôt + redémarrage de `tripoli-edge` et
|
||||||
|
`tripoli-api` via le compte `deploy`.
|
||||||
|
|
||||||
|
`tripoli-netctl` et le labo `bird-lab` ne sont pas déployés par ce
|
||||||
|
pipeline : ce sont des outils de labo qu'on lance à la main, pas des
|
||||||
|
services qui tournent en continu.
|
||||||
24
ci/forgejo-runner.service
Normal file
24
ci/forgejo-runner.service
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
[Unit]
|
||||||
|
Description=forgejo-runner - runner Forgejo Actions pour tripoli.cloud
|
||||||
|
After=docker.service
|
||||||
|
Requires=docker.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=5
|
||||||
|
# --group-add : le gid du groupe "docker" sur l'hôte (docker.sock lui
|
||||||
|
# appartient) - sans ça, le processus (uid 1001, sans privilège) n'a
|
||||||
|
# pas le droit de s'en servir, même monté dans le conteneur.
|
||||||
|
ExecStart=/usr/bin/docker run --rm \
|
||||||
|
--name forgejo-runner \
|
||||||
|
--user 1001:1001 \
|
||||||
|
--group-add 983 \
|
||||||
|
-v /opt/forgejo-runner/data:/data \
|
||||||
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||||
|
code.forgejo.org/forgejo/runner:13.1 \
|
||||||
|
forgejo-runner daemon --config /data/config.yml
|
||||||
|
ExecStop=/usr/bin/docker stop forgejo-runner
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
Loading…
Add table
Add a link
Reference in a new issue