Docker集成 #

一、GitLab Container Registry #

启用Container Registry #

  1. 进入项目Settings → Packages & Registries
  2. 启用Container Registry

镜像命名规范 #

text
registry.example.com/group/project:tag
│                    │      │      │
│                    │      │      └── 标签
│                    │      └── 项目名
│                    └── 组名
└── Registry地址

常用镜像标签 #

标签 说明
latest 最新版本
main 主分支构建
develop 开发分支构建
v1.0.0 版本标签
abc123 Commit SHA

二、Docker构建 #

基本构建 #

yaml
stages:
  - build

docker_build:
  stage: build
  image: docker:latest
  services:
    - docker:dind
  script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA

多阶段构建 #

Dockerfile:

dockerfile
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
EXPOSE 80
yaml
docker_build:
  stage: build
  image: docker:latest
  services:
    - docker:dind
  script:
    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA

缓存优化 #

yaml
docker_build:
  script:
    - docker pull $CI_REGISTRY_IMAGE:latest || true
    - docker build --cache-from $CI_REGISTRY_IMAGE:latest -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA

BuildKit构建 #

yaml
docker_build:
  variables:
    DOCKER_BUILDKIT: 1
  script:
    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .

三、多架构构建 #

使用docker buildx #

yaml
docker_build:
  image: docker:latest
  services:
    - docker:dind
  variables:
    DOCKER_CLI_EXPERIMENTAL: enabled
  before_script:
    - docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
    - docker buildx create --use
  script:
    - docker buildx build --platform linux/amd64,linux/arm64 -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA --push .

矩阵并行构建 #

yaml
docker_build:
  parallel:
    matrix:
      - ARCH: [amd64, arm64]
  image: docker:latest
  services:
    - docker:dind
  script:
    - docker build --platform linux/$ARCH -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA-$ARCH .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA-$ARCH

manifest:
  stage: manifest
  image: docker:latest
  services:
    - docker:dind
  needs:
    - job: docker_build
      artifacts: false
  script:
    - docker manifest create $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA \
        $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA-amd64 \
        $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA-arm64
    - docker manifest push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA

四、镜像扫描 #

Trivy扫描 #

yaml
container_scanning:
  stage: test
  image: docker:latest
  services:
    - docker:dind
  variables:
    IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
  script:
    - docker pull $IMAGE
    - |
      docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
        aquasec/trivy image --exit-code 1 --severity HIGH,CRITICAL $IMAGE
  allow_failure: true

GitLab Container Scanning #

yaml
include:
  - template: Jobs/Container-Scanning.gitlab-ci.yml

variables:
  CS_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA

Clair扫描 #

yaml
container_scanning:
  stage: test
  image: docker:latest
  services:
    - docker:dind
    - name: arminc/clair-db:latest
      alias: postgres
    - name: arminc/clair-local-scan:v2.0.1
      alias: clair
  script:
    - docker pull $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
    - |
      wget -O clair-scanner https://github.com/arminc/clair-scanner/releases/download/v12/clair-scanner_linux_amd64
      chmod +x clair-scanner
      ./clair-scanner -c http://clair:6060 --ip $(hostname -i) -r gl-container-scanning-report.json \
        -l clair.log $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA || true
  artifacts:
    reports:
      container_scanning: gl-container-scanning-report.json

五、镜像推送策略 #

分支推送 #

yaml
docker_build:
  script:
    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
    - |
      if [ "$CI_COMMIT_BRANCH" == "main" ]; then
        docker tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA $CI_REGISTRY_IMAGE:latest
        docker push $CI_REGISTRY_IMAGE:latest
      elif [ "$CI_COMMIT_BRANCH" == "develop" ]; then
        docker tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA $CI_REGISTRY_IMAGE:develop
        docker push $CI_REGISTRY_IMAGE:develop
      fi

标签推送 #

yaml
docker_build:
  script:
    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG
  only:
    - tags

版本管理 #

