跳至主要内容

Kubernetes 部署

適用情境

  • 正式環境或多人協作部署
  • 需要使用 Helm 管理平台固定服務
  • 需要導入 workspace-operator 做 workspace 動態排程
  • 需要 per-workspace 網路隔離(Cilium)
  • 需要 Ingress/TLS 對外公開

架構概覽

┌─────────────────────────────────────────────────────────────┐
│ Ingress Controller │
│ example.com → frontend │
│ workspace-manager.example.com → manager │
│ keycloak.example.com → keycloak │
│ workspace-runtime-{id}.example.com → runtime pod │
│ workspace-browser-{id}.example.com → browser pod │
└────────┬──────────┬──────────┬──────────────────────────────┘
│ │ │
┌─────▼────┐ ┌───▼────┐ ┌──▼───────┐ ┌──────────────┐
│ Frontend │ │Manager │ │Keycloak │ │ Workspace │
│ Deploy │ │Deploy │ │Deploy │ │ Operator │
└──────────┘ └───┬────┘ └──────────┘ │ Deploy │
│ └──────┬───────┘
┌─────▼──────┐ │ reconcile
│ Celery + │ ┌──────▼───────┐
│ Flower │ │ Workspace CR │
└────────────┘ │ (CRD) │
└──────┬───────┘
┌────────────┐ │ creates
│ PostgreSQL │ ┌──────▼───────┐
│ StatefulSet│ │ Runtime Pod │
└────────────┘ │ Browser Pod │
┌────────────┐ │ Next.js Pod │
│ Redis │ │ Service │
│ StatefulSet│ │ Ingress │
└────────────┘ │ CiliumPolicy │
┌────────────┐ └──────────────┘
│ CoTURN │
│ Deployment │
└────────────┘

Helm 管理範圍

Helm chart 管理平台固定服務:

資源類型包含項目
Deploymentfrontend、workspace-manager、workspace-operator、keycloak、coturn
StatefulSetpostgres、redis
Service所有服務的 ClusterIP Service、CoTURN NodePort
Ingressfrontend、workspace-manager、keycloak 的統一入口
ConfigMapplatform-config、workspace-routing、firewall-defaults、keycloak-realm、frontend-nginx
Secret資料庫密碼、Keycloak 密碼
RBACworkspace-operator ClusterRole、workspace-manager Role、ServiceAccount
CRDworkspaces.platform.aileron.io
Jobpostgres-bootstrap(初始化資料庫)
備註

每個 workspace 的動態資源(Pod、Service、Ingress、CiliumNetworkPolicy)不由 Helm 直接管理,而是由 workspace-operator 根據 Workspace CR 進行 reconcile。

需求

  • Kubernetes cluster(建議 1.26+)
  • kubectl
  • helm(建議 3.12+)
  • Ingress Controller(預設 nginx)
  • 可管理的 DNS(workspace host 必須可解析;可用 wildcard DNS,或自動建立每個 host 記錄)
  • TLS 憑證(若要對外公開,可搭配 cert-manager)
  • Cilium(若要完整啟用 per-workspace firewall)
  • 共享儲存(ReadWriteMany PVC 或等效方案)

Helm Chart 位置

helm/aileron/
├── Chart.yaml
├── values.yaml
├── crds/
│ └── platform.aileron.io_workspaces.yaml
├── templates/
│ ├── _helpers.tpl
│ ├── frontend-deployment.yaml
│ ├── workspace-manager-deployment.yaml
│ ├── workspace-operator-deployment.yaml
│ ├── keycloak-deployment.yaml
│ ├── coturn-deployment.yaml
│ ├── postgres-statefulset.yaml
│ ├── redis-statefulset.yaml
│ ├── ingress.yaml
│ ├── platform-configmap.yaml
│ ├── workspace-routing-configmap.yaml
│ ├── firewall-defaults-configmap.yaml
│ ├── workspace-manager-rbac.yaml
│ ├── workspace-operator-rbac.yaml
│ └── ... (其他 service / secret / job)
└── files/
└── realm.json

