CourseDevops

GitHub Actions CI/CD Tutorial

Inhoudstafel

  1. Inleiding tot CI/CD
  2. GitHub Actions Basics
  3. Verbinding met Docker Hub
  4. Automatische Image Builds
  5. Production Deployments
  6. Docker Compose Updates
  7. Kubernetes & Helm Deployments
  8. Advanced Workflows
  9. Best Practices

Inleiding tot CI/CD

Wat is CI/CD?

Continuous Integration (CI) en Continuous Deployment (CD) zijn essentiële praktijken in moderne software ontwikkeling:

Waarom GitHub Actions?

GitHub Actions is de native CI/CD oplossing van GitHub:


GitHub Actions Basics

Workflow Structuur

Een GitHub Actions workflow bestaat uit:

.github/
└── workflows/
    └── ci-cd.yml

Basis Workflow Syntax

name: CI/CD Pipeline

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

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Run tests
        run: echo "Running tests..."

Belangrijke Concepten

Triggers (on)

on:
  push: # Bij elke push
  pull_request: # Bij PR's
  schedule: # Geplande runs
    - cron: "0 2 * * *" # Elke dag om 2:00
  workflow_dispatch: # Handmatige trigger

Jobs en Steps

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm test

  deploy:
    needs: test # Wacht op test job
    runs-on: ubuntu-latest
    steps:
      - run: echo "Deploying..."

Verbinding met Docker Hub

Docker Hub Account Setup

  1. Account aanmaken op hub.docker.com
  2. Access Token genereren:
    • Docker Hub → Account Settings → Security
    • New Access Token → Beschrijving invullen

GitHub Secrets Configureren

In je GitHub repository:

  1. SettingsSecrets and variablesActions
  2. New repository secret:
    • DOCKER_HUB_USERNAME: je Docker Hub username
    • DOCKER_HUB_ACCESS_TOKEN: je access token

GitHub Secrets

Docker Login in Workflow

- name: Login to Docker Hub
  uses: docker/login-action@v3
  with:
    username: $
    password: $

Automatische Image Builds

Eenvoudige Build Workflow

name: Build and Push Docker Image

on:
  push:
    branches: [main]

jobs:
  build-and-push:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

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

      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          username: $
          password: $

      - name: Build and push Docker image
        uses: docker/build-push-action@v5
        with:
          context: .
          file: ./Dockerfile
          push: true
          tags: |
            $/myapp:latest
            $/myapp:$

Multi-platform Builds

- name: Build and push Docker image
  uses: docker/build-push-action@v5
  with:
    context: .
    platforms: linux/amd64,linux/arm64
    push: true
    tags: |
      $/myapp:latest
      $/myapp:$

Dynamische Tags

- name: Extract metadata
  id: meta
  uses: docker/metadata-action@v5
  with:
    images: $/myapp
    tags: |
      type=ref,event=branch
      type=ref,event=pr
      type=sha
      type=raw,value=latest,enable=

- name: Build and push
  uses: docker/build-push-action@v5
  with:
    context: .
    push: true
    tags: $
    labels: $

Production Deployments

Server Access Configureren

Voor deployment naar productie servers:

# GitHub Secrets toevoegen:
# SERVER_HOST: je server IP/hostname
# SERVER_USER: SSH username
# SSH_PRIVATE_KEY: private SSH key

SSH Deployment

deploy-to-production:
  needs: build-and-push
  runs-on: ubuntu-latest
  if: github.ref == 'refs/heads/main'

  steps:
    - name: Deploy to server
      uses: appleboy/ssh-action@v1.0.0
      with:
        host: $
        username: $
        key: $
        script: |
          docker pull $/myapp:latest
          docker stop myapp || true
          docker rm myapp || true
          docker run -d --name myapp -p 80:3000 \
            $/myapp:latest

Docker Compose Updates

Docker Compose Deployment

Voor applicaties met meerdere services:

