Deploy-Job
Geheimnisse gehören nicht ins Repository
Abschnitt betitelt „Geheimnisse gehören nicht ins Repository“Der Deploy-Job braucht den privaten Schlüssel, den Hostnamen, Benutzer und Pfad. Nichts davon darf in .gitlab-ci.yml stehen, die Datei ist für alle lesbar. GitLab hat dafür CI-Variablen: Werte, die der Runner dem Job als Umgebungsvariablen oder als Dateien mitgibt.
Unter Settings → CI/CD → Variables legst du an:
| Variable | Typ | Inhalt |
|---|---|---|
SITE_URL |
Variable | Öffentliche URL, zum Beispiel http://10.0.0.42 |
DEPLOY_HOST |
Variable | Hostname oder IP der VM |
DEPLOY_USER |
Variable | SSH-Benutzer |
DEPLOY_PATH |
Variable | /var/www/html/, mit Schrägstrich am Ende |
DEPLOY_SSH_KEY |
File | Inhalt von ~/.ssh/plattform_deploy |
DEPLOY_KNOWN_HOSTS |
File | Ausgabe von ssh-keyscan DEPLOY_HOST |
Zwei Typen: Bei Variable steht der Wert in der Umgebungsvariable. Bei File schreibt der Runner den Wert in eine temporäre Datei und die Umgebungsvariable enthält deren Pfad. Für Schlüssel ist File richtig, weil sie mehrzeilig sind und als Datei gebraucht werden.
Dazu zwei Häkchen:
- Protected: Die Variable wird nur an Pipelines auf geschützten Branches übergeben. Für alles, was deployen kann, Pflicht. Sonst könnte jemand auf einem Branch
testeinen Job schreiben, der den Schlüssel ausgibt. - Masked: GitLab ersetzt den Wert im Job-Log durch
[MASKED]. Funktioniert nur für einzeilige Werte ohne Leerzeichen, also für Host, User, Pfad, nicht für den Schlüssel.
Der Job
Abschnitt betitelt „Der Job“deploy: stage: deploy image: alpine:3.21 needs: - build before_script: - apk add --no-cache rsync openssh-client - mkdir -p ~/.ssh && chmod 700 ~/.ssh # Das GitLab-Formular schneidet beim Einfügen gern den letzten Zeilenumbruch ab und # Windows-Zeilenenden schleichen sich ein; beides macht den Schlüssel unlesbar # («error in libcrypto»). Darum CR entfernen und einen Zeilenumbruch am Ende sicherstellen. - '{ tr -d "\r" < "$DEPLOY_SSH_KEY"; echo; } | sed "/^$/d" > ~/.ssh/id_ed25519 && chmod 600 ~/.ssh/id_ed25519' - '{ tr -d "\r" < "$DEPLOY_KNOWN_HOSTS"; echo; } | sed "/^$/d" > ~/.ssh/known_hosts && chmod 644 ~/.ssh/known_hosts' - ssh-keygen -y -f ~/.ssh/id_ed25519 > /dev/null || { echo "DEPLOY_SSH_KEY ist kein gültiger privater Schlüssel."; exit 1; } script: - rsync -az --delete --exclude='.well-known' dist/ "$DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_PATH" environment: name: production url: $SITE_URL rules: - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCHZeile für Zeile:
needs: [build]: Der Job bekommt das Artefaktdist/des Build-Jobs und startet, sobald der fertig ist.apk add: Alpine hat weder rsync noch ssh. Jeder Job fängt bei null an.- Schlüssel und known_hosts werden aus den File-Variablen nach
~/.ssh/geschrieben, mit den Rechten, die ssh verlangt (600 für den Schlüssel, sonst verweigert ssh ihn). Die etwas umständliche Zeile mittrundsedist eine Lehre aus dem ersten Deploy dieser Plattform, siehe Fehlersuche. ssh-keygen -yliest den privaten Schlüssel und gibt den öffentlichen aus. Scheitert das, ist der Schlüssel kaputt, und der Job bricht mit einer klaren Meldung ab statt später mit einem kryptischen rsync-Fehler.rsyncwie von Hand, nur dass ssh den Schlüssel unter dem Standardnamen~/.ssh/id_ed25519findet und kein-enötig ist.environment: GitLab merkt sich unter Operate → Environments, welcher Commit wann auf «production» deployt wurde, mit Link auf die Seite.rules: Nur aufmain. Merge-Request-Pipelines und andere Branches bauen, deployen aber nicht.
Die Variable SITE_URL wird ausserdem von astro.config.mjs gelesen (site: process.env.SITE_URL), damit Sitemap und kanonische URLs die öffentliche Adresse tragen.
- Die sechs Variablen anlegen, alle
DEPLOY_*als Protected, die einzeiligen zusätzlich als Masked. - Prüfen, dass
mainein Protected Branch ist (Modul 4). - Den Job in
.gitlab-ci.ymlergänzen, über einen Merge Request aufmainbringen. - Die Pipeline auf
mainhat jetzt drei Jobs. Im Deploy-Log die rsync-Zeile suchen. - Seite im Browser prüfen, Operate → Environments zeigt «production».
Lösung anzeigen
Wenn die Pipeline grün ist, die Seite aber alt aussieht: Browser-Cache. HTML hat no-cache, aber ein harter Reload (Shift+Reload) schliesst das aus. Zweiter Verdächtiger: DEPLOY_PATH zeigt nicht auf das Document Root, das Apache ausliefert.
Selbstcheck · 3 Fragen