安裝

驗證 chart

# 語法檢查
helm lint helm/aileron

# 模板渲染預覽
helm template test-release helm/aileron

安裝

helm install aileron helm/aileron \
--namespace aileron \
--create-namespace

使用自訂 values 檔案安裝

# 複製預設 values 並修改
cp helm/aileron/values.yaml my-values.yaml
# 編輯 my-values.yaml ...

helm install aileron helm/aileron \
--namespace aileron \
--create-namespace \
-f my-values.yaml

升級

helm upgrade aileron helm/aileron \
--namespace aileron

移除

helm uninstall aileron --namespace aileron
CRD 不會自動刪除

helm uninstall 不會移除 CRD。若需完全清除:

kubectl delete crd workspaces.platform.aileron.io

Image Pre-pulling

Workspace Runtime image 較大時,每個 node 上第一個 workspace pod 會先等待 image pull。Helm chart 預設建立 workspace-runtime-prepuller DaemonSet,讓每個 node 先快取 workspaceOperator.runtimeImage 指定的 runtime image,後續 workspace pod 使用 IfNotPresent 時可直接啟動。

Runtime Image Tag 選擇

Kubernetes 模式必須使用正式用途的 latest-* runtime image,不應使用 dev-* runtime image。

Tag 類型Dockerfile target是否內含 /workspace-runtime/appKubernetes 適用性
dev-*development不保證,預期由本地 volume mount 提供不適用
latest-*production是,程式碼已打包進 image適用

例如 amd64 + lite runtime 應設定:

kubernetes:
runtimeImage: ailerondocker/workspace-runtime:latest-lite-amd64

workspaceOperator:
runtimeImage: ailerondocker/workspace-runtime:latest-lite-amd64

dev-lite-amd64 這類 image 是給 Docker Compose 開發模式使用。它依賴 ./workspace-runtime:/workspace-runtime 這類 volume mount 提供程式碼;若直接拿到 Kubernetes 使用,runtime FastAPI 可能因找不到 app.main 而無法啟動。

DaemonSet 透過 initContainer 拉取 runtime image,完成後由 registry.k8s.io/pause:3.10 container 保持 pod 存活。runtime image 升版時,Helm upgrade 會讓 DaemonSet rollout,觸發各 node 拉取新 image。

驗證 pre-puller 狀態:

kubectl get pods -n <namespace> -l app.kubernetes.io/component=workspace-runtime-prepuller -o wide
kubectl describe daemonset -n <namespace> <release-name>-aileron-workspace-runtime-prepuller

若 workspace node 有 taint,pre-puller 需要相同的 toleration,否則不會排程到那些 node:

workspaceRuntimePrepuller:
tolerations:
- key: workspace
operator: Equal
value: "true"
effect: NoSchedule

如需關閉 pre-puller:

workspaceRuntimePrepuller:
enabled: false

Public Routing 設定

Kubernetes 模式使用 host-based routing(子網域模式),不使用 path-based ingress。

目前的實作是:

  • Helm 建立 1 個平台層 Ingress,處理 Frontend、Workspace Manager、Keycloak
  • workspace-operator 在每次建立 workspace 時,另外為 workspace-runtimeworkspace-browserworkspace-nextjs 各建立 1 個獨立 Ingress

也就是說,workspace 流量不是由單一 wildcard Ingress rule 轉送,而是由 Operator 依 workspaceId 展開 host pattern,建立明確的 Ingress host 規則。

Helm Values

Value預設值說明
publicRouting.schemehttphttphttps
publicRouting.baseDomainaileron.local主要網域
publicRouting.frontendHost{baseDomain}Frontend host
publicRouting.workspaceManagerHostworkspace-manager.{baseDomain}Manager host
publicRouting.keycloakHostkeycloak.{baseDomain}Keycloak host
publicRouting.runtimeHostPatternworkspace-runtime-{workspaceId}.{baseDomain}Runtime host pattern
publicRouting.browserHostPatternworkspace-browser-{workspaceId}.{baseDomain}Browser host pattern
publicRouting.nextjsHostPatternworkspace-nextjs-{workspaceId}.{baseDomain}Next.js host pattern