deploy-compose:
  needs: build-and-push
  runs-on: ubuntu-latest

  steps:
    - name: Checkout code
      uses: actions/checkout@v4

    - name: Deploy with Docker Compose
      uses: appleboy/ssh-action@v1.0.0
      with:
        host: $
        username: $
        key: $
        script: |
          cd /opt/myapp
          git pull origin main
          docker-compose pull
          docker-compose up -d --force-recreate

Environment-specific Deployments

deploy-to-staging:
  needs: build-and-push
  runs-on: ubuntu-latest
  environment: staging

  steps:
    - name: Deploy to staging
      uses: appleboy/ssh-action@v1.0.0
      with:
        host: $
        username: $
        key: $
        script: |
          cd /opt/myapp-staging
          docker-compose -f docker-compose.staging.yml pull
          docker-compose -f docker-compose.staging.yml up -d

deploy-to-production:
  needs: [build-and-push, deploy-to-staging]
  runs-on: ubuntu-latest
  environment: production

  steps:
    - name: Deploy to production
      # ... productie deployment

Kubernetes & Helm Deployments

Kubernetes Setup

deploy-to-k8s:
  needs: build-and-push
  runs-on: ubuntu-latest

  steps:
    - name: Checkout code
      uses: actions/checkout@v4

    - name: Configure kubectl
      uses: azure/k8s-set-context@v3
      with:
        method: kubeconfig
        kubeconfig: $

    - name: Deploy to Kubernetes
      run: |
        # Update image tag in deployment
        sed -i "s|image: myapp:.*|image: $/myapp:$|" k8s/deployment.yaml

        # Apply manifests
        kubectl apply -f k8s/

        # Wait for rollout
        kubectl rollout status deployment/myapp

Helm Deployment

deploy-with-helm:
  needs: build-and-push
  runs-on: ubuntu-latest

  steps:
    - name: Checkout code
      uses: actions/checkout@v4

    - name: Configure kubectl
      uses: azure/k8s-set-context@v3
      with:
        method: kubeconfig
        kubeconfig: $

    - name: Install Helm
      uses: azure/setup-helm@v3
      with:
        version: "v3.12.0"

    - name: Deploy with Helm
      run: |
        helm upgrade --install myapp ./helm-chart \
          --set image.repository=$/myapp \
          --set image.tag=$ \
          --namespace production \
          --create-namespace \
          --wait

ArgoCD GitOps

Voor GitOps workflows met ArgoCD:

update-gitops-repo:
  needs: build-and-push
  runs-on: ubuntu-latest

  steps:
    - name: Checkout GitOps repo
      uses: actions/checkout@v4
      with:
        repository: myorg/gitops-repo
        token: $

    - name: Update image tag
      run: |
        # Update Helm values file
        sed -i "s|tag: .*|tag: $|" environments/production/values.yaml

        # Commit and push
        git config user.name "GitHub Actions"
        git config user.email "actions@github.com"
        git add .
        git commit -m "Update image tag to $"
        git push

Advanced Workflows

Multi-Environment Pipeline

name: Complete CI/CD Pipeline

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

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Run tests
        run: |
          npm install
          npm test
          npm run lint

  build:
    needs: test
    runs-on: ubuntu-latest
    if: github.event_name == 'push'

    outputs:
      image-tag: $

    steps:
      - uses: actions/checkout@v4
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          username: $
          password: $

      - name: Extract metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: $/myapp
          tags: |
            type=ref,event=branch
            type=sha
            type=raw,value=latest,enable=

      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: $
          cache-from: type=gha
          cache-to: type=gha,mode=max

  deploy-staging:
    needs: build
    if: github.ref == 'refs/heads/develop'
    runs-on: ubuntu-latest
    environment: staging

    steps:
      - name: Deploy to staging
        # ... staging deployment

  deploy-production:
    needs: build
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    environment: production

    steps:
      - name: Deploy to production
        # ... production deployment

Security Scanning

