Skip to content

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

  1. 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.

  2. 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 form crpi-<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.
  3. Create namespace lacatfly (private).

  4. Create three image repositories in the namespace:

    • twilight-drive
    • twilight-drive-backend
    • twilight-drive-frontend

    All three: visibility = private, source = local (we push from CI; do NOT bind to a GitHub repo — the twilight-drive-backend/frontend names do not exist on GitHub, they are paths inside the twilight-drive repo).

  5. 访问凭证 (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 login consumes 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):

NameValue
ACR_USERNAMEThe username copied from the ACR console.
ACR_PASSWORDThe fixed password set in the ACR console.

Repository variables (plain):

NameValue
ACR_REGISTRYPublic endpoint, e.g. crpi-61k80mbsluufppdv.cn-beijing.personal.cr.aliyuncs.com
ACR_NAMESPACElacatfly

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
BASH

This 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"
BASH

The next docker compose pull will pull from ACR.


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 trigger

5. 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" run

Each 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 minutes

docker 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:

  1. Set REGISTRY=ghcr.io/lacatfly in ~deploy/twilight/.env.
  2. docker compose pull — long, but it works.
  3. Disable ACR pushes by clearing the ACR_REGISTRY repo variable in GitHub.

The pipeline keeps pushing GHCR even when ACR is disabled, so this is a straight switch-over.

团队内部文档