
上一篇聊了怎麼選 Registry,這篇直接上手——各平台的 login/push 指令、CI Pipeline 範例、還有自動清理舊 image 的設定。
先講結論
- 每個 Registry 的
docker login方式都不同,CI 裡用變數管理 Registry 前綴,換平台時只改變數 - ECR 的 Lifecycle Policy 是省儲存費的神器,第一天就該設
- GitHub Actions + ghcr.io 的整合體驗最無痛,
GITHUB_TOKEN搞定一切
各 Registry 的 docker login / push 速查
# Docker Hub
docker login -u myuser -p mytoken
# AWS ECR(token 有效 12 小時)
aws ecr get-login-password --region ap-northeast-1 \
| docker login --username AWS --password-stdin 123456789.dkr.ecr.ap-northeast-1.amazonaws.com
# Google Artifact Registry
gcloud auth configure-docker asia-east1-docker.pkg.dev
# Azure ACR
az acr login --name myregistry
# GitHub Container Registry
echo $GITHUB_TOKEN | docker login ghcr.io -u USERNAME --password-stdin
# 自建 Harbor
docker login harbor.example.com -u admin -p Harbor12345image tag 格式的差異是最容易搞混的地方:Docker Hub 用 username/image,ECR 用一長串 AWS endpoint,ghcr.io 用 ghcr.io/org/image。建議在 CI 設定裡把 Registry 前綴抽成變數,換平台時改一個地方就好。
GitLab CI 推 ECR 的完整 Pipeline
這個範例涵蓋 test → build → deploy 三個 stage,重點在 before_script 用 AWS CLI 取得短期 token 登入 ECR:
# .gitlab-ci.yml
variables:
AWS_REGION: "ap-northeast-1"
AWS_ACCOUNT_ID: "123456789012"
ECR_REGISTRY: "${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com"
ECR_REPOSITORY: "myapp"
IMAGE: "${ECR_REGISTRY}/${ECR_REPOSITORY}"
stages:
- test
- build
- deploy
test:
stage: test
image: node:20-alpine
script:
- npm ci
- npm test
build:
stage: build
image: docker:24
services:
- docker:24-dind
before_script:
- apk add --no-cache aws-cli
- aws ecr get-login-password --region $AWS_REGION
| docker login --username AWS --password-stdin $ECR_REGISTRY
script:
# 不存在就自動建 repository
- aws ecr describe-repositories --repository-names $ECR_REPOSITORY --region $AWS_REGION
|| aws ecr create-repository --repository-name $ECR_REPOSITORY --region $AWS_REGION
- docker build
--label "git.commit=${CI_COMMIT_SHA}"
-t ${IMAGE}:${CI_COMMIT_SHA}
-t ${IMAGE}:${CI_COMMIT_REF_SLUG}
.
- docker push ${IMAGE}:${CI_COMMIT_SHA}
- docker push ${IMAGE}:${CI_COMMIT_REF_SLUG}
- |
if [ -n "$CI_COMMIT_TAG" ]; then
docker tag ${IMAGE}:${CI_COMMIT_SHA} ${IMAGE}:${CI_COMMIT_TAG}
docker push ${IMAGE}:${CI_COMMIT_TAG}
fi
rules:
- if: $CI_COMMIT_BRANCH == "main"
- if: $CI_COMMIT_TAG =~ /^v\d+/
deploy:dev:
stage: deploy
image: amazon/aws-cli:latest
script:
- aws ecs update-service
--cluster my-cluster
--service myapp-service
--force-new-deployment
--region $AWS_REGION
rules:
- if: $CI_COMMIT_BRANCH == "main"這裡有個小 trick:aws ecr describe-repositories ... || aws ecr create-repository ... 讓你不用手動先去 console 建 repository,CI 第一次跑就會自動建好。
ECR Lifecycle Policy — 不設等著被帳單嚇到
CI 每次 build 都會產生新的 image layer。沒有清理機制,儲存費用就是只增不減。Lifecycle Policy 是 ECR 最實用的功能之一:
{
"rules": [
{
"rulePriority": 1,
"description": "保留最近 5 個 v* tag",
"selection": {
"tagStatus": "tagged",
"tagPrefixList": ["v"],
"countType": "imageCountMoreThan",
"countNumber": 5
},
"action": { "type": "expire" }
},
{
"rulePriority": 10,
"description": "14 天後刪除 untagged image",
"selection": {
"tagStatus": "untagged",
"countType": "sinceImagePushed",
"countUnit": "days",
"countNumber": 14
},
"action": { "type": "expire" }
},
{
"rulePriority": 20,
"description": "30 天後刪除所有非版本號 image",
"selection": {
"tagStatus": "any",
"countType": "sinceImagePushed",
"countUnit": "days",
"countNumber": 30
},
"action": { "type": "expire" }
}
]
}aws ecr put-lifecycle-policy \
--repository-name myapp \
--lifecycle-policy-text file://lifecycle-policy.json \
--region ap-northeast-1規則按 rulePriority 從小到大評估,符合條件的 image 會在 24 小時內自動刪除。我的建議是至少設 untagged 的清理規則,這是最低限度的防線。
GitHub Actions 推 ghcr.io — 最無痛的體驗
如果你的 CI 已經在 GitHub Actions 上,ghcr.io 的整合體驗真的是零摩擦:
# .github/workflows/build-push.yml
name: Build and Push to GHCR
on:
push:
branches: [main]
tags: ['v*']
pull_request:
branches: [main]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=semver,pattern={{version}}
type=sha
- 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幾個亮點:docker/metadata-action 自動根據 git ref 生成合適的 tag;permissions.packages: write + GITHUB_TOKEN 就能 push,不用建 PAT;PR 時只 build 不 push,避免未 review 的 image 進 Registry;cache-from: type=gha 利用 Actions cache 加速 build。
回顧
Registry 的選擇決定了你 CI/CD 的日常體驗。選對了,push/pull 像呼吸一樣自然;選錯了,每天都在跟認證過期和 rate limit 搏鬥。
設定好 Lifecycle Policy 的那一刻,就是你不再害怕打開 AWS 帳單的開始。