security-scan:
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v4

    - name: Run Trivy vulnerability scanner
      uses: aquasecurity/trivy-action@master
      with:
        image-ref: $/myapp:$
        format: "sarif"
        output: "trivy-results.sarif"

    - name: Upload Trivy scan results
      uses: github/codeql-action/upload-sarif@v2
      with:
        sarif_file: "trivy-results.sarif"

Matrix Builds

test-matrix:
  runs-on: ubuntu-latest
  strategy:
    matrix:
      node-version: [16, 18, 20]
      os: [ubuntu-latest, windows-latest, macos-latest]

  steps:
    - uses: actions/checkout@v4
    - name: Setup Node.js $
      uses: actions/setup-node@v4
      with:
        node-version: $
    - run: npm test

Best Practices

1. Security

# Voorbeeld: Beveiligde workflow
- name: Login to registry
  uses: docker/login-action@v3
  with:
    username: $
    password: $

- name: Build with security scan
  run: |
    docker build -t myapp .
    trivy image myapp

2. Performance Optimalisatie

# Cache dependencies
- name: Cache Docker layers
  uses: actions/cache@v3
  with:
    path: /tmp/.buildx-cache
    key: $-buildx-$
    restore-keys: |
      $-buildx-

3. Monitoring en Debugging

- name: Notify on failure
  if: failure()
  uses: 8398a7/action-slack@v3
  with:
    status: failure
    channel: "#deployments"
  env:
    SLACK_WEBHOOK_URL: $

4. Branch Protection

Repository Settings configureren:

  1. Branch protection rules
  2. Require PR reviews
  3. Require status checks
  4. Restrict pushes

5. Environment Management

production:
  environment:
    name: production
    url: https://myapp.com
  needs: build
  if: github.ref == 'refs/heads/main'

Praktische Voorbeelden

Volledig Node.js Project

name: Node.js CI/CD

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

env:
  NODE_VERSION: "18"
  REGISTRY: docker.io
  IMAGE_NAME: myusername/myapp

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: $
          cache: "npm"

      - name: Install dependencies
        run: npm ci

      - name: Run tests
        run: |
          npm run test:unit
          npm run test:integration

      - name: Upload coverage
        uses: codecov/codecov-action@v3

  build-and-push:
    needs: test
    runs-on: ubuntu-latest
    if: github.event_name == 'push'

    steps:
      - uses: actions/checkout@v4

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

      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          registry: $
          username: $
          password: $

      - name: Extract metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: $/$
          tags: |
            type=ref,event=branch
            type=sha,prefix=-
            type=raw,value=latest,enable=

      - name: Build and push Docker image
        uses: docker/build-push-action@v5
        with:
          context: .
          platforms: linux/amd64,linux/arm64
          push: true
          tags: $
          labels: $
          cache-from: type=gha
          cache-to: type=gha,mode=max

  deploy-staging:
    needs: build-and-push
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/develop'
    environment: staging

    steps:
      - name: Deploy to staging
        uses: appleboy/ssh-action@v1.0.0
        with:
          host: $
          username: $
          key: $
          script: |
            docker pull $/$:develop-$
            docker stop myapp-staging || true
            docker rm myapp-staging || true
            docker run -d --name myapp-staging \
              -p 3001:3000 \
              -e NODE_ENV=staging \
              $/$:develop-$

  deploy-production:
    needs: build-and-push
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    environment: production

    steps:
      - name: Deploy to production
        uses: appleboy/ssh-action@v1.0.0
        with:
          host: $
          username: $
          key: $
          script: |
            docker pull $/$:latest
            docker stop myapp || true
            docker rm myapp || true
            docker run -d --name myapp \
              -p 80:3000 \
              -e NODE_ENV=production \
              $/$:latest

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

Deze tutorial biedt een complete gids voor het opzetten van professionele CI/CD pipelines met GitHub Actions, van eenvoudige builds tot complexe multi-environment deployments met Kubernetes en Helm.