CI/CD for GKE with GitHub Actions + Cloud Build

Introduction to CI/CD for Kubernetes

Continuous Integration and Continuous Deployment (CI/CD) are essential practices for modern application development, especially when working with containerized applications on Kubernetes. By automating your build, test, and deployment processes, you can accelerate development cycles, reduce errors, and ensure consistent deployments to your Google Kubernetes Engine (GKE) clusters.

In this guide, we'll explore how to set up a robust CI/CD pipeline for GKE using GitHub Actions for CI and Google Cloud Build for CD, creating a powerful automation workflow that connects your code repository to your production environment.

Why GitHub Actions + Cloud Build?

Combining GitHub Actions with Google Cloud Build offers the best of both worlds:

  • GitHub Actions: Tightly integrated with your code repository, ideal for CI tasks like testing, linting, and building container images
  • Google Cloud Build: Native Google Cloud integration, optimized for deploying to GKE with secure credentials and environment management

This combination provides a seamless workflow from code commit to production deployment while maintaining security best practices.

Setting Up the CI Pipeline with GitHub Actions

GitHub Actions will handle the Continuous Integration part of our pipeline - building and testing our application whenever code is pushed to our repository.

Sample GitHub Actions Workflow (.github/workflows/ci.yaml):

name: CI Pipeline

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

jobs:
  test:
    runs-on: ubuntu-latest
    
    steps:
    - name: Checkout code
      uses: actions/checkout@v3
      
    - name: Set up Node.js
      uses: actions/setup-node@v3
      with:
        node-version: '18'
        cache: 'npm'
        
    - name: Install dependencies
      run: npm ci
      
    - name: Run tests
      run: npm test
      
    - name: Run linting
      run: npm run lint

  build-and-push:
    needs: test
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    
    steps:
    - name: Checkout code
      uses: actions/checkout@v3
      
    - name: Set up Docker Buildx
      uses: docker/setup-buildx-action@v2
      
    - name: Authenticate to Google Cloud
      uses: google-github-actions/auth@v1
      with:
        credentials_json: ${{ secrets.GCP_SA_KEY }}
        
    - name: Configure Docker for GCR
      run: gcloud auth configure-docker --quiet
      
    - name: Build and push container
      uses: docker/build-push-action@v4
      with:
        context: .
        push: true
        tags: |
          gcr.io/${{ secrets.GCP_PROJECT }}/my-app:${{ github.sha }}
          gcr.io/${{ secrets.GCP_PROJECT }}/my-app:latest
        cache-from: type=gha
        cache-to: type=gha,mode=max

Required GitHub Secrets:

  • GCP_SA_KEY: JSON key for a Google Cloud Service Account with appropriate permissions
  • GCP_PROJECT: Your Google Cloud Project ID

Configuring the CD Pipeline with Cloud Build

Google Cloud Build will handle the Continuous Deployment part - deploying our application to GKE whenever a new container image is available.

Cloud Build Trigger Configuration:

Set up a Cloud Build trigger that responds to new images in Google Container Registry:

  1. Connect your GitHub repository to Cloud Build
  2. Create a trigger that fires when a new image is pushed to GCR
  3. Point to your cloudbuild.yaml file for deployment instructions

Sample cloudbuild.yaml:

steps:
# Update the Kubernetes deployment with the new image
- name: 'gcr.io/cloud-builders/kubectl'
  args:
  - 'set'
  - 'image'
  - 'deployment/my-app'
  - 'my-app=gcr.io/$PROJECT_ID/my-app:$SHORT_SHA'
  env:
  - 'CLOUDSDK_COMPUTE_ZONE=us-central1-a'
  - 'CLOUDSDK_CONTAINER_CLUSTER=my-gke-cluster'

# Run any post-deployment tests
- name: 'gcr.io/cloud-builders/curl'
  args: 
  - '--max-time'
  - '30'
  - '--retry'
  - '5'
  - '--retry-delay'
  - '3'
  - '--retry-max-time'
  - '60'
  - 'http://my-app-service/api/health'

images:
- 'gcr.io/$PROJECT_ID/my-app:$SHORT_SHA'

Environment-Based Deployments

For most projects, you'll want different deployment strategies for different environments (development, staging, production).

Multi-environment cloudbuild.yaml:

