Skip to content
On this page

GitHub Actions Docker集成

Docker集成是GitHub Actions中强大的功能,允许您在工作流中使用Docker容器来运行应用程序、服务或构建过程。本文档将详细介绍如何在GitHub Actions中使用Docker。

Docker基础集成

在作业中使用Docker

yaml
# .github/workflows/docker-example.yml
name: Docker Integration Example

on: [push, pull_request]

jobs:
  docker-basic:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      
      - name: Verify Docker is available
        run: |
          docker --version
          docker info
      
      - name: Run a simple Docker container
        run: |
          docker run hello-world
      
      - name: Run container with volume mount
        run: |
          docker run -v ${{ github.workspace }}:/workspace -w /workspace node:18-alpine ls -la

使用Docker容器作为作业环境

yaml
jobs:
  container-job:
    runs-on: ubuntu-latest
    container:
      image: node:18-alpine
      env:
        NODE_ENV: production
      options: --user 1001  # 运行在非root用户下
    steps:
      - name: Check container environment
        run: |
          node --version
          npm --version
          whoami
          pwd
      
      - name: Install and run application
        run: |
          npm install
          npm run build

Docker镜像构建

基本镜像构建

yaml
jobs:
  build-image:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
      
      - name: Build Docker image
        run: |
          docker build -t myapp:${{ github.sha }} .
          docker images
      
      - name: Run built image
        run: |
          docker run --rm myapp:${{ github.sha }} node --version

使用Docker Buildx

yaml
jobs:
  buildx-multi-arch:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
      
      - name: Build and export to Docker daemon
        uses: docker/build-push-action@v5
        with:
          context: .
          load: true  # 导出到Docker守护进程
          tags: myapp:latest
      
      - name: Run built image
        run: docker run --rm myapp:latest

构建并推送到容器注册表

yaml
jobs:
  build-and-push:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
      
      - name: Login to DockerHub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}
      
      - name: Extract metadata for Docker
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: myusername/myapp
          tags: |
            type=ref,event=branch
            type=ref,event=pr
            type=sha,prefix={{branch}}-
            type=raw,value=latest,enable={{is_default_branch}}
      
      - name: Build and push Docker image
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}

Docker服务容器

使用服务容器

yaml
jobs:
  test-with-services:
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres:13
        env:
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: testdb
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
      redis:
        image: redis:alpine
        options: >-
          --health-cmd "redis-cli ping"
          --health-interval 5s
          --health-timeout 3s
          --health-retries 5
    
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '18'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Run tests with services
        run: npm test
        env:
          DATABASE_URL: postgresql://postgres:postgres@localhost:5432/testdb
          REDIS_URL: redis://localhost:6379

多服务配置

yaml
jobs:
  multi-service-app:
    runs-on: ubuntu-latest
    services:
      # 数据库服务
      database:
        image: mysql:8.0
        env:
          MYSQL_ROOT_PASSWORD: rootpassword
          MYSQL_DATABASE: myapp_test
        options: >-
          --health-cmd "mysqladmin ping -h localhost -u root -prootpassword"
          --health-interval 10s
          --health-timeout 5s
          --health-retries 10
      
      # 消息队列服务
      rabbitmq:
        image: rabbitmq:3.9-management
        env:
          RABBITMQ_DEFAULT_USER: guest
          RABBITMQ_DEFAULT_PASS: guest
        options: >-
          --health-cmd "rabbitmqctl ping"
          --health-interval 10s
          --health-timeout 5s
          --health-retries 10
      
      # 缓存服务
      memcached:
        image: memcached:1.6-alpine
        options: >-
          --health-cmd "echo stats | nc localhost 11211"
          --health-interval 5s
          --health-timeout 3s
          --health-retries 5
    
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      
      - name: Setup application
        run: |
          # 配置应用连接到服务
          echo "DATABASE_HOST=localhost" >> .env
          echo "RABBITMQ_HOST=localhost" >> .env
          echo "MEMCACHED_HOST=localhost" >> .env

高级Docker配置

Docker守护进程配置

yaml
jobs:
  docker-config:
    runs-on: ubuntu-latest
    steps:
      - name: Configure Docker daemon
        run: |
          # 重启Docker服务并应用配置
          echo '{"log-driver":"json-file","log-opts":{"max-size":"10m","max-file":"3"}}' | sudo tee /etc/docker/daemon.json
          sudo systemctl restart docker
          
          # 验证配置
          docker info
          docker system df
      
      - name: Run container with custom options
        run: |
          docker run --rm -m 512m --cpus="0.5" alpine:latest free -m

Docker缓存优化

yaml
jobs:
  docker-cache:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
      
      - name: Cache Docker layers
        uses: actions/cache@v4
        with:
          path: /tmp/.buildx-cache
          key: ${{ runner.os }}-buildx-${{ github.sha }}
          restore-keys: |
            ${{ runner.os }}-buildx-
      
      - name: Build with cache
        uses: docker/build-push-action@v5
        with:
          context: .
          load: true
          tags: myapp:latest
          cache-from: type=local,src=/tmp/.buildx-cache
          cache-to: type=local,dest=/tmp/.buildx-cache-new
      
      - name: Move cache
        run: |
          rm -rf /tmp/.buildx-cache
          mv /tmp/.buildx-cache-new /tmp/.buildx-cache

