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


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.

YAML
# .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

100%
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: ⭐⭐⭐)

BASH
# 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: ⭐⭐⭐)

YAML
# 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: ⭐⭐)

YAML
# 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: ⭐⭐⭐)

YAML
# 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
🔒 Security: Store sensitive information such as server addresses and SSH keys using GitHub Secrets; do not include them in the YAML file. Add them under Settings → Secrets and variables → Actions.**


7. Complete Example: CI/CD for a Node.js Application

YAML
# ============================================
# .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

Q What is the difference between buildx and a regular build?
A 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.
Q How do you share Docker layer caches in CI?
A Use 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.
Q How do I build multi-architecture images?
A 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.
Q How are sensitive credentials managed in CI/CD?
A GitHub Secrets (Settings → Secrets). SSH keys, database passwords, and registry credentials are all stored in Secrets and referenced in YAML using ${{ secrets.XXX }}. Secrets are stored in encrypted form and automatically masked in logs.
Q How can I automatically roll back in case of a deployment failure?
A There are two ways: ① Add a health check to the deployment script; if it fails, use 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


📝 Exercises

  1. 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.
  2. Advanced Exercise (Difficulty: ⭐⭐): Use buildx to build an amd64+arm64 dual-architecture image and push it to Docker Hub.
  3. 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.
Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