{baseDomain}{workspaceId} 是 Helm template 在部署時替換的佔位符。

範例設定

example.com 為示意域名:

helm upgrade --install aileron helm/aileron \
--namespace aileron \
--create-namespace \
--set publicRouting.scheme=https \
--set publicRouting.baseDomain=example.com \
--set publicRouting.frontendHost='{baseDomain}' \
--set publicRouting.workspaceManagerHost='workspace-manager.{baseDomain}' \
--set publicRouting.keycloakHost='keycloak.{baseDomain}' \
--set publicRouting.runtimeHostPattern='workspace-runtime-{workspaceId}.{baseDomain}' \
--set publicRouting.browserHostPattern='workspace-browser-{workspaceId}.{baseDomain}' \
--set publicRouting.nextjsHostPattern='workspace-nextjs-{workspaceId}.{baseDomain}'

對外 host 對應關係:

服務Host
Frontendhttps://example.com
Workspace Managerhttps://workspace-manager.example.com
Keycloakhttps://keycloak.example.com
Workspace Runtimehttps://workspace-runtime-<workspaceId>.example.com
Workspace Browserhttps://workspace-browser-<workspaceId>.example.com
Workspace Next.jshttps://workspace-nextjs-<workspaceId>.example.com

每建立一個新的 workspace,就會依照同一組 pattern 產生另一組不同的網址。例如 default-workspace 會得到:

  • workspace-runtime-default-workspace.example.com
  • workspace-browser-default-workspace.example.com
  • workspace-nextjs-default-workspace.example.com

DNS 與 TLS 需求

DNS 記錄

需要準備的 DNS 記錄:

記錄類型名稱指向用途
A / CNAMEexample.comIngress IPFrontend
A / CNAMEworkspace-manager.example.comIngress IPManager API
A / CNAMEkeycloak.example.comIngress IP認證服務
A / CNAME*.example.com 或自動建立 workspace-*-<workspaceId>.example.comIngress IP所有 workspace 子網域
提示

Kubernetes 目前會為每個 workspace component 建立明確的 Ingress host,例如 workspace-runtime-default-workspace.example.com。DNS 層可用單一 wildcard 記錄簡化管理,也可以搭配 ExternalDNS 等工具自動建立每個 A/CNAME 記錄。

TLS 設定

使用 cert-manager 自動管理憑證:

# values.yaml
ingress:
enabled: true
className: "nginx"
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
tls:
- secretName: aileron-tls
hosts:
- example.com
- "*.example.com"
Workspace TLS 行為

values.yamlingress.tls 只會套用到 Helm 建立的那個平台 Ingress。workspace-operator 目前建立的 workspace Ingress 只包含 host/path 規則與 nginx annotation,沒有額外寫入 spec.tls。若要讓 workspace 子網域走 HTTPS,通常需要由 ingress controller 提供 wildcard / default certificate,或另外擴充 operator 的 Ingress TLS 邏輯。

或手動建立 TLS Secret:

kubectl create secret tls aileron-tls \
--cert=fullchain.pem \
--key=privkey.pem \
-n aileron

本機開發 DNS 設定 (macOS)

在本機使用 Kubernetes(Docker Desktop / Rancher Desktop / minikube)開發時,預設 baseDomainaileron.localhost。每個 workspace 會動態產生子網域,例如:

workspace-runtime-<uuid>.aileron.localhost
workspace-browser-<uuid>.aileron.localhost
workspace-nextjs-<uuid>.aileron.localhost

macOS 不會自動將 *.aileron.localhost 解析到 127.0.0.1,而 /etc/hosts 也不支援 wildcard。因此需要使用 dnsmasq 做本地 wildcard DNS 解析。

不設定會怎樣?

