Zum Hauptinhalt springen
Zurück zur Kategorie

Automatisches Deployment mit GitHub Actions: CI/CD Pipeline Einrichtung

CI/CD-Pipelines mit GitHub Actions erstellen: Docker Build und Push, SSH-Server-Deployment, Test-Automatisierung, Secrets-Verwaltung und Workflow-Optimierung.

Lesezeit: 18 Min DevOps & Automatisierung
github-actionsci/cddeploymentdevopsautomatisierungdockerssh

Inhaltsverzeichnis

Automatisches Deployment mit GitHub Actions: CI/CD Pipeline Einrichtung

GitHub Actions ist eine leistungsstarke CI/CD-Plattform, die Code-Änderungen automatisch testet, baut und auf dem Server deployt. In diesem Leitfaden bauen wir eine produktionsreife Pipeline von Grund auf.

Was ist GitHub Actions?

GitHub Actions ist eine in GitHub-Repositories integrierte Automatisierungsplattform:

  • Reagiert auf Ereignisse wie Push, PR und Schedule
  • Führt parallele und sequentielle Workflows aus
  • 2.000 Minuten/Monat kostenlos (unbegrenzt für öffentliche Repos)
  • Erweiterbar mit Tausenden von fertigen Actions

GitHub Actions Workflow-Dateien werden im YAML-Format im Verzeichnis .github/workflows/ gespeichert.

Grundlegende Workflow-Struktur

hljs yaml
# .github/workflows/deploy.yml
name: CI/CD Pipeline

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Node.js einrichten
        uses: actions/setup-node@v4
        with:
          node-version: '20'
      - name: Abhängigkeiten installieren
        run: npm ci
      - name: Tests ausführen
        run: npm test

  deploy:
    needs: test
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    steps:
      - uses: actions/checkout@v4
      - name: Deployen
        run: echo "Deploy-Schritte hier"

on / jobs / steps Struktur

Trigger (on)

hljs yaml
on:
  # Push auf Branch
  push:
    branches: [main, develop]
    paths:
      - 'src/**'
      - 'package.json'

  # Pull Request
  pull_request:
    branches: [main]
    types: [opened, synchronize, reopened]

  # Geplante Ausführung (cron)
  schedule:
    - cron: '0 2 * * *'  # Jede Nacht um 02:00

  # Manueller Trigger
  workflow_dispatch:
    inputs:
      environment:
        description: 'Deploy-Umgebung'
        required: true
        default: 'staging'
        type: choice
        options: [staging, production]

  # Wenn ein anderer Workflow abgeschlossen ist
  workflow_run:
    workflows: ["Build"]
    types: [completed]

Job-Konfiguration

hljs yaml
jobs:
  build:
    runs-on: ubuntu-latest
    env:
      NODE_ENV: test
    timeout-minutes: 30
    strategy:
      matrix:
        node-version: [18, 20, 22]
        os: [ubuntu-latest, windows-latest]
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}

Docker Build und Push

Push zu Docker Hub

hljs yaml
jobs:
  docker:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Docker-Metadaten
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: benutzername/meine-app
          tags: |
            type=ref,event=branch
            type=semver,pattern={{version}}
            type=sha,prefix=sha-

      - name: Docker Buildx einrichten
        uses: docker/setup-buildx-action@v3

      - name: Bei Docker Hub anmelden
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      - name: Build und 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

GitHub Container Registry (GHCR)

hljs yaml
      - name: Bei GHCR anmelden
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build und Push (GHCR)
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: ghcr.io/${{ github.repository }}:latest

SSH-Deployment auf Server

Verwendung von appleboy/ssh-action

hljs yaml
  deploy:
    needs: docker
    runs-on: ubuntu-latest
    environment: production
    steps:
      - name: Deployment via SSH
        uses: appleboy/ssh-action@v1.0.3
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USER }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          port: ${{ secrets.SERVER_PORT }}
          script: |
            cd /root/meine-app
            git pull origin main
            docker compose pull
            docker compose up -d --remove-orphans
            docker image prune -f
            echo "Deployment abgeschlossen: $(date)"

