TechLead
Lesson 21 of 25
5 min read
Cloud & Kubernetes

GitHub Actions Advanced

Build advanced CI/CD pipelines with GitHub Actions for Kubernetes deployments, Docker builds, Terraform automation, and multi-environment workflows

GitHub Actions for Cloud & Kubernetes

GitHub Actions provides powerful CI/CD capabilities directly integrated with your GitHub repositories. For cloud-native applications, GitHub Actions can automate the entire deployment pipeline — from building Docker images to deploying to Kubernetes clusters and applying Terraform infrastructure changes.

Common CI/CD Pipeline Stages

  • Build: Compile code, run linters, build Docker images
  • Test: Unit tests, integration tests, security scans
  • Push: Push Docker images to container registry (ECR, GCR, GHCR)
  • Deploy: Update Kubernetes manifests, apply Terraform, deploy to cloud
  • Verify: Smoke tests, health checks, rollback if needed

Docker Build and Push to GHCR

# .github/workflows/docker-build.yaml
name: Build and Push Docker Image

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

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

jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write

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

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

    - name: Log in to GitHub Container Registry
      if: github.event_name != 'pull_request'
      uses: docker/login-action@v3
      with:
        registry: ${{ env.REGISTRY }}
        username: ${{ github.actor }}
        password: ${{ secrets.GITHUB_TOKEN }}

    - name: Extract metadata
      id: meta
      uses: docker/metadata-action@v5
      with:
        images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
        tags: |
          type=sha,prefix=
          type=ref,event=branch
          type=semver,pattern={{version}}
          type=raw,value=latest,enable={{is_default_branch}}

    - name: Build and push
      uses: docker/build-push-action@v5
      with:
        context: .
        push: ${{ github.event_name != 'pull_request' }}
        tags: ${{ steps.meta.outputs.tags }}
        labels: ${{ steps.meta.outputs.labels }}
        cache-from: type=gha
        cache-to: type=gha,mode=max

Kubernetes Deployment Workflow

# .github/workflows/deploy-k8s.yaml
name: Deploy to Kubernetes

on:
  push:
    branches: [main]
  workflow_dispatch:
    inputs:
      environment:
        description: 'Target environment'
        required: true
        type: choice
        options: [staging, production]

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: ${{ github.event.inputs.environment || 'staging' }}

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

    - name: Configure AWS credentials
      uses: aws-actions/configure-aws-credentials@v4
      with:
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        aws-region: us-east-1

    - name: Update kubeconfig
      run: |
        aws eks update-kubeconfig --name my-cluster --region us-east-1

    - name: Set image tag
      run: |
        echo "IMAGE_TAG=${{ github.sha }}" >> $GITHUB_ENV

    - name: Deploy to Kubernetes
      run: |
        kubectl set image deployment/api-server \
          api=ghcr.io/${{ github.repository }}:${{ env.IMAGE_TAG }} \
          -n production

        kubectl rollout status deployment/api-server -n production --timeout=300s

    - name: Verify deployment
      run: |
        kubectl get pods -n production -l app=api-server
        # Run smoke test
        ENDPOINT=$(kubectl get svc api-server -n production -o jsonpath='{.status.loadBalancer.ingress[0].hostname}')
        curl -sf "http://$ENDPOINT/health" || (echo "Health check failed" && exit 1)

    - name: Rollback on failure
      if: failure()
      run: |
        kubectl rollout undo deployment/api-server -n production
        kubectl rollout status deployment/api-server -n production

Terraform CI/CD Pipeline

# .github/workflows/terraform.yaml
name: Terraform

on:
  push:
    branches: [main]
    paths: ['infra/**']
  pull_request:
    branches: [main]
    paths: ['infra/**']

jobs:
  terraform:
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: infra

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

    - name: Setup Terraform
      uses: hashicorp/setup-terraform@v3
      with:
        terraform_version: 1.7.0

    - name: Configure AWS Credentials
      uses: aws-actions/configure-aws-credentials@v4
      with:
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        aws-region: us-east-1

    - name: Terraform Init
      run: terraform init

    - name: Terraform Format Check
      run: terraform fmt -check

    - name: Terraform Validate
      run: terraform validate

    - name: Terraform Plan
      id: plan
      run: terraform plan -no-color -out=tfplan
      continue-on-error: true

    - name: Comment PR with Plan
      if: github.event_name == 'pull_request'
      uses: actions/github-script@v7
      with:
        script: |
          const output = `#### Terraform Plan
          \`\`\`
          ${{ steps.plan.outputs.stdout }}
          \`\`\`
          `;
          github.rest.issues.createComment({
            issue_number: context.issue.number,
            owner: context.repo.owner,
            repo: context.repo.repo,
            body: output
          });

    - name: Terraform Apply
      if: github.ref == 'refs/heads/main' && github.event_name == 'push'
      run: terraform apply -auto-approve tfplan

Reusable Workflow (Composite Action)

# .github/workflows/reusable-deploy.yaml
name: Reusable K8s Deploy

on:
  workflow_call:
    inputs:
      environment:
        required: true
        type: string
      image-tag:
        required: true
        type: string
    secrets:
      AWS_ACCESS_KEY_ID:
        required: true
      AWS_SECRET_ACCESS_KEY:
        required: true

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: ${{ inputs.environment }}
    steps:
    - uses: actions/checkout@v4

    - name: Configure AWS credentials
      uses: aws-actions/configure-aws-credentials@v4
      with:
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        aws-region: us-east-1

    - name: Deploy
      run: |
        aws eks update-kubeconfig --name my-cluster --region us-east-1
        kubectl set image deployment/api-server api=ghcr.io/myorg/myapp:${{ inputs.image-tag }} -n ${{ inputs.environment }}
        kubectl rollout status deployment/api-server -n ${{ inputs.environment }} --timeout=300s

Key Takeaways

  • GitHub Actions automates the full pipeline from Docker build to Kubernetes deployment
  • Use reusable workflows and composite actions to avoid duplication across repos
  • Terraform plans should be posted as PR comments for team review
  • Always include rollback steps in deployment workflows for safety
  • Use GitHub Environments with required reviewers for production deployments

Continue Learning