# Development deployment (on push to develop branch)
- name: 'develop'
  steps:
  - name: 'gcr.io/cloud-builders/kubectl'
    args:
    - 'apply'
    - '-f'
    - 'k8s/overlays/development/'
    env:
    - 'CLOUDSDK_COMPUTE_ZONE=us-central1-a'
    - 'CLOUDSDK_CONTAINER_CLUSTER=my-dev-cluster'

# Production deployment (on push to main branch)  
- name: 'production'
  steps:
  - name: 'gcr.io/cloud-builders/kubectl'
    args:
    - 'apply'
    - '-f'
    - 'k8s/overlays/production/'
    env:
    - 'CLOUDSDK_COMPUTE_ZONE=us-central1-a'
    - 'CLOUDSDK_CONTAINER_CLUSTER=my-prod-cluster'
  - name: 'gcr.io/cloud-builders/kubectl'
    args:
    - 'rollout'
    - 'status'
    - 'deployment/my-app'
    - '--timeout=5m'

Kustomize for Environment-Specific Configurations

Using Kustomize helps manage environment-specific configurations without duplicating entire YAML files.

Directory Structure:

k8s/
├── base/
│   ├── deployment.yaml
│   ├── service.yaml
│   └── kustomization.yaml
└── overlays/
    ├── development/
    │   ├── kustomization.yaml
    │   └── configmap.yaml
    └── production/
        ├── kustomization.yaml
        ├── configmap.yaml
        └── hpa.yaml

Sample base/kustomization.yaml:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
- deployment.yaml
- service.yaml

images:
- name: my-app
  newName: gcr.io/my-project/my-app
  newTag: latest

Sample overlays/development/kustomization.yaml:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

bases:
- ../../base

resources:
- configmap.yaml

replicas:
- name: my-app
  count: 2

images:
- name: my-app
  newTag: development

Security Best Practices

When setting up your CI/CD pipeline, security should be a top priority:

  • Least Privilege Service Accounts: Create dedicated service accounts with only the necessary permissions
  • Workload Identity: Use GKE Workload Identity to securely access Google Cloud services
  • Secret Management: Use Google Secret Manager for sensitive configuration values
  • Container Vulnerability Scanning: Enable Container Analysis to scan for vulnerabilities
  • Binary Authorization: Ensure only approved container images are deployed

Advanced CI/CD Patterns

As your pipeline matures, consider implementing these advanced patterns:

Canary Deployments:

steps:
- name: 'gcr.io/cloud-builders/kubectl'
  args: 
  - 'apply'
  - '-f'
  - 'k8s/canary/'
  
- name: 'gcr.io/cloud-builders/curl'
  args:
  - '--silent'
  - '--output'
  - '/dev/null'
  - '--write-out'
  - '"%{http_code}"'
  - 'http://canary-service/health'
  # Wait for canary to be healthy
  
- name: 'gcr.io/cloud-builders/kubectl'
  args:
  - 'apply'
  - '-f'
  - 'k8s/production/'
  # Promote canary to full production

Blue-Green Deployments:

Switch traffic between two identical environments to achieve zero-downtime deployments.

Automated Rollbacks:

Configure your pipeline to automatically roll back if health checks fail after deployment.

Monitoring Your Pipeline

Implement monitoring to track the health and performance of your CI/CD pipeline:

  • Set up Cloud Build notifications for build failures
  • Use Cloud Monitoring to track build times and success rates
  • Implement logging to trace issues through the pipeline
  • Create dashboards to visualize pipeline health

Troubleshooting Common Issues

Common challenges and solutions when setting up GKE CI/CD:

  • Authentication Errors: Verify service account permissions and JSON key format
  • Image Pull Errors: Ensure GKE has pull access to GCR
  • Resource Quotas: Monitor and adjust GKE resource quotas as needed
  • Network Policies: Check that network policies allow build agents to communicate with your cluster

Conclusion

Setting up a CI/CD pipeline for GKE using GitHub Actions and Google Cloud Build creates a powerful, automated workflow that accelerates your development process while maintaining reliability and security. By combining GitHub's excellent code integration with Google Cloud's native deployment capabilities, you get the best of both worlds.

Remember that CI/CD is not a one-time setup but an evolving practice. Start with a simple pipeline, then gradually add more advanced patterns like canary deployments, automated rollbacks, and comprehensive monitoring as your team and application mature.

With your automated pipeline in place, you can focus more on developing features and less on deployment mechanics, knowing that your code will be reliably tested, built, and deployed to your GKE clusters.

Post a Comment

0 Comments