Skip to content

Version Control and CI/CD

Introduction

Modern software development relies heavily on version control and continuous integration/continuous deployment (CI/CD). This article covers Git workflows, GitHub Actions, Docker containerization, and Kubernetes basics.


1. Git Version Control

1.1 Core Git Concepts

Working Directory
    │ git add
    ▼
Staging Area (Index)
    │ git commit
    ▼
Local Repository
    │ git push
    ▼
Remote Repository

1.2 Branching Models

Git Flow

gitGraph
    commit id: "init"
    branch develop
    checkout develop
    commit id: "feat-start"
    branch feature/login
    checkout feature/login
    commit id: "login-impl"
    commit id: "login-test"
    checkout develop
    merge feature/login id: "merge-login"
    branch release/1.0
    checkout release/1.0
    commit id: "bugfix"
    checkout main
    merge release/1.0 id: "v1.0" tag: "v1.0"
    checkout develop
    merge release/1.0 id: "sync"
Branch Purpose Lifecycle
main Production code Permanent
develop Development integration Permanent
feature/* New feature development Temporary
release/* Release preparation Temporary
hotfix/* Emergency fixes Temporary

Trunk-Based Development

main ──●──●──●──●──●──●──●──●──
       │     │        │
       └─●─┘ └──●──┘  └─●─┘
       Short-lived branches (< 1-2 days)
  • The trunk is always in a deployable state
  • Branches have very short lifespans (hours to a couple of days)
  • Relies on feature flags to hide incomplete features
  • Suited for teams with mature CI/CD practices

1.3 Merge vs Rebase

Merge: creates a merge commit, preserving complete history

git checkout main
git merge feature/login
# creates a merge commit

Rebase: "transplants" branch commits onto the target branch

git checkout feature/login
git rebase main
# replays feature commits on top of main
git checkout main
git merge feature/login  # fast-forward
Approach History Advantages Disadvantages
Merge Non-linear Preserves true history History can be messy
Rebase Linear Clean history Rewrites history; caution needed in collaboration

1.4 Cherry-pick

Applies specific commits to other branches:

git checkout main
git cherry-pick abc1234     # apply a specific commit to main
git cherry-pick abc..def    # apply a range

Use cases: syncing hotfixes from main to develop, or selectively porting features.


2. GitHub Actions

2.1 Workflow Basic Structure

# .github/workflows/ci.yml
name: CI Pipeline

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

env:
  PYTHON_VERSION: '3.11'

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ['3.10', '3.11', '3.12']

    steps:
      - uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}

      - name: Cache dependencies
        uses: actions/cache@v4
        with:
          path: ~/.cache/pip
          key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }}

      - name: Install dependencies
        run: pip install -r requirements.txt

      - name: Run tests
        run: pytest --cov=src tests/

      - name: Upload coverage
        uses: codecov/codecov-action@v4

  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: pip install ruff
      - run: ruff check src/

  deploy:
    needs: [test, lint]
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    steps:
      - uses: actions/checkout@v4
      - name: Deploy to production
        run: ./deploy.sh

2.2 Commonly Used Actions

Action Purpose
actions/checkout Check out code
actions/setup-python Configure Python
actions/setup-node Configure Node.js
actions/cache Cache dependencies
docker/build-push-action Build and push Docker images
peaceiris/actions-gh-pages Deploy to GitHub Pages

3. Docker

3.1 Core Concepts

Dockerfile → docker build → Image → docker run → Container
  (recipe)                   (image)                (running instance)
Concept Analogy Description
Image Class Read-only template containing app and dependencies
Container Instance Running instance of an Image, read-write
Dockerfile Blueprint Defines how to build an Image
Registry Repository Stores and distributes Images (e.g., Docker Hub)

3.2 Dockerfile Best Practices

# Multi-stage build
FROM python:3.11-slim AS builder

WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir --user -r requirements.txt

FROM python:3.11-slim AS runtime

# Non-root user
RUN useradd --create-home appuser
USER appuser

WORKDIR /app
COPY --from=builder /root/.local /home/appuser/.local
COPY . .

ENV PATH=/home/appuser/.local/bin:$PATH

EXPOSE 8000
HEALTHCHECK CMD curl -f http://localhost:8000/health || exit 1

CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

Optimization tips:

  • Leverage build cache: COPY dependency files first, then source code
  • Multi-stage builds to reduce image size
  • Use .dockerignore to exclude unnecessary files
  • Choose slim or alpine base images

3.3 Docker Compose

# docker-compose.yml
version: '3.8'

services:
  web:
    build: .
    ports:
      - "8000:8000"
    environment:
      - DATABASE_URL=postgresql://user:pass@db:5432/mydb
      - REDIS_URL=redis://redis:6379
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started

  db:
    image: postgres:16
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: pass
      POSTGRES_DB: mydb
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U user"]
      interval: 5s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7-alpine

volumes:
  pgdata:

4. Kubernetes Basics

4.1 Core Concepts

Concept Description
Pod Smallest deployable unit, contains one or more containers
Service Provides stable network access to Pods
Deployment Manages Pod replicas, updates, and rollbacks
ConfigMap/Secret Configuration and sensitive data management
Namespace Virtual cluster for resource isolation
Ingress HTTP/HTTPS external access entry point

4.2 Basic Resource Definitions

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
  labels:
    app: web
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: web
          image: myapp:v1.0
          ports:
            - containerPort: 8000
          resources:
            requests:
              cpu: 100m
              memory: 128Mi
            limits:
              cpu: 500m
              memory: 512Mi
          livenessProbe:
            httpGet:
              path: /health
              port: 8000
            initialDelaySeconds: 10
            periodSeconds: 30
---
apiVersion: v1
kind: Service
metadata:
  name: web-service
spec:
  selector:
    app: web
  ports:
    - port: 80
      targetPort: 8000
  type: ClusterIP

4.3 Rolling Updates

# Update image version
kubectl set image deployment/web-app web=myapp:v2.0

# Check update status
kubectl rollout status deployment/web-app

# Rollback
kubectl rollout undo deployment/web-app

5. Complete CI/CD Pipeline

graph LR
    A[Code Commit] --> B[CI: Build]
    B --> C[CI: Test]
    C --> D[CI: Code Quality]
    D --> E[CD: Build Image]
    E --> F[CD: Push to Registry]
    F --> G{Environment}
    G -->|staging| H[Deploy to Staging]
    H --> I[Integration Tests]
    I --> J[Manual Approval]
    J --> K[Deploy to Production]
    G -->|direct| K

    style A fill:#f9f
    style K fill:#bfb

5.1 Full Example: GitHub Actions + Docker + K8s

name: Deploy Pipeline

on:
  push:
    branches: [main]

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Run tests
        run: |
          pip install -r requirements.txt
          pytest tests/

      - name: Build Docker image
        run: |
          docker build -t myregistry/myapp:${{ github.sha }} .

      - name: Push to registry
        run: |
          echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
          docker push myregistry/myapp:${{ github.sha }}

      - name: Deploy to Kubernetes
        uses: azure/k8s-deploy@v5
        with:
          manifests: k8s/
          images: myregistry/myapp:${{ github.sha }}

6. Best Practices

Git Standards

  • Commit messages: use Conventional Commits (feat:, fix:, docs:, refactor:)
  • Branch protection: main branch requires PR + code review + CI passing
  • Signed commits: GPG-sign important commits

CI/CD Principles

  • Fast feedback: tests should complete within 10 minutes
  • Immutable artifacts: the same build artifact is deployed to all environments
  • Infrastructure as Code: all configuration under version control
  • Progressive delivery: canary releases, blue-green deployments

Relations to Other Topics

References

  • "Pro Git" - Scott Chacon & Ben Straub
  • GitHub Actions Official Documentation
  • Docker Official Documentation
  • Kubernetes Official Documentation

评论 #