瀏覽器無法解析 workspace 子網域 → HTTP request 發不出去 → AI Conversation 顯示 Disconnected、version-control / preview 等功能全部失效。DevTools Network 會看到 Status Code 空白

安裝與設定步驟

# 1. 安裝 dnsmasq
brew install dnsmasq

# 2. 加入 wildcard 規則
echo 'address=/aileron.localhost/127.0.0.1' >> $(brew --prefix)/etc/dnsmasq.conf

# 3. 建立 macOS resolver 設定
sudo mkdir -p /etc/resolver
echo 'nameserver 127.0.0.1' | sudo tee /etc/resolver/aileron.localhost

# 4. 啟動 dnsmasq (以 root 綁定 port 53)
sudo brew services start dnsmasq

驗證

# 確認 macOS resolver 已掛載
scutil --dns | grep -A2 aileron.localhost

# 測試解析(應回 127.0.0.1)
dscacheutil -q host -a name test.aileron.localhost

# 直接查詢 dnsmasq
dig @127.0.0.1 test.aileron.localhost +short
替代方案:nip.io

若不想安裝 dnsmasq,可改用公開 wildcard DNS 服務。在 values.yaml 設定 publicRouting.baseDomain: 127.0.0.1.nip.io,所有 *.127.0.0.1.nip.io 都會自動解析到 127.0.0.1。但需要 helm upgrade 並重建現有 workspace。

Internal vs External URL

important

internalUrlexternalUrl 分工明確,部署時不要混用。

類型用途使用場景
internalUrlCluster 內部 Service DNSpod-to-pod、service-to-service 呼叫
externalUrl公開 Ingress URL瀏覽器、OIDC redirect、WebSocket、preview

Internal URL 範例:

http://workspace-manager.<namespace>.svc.cluster.local:3001
http://workspace-runtime-<workspaceId>.<namespace>.svc.cluster.local:3002
http://workspace-browser-<workspaceId>.<namespace>.svc.cluster.local:6080
http://workspace-nextjs-<workspaceId>.<namespace>.svc.cluster.local:3003

workspace-routing ConfigMap 記錄了完整的 routing 合約,包含 service name template 與 port 對應:

設定
RUNTIME_SERVICE_NAME_TEMPLATEworkspace-runtime-{workspaceId}
BROWSER_SERVICE_NAME_TEMPLATEworkspace-browser-{workspaceId}
NEXTJS_SERVICE_NAME_TEMPLATEworkspace-nextjs-{workspaceId}
RUNTIME_SERVICE_PORT3002
BROWSER_SERVICE_PORT6080
NEXTJS_SERVICE_PORT3003

Workspace CRD

Workspace Operator 使用自訂 CRD workspaces.platform.aileron.io 管理工作區:

apiVersion: platform.aileron.io/v1alpha1
kind: Workspace
metadata:
name: ws-example
namespace: workspace-system
spec:
workspaceId: "my-workspace"
ownerId: "user-123"
provisioner: kubernetes
runtime:
imageKey: default
image: ailerondocker/workspace-runtime:k8s-local
resources: {}
browser:
enabled: true
image: ailerondocker/workspace-browser:k8s-local
nextjs:
enabled: true
image: ailerondocker/workspace-nextjs:k8s-local
workspacePath: /workspace
targetNamespace: workspace-system
git:
url: "https://github.com/example/repo.git"
branch: main
envVars:
- key: NODE_ENV
value: production
firewall:
workspace:
networkAccessEnabled: true
domainAccessMode: specific
allowedDomains:
- github.com
- api.anthropic.com
browser:
networkAccessEnabled: true
domainAccessMode: specific
allowedDomains:
- google.com

CRD Status

Operator 會將 workspace 狀態更新到 .status

欄位說明
status.phaseworkspace 整體狀態
status.targetNamespace實際部署的 namespace
status.components.runtime.phaseRuntime pod 狀態
status.components.runtime.internalUrlRuntime 內部 URL
status.components.runtime.externalUrlRuntime 外部 URL
status.components.browser.*Browser pod 狀態與 URL
status.components.nextjs.*Next.js pod 狀態與 URL
status.firewall.*.effectiveAllowedDomains實際生效的 domain 允許清單

