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
Autor
REXE Teknoloji Network & Security Team
Redaktion
REXE Teknoloji Technical Editorial
Erstveröffentlichung
Letzte Aktualisierung

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.

Häufig gestellte Fragen

Ist GitHub Actions kostenlos?

Für öffentliche Repositories vollständig kostenlos und unbegrenzt. Private Repositories erhalten 2.000 kostenlose Minuten pro Monat. Wenn dieses Limit beim GitHub Free-Plan überschritten wird, pausieren Workflows. GitHub Pro und Team-Pläne beinhalten mehr Minuten.

Wie erstelle ich einen SSH-Private-Key und füge ihn zu GitHub hinzu?

Erstellen Sie ein Schlüsselpaar auf Ihrem lokalen Rechner mit 'ssh-keygen -t ed25519 -C "github-actions"'. Fügen Sie den Public Key (~/.ssh/id_ed25519.pub) zu ~/.ssh/authorized_keys auf Ihrem Server hinzu. Speichern Sie den Private Key (~/.ssh/id_ed25519) in Ihrem GitHub-Repo unter Settings > Secrets als SSH_PRIVATE_KEY.

Wie funktioniert Job-Abhängigkeit mit needs?

'needs: test' bedeutet, dass dieser Job erst ausgeführt wird, wenn der 'test'-Job erfolgreich abgeschlossen ist. Verwenden Sie 'needs: [test, build]' für mehrere Abhängigkeiten. Wenn ein Job fehlschlägt, werden abhängige Jobs automatisch abgebrochen.

Ist es möglich, einen Workflow nur auszulösen, wenn bestimmte Dateien geändert werden?

Ja, verwenden Sie 'on.push.paths', um bestimmte Dateien oder Verzeichnisse zu überwachen. Zum Beispiel löst 'paths: ["src/**", "package.json"]' den Workflow nur aus, wenn diese Dateien geändert werden. Verwenden Sie 'paths-ignore', um bestimmte Dateien auszuschließen.

Warum ist Docker Build Cache wichtig?

Docker Build Cache verhindert das Neuerstellen unveränderter Schichten. 'cache-from: type=gha' verwendet den GitHub Actions Cache. Dies kann die Build-Zeit um 50-80% reduzieren. Es macht besonders bei Abhängigkeits-Installationsschritten (npm install, pip install) einen großen Unterschied.

Was bewirken Environment Protection Rules?

Sie fügen einen Genehmigungsmechanismus für kritische Umgebungen wie Production hinzu. Mit 'Required reviewers' kann ohne Genehmigung bestimmter Personen kein Deployment durchgeführt werden. 'Wait timer' legt eine Wartezeit vor dem Deployment fest. Dies verhindert versehentliche Deployments in Production.

Verwandte Artikel

Server-Automatisierung mit Ansible: Infrastructure as Code

Ansible-Installation, Inventory-Datei, Playbook-Erstellung, Rollenstruktur, Variablen, verschlüsselter Vault, Ad-hoc-Befehle und Server-Konfigurationsautomatisierung. Schritt-für-Schritt-Anleitung.

20 Min
ansibleautomatisierunginfrastructure as code
NetzwerkverkehrEingehend GbpsAusgehend Gbps