Deployment mit Docker Compose

hljs yaml
      - name: Docker Compose Deployment
        uses: appleboy/ssh-action@v1.0.3
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USER }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          script: |
            # Neues Image ziehen
            docker pull ghcr.io/${{ github.repository }}:latest

            # Zero-Downtime-Deployment
            docker compose -f /root/app/docker-compose.yml up -d \
              --no-deps \
              --build \
              --remove-orphans

            # Alte Images bereinigen
            docker image prune -af --filter "until=24h"

            # Health Check
            sleep 10
            curl -f http://localhost:3000/health || exit 1

Secrets-Verwaltung

GitHub Secrets speichert sensible Informationen sicher:

hljs yaml
steps:
  - name: Anwendungskonfiguration
    env:
      DATABASE_URL: ${{ secrets.DATABASE_URL }}
      JWT_SECRET: ${{ secrets.JWT_SECRET }}
      API_KEY: ${{ secrets.API_KEY }}
    run: |
      echo "Konfiguration geladen"
      # Secrets werden nie in Logs ausgegeben, automatisch maskiert

Erforderliche Secrets:

  • SERVER_HOST: Server-IP-Adresse
  • SERVER_USER: SSH-Benutzername
  • SSH_PRIVATE_KEY: Privater SSH-Schlüssel
  • DOCKERHUB_USERNAME: Docker Hub Benutzername
  • DOCKERHUB_TOKEN: Docker Hub Zugriffstoken

Cache-Nutzung

hljs yaml
      - name: Node.js-Abhängigkeiten cachen
        uses: actions/cache@v4
        with:
          path: ~/.npm
          key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-node-

      - name: npm ci (gecacht)
        run: npm ci

      # Docker Layer Cache
      - name: Build und Push
        uses: docker/build-push-action@v5
        with:
          cache-from: type=gha
          cache-to: type=gha,mode=max

Matrix Builds

hljs yaml
jobs:
  test:
    strategy:
      fail-fast: false
      matrix:
        node: [18, 20]
        os: [ubuntu-latest, macos-latest]
        include:
          - node: 20
            os: ubuntu-latest
            coverage: true
        exclude:
          - node: 18
            os: macos-latest
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node }}
      - run: npm ci
      - run: npm test
      - name: Coverage-Bericht
        if: matrix.coverage
        run: npm run coverage

Environment Protection Rules

hljs yaml
jobs:
  deploy-production:
    runs-on: ubuntu-latest
    environment:
      name: production
      url: https://example.com
    steps:
      - name: Production-Deployment
        run: echo "Deployment in Production"

In GitHub unter Settings > Environments > production:

  • Required reviewers (Genehmigung erforderlich)
  • Wait timer (Wartezeit)
  • Deployment branches (nur main)

Vollständiges CI/CD Pipeline Beispiel

hljs yaml
name: Full CI/CD

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
      - run: npm ci
      - run: npm run lint
      - run: npm test -- --coverage
      - name: Coverage hochladen
        uses: codecov/codecov-action@v4

  build:
    needs: test
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    steps:
      - uses: actions/checkout@v4
      - uses: docker/setup-buildx-action@v3
      - uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
      - uses: docker/build-push-action@v5
        with:
          push: ${{ github.event_name != 'pull_request' }}
          tags: ${{ steps.meta.outputs.tags }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

  deploy:
    needs: build
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    environment:
      name: production
      url: https://example.com
    steps:
      - uses: appleboy/ssh-action@v1.0.3
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USER }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          script: |
            docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:main
            docker compose -f /root/app/docker-compose.yml up -d
            docker image prune -f

Fazit

Durch die Einrichtung einer vollautomatischen CI/CD-Pipeline mit GitHub Actions können Sie die Codequalität verbessern, Deployment-Fehler reduzieren und Ihren Entwicklungsprozess beschleunigen. Mit Secrets-Verwaltung, Cache-Optimierung und Environment Protection Rules erhalten Sie eine produktionsreife Pipeline.