多阶段构建

text
# Dockerfile
# 构建阶段
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build

# 运行阶段
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 3000
CMD ["node", "dist/index.js"]
yaml
jobs:
  multi-stage-build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
      
      - name: Build multi-stage image
        run: |
          docker build --target builder -t myapp:builder .
          docker build --target final -t myapp:final .
          
          # 验证构建结果
          docker images

Docker Compose集成

使用Docker Compose

yaml
# docker-compose.test.yml
version: '3.8'
services:
  app:
    build: .
    environment:
      - DATABASE_URL=postgresql://postgres:password@db:5432/test
      - REDIS_URL=redis://redis:6379
    depends_on:
      - db
      - redis
  
  db:
    image: postgres:13
    environment:
      POSTGRES_DB: test
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: password
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      timeout: 5s
      retries: 5
  
  redis:
    image: redis:alpine
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5
yaml
jobs:
  docker-compose-test:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      
      - name: Start services with Docker Compose
        run: |
          docker-compose -f docker-compose.test.yml up -d
          # 等待服务启动
          sleep 30
      
      - name: Run tests
        run: |
          # 执行测试
          docker-compose -f docker-compose.test.yml exec app npm test
      
      - name: View logs
        if: always()
        run: docker-compose -f docker-compose.test.yml logs
      
      - name: Cleanup
        if: always()
        run: docker-compose -f docker-compose.test.yml down -v

安全最佳实践

安全镜像扫描

yaml
jobs:
  security-scan:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      
      - name: Build Docker image
        run: docker build -t myapp:${{ github.sha }} .
      
      - name: Run Trivy security scan
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: 'myapp:${{ github.sha }}'
          format: 'table'
          exit-code: '1'
          severity: 'CRITICAL,HIGH'
      
      - name: Run Snyk container scan
        uses: snyk/actions/docker@master
        env:
          SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
        with:
          image: 'myapp:${{ github.sha }}'
          args: --file=Dockerfile

非root用户运行

text
# Dockerfile
FROM node:18-alpine

# 创建非root用户
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001

# 设置工作目录
WORKDIR /app

# 复制文件并设置权限
COPY --chown=nextjs:nodejs package*.json ./
USER nextjs
RUN npm ci --only=production
COPY --chown=nextjs:nodejs . .
RUN npm run build

EXPOSE 3000
CMD ["node", "server.js"]
yaml
jobs:
  secure-container:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      
      - name: Build and run as non-root
        run: |
          docker build -t secure-app .
          docker run --rm -u 1001:1001 secure-app node --version

实际应用示例

完整的Docker CI/CD流程

yaml
name: Docker CI/CD Pipeline

on:
  push:
    branches: [main, develop]
    tags: ['v*']
  pull_request:
    branches: [main]

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  test:
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres:13
        env:
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: testdb
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
      redis:
        image: redis:alpine
        options: >-
          --health-cmd "redis-cli ping"
          --health-interval 5s
          --health-timeout 3s
          --health-retries 5
    
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '18'
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Run unit tests
        run: npm run test:unit
        env:
          DATABASE_URL: postgresql://postgres:postgres@localhost:5432/testdb
          REDIS_URL: redis://localhost:6379
      
      - name: Run integration tests
        run: npm run test:integration

  build-and-push:
    needs: test
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      
      - name: Setup Docker Buildx
        uses: docker/setup-buildx-action@v3
      
      - name: Log in to Container Registry
        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=ref,event=branch
            type=ref,event=pr
            type=sha,prefix={{branch}}-
            type=raw,value=latest,enable={{is_default_branch}}
            type=semver,pattern={{version}}
            type=semver,pattern={{major}}.{{minor}}
      
      - name: Build and push Docker image
        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

  deploy-staging:
    needs: build-and-push
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/develop'
    
    steps:
      - name: Deploy to staging
        run: |
          echo "Deploying image ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} to staging"
          # 部署逻辑(例如Kubernetes, ECS等)

  deploy-production:
    needs: build-and-push
    runs-on: ubuntu-latest
    if: startsWith(github.ref, 'refs/tags/v')
    
    steps:
      - name: Deploy to production
        run: |
          echo "Deploying image ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }} to production"
          # 生产部署逻辑

数据库迁移和种子

yaml
jobs:
  db-migration:
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres:13
        env:
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: myapp
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
    
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      
      - name: Build app image
        run: docker build -t myapp .
      
      - name: Run database migrations
        run: |
          docker run --network host myapp npx prisma migrate deploy
          # 或者运行其他迁移命令

通过合理使用Docker集成,可以创建可重复、可移植和高效的构建、测试和部署流程,确保应用程序在不同环境中的一致性。