CI/CD 相关配置
约 1228 字大约 4 分钟
2025-11-04
Node(nest) Dockerfile
dockerfile
# ===========================
# 构建阶段
# ===========================
FROM node:20-alpine AS builder
WORKDIR /app
# 复制依赖文件
COPY package*.json ./
# 安装依赖(包含 devDependencies)
RUN npm ci
# 复制源代码
COPY . .
# 构建 Nest 应用
RUN npm run build
# ===========================
# 运行阶段
# ===========================
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
# 复制 package.json 以安装生产依赖
COPY package*.json ./
# 仅安装生产依赖
RUN npm ci --omit=dev && npm cache clean --force
# 从构建阶段复制编译结果
COPY --from=builder /app/dist ./dist
EXPOSE 3000
CMD ["node", "dist/main.js"].dockerignore
node_modules
dist
.git
.gitignore
.vscode
test
Dockerfile
docker-compose*
*.md
.envGHCR 推送工作流模板
name: Build and Push to GHCR
on:
push:
branches:
- main
- master
- release/*
workflow_dispatch:
inputs:
image_name:
description: 'Docker image name (e.g., nestapp)'
required: false
default: yumengjh/nestapp
tag:
description: 'Docker tag (e.g., 1.0.0)'
required: false
default: 1.0.0
platforms:
description: 'Build platforms (comma separated)'
required: false
default: 'linux/amd64,linux/arm64'
build_args:
description: 'Build arguments (key=value,key=value)'
required: false
context:
description: 'Build context path'
required: false
default: '.'
dockerfile:
description: 'Dockerfile path'
required: false
default: './Dockerfile'
env:
DEFAULT_REGISTRY_URL: ghcr.io
DEFAULT_REGISTRY_USERNAME: ${{ github.actor }}
DEFAULT_REGISTRY_PASSWORD: ${{ secrets.GH_TOKEN }}
jobs:
build-and-push:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Prepare variables
id: vars
run: |
# 设置默认值
IMAGE_NAME="${{ github.event.inputs.image_name || 'yumengjh/nestapp' }}"
TAG="${{ github.event.inputs.tag || '1.0.0' }}"
PLATFORMS="${{ github.event.inputs.platforms || 'linux/amd64,linux/arm64' }}"
CONTEXT="${{ github.event.inputs.context || '.' }}"
DOCKERFILE="${{ github.event.inputs.dockerfile || './Dockerfile' }}"
BUILD_ARGS="${{ github.event.inputs.build_args }}"
echo "IMAGE_NAME=$IMAGE_NAME" >> $GITHUB_ENV
echo "TAG=$TAG" >> $GITHUB_ENV
echo "PLATFORMS=$PLATFORMS" >> $GITHUB_ENV
echo "CONTEXT=$CONTEXT" >> $GITHUB_ENV
echo "DOCKERFILE=$DOCKERFILE" >> $GITHUB_ENV
echo "BUILD_ARGS=$BUILD_ARGS" >> $GITHUB_ENV
# Registry info
echo "REGISTRY_URL=${{ env.DEFAULT_REGISTRY_URL }}" >> $GITHUB_ENV
echo "REGISTRY_USERNAME=${{ env.DEFAULT_REGISTRY_USERNAME }}" >> $GITHUB_ENV
echo "REGISTRY_PASSWORD=${{ env.DEFAULT_REGISTRY_PASSWORD }}" >> $GITHUB_ENV
- name: Show build configuration
run: |
echo "-------------------------------------------"
echo "Registry URL: $REGISTRY_URL"
echo "Image: $IMAGE_NAME"
echo "Tag: $TAG"
echo "Platforms: $PLATFORMS"
echo "Context: $CONTEXT"
echo "Dockerfile: $DOCKERFILE"
echo "Build Args: $BUILD_ARGS"
echo "-------------------------------------------"
- name: Login to GHCR
run: |
echo "$REGISTRY_PASSWORD" | docker login "$REGISTRY_URL" -u "$REGISTRY_USERNAME" --password-stdin
- name: Build and push multi-arch image
uses: docker/build-push-action@v6
with:
context: ${{ env.CONTEXT }}
file: ${{ env.DOCKERFILE }}
push: true
platforms: ${{ env.PLATFORMS }}
tags: |
${{ env.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:${{ env.TAG }}
${{ env.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:latest
build-args: ${{ env.BUILD_ARGS }}
- name: Build summary
if: success()
run: |
echo "-------------------------------------------"
echo "Build and push completed successfully."
echo "Registry URL: $REGISTRY_URL"
echo "Image: $IMAGE_NAME"
echo "Tag: $TAG"
echo "Platforms: $PLATFORMS"
echo "-------------------------------------------"ACR 推送工作流模板
name: Build & Push to ACR
on:
push:
branches:
- main
- master
- release/*
workflow_dispatch:
inputs:
image_name:
description: 'Docker image name'
required: false
default: nestapp
tag:
description: 'Docker tag'
required: false
default: 1.0.0
platforms:
description: 'Build platforms (comma separated)'
required: false
default: linux/amd64,linux/arm64
env:
ACR_URL: ${{ secrets.ACR_REGISTRY }}
ACR_USERNAME: ${{ secrets.ACR_USERNAME }}
ACR_PASSWORD: ${{ secrets.ACR_PASSWORD }}
jobs:
build-and-push:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- name: Login to ACR
run: |
echo "$ACR_PASSWORD" | docker login $ACR_URL -u $ACR_USERNAME --password-stdin
- name: Build and push multi-arch image
uses: docker/build-push-action@v6
with:
context: .
file: ./Dockerfile
push: true
platforms: ${{ github.event.inputs.platforms }}
tags: |
$ACR_URL/${{ github.event.inputs.image_name }}:${{ github.event.inputs.tag }}
$ACR_URL/${{ github.event.inputs.image_name }}:latestmanifest
GHCR(GitHub Container Registry)在多架构镜像的处理逻辑上和 ACR 或 Docker Hub 没有本质区别,它也是基于 OCI 镜像规范 来管理镜像和 manifest 的
当你使用 docker buildx 构建 multi-platform 镜像时,会生成一个 顶层 manifest list,指向不同平台的具体镜像。
cr.io/your-org/your-image:latest
├─ linux/amd64 → sha256:aaa...
├─ linux/arm64 → sha256:bbb...
├─ linux/arm/v7 → sha256:ccc...顶层 manifest (application/vnd.oci.image.index.v1+json) 是 一个索引,只显示一条记录在界面上,但它包含了每个平台镜像的 digest。
在 GHCR Web 界面上,你看到的也通常只有一个条目(最新版本或 tag),不会直接显示每个平台的 digest。
实际上多平台的镜像已经上传完毕,Docker 客户端拉取镜像时,会自动选择当前主机的架构:
# 在 amd64 主机上
docker pull ghcr.io/your-org/your-image:latest
# 自动拉取 linux/amd64 镜像
# 在 arm64 主机上
docker pull ghcr.io/your-org/your-image:latest
# 自动拉取 linux/arm64 镜像你可以使用 docker buildx imagetools inspect 查看 GHCR 上的所有平台信息:
docker buildx imagetools inspect ghcr.io/your-org/your-image:latest输出示例:
Name: ghcr.io/your-org/your-image:latest
MediaType: application/vnd.oci.image.index.v1+json
Digest: sha256:...
Manifests:
Name: ghcr.io/your-org/your-image:latest@sha256:aaa...
MediaType: application/vnd.oci.image.manifest.v1+json
Platform: linux/amd64
Name: ghcr.io/your-org/your-image:latest@sha256:bbb...
MediaType: application/vnd.oci.image.manifest.v1+json
Platform: linux/arm64attestation manifest
如果你在 workflow 中开启了 attest(如 buildx 支持的 --attest type=provenance),会生成一些 unknown/unknown 的 attestation manifests。
功能:
- 用于安全审计和镜像来源验证
- 不影响正常的拉取和运行
- 在 Web 界面上通常不会显示,但通过
docker buildx imagetools inspect可以看到
重要
GHCR、ACR、Docker Hub 都支持 multi-platform 镜像。
Web 界面通常只显示一条 tag,但实际每个平台的镜像都存在。
拉取镜像时,Docker 根据本地平台自动选择正确架构。
可以使用 docker buildx imagetools inspect <镜像> 来查看每个平台的 digest。
attestation manifest 是安全信息,不影响使用。
Web 界面看到的“版本号”和 Digest
- 版本号(tag):比如
latest或1.0.0,这是你给镜像打的标签。 - Digest:Web 界面显示的 Digest,指的是 顶层 manifest list 的 digest,也就是 multi-arch 镜像的索引本身的 SHA256,而不是某个平台的实际镜像 SHA。
换句话说,这个 digest 是 “索引文件”的 checksum,不是 amd64 或 arm64 镜像的 digest。
manifest list 与具体平台镜像
当你构建多架构镜像并推送时,docker buildx 会生成:
顶层 manifest list(索引文件)
- 指向每个平台的镜像
- Web 界面显示的 digest 就是这个
各个平台的实际镜像(amd64、arm64、arm/v7 等)
- 每个平台镜像也有自己的 digest
- 拉取镜像时,Docker 会根据本机架构自动选择对应平台镜像
可以通过命令查看全部平台:
docker buildx imagetools inspect <image>:<tag>