GitHub Actions CI/CD Pipeline — From Build to Kubernetes Deploy
Build a complete CI/CD pipeline with GitHub Actions: test → build → Docker image → Kubernetes deployment. Includes real workflow examples.
TestForge Team ·
Full Pipeline Overview
PR Open → Test → Build
main merge → Docker Build → Registry Push → K8s Deploy
1. Basic CI Workflow (Tests)
# .github/workflows/ci.yml
name: CI
on:
pull_request:
branches: [main, develop]
jobs:
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_DB: testdb
POSTGRES_USER: test
POSTGRES_PASSWORD: test
ports: ["5432:5432"]
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
redis:
image: redis:7-alpine
ports: ["6379:6379"]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
cache: gradle
- name: Test
run: ./gradlew test
env:
SPRING_DATASOURCE_URL: jdbc:postgresql://localhost:5432/testdb
SPRING_DATASOURCE_USERNAME: test
SPRING_DATASOURCE_PASSWORD: test
SPRING_REDIS_HOST: localhost
- name: Upload test results
uses: actions/upload-artifact@v4
if: always()
with:
name: test-results
path: build/reports/tests/
2. Docker Build + Registry Push
# .github/workflows/build.yml
name: Build & Push
on:
push:
branches: [main]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=sha,prefix=sha-
type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }}
- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
cache-from: type=gha
cache-to: type=gha,mode=max
3. Kubernetes Deploy
deploy:
needs: build
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- name: Configure kubectl (EKS)
run: |
aws eks update-kubeconfig \
--region ap-northeast-2 \
--name my-cluster
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- name: Deploy
run: |
IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:sha-${{ github.sha }}"
kubectl set image deployment/my-app \
my-app=$IMAGE \
--namespace=production
kubectl rollout status deployment/my-app \
--namespace=production \
--timeout=5m
4. Environment-Based Deploy Branching
on:
push:
branches:
- main # → production deploy
- develop # → staging deploy
jobs:
deploy:
environment: ${{ github.ref == 'refs/heads/main' && 'production' || 'staging' }}
5. Security Hardening: OIDC Authentication (No Access Keys)
permissions:
id-token: write
contents: read
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789:role/github-actions-role
aws-region: ap-northeast-2
Requires setting up a GitHub OIDC provider trust relationship in AWS IAM.
6. Slack Notifications
- name: Notify Slack
if: always()
uses: slackapi/slack-github-action@v1
with:
payload: |
{
"text": "${{ job.status == 'success' && '✅' || '❌' }} Deploy ${{ job.status }}: ${{ github.repository }}@${{ github.sha }}",
"blocks": [{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*${{ github.workflow }}* — <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View Run>"
}
}]
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
Cost Optimization Tips
# Cache to speed up builds
- uses: actions/cache@v4
with:
path: ~/.gradle/caches
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}
# Only run on relevant file changes
on:
push:
paths:
- 'src/**'
- 'Dockerfile'
- '.github/workflows/**'
# Limit concurrent runs per branch
concurrency:
group: deploy-${{ github.ref }}
cancel-in-progress: true
Pipeline Timing Targets
| Stage | Target |
|---|---|
| Unit tests | < 2 minutes |
| Docker build (cached) | < 1 minute |
| Deploy + rollout | < 3 minutes |
| Total | < 5 minutes |