helm/templates/syncctl.yaml
Rama newjitsu · réplica de lectura del repositorio original.
{{- include "jitsu-dev.validateReverseEtl" . -}}
{{- if gt (int .Values.scaling.syncctl.replicas) 0 }}
{{- $reverse := .Values.reverseEtl | default dict }}
apiVersion: apps/v1
kind: Deployment
metadata:
name: syncctl
labels:
{{- include "jitsu-dev.labels" . | nindent 4 }}
app.kubernetes.io/name: syncctl
spec:
replicas: {{ .Values.scaling.syncctl.replicas }}
selector:
matchLabels:
app.kubernetes.io/name: syncctl
app.kubernetes.io/instance: {{ .Release.Name }}
template:
metadata:
labels:
app.kubernetes.io/name: syncctl
app.kubernetes.io/instance: {{ .Release.Name }}
spec:
serviceAccountName: syncctl
initContainers:
- name: build
image: golang:1.26-bookworm
workingDir: /src/sync-controller
command:
- sh
- -c
- |
echo "Copying source..."
cp -r /project/bulker/* /src/
echo "Waiting for build lock (Go builds are serialized to avoid overloading the node)..."
flock /go-cache/build.lock \
go build -buildvcs=false -o /build/syncctl .
echo "Build complete"
volumeMounts:
- name: project
mountPath: /project
readOnly: true
- name: build
mountPath: /build
- name: go-cache
mountPath: /go-cache
- name: src
mountPath: /src
env:
- name: GOCACHE
value: /go-cache/build
- name: GOMODCACHE
value: /go-cache/mod
# Cap the build so a compile can't starve the kubelet or running
# pods. Builds are additionally serialized via the flock above, so
# at most one build is active — and it reuses the shared GOCACHE
# warmed by whichever build ran first. Go 1.26 is container-aware:
# GOMAXPROCS follows the cgroup CPU limit automatically.
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: "3"
memory: 3Gi
containers:
- name: syncctl
image: debian:bookworm-slim
command: ["/build/syncctl"]
ports:
- name: http
containerPort: 3043
protocol: TCP
- name: metrics
containerPort: 9091
protocol: TCP
envFrom:
- secretRef:
name: jitsu-secrets
# Connection URLs of the in-cluster dependencies (helm-deps chart).
# Listed after jitsu-secrets so its keys win over any legacy URL
# keys there; explicit env entries below still take precedence.
- secretRef:
name: jitsu-deps-urls
optional: true
{{- with $reverse.controllerSecret }}
- secretRef:
name: {{ . | quote }}
{{- end }}
{{- with .Values.syncctlExtraEnvFrom }}
{{- toYaml . | nindent 12 }}
{{- end }}
env:
# Prefixed values override legacy envFrom settings, including while disabled.
- name: SYNCCTL_REVERSE_ENABLED
value: {{ .Values.reverseEtl.enabled | quote }}
- name: SYNCCTL_REVERSE_RUNNER_IMAGE
value: {{ .Values.reverseEtl.runnerImage | quote }}
- name: SYNCCTL_REVERSE_RUNTIME_SECRET
value: {{ .Values.reverseEtl.runtimeSecret | quote }}
- name: SYNCCTL_EXTRA_CA_CONFIG_MAP
value: {{ .Values.syncctlExtraCAConfigMap | default "" | quote }}
- name: SYNCCTL_REVERSE_SERVICE_ACCOUNT
value: {{ include "jitsu-dev.reverseServiceAccount" . | quote }}
- name: SYNCCTL_REVERSE_RUNNER_RESOURCES
value: {{ .Values.reverseEtl.resources | toJson | quote }}
- name: SYNCCTL_REVERSE_SCRATCH_SIZE_LIMIT
value: {{ .Values.reverseEtl.scratchSizeLimit | quote }}
- name: KUBERNETES_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
# Sync Pods (CronJob + ad-hoc job_runner) run as `sync-pod`, a
# least-privilege SA bound only to coordination.k8s.io/leases.
# Separating it from syncctl's own SA keeps the pod-side blast
# radius tiny (no pod create/delete, no Secret read, no log access).
{{- include "jitsu-dev.env" (dict "ctx" . "service" "syncctl"
"extra" (merge (dict "HTTP_PORT" "3043" "PODS_SERVICE_ACCOUNT" "sync-pod") (ternary (dict "SSL_CERT_FILE" "/etc/jitsu-ca/ca.crt") (dict) (not (empty .Values.syncctlExtraCAConfigMap))))
"exclude" (list "KUBERNETES_NAMESPACE")) | nindent 12 }}
volumeMounts:
- name: build
mountPath: /build
readOnly: true
{{- if .Values.syncctlExtraCAConfigMap }}
- name: extra-ca
mountPath: /etc/jitsu-ca
readOnly: true
{{- end }}
livenessProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 5
periodSeconds: 10
readinessProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 5
periodSeconds: 10
volumes:
{{- if .Values.syncctlExtraCAConfigMap }}
- name: extra-ca
configMap:
name: {{ .Values.syncctlExtraCAConfigMap | quote }}
items:
- key: ca.crt
path: ca.crt
{{- end }}
- name: project
hostPath:
path: {{ .Values.syncctlProjectRoot | default (include "jitsu-dev.projectRoot" .) | quote }}
type: Directory
- name: build
emptyDir: {}
- name: src
emptyDir: {}
- name: go-cache
persistentVolumeClaim:
claimName: go-cache
---
apiVersion: v1
kind: Service
metadata:
name: syncctl
labels:
{{- include "jitsu-dev.labels" . | nindent 4 }}
app.kubernetes.io/name: syncctl
spec:
# Controller authentication is optional; never expose its control plane by default.
type: ClusterIP
ports:
- port: 3043
targetPort: http
protocol: TCP
name: http
- port: 9091
targetPort: metrics
protocol: TCP
name: metrics
selector:
app.kubernetes.io/name: syncctl
app.kubernetes.io/instance: {{ .Release.Name }}
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: syncctl
labels:
{{- include "jitsu-dev.labels" . | nindent 4 }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: {{ .Release.Name }}-syncctl
labels:
{{- include "jitsu-dev.labels" . | nindent 4 }}
rules:
- apiGroups: [""]
resources: ["pods", "services", "configmaps", "secrets"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
# JobRunner.getPodLogs (bulker/sync-controller/job_runner.go) tails source +
# sidecar logs to surface them on sync failure.
- apiGroups: [""]
resources: ["pods/log"]
verbs: ["get"]
# JobRunner.getPodResUsage (bulker/sync-controller/job_runner.go) execs
# `cat /sys/fs/cgroup/...` inside the running source container to sample
# CPU/memory usage. Requires create on pods/exec.
- apiGroups: [""]
resources: ["pods/exec"]
verbs: ["create"]
- apiGroups: ["apps"]
resources: ["deployments", "statefulsets"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: ["batch"]
resources: ["jobs", "cronjobs"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: {{ .Release.Name }}-syncctl
labels:
{{- include "jitsu-dev.labels" . | nindent 4 }}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: {{ .Release.Name }}-syncctl
subjects:
- kind: ServiceAccount
name: syncctl
namespace: {{ .Release.Namespace }}
---
# `sync-pod` is the dedicated SA used by sync runtime pods (CronJob fires +
# ad-hoc job_runner pods). It only needs coordination.k8s.io/leases — the
# sidecar uses one Lease per syncID as a cross-pod mutex (see
# bulker/sync-sidecar/lease.go). Namespace-scoped Role: the lease lives in
# syncctl's own namespace.
apiVersion: v1
kind: ServiceAccount
metadata:
name: sync-pod
labels:
{{- include "jitsu-dev.labels" . | nindent 4 }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: {{ .Release.Name }}-sync-pod
labels:
{{- include "jitsu-dev.labels" . | nindent 4 }}
rules:
- apiGroups: ["coordination.k8s.io"]
resources: ["leases"]
verbs: ["get", "create", "update", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: {{ .Release.Name }}-sync-pod
labels:
{{- include "jitsu-dev.labels" . | nindent 4 }}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: {{ .Release.Name }}-sync-pod
subjects:
- kind: ServiceAccount
name: sync-pod
namespace: {{ .Release.Namespace }}
{{- end }}