yaml
docker_build:
  script:
    - |
      if [[ $CI_COMMIT_TAG =~ ^v([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then
        MAJOR="${BASH_REMATCH[1]}"
        MINOR="${BASH_REMATCH[2]}"
        PATCH="${BASH_REMATCH[3]}"
        
        docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG .
        docker tag $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG $CI_REGISTRY_IMAGE:$MAJOR.$MINOR
        docker tag $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG $CI_REGISTRY_IMAGE:$MAJOR
        
        docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG
        docker push $CI_REGISTRY_IMAGE:$MAJOR.$MINOR
        docker push $CI_REGISTRY_IMAGE:$MAJOR
      fi
  only:
    - tags

六、Docker Compose #

构建和测试 #

yaml
test:
  stage: test
  image: docker:latest
  services:
    - docker:dind
  script:
    - docker-compose -f docker-compose.test.yml up --abort-on-container-exit

docker-compose.test.yml:

yaml
version: '3.8'
services:
  app:
    build: .
    depends_on:
      - db
      - redis
    environment:
      - DATABASE_URL=postgres://test:test@db:5432/test
      - REDIS_URL=redis://redis:6379
    command: npm test

  db:
    image: postgres:14
    environment:
      - POSTGRES_DB=test
      - POSTGRES_USER=test
      - POSTGRES_PASSWORD=test

  redis:
    image: redis:7

部署 #

yaml
deploy:
  stage: deploy
  image: docker:latest
  services:
    - docker:dind
  script:
    - docker-compose -f docker-compose.prod.yml pull
    - docker-compose -f docker-compose.prod.yml up -d

七、完整示例 #

yaml
stages:
  - lint
  - build
  - test
  - scan
  - push
  - deploy

variables:
  DOCKER_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA

lint_dockerfile:
  stage: lint
  image: hadolint/hadolint:latest-debian
  script:
    - hadolint Dockerfile
  allow_failure: true

docker_build:
  stage: build
  image: docker:latest
  services:
    - docker:dind
  script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
    - docker pull $CI_REGISTRY_IMAGE:latest || true
    - docker build --cache-from $CI_REGISTRY_IMAGE:latest -t $DOCKER_IMAGE .
    - docker push $DOCKER_IMAGE

container_scanning:
  stage: scan
  image: docker:latest
  services:
    - docker:dind
  script:
    - docker pull $DOCKER_IMAGE
    - |
      docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
        aquasec/trivy image --exit-code 0 --severity HIGH,CRITICAL -o trivy-report.json $DOCKER_IMAGE
  artifacts:
    paths:
      - trivy-report.json
    expire_in: 1 week
  allow_failure: true

push_latest:
  stage: push
  image: docker:latest
  services:
    - docker:dind
  script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
    - docker pull $DOCKER_IMAGE
    - docker tag $DOCKER_IMAGE $CI_REGISTRY_IMAGE:latest
    - docker push $CI_REGISTRY_IMAGE:latest
  only:
    - main

deploy_staging:
  stage: deploy
  image: docker:latest
  services:
    - docker:dind
  environment:
    name: staging
    url: https://staging.example.com
  script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
    - docker pull $DOCKER_IMAGE
    - docker tag $DOCKER_IMAGE $CI_REGISTRY_IMAGE:staging
    - docker push $CI_REGISTRY_IMAGE:staging
    - |
      ssh deploy@staging.example.com << EOF
        docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
        docker pull $CI_REGISTRY_IMAGE:staging
        docker stop myapp || true
        docker rm myapp || true
        docker run -d --name myapp -p 3000:3000 $CI_REGISTRY_IMAGE:staging
      EOF
  only:
    - develop

deploy_production:
  stage: deploy
  image: bitnami/kubectl:latest
  environment:
    name: production
    url: https://example.com
  script:
    - kubectl set image deployment/myapp myapp=$DOCKER_IMAGE
    - kubectl rollout status deployment/myapp
  only:
    - main
  when: manual

八、最佳实践 #

1. 使用多阶段构建 #

dockerfile
FROM node:18 AS builder
# 构建阶段

FROM nginx:alpine
# 运行阶段

2. 使用alpine镜像 #

dockerfile
FROM node:18-alpine

3. 缓存优化 #

yaml
script:
  - docker pull $CI_REGISTRY_IMAGE:latest || true
  - docker build --cache-from $CI_REGISTRY_IMAGE:latest -t $IMAGE .

4. 安全扫描 #

yaml
include:
  - template: Jobs/Container-Scanning.gitlab-ci.yml

5. 镜像清理 #

yaml
cleanup:
  stage: cleanup
  image: docker:latest
  script:
    - docker image prune -af
  when: always

下一步 #

现在你已经掌握了Docker集成,接下来让我们学习 Kubernetes部署

最后更新:2026-03-28