操作觸發

透過 spec.operations 可觸發元件重啟:

spec:
operations:
restartWorkspaceAt: "2026-04-09T10:00:00Z" # 重啟整個 workspace
restartRuntimeAt: "2026-04-09T10:00:00Z" # 僅重啟 runtime
restartBrowserAt: "2026-04-09T10:00:00Z" # 僅重啟 browser
restartNextjsAt: "2026-04-09T10:00:00Z" # 僅重啟 nextjs

Kubernetes 設定

Helm Value環境變數預設值說明
kubernetes.provisionerRUNTIME_PROVISIONERkubernetes預設 provisioner
kubernetes.defaultNamespaceRUNTIME_K8S_NAMESPACEworkspace-system預設 namespace
kubernetes.allowedNamespacesRUNTIME_K8S_ALLOWED_NAMESPACES[workspace-system, default]允許的 namespace 列表
kubernetes.serviceTypeRUNTIME_K8S_SERVICE_TYPEClusterIPService type
kubernetes.nodePortRUNTIME_K8S_NODE_PORT(空)NodePort 設定
kubernetes.nodeAddressRUNTIME_K8S_NODE_ADDRESS127.0.0.1Node 位址
kubernetes.pvcNameRUNTIME_K8S_PVC_NAMEworkspace-runtime-pvcPVC 名稱
workspaceOperator.runtimeImageRUNTIME_K8S_IMAGEailerondocker/workspace-runtime:latest-lite-amd64Runtime 映像,也供 pre-puller 使用
kubernetes.browserImageRUNTIME_K8S_BROWSER_IMAGEailerondocker/workspace-browser:k8s-localBrowser 映像
kubernetes.nextjsImageRUNTIME_K8S_NEXTJS_IMAGEailerondocker/workspace-nextjs:k8s-localNext.js 映像
kubernetes.watchNamespaceWATCH_NAMESPACE(空,所有 namespace)Operator watch namespace

覆寫 namespace 與 allowlist 範例

helm upgrade --install aileron helm/aileron \
--namespace aileron \
--create-namespace \
--set kubernetes.defaultNamespace=workspace-system \
--set kubernetes.allowedNamespaces[0]=workspace-system \
--set kubernetes.allowedNamespaces[1]=team-a \
--set kubernetes.allowedNamespaces[2]=team-b

RBAC 與 Service Account

Workspace Operator

Operator 需要 ClusterRole 層級的權限來跨 namespace 管理 workspace 資源:

API GroupResourcesVerbs
"" (core)pods, services, PVC, events, configmaps, secrets全部
appsdeployments, statefulsets全部
networking.k8s.ioingresses全部
cilium.iociliumnetworkpolicies全部(僅 cilium 啟用時)
platform.aileron.ioworkspaces, workspaces/status, workspaces/finalizers全部

Workspace Manager

Manager 僅需 Role 層級權限(限定在 workspace namespace 內):

API GroupResourcesVerbs
platform.aileron.ioworkspaces全部

Storage 與 Persistence

平台服務持久化

服務預設大小Access Mode用途
PostgreSQL10GiReadWriteOnce資料庫
Redis5GiReadWriteOnce快取與任務佇列
# values.yaml 範例
postgres:
persistence:
enabled: true
size: 20Gi
storageClass: "fast-ssd"

redis:
persistence:
enabled: true
size: 5Gi

Workspace 儲存

Workspace 使用 PVC 掛載工作目錄,Operator 會根據 kubernetes.pvcName 配置自動建立掛載:

kubernetes:
pvcName: workspace-runtime-pvc
共享儲存

若多個 workspace 需要共享基礎映像或工具,可使用 ReadWriteMany 的 StorageClass(如 NFS、CephFS、EFS)。

Knowledge Base 儲存

