mirror of
https://github.com/nexu-io/open-design.git
synced 2026-05-31 19:04:39 +07:00
Merge 751dc3eb57 into 53fb175855
This commit is contained in:
commit
85bb91d211
1 changed files with 307 additions and 0 deletions
307
docs/deployment/cloud/aliyun.md
Normal file
307
docs/deployment/cloud/aliyun.md
Normal file
|
|
@ -0,0 +1,307 @@
|
|||
# Alibaba Cloud (阿里云) Deployment
|
||||
|
||||
This guide covers self-hosting Open Design on Alibaba Cloud for users in mainland China and the broader Asia-Pacific region. It documents the supported deployment paths, image-pull optimisations, and ICP filing considerations for public-facing instances.
|
||||
|
||||
> **Status:** This is a docs-only guide. The flows below follow Alibaba Cloud's published product behaviour and the existing [`docs/deployment/docker.md`](../docker.md) and [`docs/install-guide.md`](../../install-guide.md) container model. Live ROS templates, one-click scripts, and verification screenshots are tracked as a follow-up under issue #1025; contributions from operators with active Alibaba Cloud accounts are welcome.
|
||||
|
||||
## Why Alibaba Cloud?
|
||||
|
||||
For users in mainland China, deploying Open Design to Alibaba Cloud (阿里云) instead of an overseas provider gives you:
|
||||
|
||||
- **Lower latency** for users on China Telecom, China Unicom, and China Mobile networks.
|
||||
- **Image acceleration** via Alibaba Cloud Container Registry (容器镜像服务 ACR) — Docker Hub pulls from the mainland are unreliable; ACR mirrors solve this.
|
||||
- **Compliant public hosting** through ICP filing (备案), required for any internet-facing service on a `.cn` domain or a domain pointed at a mainland China IP.
|
||||
|
||||
If your users are outside mainland China, AWS, GCP, or Azure are typically simpler. This guide is targeted at the mainland use case.
|
||||
|
||||
## Deployment paths
|
||||
|
||||
| Path | Best for | Complexity | Cost shape |
|
||||
|------|----------|------------|------------|
|
||||
| **A. ECS (云服务器 ECS)** | Single-machine self-host, small teams, evaluation | Low | Pay-as-you-go or subscription, fixed per VM |
|
||||
| **B. ACK (容器服务 ACK)** | Multi-tenant teams, autoscaling, HA | Medium-High | Cluster + node pool + load balancer |
|
||||
| **C. ROS (资源编排 ROS)** | Repeatable infra-as-code provisioning | Medium | Same as the underlying ECS/ACK resources |
|
||||
|
||||
Most first-time deployments should start with **Path A (ECS)**.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- An Alibaba Cloud account with a verified payment method.
|
||||
- For mainland China regions (e.g. `cn-hangzhou`, `cn-shanghai`, `cn-beijing`): a real-name verified (实名认证) account.
|
||||
- For public domains served from a mainland China region: an [ICP filing](#icp-filing-备案) for the domain.
|
||||
- Local install of the [Alibaba Cloud CLI (`aliyun`)](https://www.alibabacloud.com/help/en/cli) if you plan to script provisioning.
|
||||
|
||||
## Path A — Deploy to ECS
|
||||
|
||||
This path puts Open Design on a single ECS instance using the same Docker Compose stack documented in [`docs/deployment/docker.md`](../docker.md).
|
||||
|
||||
### Step 1: Create the ECS instance
|
||||
|
||||
Use the Alibaba Cloud console or the CLI. A reasonable starting shape for evaluation:
|
||||
|
||||
| Setting | Recommended value | Notes |
|
||||
|---------|------------------|-------|
|
||||
| Instance type | `ecs.t6-c1m2.large` (2 vCPU / 4 GiB) | Lightweight; bump to `ecs.c7` for production |
|
||||
| Image | Ubuntu 24.04 LTS 64-bit | Open Design's Docker image is `linux/amd64` and `linux/arm64` |
|
||||
| Storage | 40 GiB ESSD | Enough for the image, agent CWDs, and SQLite |
|
||||
| Network | VPC with a public IP or EIP | Required if users will reach the instance directly |
|
||||
| Security group | Inbound `22/tcp` (your IP only), outbound all | Add `443/tcp` only behind a reverse proxy — see [Network exposure](#network-exposure) |
|
||||
|
||||
CLI equivalent (replace placeholders):
|
||||
|
||||
```bash
|
||||
aliyun ecs RunInstances \
|
||||
--RegionId cn-hangzhou \
|
||||
--ImageId ubuntu_24_04_x64_20G_alibase \
|
||||
--InstanceType ecs.t6-c1m2.large \
|
||||
--SecurityGroupId sg-xxxxxxxx \
|
||||
--VSwitchId vsw-xxxxxxxx \
|
||||
--InstanceChargeType PostPaid \
|
||||
--SystemDisk.Category cloud_essd \
|
||||
--SystemDisk.Size 40
|
||||
```
|
||||
|
||||
### Step 2: Install Docker
|
||||
|
||||
SSH in and install Docker Engine plus the Compose plugin:
|
||||
|
||||
```bash
|
||||
# Aliyun's mirrored Docker install script (faster from mainland China than get.docker.com)
|
||||
curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
|
||||
sudo usermod -aG docker $USER
|
||||
```
|
||||
|
||||
Log out and back in so the group change takes effect.
|
||||
|
||||
### Step 3: Configure image acceleration (镜像加速)
|
||||
|
||||
Pulling from `docker.io` directly inside mainland China is slow and intermittently fails. Configure ACR's free public mirror:
|
||||
|
||||
```bash
|
||||
sudo mkdir -p /etc/docker
|
||||
sudo tee /etc/docker/daemon.json > /dev/null <<EOF
|
||||
{
|
||||
"registry-mirrors": [
|
||||
"https://<your-acr-prefix>.mirror.aliyuncs.com"
|
||||
]
|
||||
}
|
||||
EOF
|
||||
sudo systemctl restart docker
|
||||
```
|
||||
|
||||
Get your personal mirror prefix from the Alibaba Cloud console under **Container Registry → Image Tools → Image Accelerator** (容器镜像服务 → 镜像工具 → 镜像加速器).
|
||||
|
||||
### Step 4: Run Open Design
|
||||
|
||||
From this point the flow matches [`docs/install-guide.md`](../../install-guide.md):
|
||||
|
||||
```bash
|
||||
git clone https://github.com/nexu-io/open-design.git
|
||||
cd open-design
|
||||
bash deploy/scripts/install.sh --non-interactive --port 7456
|
||||
```
|
||||
|
||||
### Step 5: Put a reverse proxy in front
|
||||
|
||||
Open Design binds to `127.0.0.1:7456` by design — the daemon is never directly exposed to the network. For public access, terminate TLS at Nginx or an Alibaba Cloud SLB / ALB and forward to `127.0.0.1:7456`. Do not expose port 7456 directly through the security group. See the network section of [`docs/install-guide.md`](../../install-guide.md) for the full rationale.
|
||||
|
||||
A minimal Nginx block:
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name design.example.cn;
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/design.example.cn/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/design.example.cn/privkey.pem;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:7456;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
|
||||
# Forward the bearer token to the daemon. The Compose stack always
|
||||
# generates an OD_API_TOKEN and binds the daemon to 0.0.0.0 inside
|
||||
# the container, so every /api/* call coming through Nginx must
|
||||
# carry Authorization: Bearer <token>. Only /api/health,
|
||||
# /api/version, and /api/daemon/status are exempt. The loopback
|
||||
# short-circuit in the daemon checks the TCP peer address, not the
|
||||
# X-Forwarded-For header, so reverse-proxy traffic never gets the
|
||||
# localhost bypass.
|
||||
proxy_set_header Authorization "Bearer <OD_API_TOKEN from deploy/.env>";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Set `OPEN_DESIGN_ALLOWED_ORIGINS` in `deploy/.env` to the public URL so CORS clears the proxy. The `OD_API_TOKEN` referenced in the Nginx block above is generated automatically by `deploy/scripts/install.sh` and written into `deploy/.env`; copy that value (or wire `proxy_set_header Authorization` to read it from a secrets manager) so the proxy authenticates on every request.
|
||||
|
||||
## Path B — Deploy to ACK
|
||||
|
||||
[Container Service for Kubernetes (容器服务 ACK)](https://www.alibabacloud.com/product/kubernetes) is the right choice when you need:
|
||||
|
||||
- Horizontal scaling beyond a single VM.
|
||||
- High availability across availability zones.
|
||||
- Standard Kubernetes tooling (`kubectl`, Helm) for ops.
|
||||
|
||||
Open Design does not yet ship an official Helm chart. The minimal manifest below is a starting point — production users should harden it (resource limits, PodDisruptionBudget, NetworkPolicy, persistent storage class).
|
||||
|
||||
> **Required env for Kubernetes:** the daemon defaults to `OD_BIND_HOST=127.0.0.1`, which makes the readiness probe (and the Service) unable to reach the container. To make the Pod reachable inside the cluster you must set `OD_BIND_HOST=0.0.0.0`, and the daemon's bound-API-token guard then requires `OD_API_TOKEN` to be set whenever it binds to a non-loopback interface. Both env vars are reflected in the manifest below.
|
||||
>
|
||||
> Also note that the daemon reads `OD_ALLOWED_ORIGINS` directly. The `OPEN_DESIGN_ALLOWED_ORIGINS` name documented in `deploy/.env.example` is a Compose-only alias mapped in `deploy/docker-compose.yml`; for the direct-container ACK path you must set `OD_ALLOWED_ORIGINS` instead.
|
||||
|
||||
Create the API-token secret first:
|
||||
|
||||
```bash
|
||||
# Generate a random token and store it in the cluster
|
||||
kubectl create secret generic open-design-secrets \
|
||||
--from-literal=api-token="$(openssl rand -hex 32)"
|
||||
```
|
||||
|
||||
Then apply the manifest:
|
||||
|
||||
```yaml
|
||||
# open-design.yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: open-design
|
||||
spec:
|
||||
replicas: 1 # Open Design uses local SQLite; multi-replica needs shared storage
|
||||
selector:
|
||||
matchLabels: { app: open-design }
|
||||
template:
|
||||
metadata:
|
||||
labels: { app: open-design }
|
||||
spec:
|
||||
containers:
|
||||
- name: open-design
|
||||
image: registry.cn-hangzhou.aliyuncs.com/<your-namespace>/open-design:latest
|
||||
ports:
|
||||
- containerPort: 7456
|
||||
env:
|
||||
- name: OD_DATA_DIR
|
||||
value: /data
|
||||
- name: OD_BIND_HOST
|
||||
value: "0.0.0.0" # required so the readinessProbe and Service can reach the daemon
|
||||
- name: OD_API_TOKEN
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: open-design-secrets
|
||||
key: api-token # required whenever OD_BIND_HOST is non-loopback
|
||||
- name: OD_ALLOWED_ORIGINS
|
||||
value: "https://design.example.cn" # set when fronting with an Ingress; daemon reads OD_*, not OPEN_DESIGN_*
|
||||
volumeMounts:
|
||||
- name: data
|
||||
mountPath: /data
|
||||
readinessProbe:
|
||||
httpGet: { path: /api/health, port: 7456 }
|
||||
initialDelaySeconds: 10
|
||||
resources:
|
||||
requests: { cpu: 250m, memory: 384Mi }
|
||||
limits: { cpu: "1", memory: 768Mi }
|
||||
volumes:
|
||||
- name: data
|
||||
persistentVolumeClaim:
|
||||
claimName: open-design-data
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: open-design
|
||||
spec:
|
||||
type: ClusterIP
|
||||
selector: { app: open-design }
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 7456
|
||||
```
|
||||
|
||||
Apply with `kubectl apply -f open-design.yaml`. Front the Service with an Ingress (NGINX Ingress Controller is preinstalled on ACK Pro) and an ACM-issued certificate. With `OD_API_TOKEN` set, every `/api/*` request from non-loopback origins must carry an `Authorization: Bearer <token>` header — wire that into your Ingress / proxy auth layer.
|
||||
|
||||
> **Note on `replicas: 1`:** the daemon writes SQLite at `.od/app.sqlite` (see [`AGENTS.md`](../../../AGENTS.md) FAQ "Where is data written?"). Running multiple replicas without shared storage will diverge state. A multi-replica ACK topology needs an external database; that is out of scope for this guide.
|
||||
|
||||
## Path C — ROS templates
|
||||
|
||||
Resource Orchestration Service (资源编排 ROS) provisions the same resources declaratively. There is no first-party ROS template for Open Design yet. Operators with Alibaba Cloud access are welcome to contribute one as a follow-up to this guide; the natural location is `deploy/aliyun/ros/`.
|
||||
|
||||
Until a template lands, treat ROS as advanced infra-as-code and use Path A or Path B above.
|
||||
|
||||
## Image acceleration (镜像加速)
|
||||
|
||||
If you have not already done it during ECS setup, configure Docker's image mirror so image pulls from `docker.io` go through Alibaba Cloud's mirror:
|
||||
|
||||
1. Log in to the Alibaba Cloud console.
|
||||
2. Open **Container Registry → Image Tools → Image Accelerator** (容器镜像服务 → 镜像工具 → 镜像加速器).
|
||||
3. Copy your personal accelerator URL (looks like `https://<prefix>.mirror.aliyuncs.com`).
|
||||
4. Add it to `/etc/docker/daemon.json` under `registry-mirrors` and restart Docker.
|
||||
|
||||
For ACK, image acceleration is enabled by default on the cluster's node pool — no per-node config needed.
|
||||
|
||||
## ICP filing (备案)
|
||||
|
||||
Any service that serves a domain pointed at a mainland China IP, or serves a `.cn` domain, must complete ICP filing through the Ministry of Industry and Information Technology (工信部). Skipping this is a hard block — Alibaba Cloud will firewall HTTP/HTTPS on unfiled domains.
|
||||
|
||||
| Item | Detail |
|
||||
|------|--------|
|
||||
| Who files | The domain owner (个人 or 企业) |
|
||||
| Where | Alibaba Cloud's ICP filing console (备案系统) |
|
||||
| What you need | Real-name verified Alibaba Cloud account, hosting purchased for ≥ 3 months in a mainland region, valid Chinese ID or business licence, photo on a blue/white background (taken via the Aliyun ICP app) |
|
||||
| How long | Typically 7–20 business days |
|
||||
| Result | A 备案号 (ICP filing number) you must display in the site footer, e.g. `京ICP备XXXXXXX号` |
|
||||
|
||||
You do **not** need ICP filing if:
|
||||
|
||||
- You deploy to a Hong Kong, Singapore, or other non-mainland Alibaba Cloud region (no firewall on those regions for HTTP/HTTPS).
|
||||
- The service is internal (no public domain, accessed only via VPN or private IP).
|
||||
|
||||
A practical pattern for many teams: deploy to **Hong Kong (`cn-hongkong`)** first to skip ICP, then migrate to a mainland region once filing is complete and latency to mainland users matters more than time-to-launch.
|
||||
|
||||
## Network exposure
|
||||
|
||||
Open Design's daemon binds to `127.0.0.1:7456` and is never exposed directly. Public access must go through:
|
||||
|
||||
1. A reverse proxy that terminates TLS (Nginx, Caddy, Alibaba Cloud SLB/ALB).
|
||||
2. An ICP-filed domain (for mainland regions).
|
||||
3. CORS allowlist set via `OPEN_DESIGN_ALLOWED_ORIGINS` (Compose / `deploy/.env` path) or `OD_ALLOWED_ORIGINS` (direct-container / Kubernetes path).
|
||||
|
||||
See [`docs/install-guide.md`](../../install-guide.md) for the full topology and reverse-proxy guidance.
|
||||
|
||||
## Common pitfalls
|
||||
|
||||
| Symptom | Likely cause | Fix |
|
||||
|---------|--------------|-----|
|
||||
| `docker pull` hangs or times out | No image acceleration configured; pulling from `docker.io` directly | Configure ACR mirror in `/etc/docker/daemon.json` |
|
||||
| HTTP/HTTPS to your domain is blocked | Domain not ICP-filed for a mainland region | Complete ICP filing, or move to a non-mainland region (HK/SG) |
|
||||
| `aliyun ecs RunInstances` fails with "Real-name authentication required" | Account not 实名认证 | Complete real-name verification in the account console |
|
||||
| ACK pods stuck in `ImagePullBackOff` from a private ACR repo | Cluster lacks pull credentials for the namespace | Create an `aliyun-acr-credential-helper` secret or use the cluster's ACR plugin |
|
||||
| 200 from `/api/health` but UI fails to load | CORS rejecting the proxied origin, **or** the reverse proxy not forwarding the bearer token (Compose always generates `OD_API_TOKEN`; only `/api/health`, `/api/version`, `/api/daemon/status` skip auth) | Set `OPEN_DESIGN_ALLOWED_ORIGINS` (Compose / `deploy/.env`) or `OD_ALLOWED_ORIGINS` (direct container / ACK) to the public URL, **and** add `proxy_set_header Authorization "Bearer <OD_API_TOKEN>"` to the Nginx / SLB upstream config |
|
||||
| ACK Pod stuck in `NotReady`, readiness probe fails | Daemon defaulting to `OD_BIND_HOST=127.0.0.1` so the kubelet can't reach it | Set `OD_BIND_HOST=0.0.0.0` and `OD_API_TOKEN` in the Pod env (the daemon refuses non-loopback binds without a token) |
|
||||
| SLB health check fails | SLB hits `7456` but security group blocks intra-VPC | Allow the SLB backend CIDR on `7456/tcp` in the security group |
|
||||
|
||||
## Follow-up work
|
||||
|
||||
This guide is intentionally docs-only. Tracked under #1025, contributions welcome:
|
||||
|
||||
- A first-party ROS template for one-click ECS provisioning.
|
||||
- An official Helm chart for ACK with multi-replica safety (external DB or shared volume topology).
|
||||
- An end-to-end one-click script equivalent to `deploy/scripts/install.sh` but targeting ECS via the `aliyun` CLI.
|
||||
- A Chinese-language sibling of this file at `docs/deployment/cloud/aliyun.zh-CN.md` once the English content stabilises.
|
||||
- Verification screenshots under `docs/screenshots/deployment/aliyun/`, mirroring the Docker layout.
|
||||
|
||||
## References
|
||||
|
||||
- Open Design Docker deployment: [`docs/deployment/docker.md`](../docker.md)
|
||||
- Open Design one-click installer: [`docs/install-guide.md`](../../install-guide.md)
|
||||
- Alibaba Cloud ECS docs: <https://www.alibabacloud.com/help/en/ecs>
|
||||
- Alibaba Cloud ACK docs: <https://www.alibabacloud.com/help/en/ack>
|
||||
- Alibaba Cloud Container Registry docs: <https://www.alibabacloud.com/help/en/acr>
|
||||
- ICP filing overview (English): <https://www.alibabacloud.com/help/en/icp-filing>
|
||||
- ICP filing console (Chinese): <https://beian.aliyun.com>
|
||||
Loading…
Reference in a new issue