三組 namespace,在同一個 Dockerfile 內各佔一個 prefix。不要寫死值 —
所有「會變」的欄位(commit / branch / tag / build-time / version)都用 ARG 接 build-arg。
# syntax=docker/dockerfile:1.7
# ─── Build args (CI 端注入,build 當下決定) ───
ARG VERSION
ARG COMMIT_SHA
ARG GIT_BRANCH
ARG GIT_TAG=""
ARG BUILD_TIME
FROM nginx:1.27-alpine
# ─── Group 1 · OCI standard (任何工具都讀得懂) ───
LABEL org.opencontainers.image.title="acme-api" \
org.opencontainers.image.version="${VERSION}" \
org.opencontainers.image.revision="${COMMIT_SHA}" \
org.opencontainers.image.created="${BUILD_TIME}" \
org.opencontainers.image.source="https://github.com/acme/acme-api" \
org.opencontainers.image.vendor="Acme Inc." \
org.opencontainers.image.licenses="Apache-2.0"
# ─── Group 2 · R2K spec (R2K 工具鏈專用) ───
LABEL dev.releaseasknowledge.version="1.0" \
dev.releaseasknowledge.level="1" \
dev.releaseasknowledge.commit="${COMMIT_SHA}" \
dev.releaseasknowledge.branch="${GIT_BRANCH}" \
dev.releaseasknowledge.tag="${GIT_TAG}" \
dev.releaseasknowledge.build-time="${BUILD_TIME}" \
dev.releaseasknowledge.repo="https://github.com/acme/acme-api"
# ─── Group 3 · Vendor extension (反向域名,自家業務) ───
LABEL com.acme.case_type="api" \
com.acme.build_id="${COMMIT_SHA}"
# ─── 你原本就有的東西 ───
COPY ./dist /usr/share/nginx/html
EXPOSE 80
三條原則:
org.opencontainers.image.* — 寫給所有 container 工具看(Trivy / Cosign / docker inspect 原生支援)dev.releaseasknowledge.* — 寫給 R2K 工具鏈讀(CLI / scanner / diff plugin)com.<yourco>.*)— 不汙染標準 namespace
所有的「真相」都來自 git:commit / branch / tag,
加上 date -u 的 build time。
不要另外維護一個 VERSION.txt 讓人手動改 — 那會變成第二份 source of truth,終究會跟 git 不一致。
#!/usr/bin/env bash
set -euo pipefail
# 一切從 git 來,不從手動維護的檔案來
VERSION="${VERSION:-$(git describe --tags --always --dirty)}"
COMMIT_SHA="$(git rev-parse HEAD)"
GIT_BRANCH="$(git rev-parse --abbrev-ref HEAD)"
GIT_TAG="$(git tag --points-at HEAD || true)"
BUILD_TIME="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
docker build \
--build-arg VERSION="${VERSION}" \
--build-arg COMMIT_SHA="${COMMIT_SHA}" \
--build-arg GIT_BRANCH="${GIT_BRANCH}" \
--build-arg GIT_TAG="${GIT_TAG}" \
--build-arg BUILD_TIME="${BUILD_TIME}" \
-t acme-api:"${VERSION}" \
-t acme-api:"${COMMIT_SHA:0:12}" \
.
GitHub Actions 版本(直接抄進 .github/workflows/build.yml):
name: build
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 } # 需要完整 git history 才有 tag
- name: Compute build args
id: meta
run: |
echo "version=$(git describe --tags --always)" >> $GITHUB_OUTPUT
echo "commit=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
echo "branch=${GITHUB_REF_NAME}" >> $GITHUB_OUTPUT
echo "tag=$(git tag --points-at HEAD)" >> $GITHUB_OUTPUT
echo "build_time=$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> $GITHUB_OUTPUT
- uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ghcr.io/acme/acme-api:${{ steps.meta.outputs.version }}
build-args: |
VERSION=${{ steps.meta.outputs.version }}
COMMIT_SHA=${{ steps.meta.outputs.commit }}
GIT_BRANCH=${{ steps.meta.outputs.branch }}
GIT_TAG=${{ steps.meta.outputs.tag }}
BUILD_TIME=${{ steps.meta.outputs.build_time }}
Image 一推上 registry,任何工具都能用 兩個 HTTP call、~7 KB 讀完所有 LABEL — 不必 pull 完整 image。
本機驗的時候 docker inspect 最快,正式驗的時候用 oras / crane 直接讀 registry。
# 看所有 LABEL 是否落地
$ docker inspect acme-api:1.2.3 \
--format '{{json .Config.Labels}}' | jq .
# 只挑 R2K 的 LABEL
$ docker inspect acme-api:1.2.3 \
--format '{{json .Config.Labels}}' \
| jq 'with_entries(select(.key | startswith("dev.releaseasknowledge.")))'
輸出(節錄):
{
"dev.releaseasknowledge.build-time": "2026-05-10T14:32:11Z",
"dev.releaseasknowledge.commit": "a1b2c3d4e5f67890abcdef1234567890abcdef12",
"dev.releaseasknowledge.branch": "main",
"dev.releaseasknowledge.tag": "v1.2.3",
"dev.releaseasknowledge.level": "1",
"dev.releaseasknowledge.version": "1.0",
"dev.releaseasknowledge.repo": "https://github.com/acme/acme-api",
"org.opencontainers.image.revision": "a1b2c3d4e5f67890abcdef1234567890abcdef12",
"org.opencontainers.image.created": "2026-05-10T14:32:11Z",
"org.opencontainers.image.title": "acme-api",
"org.opencontainers.image.version": "v1.2.3"
}# oras 直接從 registry 讀 image config (~7 KB · ~50 ms)
$ oras manifest fetch-config --pretty \
registry.acme.com/acme-api:1.2.3
# 或用 crane
$ crane config registry.acme.com/acme-api:1.2.3 | jq .config.Labels
# Trivy 一行掃完整個 registry,1000 image 大約 1 分鐘
$ trivy image --format json --list-all-pkgs \
registry.acme.com/acme-api:1.2.3 \
| jq .Metadata.ImageConfig.config.Labels
com.yourco.*),不汙染標準 namespace→ 達成後可寫進工程 blog / 放進 RFP / 在 README 掛 R2K Level 1 badge