Knowledge Base 會使用獨立的共享 PVC,由 Helm chart 管理:

kubernetes:
knowledgeBases:
pvcName: knowledge-bases-pvc
size: 20Gi
accessModes:
- ReadWriteMany
storageClassName: hostpath

掛載流程如下:

  • Helm 建立 knowledge-bases-pvc
  • workspace-manager 將它掛到 /host/knowledge-bases
  • workspace-operator 再把每個 attach 的 KB 以 subPath=<kbId> 掛進 runtime Pod 的 /knowledge/<alias>

本機 dev 建議:

  • 預設 hostpath 是單節點 fallback,適合 Docker Desktop 或本機 Kubernetes smoke test
  • 它不是多節點共享 RWX 的替代品

正式環境建議:

  • kubernetes.knowledgeBases.storageClassName 切到真正的共享 RWX 類型,例如 nfs
  • helm/values-rke.yaml 已內建這個覆寫
  • 在驗證 KB attach / mount 之前,先確認 knowledge-bases-pvc 已經 Bound

建議檢查:

kubectl get pvc -n aileron knowledge-bases-pvc
kubectl describe pvc -n aileron knowledge-bases-pvc
kubectl describe deployment -n aileron aileron-workspace-manager

CoTURN (WebRTC TURN Server)

workspace-browser 使用 neko 透過 WebRTC 串流桌面畫面。在 Kubernetes 環境中,neko pod 與使用者瀏覽器之間存在多層 NAT,必須透過 TURN server 做 relay 才能建立 WebRTC 連線。

為什麼需要 TURN

WebRTC 建立連線時,雙方透過 ICE 協議交換候選位址(candidate):

瀏覽器(Mac) ←── WebSocket 信令 ──→ neko pod(K8s)
│ │
└──── 嘗試直連(host candidate)────────┘
pod IP(10.x.x.x)對外不可達 → 失敗
│ │
└──── TURN relay ─── coturn ──────────┘
雙方各在 coturn 上拿到 relay 位址 → 成功

若沒有 TURN,ICE 只有 host candidate(pod 內部 IP),瀏覽器完全連不到,browser 元件會卡在 Connecting...

架構說明

[瀏覽器] [K8s Node]
│ turn:nodeIP:nodePort │
│──────────────────────────→│ NodePort 30479
│ │ ↓ (kube-proxy)
│ [coturn pod]
│ hostNetwork: true
│ port 3478 直接綁 nodeIP
│ relay: nodeIP:49152-65535
│←─── relay at nodeIP:49xxx ─┘

[neko pod]
│ turn:nodeIP:nodePort
│──────────────────────────→ [coturn] (同 K8s 內部可達)
│←─── relay at nodeIP:49yyy ─┘

兩端 relay 都在 nodeIP,coturn 在中間轉發 → WebRTC 連線建立

為什麼需要 hostNetwork: true

TURN relay 使用的 ephemeral UDP 埠(預設 49152–65535)無法透過 NodePort 一一對應(NodePort 只能對應單一 port)。啟用 hostNetwork: true 後:

  • coturn pod 直接使用 node 的網路 namespace
  • relay 埠直接綁定在 node IP,對外可達
  • 信令 port(3478)同樣直接在 node IP 上,不需要 NodePort 路由也能存取

這是 NodePort-only K8s 環境中部署 TURN server 的標準做法。

hostfrontendHost 的區別

neko v3 將 ICE server 設定分為 backend(neko pod → TURN)與 frontend(瀏覽器 → TURN)兩組,這讓本機與正式環境可以使用不同的位址:

設定適用方說明
coturn.hostneko pod(backend)Pod 連到 TURN 用的 IP,使用 node IP
coturn.frontendHost使用者瀏覽器(frontend)瀏覽器連到 TURN 用的 IP;若未設定,預設同 host
正式環境

正式 K8s 環境的瀏覽器直接連 node IP(host),不需要設定 frontendHost(留空即自動 fallback 到 host)。

