環境:接上你自己的 dev
你改了一兩個服務,想在箱子裡把它們跑起來,其餘的照舊用你現成的 dev:同一個資料庫、同一個 Redis、沒改的內部服務。環境就是做這件事的。
一個環境是你的一套 dev(或 staging),由三樣東西組成:
- 連線:你在自己內網跑的連接器。它主動往外連 ParallelSandbox,你的防火牆不用開任何入口。箱子只連得到你在連線上列出的
host:port。一個環境可以有好幾條連線,一個網路一條(一個 VPC、一間辦公室);每條有自己的 token 與自己的位址清單。 - 服務設定:每個服務在這個環境的整組環境變數,從 AWS ECS 匯入或上傳
.env。箱子裡是/work/.sbx/env/<服務>.env與.sh。 - externalBaseUrl:沒改的 HTTP 服務的網址(選填),與
sandbox_start的同名參數一樣。
agent 開箱時帶 environment,箱子裡連 db.internal:5432 就會經你的連接器到你的資料庫,容器裡也一樣;改過的服務用它自己的設定檔啟動,設定跟它在 dev 上跑的時候一模一樣。
箱子怎麼連到一個位址
- 開箱時,箱子從環境的所有連線拿到位址清單。每個主機名在箱子裡有自己的位址(取自
198.18.0.0/15),箱子的 DNS 用它回答這個名字;往那個host:port(IP 位址則是IP:port)的連線,會轉到 boxd 替這個位址開的入口。boxd 不在位址本身的 port 上聽,所以 port 80 的位址(例如內部 load balancer)跟其他位址一樣能用,箱子裡的程式也可以聽同一個 port 號而不衝突:你在箱子裡跑自己的 Postgres 聽 5432 時,localhost:5432是你的,db.internal:5432照樣連到你的內網。 - 箱子裡的程式連過去時,ParallelSandbox 把這條連線交給「列了這個確切
host:port」的那條連線(那個網路),由那條連線的連接器在你的內網裡接通。兩條連線列了同一個位址時,用名稱排序在前的那條;每個位址請列在連得到它的那條連線上。 - 你的名字由連接器在你的內網裡解析,所以私有 DNS(Route 53 私有 hosted zone、Cloud Map、辦公室 DNS)跟你的服務用起來一樣。
- 預設 bridge、自訂的 Docker 網路與
docker compose裡的容器,跟箱子自己的程式用同樣的方式連到這些位址:箱子的規則兩邊都套,箱子裡 Docker 的 DNS 也轉給箱子的解析器。 - 從頭到尾都是單純的 TCP:到你服務的 TLS 在中間不會被解開,憑證的行為跟在你的 dev 上一樣。
- 箱子經連線送出去的資料算它往外的流量(
box_egress,見點數);你的網路回過來的不計量。 - 位址也可以是公開的主機,例如
api.partner.example:443。箱子就經連接器連過去,流量從你自己的網路、用它的公開 IP 出去。只接受已知 IP 的外部服務,就要這樣連:箱子自己連網際網路的流量,是從它所在主機的公開 IP 出去的,每台主機不一樣,主機來來去去也會跟著變(其他事實)。
你在箱子 services 宣告的名字若也是環境的位址,就改指到箱子(見在箱子裡用);link 到你另一個箱子、而且 host:port 相同的,也會蓋過那個位址(連到別的箱子)。你的網路上沒有任何東西能連進箱子:留在 dev 上的服務照樣呼叫 dev 上的那一份。
沒列出的東西一律連不到,各有各的樣子(在箱子上實測):
- 列出的私有 IP 位址用沒列出的 port 連,跟沒列出的一樣沒有回應:連線會逾時,
private-endpoints.log也不會寫任何東西。 - 列出的主機或箱子裡宣告的名字,用沒列、沒宣告的 port 連,會留在箱子裡:箱子裡在
0.0.0.0聽那個 port 的程式會回應,沒有的話連線當場被拒絕。 - 沒列出(也沒宣告)的主機名,由 ParallelSandbox 的解析器去查,它只回答公開的 DNS,不知道你的私有區域:
- 只存在你私有 DNS 裡的名字(Cloud Map、Route 53 私有託管區域、辦公室的 DNS、私有 API Gateway 的
<api-id>.execute-api.<region>.amazonaws.com)解析不到:Could not resolve host、no such host、NXDOMAIN。把它連同撥的 port(API Gateway 是443)列在連得到它的那條連線上,連接器就會在你的網路裡解析它; - 公開 DNS 會解析成私有 IP 的名字,例如 RDS endpoint 或
internal-….elb.amazonaws.com的 load balancer,解析得到,接著連線就像沒列出的私有 IP 一樣逾時;要連到它就把它列上去; - 公開的名字照常解析,直接連上網際網路。
- 只存在你私有 DNS 裡的名字(Cloud Map、Route 53 私有託管區域、辦公室的 DNS、私有 API Gateway 的
- 列出的 IP 位址是在箱子裡被攔下來的:boxd 的規則把那個 IP 與 port 的連線,不管來自箱子的程式還是容器,都送到它為這條連線開的入口。
- 沒列出的私有 IP 位址沒有回應:連線會逾時,
private-endpoints.log也不會寫任何東西。 - port 80 不一樣:列在 port 80 的位址本身跟其他位址一樣能用,但列出的主機或宣告的名字在沒列、沒宣告 80 的情況下用 80 連,會連到 boxd 的場景入口,它回 502
nothing listens on <name>:80 in this box: that name is declared on another port. Dial the port it is declared on, or declare it on port 80 with targetPort, the port your process listens on。2026-09-24 20:30 UTC 之前開的箱子則是連到第一個宣告的服務。
設定
環境由 agent 用 REST(見下)設好,再用 sandbox_environments 讀結果;沒有畫面。只有兩件事要有人在你的網路裡做:跑連接器,以及用 AWS 的話建角色。順序:
- 建環境:
POST /v1/environments,取個名字,例如dev。agent 開箱時用這個名字。 - 新增連線,一個網路一條:
POST /v1/environments/{env}/connections。回應裡有runCommand,是一行docker run,token 只顯示這一次(換發時再給一次)。在那個網路裡連得到那些資料庫與服務的機器上跑它(見下方「連接器」)。幾秒內sandbox_environments就會顯示這條連線online。 - 設每條連線的位址:
PUT /v1/environments/{env}/connections/{id}/endpoints,送整份清單,一個host:port一筆,照你服務設定裡的寫法,例如mydb.xxxx.ap-northeast-1.rds.amazonaws.com:5432、cache.internal:6379、10.0.1.5:8080。一行一個host:port,照你服務設定裡的寫法,例如mydb.xxxx.ap-northeast-1.rds.amazonaws.com:5432、cache.internal:6379、10.0.1.5:8080。有了服務設定之後,GET /v1/environments/{env}/suggested-endpoints會回從設定裡找到的位址:每個值裡的host:port、每個網址(沒寫 port 的用 scheme 的預設:postgres 5432、mysql 3306、redis 6379、mongodb 27017、amqp 5672、http 80、https 443),以及配得上*_PORT的*_HOST變數;主機是私有 IPv4(10.0.0.0/8、172.16.0.0/12、192.168.0.0/16),或結尾是.internal、.local、.lan、.corp、.svc、.cluster.local、.rds.amazonaws.com、.cache.amazonaws.com、.es.amazonaws.com、.mq.amazonaws.com、.docdb.amazonaws.com、.redshift.amazonaws.com的才列。其他的,例如內部 load balancer 的internal-*.elb.amazonaws.com或用你們自己網域的私有 zone,要自己加。可能會改的服務,它們的內網名字也要列:沒改它的箱子連到你 dev 上的那一份,在sandbox_start宣告了那個名字的箱子則會接走它。 - 服務設定:從 AWS 匯入(
POST .../services/import)或上傳.env(PUT .../services/{name}),下面都有說。
用 REST 設定
上面的事都是 https://api.parallelsandbox.com 的 REST,帶 Authorization: Bearer <token>。token 是帳號的 psbx_ API key,或已接上的 client 的 OAuth access token(一小時過期;key 從哪來見REST)。MCP 工具 sandbox_environments 只讀:列出環境、連線與連得到的位址;建立與修改走 REST。{env} 是環境的名字,{id} 是 GET /v1/environments/{env} 裡連線的 id。設定的值只能寫、讀回來只有變數名字。
| 請求 | 內容 | 回傳 |
|---|---|---|
GET /v1/environments |
{ "environments": [...] },每個跟下面一樣 |
|
POST /v1/environments |
{ "name": "dev" },可加 externalBaseUrl;名字是 1 到 32 個小寫英文、數字與減號 |
環境:name、externalBaseUrl、awsRoleArn、awsRegion、connections[](id、name、online、sessions、lastSeenAt、connectorVersion、endpoints)、services[] |
GET、PUT、DELETE /v1/environments/{env} |
PUT:externalBaseUrl、awsRoleArn、awsRegion 任選(有角色就要有 region;"" 清掉) |
環境;DELETE 回 { "ok": true } |
POST /v1/environments/{env}/connections |
{ "name": "office" } |
connection、token 與 runCommand(連接器的 docker run 那一行);token 只在這裡與換發時出現 |
POST /v1/environments/{env}/connections/{id}/token |
新的 token 與 runCommand;舊 token 立刻失效,用它的連接器會被斷線 |
|
DELETE /v1/environments/{env}/connections/{id} |
{ "ok": true } |
|
PUT /v1/environments/{env}/connections/{id}/endpoints |
{ "endpoints": [{ "host": "mydb.internal", "port": 5432 }] },整份清單,最多 100 個 |
連線 |
GET /v1/environments/{env}/suggested-endpoints |
{ "endpoints": [...] },從服務設定裡找到的位址 |
|
GET /v1/environments/{env}/services |
{ "services": [...] },有 name、source(dotenv 或 aws-ecs)、keys、syncedAt |
|
PUT /v1/environments/{env}/services/{name} |
{ "dotenv": "KEY=value\n…" } |
服務 |
POST /v1/environments/{env}/services/import |
{ "region", "cluster", "service" },task 有好幾個容器時加 container,name 預設是 service |
服務 |
POST /v1/environments/{env}/services/{name}/sync |
服務,重新從 ECS 匯入 | |
DELETE /v1/environments/{env}/services/{name} |
{ "ok": true } |
|
GET、PUT、DELETE /v1/aws |
PUT:{ "roleArn", "region" } |
匯入用的唯讀角色,附它要信任的 externalId 與一鍵建立的 CloudFormation quickCreateUrl |
GET /v1/aws/ecs/services?region=… |
那個角色看得到的 ECS 服務 |
例如建一個環境、一條連線與一個服務的設定:
A=https://api.parallelsandbox.com/v1; H="Authorization: Bearer $PSBX_TOKEN" # API key 或 OAuth access token
curl -s -H "$H" -X POST $A/environments -d '{"name":"dev"}'
curl -s -H "$H" -X POST $A/environments/dev/connections -d '{"name":"aws"}' # token 與 runCommand,只出現這一次
curl -s -H "$H" -X PUT $A/environments/dev/connections/<id>/endpoints -d '{"endpoints":[{"host":"mydb.internal","port":5432}]}'
jq -Rs '{dotenv: .}' api.env | curl -s -H "$H" -X PUT $A/environments/dev/services/api --data-binary @-
連接器
docker run -d --name parallelsandbox-connector --restart=always \
-e PSBX_CONNECTOR_TOKEN=psbx_conn_... \
public.ecr.aws/b2n6a1j1/connector:latest
| 環境變數 | 說明 |
|---|---|
PSBX_CONNECTOR_TOKEN |
必填,建立連線時拿到的 token |
PSBX_URL |
ParallelSandbox 的 API,預設 https://api.parallelsandbox.com |
PSBX_SESSIONS |
同時開幾條到 ParallelSandbox 的連線,預設 2,範圍 1 到 8 |
PSBX_ALLOW |
選填,這台連接器只准連的目標,逗號分隔:db.internal:5432,*.svc.local:*,10.0.0.0/8:* |
- 放在連得到目標的地方:跟你的服務同一個 VPC、同一個 Kubernetes cluster,或同一台機器。在 AWS 上可以跑成一個 ECS service(Fargate,1 個 task),子網與 security group 用你服務的那組(指令在團隊設定)。
- 只往外連
api.parallelsandbox.com:443(WebSocket over TLS)。有 HTTP proxy 的話,照慣例設HTTPS_PROXY。 - 同一個 token 可以跑好幾份,任何一份在線上箱子就連得到。ParallelSandbox 換版時連接器會自己換到新的機器,新開的連線走新機器;已經開著的連線繼續用舊機器,舊機器最長一小時後下線時才斷,資料庫連線池會自己重連。
- 能連到哪裡由連線上列的位址決定,ParallelSandbox 那邊先擋;
PSBX_ALLOW讓你在自己的機器上再限一次。 - 連接器只有容器 image(
public.ecr.aws/b2n6a1j1/connector,linux/amd64 與 linux/arm64),沒有另外的執行檔可下載。沒有 Docker 的主機要用別的 OCI runtime,例如 Podman(帶同樣的-e變數),或跑在 ECS、Kubernetes 上。 - token 外洩或換人管理時,呼叫
POST /v1/environments/{env}/connections/{id}/token:舊 token 立刻失效,連著的連接器馬上斷線,用新 token 重新啟動。
規模與備援
- 每個網路一個連接器,就能服務帳號裡的每個箱子,不管是誰開的。箱子往你的位址開的每條連線,是連接器某條 session(它連到 ParallelSandbox 的 WebSocket)裡的一條 stream,不是另外一條連線。
PSBX_SESSIONS(預設 2,1 到 8)是一份連接器同時開幾條 session。多於一條時,ParallelSandbox 換版期間另一條還在。- 要備援,就在另一台主機或另一個可用區域用同一個 token 再跑一份。ParallelSandbox 把每一份的 session 放在同一池:新的箱子連線先用收到它的那台 ParallelSandbox 機器上的 session,再來是別台機器上的,換版中正要移走的放最後。每一份都要連得到連線上列的每個位址:選中的那份連不到目標時,箱子當場拿到 502,不會改試另一份。
- 連接器只是在 stream 與你的目標之間搬 bytes,最吃的是網路。在 AWS 上看 Fargate task 在 CloudWatch 的 CPU 與網路指標,偏高就加 CPU 或再跑一份。
- 箱子凍結或停掉時,ParallelSandbox 會在大約一分鐘內關掉它的連線,不會一直佔著連接器上的 stream。
- 環境與連接器在 ParallelSandbox 不收費;箱子經連線送出去的資料算它往外的流量(點數)。連接器本身的運算與網路(Fargate task、NAT gateway、辦公室的主機)由你的雲端或機房計費。
從 AWS 匯入服務設定
ParallelSandbox 用你授權的唯讀角色讀 ECS 服務目前的 task definition:environment 原樣帶入,secrets 裡引用的 SSM 參數與 Secrets Manager 值(含 :json-key)解開後帶入。environmentFiles(S3 上的檔案)讀不到,會列在「沒匯入」裡。設定預設用 ECS 服務的名字存,也可以另外取名;這個名字就是 /work/.sbx/env/<服務>.env 裡的 <服務>。
要帶上 S3 的 environmentFiles 檔案,就把它的內容當 .env 用另一個名字上傳(PUT .../services/{name}),例如 api-files,服務啟動時兩個檔案都給,這個放前面,讓 task definition 自己的值蓋過它,跟在 ECS 上一樣:docker run --env-file /work/.sbx/env/api-files.env --env-file /work/.sbx/env/api.env ...,或 . api-files.sh && . api.sh。用匯入的那個服務名稱上傳,會取代它匯入的設定。
連接 AWS 帳號(一個帳號做一次,走 REST):
GET /v1/aws回角色要信任的externalId與quickCreateUrl:握有 AWS 帳號的人打開它,主控台會出現已經填好參數的 stack,建立即可。也可以自己建角色:信任arn:aws:iam::580360261327:root,條件sts:ExternalId是那個externalId;權限要ecs:ListClusters、ecs:ListServices、ecs:DescribeServices、ecs:DescribeTaskDefinition、ssm:GetParameters、secretsmanager:GetSecretValue,SecureString 或自訂 KMS 金鑰加密的 secret 另外要kms:Decrypt。範本:https://parallelsandbox-releases.s3.ap-northeast-1.amazonaws.com/releases/aws/readonly-role.yaml。PUT /v1/aws,內容{ "roleArn": "<stack Outputs 裡的 RoleArn>", "region": "<服務所在的 region>" }。ParallelSandbox 會實際登入一次,失敗時錯誤訊息會說原因。
之後 GET /v1/aws/ecs/services?region=… 列出角色看得到的服務,POST /v1/environments/{env}/services/import({ "region", "cluster", "service" })匯入一個(讀不到的會放在 sourceRef.skipped)。服務的 task definition 換版之後,POST /v1/environments/{env}/services/{name}/sync 再讀一次。
設定值在 ParallelSandbox 加密存放,只在箱子被認領的那一刻交給那個箱子;agent 只看得到變數名稱。
不在 ECS 上的服務的設定
服務跑在別的地方(Kubernetes、VM、伺服器上的 docker compose)時,把它的設定(一行一個 KEY=value)用 PUT /v1/environments/{環境}/services/{服務} 上傳,內容是 { "dotenv": "KEY=value\n…" };箱子拿到的 /work/.sbx/env/<服務>.env 與 .sh 跟匯入的一樣。從設定實際所在的地方整理出這個檔案,例如 Deployment 的 env 與它引用的 ConfigMap、Secret,systemd unit 的 EnvironmentFile,或 compose 的 env_file。上傳的設定不會同步:設定改了就再上傳一次。
箱子裡的 AWS 權限
服務在 AWS 上的權限通常不是寫在環境變數裡,而是 AWS 直接發給那台機器的身分(ECS 的 task role)。服務搬進箱子之後這個身分就沒了,所以要 S3、SQS 的功能會失敗。
環境可以指定一個角色給箱子用,走 OIDC,跟 GitHub Actions、Vercel 連 AWS 是同一套做法:
GET /v1/environments/{env}回這個環境的身分(awsSubject,即tenant:<帳號>:env:<環境>)與awsBoxRoleQuickCreateUrl,一鍵用 CloudFormation 建角色的連結。- 在 AWS 建好角色、給它箱子需要的權限,再
PUT /v1/environments/{env},內容{ "awsRoleArn": "<角色 ARN>", "awsRegion": "<region>" }。 - 之後開的箱子裡,
AWS_CONTAINER_CREDENTIALS_FULL_URI已經設好(指到箱子裡跟 ECS 一樣的憑證位址),AWS_REGION與AWS_DEFAULT_REGION也有;AWS 的 SDK 與 CLI 自己拿憑證、到期自己換,程式不用改。容器也一樣:用docker run --env-file /work/.sbx/env/<服務>.env起的容器就有。在箱子裡跑aws sts get-caller-identity會看到assumed-role/<角色>/psbx-<箱子 id>。
平行沙盒替箱子簽一張十分鐘的身分證明,箱子自己拿它跟 AWS 換一小時到期的憑證。長期金鑰不存在任何地方,平行沙盒手上也沒有能進你帳號的權限;你在 AWS 刪掉角色或改信任政策就立刻失效。角色只信任你指定的那個環境。
要在箱子裡從你們自己的 ECR 拉 image(例如沒改的服務在 dev 上正在跑的那一版),角色要有 ecr:GetAuthorizationToken,以及對那些 repository 的 ecr:BatchCheckLayerAvailability、ecr:GetDownloadUrlForLayer、ecr:BatchGetImage(AWS 管理的 policy AmazonEC2ContainerRegistryReadOnly 四個都有)。然後在箱子裡:aws ecr get-login-password --region <region> | docker login --username AWS --password-stdin <account>.dkr.ecr.<region>.amazonaws.com,再 docker run --env-file /work/.sbx/env/<服務>.env … <image>。沒有這些權限的話,登入會失敗:AccessDeniedException … is not authorized to perform: ecr:GetAuthorizationToken。
在箱子裡用
sandbox_environments {}
sandbox_start { "name": "退款流程改版", "goal": "接 dev 的資料庫與服務改版 api 的退款流程;經 api 退的款在 dev 記下來就算完成", "environment": "dev", "services": [{ "name": "api", "port": 8080 }] }
sandbox_start 的回傳多一個 environment:reachable 是箱子裡連得到的內網位址,connectorOnline 是連接器在不在線上(有好幾條連線時,是其中有沒有任何一條在線上),connections[] 列出每條連線的 name、online、sessions、lastSeenAt、connectorVersion 與 reachable(走這條連線的位址,箱子自己跑的名字不算),services 是每個服務的設定檔路徑。有連線斷掉時,next 會點名它們與它們的位址;一條都不在線時,它說連接器不在線上。sandbox_status 回同一份 environment,每次呼叫都重新讀,所以箱子開起來之後才斷掉的連線也看得到。
環境有 externalBaseUrl 的話,箱子會沿用它,你宣告的每個服務一開始都是 external 模式:name:port 的 HTTP 轉到 externalBaseUrl。要在箱子裡跑的服務切成 box;boxd 不佔它們的 port,所以程式是不是已經在跑都沒關係:
sandbox_wire { "id": "<id>", "service": "api", "mode": "box" }
用各自的設定啟動改過的服務:
sandbox_exec { "id": "<id>", "cmd": "cd api && docker build -t api . && docker run -d --name api --env-file /work/.sbx/env/api.env -p 8080:8080 api", "note": "用 dev 的設定 build 並啟動 api" }
sandbox_exec { "id": "<id>", "cmd": "cd api && . /work/.sbx/env/api.sh && go run ./cmd/api", "background": true, "note": "用 dev 的設定跑 api" }
.env是docker run --env-file的格式,值原樣;.sh是export KEY='VALUE',給直接跑的程式 source。多行的值只在.sh裡。只想在箱子裡改一個值,就在--env-file後面加-e KEY=value,後面的會蓋過前面的。直接跑的程式,先 source
.sh,再覆寫箱子裡要換的值,只給那一條命令,或用export:. /work/.sbx/env/api.sh && DATABASE_URL="$(psbx-testdb url)" ./bin/backfill不要 source
.env(set -a; . api.env):它的值沒加引號,含空白或引號的值會讓 shell 報錯停下,含$的值則會不聲不響地變掉。用
version宣告的已發布版本,會自動拿到它發布時登記的那個服務的設定檔/work/.sbx/env/<服務>.env,不用你加任何參數(跑發布好的版本)。docker compose的env_file會展開值裡的$;值裡有$時改用docker run --env-file,或先 source.sh再用environment:帶進去。箱子裡的程式與容器照原本的主機名連就好,不用改設定:名字在箱子裡解析到各自的位址,boxd 把每條連線經 ParallelSandbox 與你的連接器接過去。
別的服務用內網主機名呼叫你改的那個服務時(例如路由器轉給
api.svc.local:8080),在sandbox_start把它用同一個名字宣告在services:[{ "name": "api.svc.local", "port": 8080 }]。port是呼叫端連的 port;你的程式聽別的 port 時,把它加成targetPort,呼叫端用 port 80 連的時候(例如內部 load balancer)一定要給:[{ "name": "internal-lb.svc.local", "port": 80, "targetPort": 8080 }]。這個名字在箱子裡就指到箱子自己,不經連接器,reachable也不列它;其他沒改的照樣連你的 dev。跑在你 dev 上的呼叫端還是會連到 dev 上的那一份;測試需要的話,把呼叫端也放進箱子跑。之後才用sandbox_wire補宣告(port是你自己的程式,version是已發布的版本),在sandbox_status→health.features有take-over的箱子上(2026-09-25 12:40 UTC 起開的箱子都有)會就地接手這個名字:位址不變,經連接器開著的連線會被關掉,程式重連到箱子裡的那份。比較舊的箱子上,這種名字照樣經連接器連出去,所以在那種箱子上要在sandbox_start宣告(接手 link 或環境位址)。用自己的設定啟動的改過的服務,連的是你 dev 真正的資料庫:它的 migration 與寫入都會落在那裡。測試用的話,
psbx-testdb up <名字>會在箱子裡開一個乾淨的 Postgres 16,把連線資訊寫到/work/.sbx/testdb/<名字>.env(DATABASE_URL、TEST_DATABASE_URL等,都是export行;--as 變數名再多寫一個);它聽127.0.0.1,容器要加--network host才連得到。它的選項列在團隊設定。要讓服務用自己的資料庫,就在容器裡開一個,並在--env-file後面蓋掉設定(-e DATABASE_URL=…)。團隊設定有完整的命令。呼叫端不是用名字、而是從自己的註冊表拿到對方的 IP 直接連的時候(例如閘道從 Redis 名冊拿 worker 的 IP),在
services用位址範圍宣告:[{ "name": "172.16.0.0/16", "port": 9090 }]。箱子裡(含容器)往這個範圍的那個 port 的連線,都會轉到箱子裡這個服務的targetPort,往其他 port 的不動。只收私有位址:10.0.0.0/8、172.16.0.0/12、192.168.0.0/16、100.64.0.0/10,只能在sandbox_start宣告。同 port 又落在範圍內的內網位址不會再經連接器。
團隊設定有完整的例子:兩個網路、好幾個改過的服務、Sentry,以及從另一個箱子用改過的服務。
排錯
sandbox_environments:每個環境的reachable、connectorOnline(任何一條連線在線上就是 true),以及connections[]:每條連線的online、sessions、lastSeenAt、connectorVersion與它帶的位址。先看connections[]:connectorOnline還是true時,斷掉的office也會在這裡看得出來。箱子用的那個環境,sandbox_status→environment.connections[]也看得到同樣的內容。要最後確認某個網路,仍然是在箱子裡用真正的用戶端(curl、pg_isready、psql與redis-cli箱子裡都有;只做 TCP 連線不管怎樣都會成功,因為 boxd 會先接起來)連它的一個位址,再看health.privateEndpoints裡那個位址。sandbox_status的health.privateEndpoints:每個位址在箱子裡由 boxd 開的入口(localIp、localPort)、目前幾條連線、開過幾條、失敗幾次、最近一次失敗的原因。/work/.sbx/logs/private-endpoints.log:每次接不通一行。403:這個位址已經不在環境的任何連線上,用PUT /v1/environments/{env}/connections/{id}/endpoints加回去。箱子開起來之後才加的位址,那個箱子完全不知道(箱子裡沒有它的名字的位址),要開新的箱子。- 你的內部名稱(例如私有 API Gateway 的
<api-id>.execute-api.<region>.amazonaws.com)回Could not resolve host(no such host、NXDOMAIN):這個名字沒有列在環境上,公網 DNS 也不認得它。把它連同 port 加到連得到它的那條連線上。PUT .../endpoints會換掉整份清單,所以先從GET /v1/environments/{env}拿目前的清單,加上新位址再送回去;之後開新的箱子。 503:列了這個位址的那條連線,它的連接器離線,訊息裡會寫出是哪一條連線(the connector "office" of this environment is offline; …);看連接器的 log(docker logs parallelsandbox-connector,或 ECS task 的 log)。箱子裡的用戶端看到的是連線先被接起、然後被重置。502:連接器連不上目標,檢查連接器所在的機器到那個位址之間的 DNS、路由與 security group。- 箱子剛解凍就出現錯誤:boxd 會在解凍後約 15 秒內,關掉凍結前就開著、連到你位址的連線,讓程式拿到錯誤,而不是握著一條永遠不會回應的連線。資料庫連線池通常會自己重連;長時間連著的用戶端要自己重試。
限制
- 只轉 TCP。位址是確切的主機名或 IPv4 加上 port,不支援萬用字元或範圍。
- 每條連線最多 100 個位址,每個箱子所有連線合計最多 200 個。
- 箱子在被認領的那一刻拿到環境的位址、設定、
externalBaseUrl與 AWS 角色:之後加的位址或改的設定,要開新的箱子才會拿到。刪掉位址、連線或環境則立刻生效,已經在跑的箱子也連不到。