Deployment von containerisierten Anwendungen mit GitHub Actions
Mit GitHub Actions kannst du dein Container-Image direkt aus deinem Quellcode-Repository erstellen und veröffentlichen und dann sofort deine Anwendung auf der mittwald Cloud-Plattform deployen. Öffentliche mittwald GitHub Actions kümmern sich um die API-Aufrufe, wenden deine Stack-Definition an und erstellen (falls erforderlich) die betroffenen Dienste neu.
Voraussetzungen
- mStudio API-Token; siehe die Dokumentation zu API-Token erhalten
- GitHub Actions Runner; jeder Runner mit Docker-Unterstützung, z.B.
ubuntu-latest
Welche Action solltest du verwenden?
Du hast jetzt zwei unterschiedliche Deployment-Ansätze:
| Anwendungsfall | Empfohlene Action | Warum |
|---|---|---|
| Du willst schnell direkt aus dem Quellcode deployen, ohne zuerst Dockerfiles oder Stack-Dateien anzulegen | mittwald/zerodeploy-action | Nutzt Railpack, erkennt Build-Schritte automatisch und deployt mit nahezu null Konfiguration in mittwald Container Hosting. |
Du hast bereits ein Dockerfile und/oder eine explizite Stack-Definition (stack.yaml) und willst volle Kontrolle über Services, Ports und Rollout-Verhalten | mittwald/deploy-container-action | Ideal für einen expliziten, infrastrukturfokussierten Deployment-Ansatz. |
Wenn du den kürzesten Weg vom Repository zum laufenden Service willst, nutze zerodeploy-action. Wenn dein Team bereits Build- und Runtime-Definitionen für Container pflegt, bleib bei deploy-container-action.
Deploy mit mittwald/zerodeploy-action
mittwald/zerodeploy-action ist für Repositories gedacht, die noch kein Docker-Setup haben. Sie bestimmt automatisch die beste Methode, um ein Docker-Image aus deinem Code zu erstellen, und deployt dieses in dein Zielprojekt, indem eine dynamisch bereitgestellte Container-Registry verwendet wird.
Benötigte Inputs
MITTWALD_API_TOKENSecret; API-Token aus mStudio- Projekt-ID (zum Beispiel
p-XXXXXX)
Beispiel-Workflow
Erstelle .github/workflows/zerodeploy.yml:
name: Deploy to mittwald Container Hosting
on:
workflow_dispatch:
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Create .env for deployment
run: |
{
echo "APP_ENV=production"
echo "APP_SECRET=${{ secrets.APP_SECRET }}"
} > .env
- name: Deploy with zerodeploy
uses: mittwald/zerodeploy-action@v1
with:
mittwald-api-token: ${{ secrets.MITTWALD_API_TOKEN }}
mittwald-project-id: "p-XXXXXX"
Die Action prüft automatisch, ob .env unter $GITHUB_WORKSPACE/.env existiert, und bindet die Datei beim Deployment ein, wenn sie vorhanden ist.
Deploy mit mittwald/deploy-container-action
Die offizielle mittwald/deploy-container-action kümmert sich um die API-Aufrufe, wendet deine Stack-Definition an und erstellt (falls erforderlich) die betroffenen Dienste neu.
Voraussetzungen für mittwald/deploy-container-action
- Stack-ID; siehe die Dokumentation zu Standard-Stack identifizieren
- (Optional) Private Registry; siehe Verwendung privater Registries
Identifiziere deinen Stack
Folge der Dokumentation zu Standard-Stack identifizieren deines Projekts. Zur Vereinfachung empfehlen wir, die Stack-ID in einer GitHub-Variablen zu speichern oder (wenn du in mehrere Umgebungen deployst) ein GitHub Environment Secret zu verwenden, z.B. STACK_ID.
Schreibe deine stack.yaml
Erstelle als nächstes eine Datei, die deine Stack-Definition irgendwo in deinem Repository enthält (für dieses Beispiel nehmen wir an, deploy/stack.yaml). Diese Datei sollte das gleiche Format wie die PUT/-Operation haben.
services:
app:
image: "{{ .Env.IMAGE_TAG }}"
description: "Meine Webanwendung"
ports:
- "8080/tcp"
environment:
APP_ENV: production
volumes: {}
Die Platzhalter innerhalb von {{ … }} werden aus Umgebungsvariablen aufgelöst, die du im Workflow übergibst, sodass deine Secrets in GitHub bleiben und niemals im Repository eingecheckt werden müssen.
Füge Geheimnisse und Variablen zum Repository hinzu
MITTWALD_API_TOKEN; API-Token für die mStudio-APISTACK_ID; die in Schritt 1 kopierte ID. Du könntest dies als einfache Konfigurationsvariable festlegen, könntest diese Variable aber auch deiner Ziel-Environment zuordnen, wenn du environment-abhängig in verschiedene Stacks deployen möchtest.
Erstelle den GitHub Action Workflow
Erstelle .github/workflows/deploy.yml:
name: Build & Deploy to mittwald
on:
push:
tags: [v*]
# alternatively:
# push:
# branches: [main]
workflow_dispatch:
jobs:
build-and-deploy:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
attestations: write
id-token: write
environment:
name: production
url: https://your-app.example
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- name: Anmelden bei GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Metadaten (Tags, Labels) für Docker extrahieren
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
- name: Build & Push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Bereitstellung an mStudio
uses: mittwald/deploy-container-action@v1
env:
IMAGE_TAG: ${{ fromJSON(steps.meta.outputs.json).tags[0] }}
with:
api_token: ${{ secrets.MITTWALD_API_TOKEN }}
stack_id: ${{ vars.STACK_ID }}
stack_file: "${{ github.workspace }}/configs/stack.yaml"
Mit diesem Workflow wird bei jedem Push eines neuen Git-Tags (oder je nach Konfiguration, bei jedem Push zu main) GitHub ein neues Image erstellen, es in die GitHub Container Registry hochladen und die neuen Images ins mStudio deployen.
Fortgeschrittene Muster für mittwald/deploy-container-action
Die folgenden Optionen gelten für mittwald/deploy-container-action (den stack-basierten Workflow), nicht für mittwald/zerodeploy-action.
Selektive Neustarts
Wenn du Ausfallzeiten für zustandsbehaftete Dienste (z.B. Datenbanken) vermeiden möchtest, kannst du der Action sagen, dass sie die Neuerstellung nach dem Update überspringen soll:
with:
skip_recreation: "mysql,redis"
Nicht aufgelistete Dienste werden nur neu gestartet, wenn die API meldet, dass ein Neustart tatsächlich erforderlich ist.
Nächste Schritte
- Füge ein Ingress hinzu, um HTTP-Ports für das öffentliche Internet freizugeben.
- Verwende Terraform für vollständig deklarative Infrastruktur und lasse GitHub Actions
terraform applyaufrufen. - Definiere Umgebungen dynamisch pro Pull-Request, um kurzlebige Überprüfungsumgebungen für jeden PR zu erstellen.