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