Appearance
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集成,可以创建可重复、可移植和高效的构建、测试和部署流程,确保应用程序在不同环境中的一致性。