mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-09-19 11:06:18 +00:00
* fix(helm): default sandbox Services to ClusterIP (#3929) The K8s sandbox provisioner supports both NodePort and ClusterIP via SANDBOX_SERVICE_TYPE (added in #4016), but the Helm chart never set it, so real-cluster installs inherited the NodePort default. That bound the code-execution sandbox on every node's interfaces - including externally reachable ones on GKE/EKS/AKS - and pinned every sandbox URL to one node IP (SPOF on node reboot/drain/ephemeral-IP). Default the chart to ClusterIP: the provisioner returns a cluster-DNS URL (http://sandbox-<id>-svc.<ns>.svc.cluster.local:8080) so the gateway-> sandbox hop stays inside the cluster network - no node IP, no 30xxx port, no external exposure. The chart always runs the gateway in-cluster, so ClusterIP is always correct there. NodePort remains an opt-in (provisioner.sandboxServiceType: NodePort + nodeHost) for the Docker-Compose/hybrid path where the gateway is not in K8s and cannot resolve .svc.cluster.local; the provisioner code default stays NodePort for that path. - values.yaml: add provisioner.sandboxServiceType ("ClusterIP") - provisioner-deployment.yaml: emit SANDBOX_SERVICE_TYPE; gate the NODE_HOST block on NodePort mode (default "ClusterIP" for upgrade safety) - NOTES.txt + README.md: document ClusterIP default + NodePort opt-in No change to docker/provisioner/app.py (already mode-aware since #4016) or RBAC (services verbs already cover ClusterIP). * test(helm): assert sandbox Service-type gating + CHANGELOG the default flip (#3929) Address review on #4190: - Add scripts/check_chart_sandbox_service.sh: renders the chart for the default (ClusterIP, no NODE_HOST), the NodePort opt-in (both emitted), and NodePort+nodeHost (literal value, not downward API). Locks in the #3929 gating so a regression (e.g. re-adding an unconditional NODE_HOST, or dropping the `default "ClusterIP"` upgrade-safety fallback) fails CI. Wired into .github/workflows/chart.yaml validate-chart job. (#2) - CHANGELOG [Unreleased] -> Changed: note the NodePort->ClusterIP default flip on upgrade + the `sandboxServiceType: NodePort` opt-back-in. (#4) No chart template changes (the gating itself landed in the first commit). --------- Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
103 lines
4.2 KiB
YAML
103 lines
4.2 KiB
YAML
{{- if .Values.provisioner.enabled -}}
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: {{ include "deer-flow.fullname" . }}-provisioner
|
|
namespace: {{ include "deer-flow.namespace" . }}
|
|
labels:
|
|
{{- include "deer-flow.labels" . | nindent 4 }}
|
|
app.kubernetes.io/component: provisioner
|
|
spec:
|
|
replicas: 1
|
|
selector:
|
|
matchLabels:
|
|
{{- include "deer-flow.selectorLabels" . | nindent 6 }}
|
|
app.kubernetes.io/component: provisioner
|
|
template:
|
|
metadata:
|
|
labels:
|
|
{{- include "deer-flow.selectorLabels" . | nindent 8 }}
|
|
app.kubernetes.io/component: provisioner
|
|
spec:
|
|
{{- include "deer-flow.imagePullSecrets" . | nindent 6 }}
|
|
serviceAccountName: {{ include "deer-flow.fullname" . }}-provisioner
|
|
securityContext:
|
|
# Non-root: provisioner image has no USER directive. uid/gid 1000.
|
|
# No fsGroup — it mounts no PVCs (it references PVC names for the
|
|
# sandbox Pods it spawns, never mounting them itself).
|
|
runAsNonRoot: true
|
|
runAsUser: 1000
|
|
runAsGroup: 1000
|
|
seccompProfile:
|
|
type: RuntimeDefault
|
|
containers:
|
|
- name: provisioner
|
|
image: {{ include "deer-flow.provisionerImage" . }}
|
|
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
|
securityContext:
|
|
allowPrivilegeEscalation: false
|
|
capabilities:
|
|
drop: ["ALL"]
|
|
ports:
|
|
- name: http
|
|
containerPort: 8002
|
|
env:
|
|
# Running as non-root uid 1000: suppress .pyc writes under root-owned /app.
|
|
- name: PYTHONDONTWRITEBYTECODE
|
|
value: "1"
|
|
- name: K8S_NAMESPACE
|
|
value: {{ include "deer-flow.namespace" . | quote }}
|
|
- name: SANDBOX_IMAGE
|
|
value: {{ .Values.provisioner.sandboxImage | quote }}
|
|
# In-cluster API access via the mounted ServiceAccount token.
|
|
# K8S_API_SERVER is intentionally unset (do NOT use host.docker.internal).
|
|
# Service type for per-sandbox Services. ClusterIP (default) routes the
|
|
# gateway->sandbox hop over cluster DNS; NodePort is the compose/hybrid
|
|
# escape hatch (gateway not in K8s), which needs NODE_HOST below.
|
|
- name: SANDBOX_SERVICE_TYPE
|
|
value: {{ .Values.provisioner.sandboxServiceType | default "ClusterIP" | quote }}
|
|
{{- if eq (default "ClusterIP" .Values.provisioner.sandboxServiceType) "NodePort" }}
|
|
# NODE_HOST: address the gateway uses to reach sandbox NodePorts
|
|
# (NodePort mode only). When `provisioner.nodeHost` is set, use it;
|
|
# otherwise default to this pod's node IP via the downward API. A
|
|
# NodePort is exposed on every node, so <node-IP>:<NodePort> routes
|
|
# from the gateway on most clusters. Override only when pod->node-IP
|
|
# traffic is blocked by the CNI/policy.
|
|
- name: NODE_HOST
|
|
{{- if .Values.provisioner.nodeHost }}
|
|
value: {{ .Values.provisioner.nodeHost | quote }}
|
|
{{- else }}
|
|
valueFrom:
|
|
fieldRef:
|
|
fieldPath: status.hostIP
|
|
{{- end }}
|
|
{{- end }}
|
|
# PVC mode — sandbox Pods mount the same PVCs the gateway uses.
|
|
{{- if .Values.persistence.home.enabled }}
|
|
- name: USERDATA_PVC_NAME
|
|
value: {{ include "deer-flow.homePVC" . | quote }}
|
|
{{- end }}
|
|
{{- if .Values.skills.existingClaim }}
|
|
- name: SKILLS_PVC_NAME
|
|
value: {{ .Values.skills.existingClaim | quote }}
|
|
{{- end }}
|
|
- name: SANDBOX_CONTAINER_PORT
|
|
value: {{ .Values.provisioner.sandboxPort | quote }}
|
|
readinessProbe:
|
|
httpGet:
|
|
path: /health
|
|
port: http
|
|
initialDelaySeconds: 5
|
|
periodSeconds: 10
|
|
livenessProbe:
|
|
httpGet:
|
|
path: /health
|
|
port: http
|
|
initialDelaySeconds: 15
|
|
periodSeconds: 20
|
|
{{- with .Values.provisioner.resources }}
|
|
resources:
|
|
{{- toYaml . | nindent 12 }}
|
|
{{- end }}
|
|
{{- end -}}
|