本機開發(Docker Desktop)

Docker Desktop 對 Mac 上的瀏覽器只透過 localhost proxy NodePort,不直接暴露 VM IP(192.168.65.3)。因此:

  • host = 192.168.65.3(pod 可達的 node IP)
  • frontendHost = 127.0.0.1(Mac 瀏覽器透過 Docker Desktop localhost proxy 可達)

Helm Values

Helm Value預設值說明
coturn.enabledtrue是否啟用 TURN server
coturn.port3478coturn 監聽 port(container 內)
coturn.nodePort30478K8s NodePort
coturn.host192.168.65.3node 的外部可達 IP(backend 用)
coturn.frontendHost""(同 host瀏覽器連 TURN 用的 IP(frontend 用)
coturn.usernameaileronTURN 認證帳號
coturn.credentialaileron-turn-secretTURN 認證密碼(正式環境請換強密碼)
coturn.realmaileron.localhostTURN realm

各環境設定範例

正式環境(NodePort,node 有 public IP):

coturn:
host: "203.0.113.10" # node 的公網 IP
nodePort: 30479
username: "aileron"
credential: "your-strong-secret-here"
# frontendHost 不需設定,自動同 host

防火牆需開放:

  • nodePort(預設 30479):UDP + TCP,供 TURN 信令使用
  • 49152–65535:UDP,供 TURN relay media 使用

本機開發(Docker Desktop):

coturn:
host: "192.168.65.3" # Docker Desktop node IP(pod 可達)
frontendHost: "127.0.0.1" # Mac 瀏覽器透過 localhost proxy 可達
nodePort: 30479

多 node 正式環境注意事項:

hostNetwork: true 在每個 node 上只能跑一個 coturn(port 3478 衝突)。若 cluster 有多個 node,需用 nodeSelector 固定 coturn 到指定 node,並確保 coturn.host 設為該 node 的 IP:

# 將 coturn 固定到特定 node(先為 node 加 label)
kubectl label node <turn-node> role=turn

# values.yaml 加入
# coturn.nodeSelector.role: turn

驗證 TURN 連線

連線正常時,browser pod log 會出現:

ICE connection state changed: connected
peer connection state changed: connected
set webrtc connected: connected=true

若 browser 卡在 Connecting...,依序檢查:

  1. coturn pod 是否正常啟動:

    kubectl logs -n aileron deployment/aileron-coturn | grep "Relay address"
    # 應看到 node IP(如 203.0.113.10)而非 pod 內部 IP(10.x.x.x)
  2. relay 位址是否正確(node IP,非 pod IP):

    INFO: Relay address to use: 203.0.113.10 ← 正確
    INFO: Relay address to use: 10.1.0.x ← 錯誤(缺 --external-ip 或 hostNetwork)
  3. 防火牆是否放行 UDP 49152–65535。

  4. 本機開發: frontendHost 是否設為 127.0.0.1,而非 Docker Desktop VM IP。

Ingress 設定

預設使用 nginx ingress controller,並配置 WebSocket 所需的長超時:

ingress:
enabled: true
className: "nginx"
annotations:
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-http-version: "1.1"

Helm chart 會自動為以下服務產生 Ingress rule:

  • Frontend(frontendHost
  • Workspace Manager(workspaceManagerHost
  • Keycloak(keycloakHost
備註

Workspace 動態 Ingress(runtime、browser、nextjs)由 Operator 在 reconcile 時建立,而且每個 workspace 都會有自己獨立的 host 與 Ingress,不在此 Helm 管理 Ingress 範圍內。

Firewall Defaults

Kubernetes 模式下,平台安裝一份 firewall defaults ConfigMap,區分 workspace 和 browser 兩組:

預設允許 Domain

Workspace Runtime:

  • github.comapi.github.comobjects.githubusercontent.comraw.githubusercontent.com
  • registry.npmjs.orgnpmjs.com
  • pypi.orgfiles.pythonhosted.org
  • api.anthropic.com

Workspace Browser:

  • github.com
  • google.comgstatic.comgoogleapis.com

覆寫 firewall 預設 domain

helm upgrade --install aileron helm/aileron \
--namespace aileron \
--set firewall.defaults.workspace.allowedDomains[0]=github.com \
--set firewall.defaults.workspace.allowedDomains[1]=registry.npmjs.org \
--set firewall.defaults.browser.allowedDomains[0]=google.com \
--set firewall.defaults.browser.allowedDomains[1]=gstatic.com

Operator 與 Manager 透過 FIREWALL_DEFAULTS_CONFIGMAP_NAME 環境變數取得這份 ConfigMap。啟用 Cilium 後,Operator 會為每個 workspace 建立 CiliumNetworkPolicy

# 啟用 Cilium
cilium:
enabled: true

Helm Values 完整參考

Global

Value預設值說明
global.imagePullSecrets[]Image pull secrets
global.storageClass""預設 StorageClass

服務開關

Value預設值說明
frontend.enabledtrue啟用 Frontend
workspaceManager.enabledtrue啟用 Manager
workspaceOperator.enabledtrue啟用 Operator
postgres.enabledtrue啟用 PostgreSQL
redis.enabledtrue啟用 Redis
keycloak.enabledtrue啟用 Keycloak
coturn.enabledtrue啟用 CoTURN

服務映像

Value預設值
frontend.image.repositoryailerondocker/workspace-ui
frontend.image.tagk8s-local
workspaceManager.image.repositoryailerondocker/workspace-manager
workspaceManager.image.tagk8s-local
workspaceOperator.image.repositoryailerondocker/workspace-operator
workspaceOperator.image.tagk8s-local

認證

Value預設值說明
postgres.auth.usernamepostgresDB 使用者
postgres.auth.passwordpostgresDB 密碼
postgres.auth.appDatabaseaileron應用程式 DB
postgres.auth.keycloakDatabasekeycloakKeycloak DB
keycloak.auth.adminUseradminKeycloak 管理員
keycloak.auth.adminPasswordadminKeycloak 密碼
workspaceManager.env.SECRET_KEY(開發預設值)JWT signing key

Kubernetes Storage

Value預設值說明
kubernetes.pvcNameworkspace-runtime-pvcWorkspace working directory PVC
kubernetes.knowledgeBases.pvcNameknowledge-bases-pvcKnowledge Base 專用共享 PVC 名稱
kubernetes.knowledgeBases.size20GiKnowledge Base PVC 容量
kubernetes.knowledgeBases.accessModes[ReadWriteMany]Knowledge Base PVC access modes
kubernetes.knowledgeBases.storageClassNamehostpath本機 dev 預設 fallback;正式環境請切到共享 RWX 類型,例如 nfs

驗證部署

安裝完成後,執行以下檢查:

# 檢查所有 Pod 狀態
kubectl get pods -n aileron

# 檢查 Service
kubectl get svc -n aileron

# 檢查 Ingress
kubectl get ingress -n aileron

# 檢查 CRD 是否安裝
kubectl get crd workspaces.platform.aileron.io

# 查看 Workspace CR(若已建立)
kubectl get workspaces -A

# 檢查 ConfigMap
kubectl get configmap -n aileron

# 查看特定服務 log
kubectl logs -n aileron deployment/aileron-workspace-manager
kubectl logs -n aileron deployment/aileron-workspace-operator

當前限制

  • workspace 動態 host 是否由單一 Ingress、Gateway API 或自訂 controller 承接,取決於實際叢集的 ingress 能力
  • 完整啟用 public domain routing 需先完成 DNS 與 TLS 準備,否則 Keycloak/OIDC、preview 與 WebSocket 無法正常對外工作
  • 完整啟用 per-workspace domain allowlist 需安裝並配置 Cilium
  • CoTURN host 必須設為 node 的實際可路由 IP;正式環境不需設 frontendHost(預設同 host),本機 Docker Desktop 開發需額外設 frontendHost: "127.0.0.1"