CI/CD Integration
Code push → automated testing → automated image building → automated deployment—CI/CD transforms the release process from manual operations into an automated pipeline.
1. What You'll Learn
- GitHub Actions Basic Workflows
- Docker buildx cross-platform builds
- CI Caching Policy
- Multi-environment deployment process
- Automatic Tagging and Version Control for Mirrors
2. A Developer’s True Story
(1) Pain Point: Manual SSH operations required for every deployment
Every time she deploys, Alice has to manually SSH into the server, pull the code, build the image, and restart the container. The process is: ssh server → git pull → docker build → docker stop → docker run. Each deployment takes 15 minutes, and with 3–4 releases per week, she wastes a total of 1 hour per week on repetitive tasks. To make matters worse, she once accidentally entered the wrong command during a midnight release.
(2) A Solution Using GitHub Actions CI/CD
Bob wrote a workflow using GitHub Actions: push code → automated testing → build image → push to repository → SSH deployment.
# .github/workflows/deploy.yml
name: Build and Deploy
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/build-push-action@v5
with:
push: true
tags: myorg/myapp:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
(3) Time spent: 1 hour/week → 0 minutes
Deployment is fully automated—Alice simply pushes the code, and it goes live automatically within 5 minutes. Manual work has been reduced from 1 hour per week to 0, with zero human error.
3. GitHub Actions Workflow Structure
(1) Workflow YAML Structure
graph LR
PUSH["git push"] --> TRIGGER["on: push"]
TRIGGER --> JOB1["Job: test"]
JOB1 --> JOB2["Job: build"]
JOB2 --> JOB3["Job: deploy"]
| Field | Purpose | Example |
|---|---|---|
name |
Workflow Name | Build and Deploy |
on |
Trigger Condition | push, pull_request |
jobs |
Mission Definition | build, test, deploy |
runs-on |
Runtime Environment | ubuntu-latest |
steps |
Execution steps | checkout, build, push |
(1) Comparison of Three CI/CD Strategies
| Platform | Features | Use Cases |
|---|---|---|
| GitHub Actions | Native GitHub integration, 2,000 free minutes/month | Open-source projects + GitHub hosting |
| GitLab CI | GitLab native, self-hosted Runner | On-premises GitLab |
| Jenkins | A long-established CI/CD platform with a wide range of plugins | Traditional enterprises + complex pipelines |
4. Docker buildx Cross-Platform Building
▶ Example: buildx multi-architecture builds (Difficulty: ⭐⭐⭐)
# Create buildx builder instance
docker buildx create --name multiarch --use
# Build for amd64 + arm64 simultaneously
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t myorg/myapp:latest \
--push .
# Inspect built platforms
docker buildx imagetools inspect myorg/myapp:latest
(1) buildx vs 普Through build
| Dimension | docker build | docker buildx |
|---|---|---|
| Multiple Architectures | ❌ Can only build the current architecture | ✅ Build multiple architectures simultaneously |
| Cache Export | ❌ Local Cache | ✅ gha/registry/s3 |
| Output Format | Local Image | Image/OCI/tar/registry |
| Parallel Build | ❌ | ✅ |
5. CI Caching Strategy
(1) Comparison of Cache Types
| Type | Syntax | Position | Usage Scenarios |
|---|---|---|---|
| gha | cache-from: type=gha |
GitHub Actions Cache | GitHub Actions CI |
| registry | cache-from: type=registry |
Docker Registry | Any CI Platform |
| local | cache-from: type=local |
Local file system | Self-hosted Runner |
▶ Example: CI Cache Configuration (Difficulty: ⭐⭐⭐)
# GitHub Actions with build cache
- uses: docker/build-push-action@v5
with:
context: .
push: true
tags: myorg/myapp:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
▶ Example: Automatic Tagging Policy (Difficulty: ⭐⭐)
# Docker metadata action for automatic tagging
- uses: docker/metadata-action@v5
id: meta
with:
images: myorg/myapp
tags: |
type=sha,prefix=
type=ref,event=branch
type=semver,pattern={{version}}
type=raw,value=latest,enable={{is_default_branch}}
6. Steps for SSH Deployment
▶ Example: Deploying via SSH to a Server (Difficulty: ⭐⭐⭐)
# Deploy step in GitHub Actions
- name: Deploy to server
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
cd /opt/myapp
docker compose pull
docker compose up -d --remove-orphans
docker image prune -f
7. Complete Example: CI/CD for a Node.js Application
# ============================================
# .github/workflows/deploy.yml
# Full CI/CD: test → build → push → deploy
# ============================================
name: CI/CD Pipeline
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
REGISTRY: docker.io
IMAGE_NAME: myorg/myapp
jobs:
# ---------- Job 1: Test ----------
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run tests
run: |
docker build --target builder -t test-image .
docker run test-image npm test
# ---------- Job 2: Build & Push ----------
build:
needs: test
runs-on: ubuntu-latest
if: github.event_name == 'push'
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- uses: docker/metadata-action@v5
id: meta
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=sha,prefix=
type=raw,value=latest
- 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
# ---------- Job 3: Deploy ----------
deploy:
needs: build
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- name: Deploy via SSH
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
cd /opt/myapp
docker compose pull
docker compose up -d --remove-orphans
docker image prune -f
echo "Deployed at $(date)"
❓ FAQ
buildx and a regular build?buildx is a CLI extension for Docker BuildKit. Its main advantages are: ① Simultaneous builds for multiple architectures (amd64+arm64); ② Advanced cache export (gha/registry); ③ Parallel builds. GitHub Actions recommends always using buildx.cache-from: type=gha (GitHub Actions) or type=registry (general-purpose). GHA caches are stored in the GitHub Actions Cache and are automatically shared among workflows in the same repository. Registry caches are stored in a special manifest within the registry and can be used by any CI platform.docker buildx build --platform linux/amd64,linux/arm64 -t myapp:latest --push .. You’ll need the QEMU emulator (docker/setup-qemu-action) and the buildx builder. The build output is a manifest list, which automatically selects the image for the current architecture during docker pull.${{ secrets.XXX }}. Secrets are stored in encrypted form and automatically masked in logs.docker compose rollback (Swarm) or manually switch back to the previous version; ② Keep the latest N versions tagged in the image; to roll back, simply use docker compose up -d myapp:previous-sha. Automatic rollback is supported natively in Swarm and K8s.📖 Summary
- GitHub Actions workflow: a three-tier structure of on(trigger) → jobs → steps
docker buildxsupports multi-architecture builds and advanced cache exportcache-from: type=ghaAccelerating Builds Using GitHub Actions Caching- Automatic tags: Git SHA + semver + latest multi-tag strategy
- SSH Deployment: Use
secretsto manage sensitive information; usecompose pull + upto achieve zero-downtime updates - Complete pipeline: test → build → push → deploy; “push” means going live
📝 Exercises
- Basic Exercise (Difficulty: ⭐): Write a GitHub Actions build workflow for the Flask app from Lesson 12 so that it automatically builds and pushes the image when a commit is pushed to the main branch.
- Advanced Exercise (Difficulty: ⭐⭐): Use
buildxto build an amd64+arm64 dual-architecture image and push it to Docker Hub. - Challenge (Difficulty: ⭐⭐⭐): Set up a complete CI/CD pipeline (test → build → SSH deployment), use GitHub Secrets to manage server keys, and verify that deployment occurs automatically after a push.



