Aller au contenu

Déployer avec GitLab CI et redémarrer une commande

Cet exemple compile un projet Node.js, envoie le résultat par SFTP avec mot de passe et redémarre une commande Zaroz Cloud via l'API du Kernel. Le mot de passe SFTP transfère les fichiers ; un jeton API fin et séparé contrôle uniquement le redémarrage.

Tu peux consulter Get Order Status et Toggle Order dans la référence OpenAPI du Kernel.

Ce qu'il te faut

Depuis le tableau de bord de la commande, récupère l'hôte, le port, l'utilisateur, le mot de passe et le chemin SFTP.

Crée un jeton dédié avec ces deux autorisations, toutes deux limitées à cette commande précise :

  • orders.read — permet au job d'attendre l'arrêt du serveur.
  • orders.server.toggle — permet de l'arrêter et de le démarrer.

Ne lui donne aucun accès au terminal, à la facturation, aux bases de données ou à toutes les commandes.

Ajouter les variables CI/CD protégées

Dans GitLab, ouvre Settings → CI/CD → Variables et ajoute :

Variable Type Options conseillées
ZAROZ_API_TOKEN Variable Masked, protected
ZAROZ_ORDER_ID Variable Protected
ZAROZ_SFTP_HOST Variable Protected
ZAROZ_SFTP_PORT Variable Protected
ZAROZ_SFTP_USER Variable Protected
ZAROZ_SFTP_PATH Variable Protected
ZAROZ_SFTP_PASSWORD Variable Masked, protected
ZAROZ_SFTP_KNOWN_HOSTS File Protected

Génère ZAROZ_SFTP_KNOWN_HOSTS depuis une machine de confiance et vérifie l'empreinte de l'hôte. N'utilise pas StrictHostKeyChecking=no dans une pipeline de production.

Protège l'environnement de déploiement

Les variables protégées ne sont disponibles que pour les branches ou tags protégés. Protège aussi l'environnement de production pour qu'une merge request non fiable ne puisse pas extraire les identifiants.

Ajouter .gitlab-ci.yml

L'exemple suppose que npm run build produit un dossier dist/. Adapte la commande et le dossier à ton projet.

.gitlab-ci.yml
stages:
  - build
  - deploy

build:
  stage: build
  image: node:22-alpine
  script:
    - npm ci
    - npm run build
  artifacts:
    paths:
      - dist/
    expire_in: 1 hour

deploy-production:
  stage: deploy
  image: alpine:3.22
  needs:
    - job: build
      artifacts: true
  environment:
    name: production
  rules:
    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
  before_script:
    - apk add --no-cache curl jq lftp openssh-client
    - install -d -m 700 ~/.ssh
    - cp "$ZAROZ_SFTP_KNOWN_HOSTS" ~/.ssh/known_hosts
    - chmod 600 ~/.ssh/known_hosts
  script:
    - |
      export LFTP_PASSWORD="$ZAROZ_SFTP_PASSWORD"
      lftp -e "
        set cmd:fail-exit true;
        set sftp:connect-program \"ssh -a -x -o StrictHostKeyChecking=yes\";
        open --env-password --user \"$ZAROZ_SFTP_USER\" -p \"$ZAROZ_SFTP_PORT\" \"sftp://$ZAROZ_SFTP_HOST\";
        mirror --reverse --verbose --no-perms dist/ \"$ZAROZ_SFTP_PATH\";
        bye
      "
      unset LFTP_PASSWORD
    - |
      api() {
        curl --fail-with-body --silent --show-error \
          -H "Authorization: Bearer $ZAROZ_API_TOKEN" \
          -H 'Accept: application/json' \
          "$@"
      }

      API="https://kernel.zaroz.cloud/api/v1/orders/$ZAROZ_ORDER_ID"

      api -X POST \
        -H 'Content-Type: application/json' \
        -d '{"active":false}' \
        "$API/toggle"

      stopped=false
      for attempt in $(seq 1 30); do
        state="$(api "$API/status" | jq -r '.state')"
        echo "Waiting for stop: $state"

        case "$state" in
          Suspended|NotProvisioned)
            stopped=true
            break
            ;;
          Failed)
            echo "The order entered Failed state; refusing to start it automatically"
            exit 1
            ;;
        esac

        sleep 2
      done

      if [ "$stopped" != true ]; then
        echo "The order did not stop within 60 seconds"
        exit 1
      fi

      api -X POST \
        -H 'Content-Type: application/json' \
        -d '{"active":true}' \
        "$API/toggle"

      echo "Deployment uploaded and start requested"

Pourquoi attendre entre l'arrêt et le démarrage

L'endpoint change l'état souhaité, mais Kubernetes a encore besoin de temps pour arrêter la charge. Redémarrer immédiatement peut entrer en concurrence avec la terminaison. Lire l'état évite cette course et donne une erreur claire dans GitLab.

Si ton logiciel sait se recharger proprement, préfère cette méthode. Un cycle arrêt/démarrage interrompt les joueurs ou utilisateurs actifs.

Adapter l'envoi

Le lot SFTP écrase les fichiers correspondants, mais ne supprime pas les fichiers distants absents de dist/. Pour un miroir exact, déploie dans des dossiers versionnés et bascule la version active depuis le script de démarrage de ton application.

N'envoie jamais de secrets depuis le dépôt. Garde-les dans le tableau de bord Zaroz ou dans des variables CI/CD protégées.

Dépannage

L'API renvoie 403

Vérifie que les deux autorisations ciblent l'UUID de ZAROZ_ORDER_ID et que le propriétaire du jeton peut encore contrôler la commande.

SFTP signale une erreur de clé d'hôte

Mets ZAROZ_SFTP_KNOWN_HOSTS à jour uniquement après avoir vérifié la nouvelle empreinte par un canal de confiance.

L'envoi fonctionne, mais pas le redémarrage

Vérifie orders.server.toggle en plus de orders.read, ainsi que l'expiration du jeton.

Le serveur reste en Stopping plus de 60 secondes

Laisse le job échouer au lieu de basculer l'état en boucle. Inspecte la commande dans le tableau de bord et relance après l'arrêt complet.