Skip to content

Commit d5112bb

Browse files
committed
docs(gateway): improve cluster deployment guide
- Fix subnet scheme to use SUBNET_INDEX 0-based allocation (10.8.0.0/18, 10.8.64.0/18, ...) matching the deploy script behavior - Add SUBNET_INDEX mapping table for quick reference - Add cluster sync and peer discovery section explaining bootnode vs auto-discovery mechanisms - Add image version note about sync enable logic differences - Fix gateway-1 config to use empty bootnode (first node doesn't need one) - Expand verify section with Status API and health check criteria - Add Cloudflare DNS:Edit permission note for API token setup
1 parent 5cfd7db commit d5112bb

1 file changed

Lines changed: 59 additions & 19 deletions

File tree

gateway/docs/cluster-deployment.md

Lines changed: 59 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -75,36 +75,65 @@ address = "unix:/run/dstack/admin.sock" # Use Unix Domain Socket
7575

7676
| Node | node_id | Gateway IP | Client IP range | bootnode |
7777
|------|---------|------------|-----------------|----------|
78-
| gateway-1 | 1 | 10.8.128.1/16 | 10.8.128.0/18 | gateway-2 |
79-
| gateway-2 | 2 | 10.8.0.1/16 | 10.8.0.0/18 | gateway-1 |
78+
| gateway-1 | 1 | 10.8.0.1/16 | 10.8.0.0/18 | (none or gateway-2) |
79+
| gateway-2 | 2 | 10.8.64.1/16 | 10.8.64.0/18 | gateway-1 |
8080

8181
Notes:
82-
- Each node's node_id must be unique
83-
- Each node's Client IP range should not overlap (used for allocating IPs to different CVMs)
84-
- bootnode is configured as another node's RPC URL, used for cluster discovery at startup
82+
- Each node's `node_id` must be unique and greater than 0
83+
- Each node's Client IP range must not overlap (used for allocating IPs to different CVMs)
84+
- `bootnode` is optional — it speeds up initial peer discovery but is not required. Without a bootnode, a node will auto-discover peers when they connect via sync RPC
85+
- If a bootnode is set, its hostname must be resolvable before cluster bootstrap
8586

8687
### 2.2 CIDR Description
8788

8889
Client IP range (/18):
8990
- /18 means the first 18 bits are the network prefix
90-
- For example, 10.8.128.0/18 covers the address range 10.8.128.0 ~ 10.8.191.255
91+
- For example, 10.8.0.0/18 covers the address range 10.8.0.0 ~ 10.8.63.255
9192
- Each Gateway's /18 range does not overlap, so each Gateway can allocate IPs locally without syncing with other Gateways
93+
- With 4 possible /18 ranges in a /16 network, a cluster supports up to 4 gateway nodes
9294

9395
Gateway IP (/16):
9496
- Gateway IP uses /16 netmask to allow network routing to cover the larger 10.8.0.0/16 address space
9597
- This way, when another Gateway allocates an address in a /18 subnet, traffic can still be correctly routed
9698

99+
Subnet mapping:
100+
101+
| SUBNET_INDEX | Gateway IP | Client IP range | Address range | Usable IPs |
102+
|-------------|------------|-----------------|---------------|------------|
103+
| 0 | 10.8.0.1/16 | 10.8.0.0/18 | 10.8.0.0 ~ 10.8.63.255 | 16,382 |
104+
| 1 | 10.8.64.1/16 | 10.8.64.0/18 | 10.8.64.0 ~ 10.8.127.255 | 16,382 |
105+
| 2 | 10.8.128.1/16 | 10.8.128.0/18 | 10.8.128.0 ~ 10.8.191.255 | 16,382 |
106+
| 3 | 10.8.192.1/16 | 10.8.192.0/18 | 10.8.192.0 ~ 10.8.255.255 | 16,382 |
107+
97108
### 2.3 WireGuard Configuration Fields
98109

99110
Key fields in the `[core.wg]` section:
100111

101-
- `ip`: Gateway's own WireGuard address in CIDR format (e.g., 10.8.128.1/16)
102-
- `client_ip_range`: Address pool range for allocating to CVMs (e.g., 10.8.128.0/18)
103-
- `reserved_net`: Reserved address range that will not be allocated to CVMs (e.g., 10.8.128.1/32, reserving the gateway's own address)
112+
- `ip`: Gateway's own WireGuard address in CIDR format (e.g., 10.8.0.1/16)
113+
- `client_ip_range`: Address pool range for allocating to CVMs (e.g., 10.8.0.0/18)
114+
- `reserved_net`: Reserved address range that will not be allocated to CVMs (e.g., 10.8.0.1/32, reserving the gateway's own address)
104115

105116
Recommendation: Design client_ip_range and reserved_net to ensure clear address pool planning for each Gateway, avoiding address conflicts.
106117

107-
### 2.4 Configuration File Examples
118+
### 2.4 Cluster Sync and Peer Discovery
119+
120+
> **Image version note**: The sync enable logic varies by gateway image version. In `dstacktee/dstack-gateway:0.5.8`, sync is enabled when `NODE_ID > 0` (regardless of `BOOTNODE_URL`). In some custom-built images, sync may only be enabled when `BOOTNODE_URL` is non-empty. Check your image's `entrypoint.sh` to confirm the behavior. When in doubt, set `NODE_ID > 0` and provide a `BOOTNODE_URL` on at least one node.
121+
122+
Gateway nodes discover each other through two mechanisms:
123+
124+
1. **Bootnode discovery** (active): A node with `bootnode` configured will fetch the peer list from the bootnode at startup, then periodically retry until peers are found.
125+
126+
2. **Auto-discovery** (passive): When a remote node sends a sync request, the local node automatically adds it as a peer. This means the first node in a cluster does not need a bootnode — it will be discovered when the second node connects to it.
127+
128+
This allows a simple deployment order:
129+
1. Start gateway-1 with `bootnode = ""` (no bootnode)
130+
2. Start gateway-2 with `bootnode = "https://rpc.gateway-1:9012"`
131+
3. Gateway-2 fetches peers from gateway-1 and starts syncing
132+
4. Gateway-1 auto-discovers gateway-2 from the incoming sync request
133+
134+
> Note: `bootnode` is only used for initial discovery. Once peers are discovered, they are persisted in the KV store and survive restarts.
135+
136+
### 2.5 Configuration File Examples
108137

109138
gateway-1.toml:
110139

@@ -141,17 +170,17 @@ enabled = true
141170
interval = "30s"
142171
timeout = "60s"
143172
my_url = "https://rpc.gateway-1.demo.dstack.org:9012"
144-
bootnode = "https://rpc.gateway-2.demo.dstack.org:9012"
173+
bootnode = ""
145174
node_id = 1
146175
data_dir = "/var/lib/gateway/data"
147176

148177
[core.wg]
149178
private_key = "<node1-private-key>"
150179
public_key = "<node1-public-key>"
151180
listen_port = 9013
152-
ip = "10.8.128.1/16"
153-
reserved_net = ["10.8.128.1/32"]
154-
client_ip_range = "10.8.128.0/18"
181+
ip = "10.8.0.1/16"
182+
reserved_net = ["10.8.0.1/32"]
183+
client_ip_range = "10.8.0.0/18"
155184
config_path = "/var/lib/gateway/wg.conf"
156185
interface = "wg-gw1"
157186
endpoint = "<host ip>:9013"
@@ -205,9 +234,9 @@ data_dir = "/var/lib/gateway/data"
205234
private_key = "<node2-private-key>"
206235
public_key = "<node2-public-key>"
207236
listen_port = 9013
208-
ip = "10.8.0.1/16"
209-
reserved_net = ["10.8.0.1/32"]
210-
client_ip_range = "10.8.0.0/18"
237+
ip = "10.8.64.1/16"
238+
reserved_net = ["10.8.64.1/32"]
239+
client_ip_range = "10.8.64.0/18"
211240
config_path = "/var/lib/gateway/wg.conf"
212241
interface = "wg-gw2"
213242
endpoint = "<host ip>:9013"
@@ -218,13 +247,22 @@ listen_port = 9014
218247
external_port = 443
219248
```
220249

221-
### 2.5 Verify Cluster Sync
250+
### 2.6 Verify Cluster Sync
222251

223252
```bash
224-
# Check sync status on any node
253+
# Check sync status on any node (replace port with your admin port)
225254
curl -s http://localhost:9016/prpc/Admin.WaveKvStatus | jq .
255+
256+
# List known cluster nodes
257+
curl -s http://localhost:9016/prpc/Admin.Status | jq '.nodes'
226258
```
227259

260+
A healthy cluster sync shows:
261+
- `enabled: true` on all nodes
262+
- Each node appears in every other node's `.nodes` array
263+
- `last_seen` timestamps are recent (within the sync interval)
264+
- `peer_ack` values are close to `local_ack` (no large lag)
265+
228266
## 3. Adding Reverse Proxy Domains
229267

230268
Gateway supports automatic TLS certificate management via the ACME protocol.
@@ -245,6 +283,8 @@ curl -X POST "http://localhost:9016/prpc/Admin.SetCertbotConfig" \
245283

246284
Gateway uses DNS-01 validation, which requires configuring DNS provider API credentials.
247285

286+
The Cloudflare API token needs the **DNS:Edit** permission on the target zone. Create one at [Cloudflare API Tokens](https://dash.cloudflare.com/profile/api-tokens) with the "Edit zone DNS" template.
287+
248288
Cloudflare example:
249289

250290
```bash

0 commit comments

Comments
 (0)