主题
Aliyun ACR mirror setup (one-time)
GitHub Container Registry pulls into the Alibaba ECS in Beijing hang behind the GFW. The auto-deploy pipeline therefore pushes the same images to Aliyun Container Registry (ACR) in the same region as the ECS, and compose.yml reads them from there.
This runbook captures every step that has to happen once before the pipeline can use ACR. After this is done, gh workflow run docker-build.yml -r main publishes to both registries and ecs-deploy.sh pulls from ACR over Aliyun's internal network.
1. Aliyun console
Log in to the Aliyun console, open Container Registry (ACR) Personal Edition. The personal edition is free, has 300-repo / 5GB egress-per-day limits, and supports private namespaces.
Region: Beijing (
cn-beijing) — same region as the ECS so the pull is served over the Aliyun VPC backbone. The instance-specific hostname is of the formcrpi-<instance-id>.<region>.personal.cr.aliyuncs.com. Each instance exposes two endpoints:- public:
crpi-<id>.cn-beijing.personal.cr.aliyuncs.com— for CI push from GitHub Actions and from a local workstation. - VPC:
crpi-<id>-vpc.cn-beijing.personal.cr.aliyuncs.com— for the ECS to pull over the internal network. Same images, same auth, no egress charges.
- public:
Create namespace
lacatfly(private).Create three image repositories in the namespace:
twilight-drivetwilight-drive-backendtwilight-drive-frontend
All three: visibility = private, source = local (we push from CI; do NOT bind to a GitHub repo — the
twilight-drive-backend/frontendnames do not exist on GitHub, they are paths inside thetwilight-driverepo).访问凭证 (Access credentials) → set a fixed password specifically for the registry. This is NOT the Aliyun account login password and NOT the AccessKey ID/Secret — it's a separate field on the ACR instance page.
docker loginconsumes the Aliyun account email as username and this fixed password as password.
2. GitHub repo secrets + variables
Under Settings → Secrets and variables → Actions:
Repository secrets (encrypted):
| Name | Value |
|---|---|
ACR_USERNAME | The username copied from the ACR console. |
ACR_PASSWORD | The fixed password set in the ACR console. |
Repository variables (plain):
| Name | Value |
|---|---|
ACR_REGISTRY | Public endpoint, e.g. crpi-61k80mbsluufppdv.cn-beijing.personal.cr.aliyuncs.com |
ACR_NAMESPACE | lacatfly |
ACR_REGISTRY is the public endpoint — GitHub Actions runners are outside the Aliyun VPC, so they must push there. The ECS reaches the same images via the matching VPC endpoint (step 3 below).
docker-build.yml only enables the ACR push steps when vars.ACR_REGISTRY is non-empty, so you can stage the secrets first and flip the switch by setting the variable.
3. ECS host: docker login + compose env
SSH to ECS (ssh-ecs.fsagent.cc). Use the VPC endpoint of the ACR instance — same images, served over Aliyun's internal network, zero egress cost.
bash
sudo -u deploy bash <<'BASH'
set -euo pipefail
ACR_REGISTRY=crpi-61k80mbsluufppdv-vpc.cn-beijing.personal.cr.aliyuncs.com
ACR_USERNAME='hanliang730@hotmail.com'
ACR_PASSWORD='<fixed-password-from-step-1>' # better: read from a 0400 file you scp'd
echo "$ACR_PASSWORD" | docker login "$ACR_REGISTRY" -u "$ACR_USERNAME" --password-stdin
BASHThis persists credentials in ~deploy/.docker/config.json (mode 600) — every subsequent docker compose pull reuses them.
Then update the env file the compose stack reads:
bash
sudo -u deploy bash <<'BASH'
ENV=/home/deploy/twilight/.env
REGISTRY_LINE='REGISTRY=crpi-61k80mbsluufppdv-vpc.cn-beijing.personal.cr.aliyuncs.com/lacatfly'
# Point compose at ACR (per deploy/env.example: REGISTRY=...).
if grep -q '^REGISTRY=' "$ENV"; then
sed -i "s|^REGISTRY=.*|$REGISTRY_LINE|" "$ENV"
else
echo "$REGISTRY_LINE" >> "$ENV"
fi
grep '^REGISTRY=' "$ENV"
BASHThe next docker compose pull will pull from ACR.
4. Manual deploy (optional, but recommended setup)
The local-only fallback script scripts/deploy/manual.sh builds and pushes straight from a workstation to ACR, then triggers deploy <tag> on the ECS. Use it when GitHub Actions is broken or you need a one-off ship.
It looks for the ACR password in the macOS keychain under the entry aliyun-acr-twilight-drive. To store it:
bash
security add-generic-password -a "$USER" -s 'aliyun-acr-twilight-drive' -w
# (paste password, press Return)Other env it needs (export in your shell or a sourced .env):
bash
export ACR_REGISTRY=crpi-61k80mbsluufppdv.cn-beijing.personal.cr.aliyuncs.com
export ACR_NAMESPACE=lacatfly
export ACR_USERNAME='hanliang730@hotmail.com'
# DEPLOY_HOST / DEPLOY_USER default to ssh-ecs.fsagent.cc / deploy.Note: from a workstation use the public endpoint (no -vpc suffix). The VPC endpoint is reachable only from inside Aliyun's network. ACR resolves the two endpoints to the same backing storage, so an image pushed via public is immediately pullable via VPC from the ECS.
Run:
bash
./scripts/deploy/manual.sh # full: build + push + remote deploy
IMAGES=frontend ./scripts/deploy/manual.sh # ship a frontend-only fix
SKIP_DEPLOY=1 ./scripts/deploy/manual.sh # push only, no remote trigger5. Sanity-check end-to-end
After step 3 the pipeline should be live. Trigger it:
bash
# A one-line no-op commit so docker-build.yml's path filter fires:
git commit --allow-empty -m "ci: smoke ACR push"
git push origin main
gh run watch # pick the latest "Docker build" runEach matrix job should run two pairs of login+push (GHCR + ACR). On the ECS:
bash
ssh ssh-ecs.fsagent.cc
docker compose -f /home/deploy/twilight/source/deploy/compose.yml \
pull data # should resolve to crpi-...-vpc.cn-beijing.personal.cr.aliyuncs.com/...
# and complete in seconds, not minutesdocker compose images should show all three repos pointing at crpi-...-vpc.cn-beijing.personal.cr.aliyuncs.com/lacatfly/....
If a compose pull still hangs, double-check ~deploy/.docker/config.json contains the ACR auth entry and that REGISTRY= in ~deploy/twilight/.env actually matches the variable in compose.yml (the default is ghcr.io).
6. Rollback
ACR's value is "fast pull"; GHCR is still authoritative. To fall back:
- Set
REGISTRY=ghcr.io/lacatflyin~deploy/twilight/.env. docker compose pull— long, but it works.- Disable ACR pushes by clearing the
ACR_REGISTRYrepo variable in GitHub.
The pipeline keeps pushing GHCR even when ACR is disabled, so this is a straight switch-over.