diff --git a/deploy/helm/fluxer-api/Chart.yaml b/deploy/helm/fluxer-api/Chart.yaml new file mode 100644 index 000000000..bb777fcb1 --- /dev/null +++ b/deploy/helm/fluxer-api/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: fluxer-api +description: Fluxer HTTP API and background job workers +type: application +version: 0.1.0 +appVersion: "v1" diff --git a/deploy/helm/fluxer-api/templates/_helpers.tpl b/deploy/helm/fluxer-api/templates/_helpers.tpl new file mode 100644 index 000000000..97971fb73 --- /dev/null +++ b/deploy/helm/fluxer-api/templates/_helpers.tpl @@ -0,0 +1,244 @@ +{{- define "fluxer-api.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end }} + +{{- define "fluxer-api.selectorLabels" -}} +app.kubernetes.io/name: {{ .name }} +app.kubernetes.io/instance: {{ .root.Release.Name }} +{{- end }} + +{{- define "fluxer-api.labels" -}} +{{ include "fluxer-api.selectorLabels" . }} +app.kubernetes.io/component: {{ .component }} +app.kubernetes.io/part-of: fluxer +app.kubernetes.io/managed-by: {{ .root.Release.Service }} +helm.sh/chart: {{ include "fluxer-api.chart" .root }} +{{- end }} + +{{- define "fluxer-api.image" -}} +{{- $g := .root.Values.image | default dict -}} +{{- $i := .w.image | default dict -}} +{{- $repo := $i.repository -}} +{{- if not $repo -}} +{{- $repo = printf "%s/%s" (required "image.registry is required" $g.registry) ($i.name | default "fluxer-api") -}} +{{- end -}} +{{- $tag := required "image.tag is required" ($i.tag | default $g.tag) -}} +{{- if $i.digest -}} +{{- printf "%s:%s@%s" $repo $tag $i.digest | quote -}} +{{- else -}} +{{- printf "%s:%s" $repo $tag | quote -}} +{{- end -}} +{{- end }} + +{{- define "fluxer-api.pick" -}} +{{- $v := ternary (get .w .key) (get .root.Values .key) (hasKey .w .key) -}} +{{- if $v }} +{{- toYaml $v }} +{{- end }} +{{- end }} + +{{- define "fluxer-api.str" -}} +{{- if and (kindIs "float64" .) (eq . (floor .)) -}} +{{- int64 . | toString | quote -}} +{{- else -}} +{{- toString . | quote -}} +{{- end -}} +{{- end }} + +{{- define "fluxer-api.env" -}} +{{- $env := dict -}} +{{- range $k, $val := .root.Values.env | default dict }} +{{- $_ := set $env $k $val }} +{{- end }} +{{- range $k, $val := .w.env | default dict }} +{{- $_ := set $env $k $val }} +{{- end }} +{{- range $k, $val := $env }} +{{- if not (kindIs "invalid" $val) }} +- name: {{ $k }} + value: {{ include "fluxer-api.str" $val }} +{{- end }} +{{- end }} +{{- with .w.buildVersion }} +- name: BUILD_VERSION + value: {{ include "fluxer-api.str" . }} +{{- end }} +{{- with concat (.root.Values.extraEnv | default list) (.w.extraEnv | default list) }} +{{ toYaml . }} +{{- end }} +{{- end }} + +{{- define "fluxer-api.topologySpread" -}} +{{- $tscs := ternary .w.topologySpreadConstraints .root.Values.topologySpreadConstraints (hasKey .w "topologySpreadConstraints") -}} +{{- range $tscs }} +{{- $c := deepCopy . }} +{{- if not $c.labelSelector }} +{{- $_ := set $c "labelSelector" (dict "matchLabels" (include "fluxer-api.selectorLabels" $ | fromYaml)) }} +{{- end }} +- {{- toYaml $c | nindent 2 }} +{{- end }} +{{- end }} + +{{- define "fluxer-api.pdb" -}} +{{- with .w.pdb }} +--- +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: {{ $.name }}-pdb + namespace: {{ $.root.Release.Namespace }} + labels: + {{- include "fluxer-api.labels" $ | nindent 4 }} +spec: + {{- toYaml . | nindent 2 }} + selector: + matchLabels: + {{- include "fluxer-api.selectorLabels" $ | nindent 6 }} +{{- end }} +{{- end }} + +{{- define "fluxer-api.hpa" -}} +{{- with .w.hpa }} +--- +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: {{ $.name }} + namespace: {{ $.root.Release.Namespace }} + labels: + {{- include "fluxer-api.labels" $ | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ $.name }} + minReplicas: {{ required (printf "%s.hpa.minReplicas is required" $.name) .minReplicas }} + maxReplicas: {{ required (printf "%s.hpa.maxReplicas is required" $.name) .maxReplicas }} + {{- with .targetCPUUtilizationPercentage }} + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ . }} + {{- end }} + {{- with .behavior }} + behavior: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} +{{- end }} + +{{- define "fluxer-api.deployment" -}} +{{- $root := .root -}} +{{- $v := $root.Values -}} +{{- $w := .w -}} +{{- $envFrom := concat ($v.envFrom | default list) ($w.envFrom | default list) -}} +{{- $podAnnotations := merge (dict) ($w.podAnnotations | default dict) ($v.podAnnotations | default dict) -}} +{{- $wProbes := $w.probes | default dict -}} +{{- $gProbes := .probes | default dict -}} +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .name }} + namespace: {{ $root.Release.Namespace }} + labels: + {{- include "fluxer-api.labels" . | nindent 4 }} +spec: + {{- if not $w.hpa }} + replicas: {{ if kindIs "invalid" $w.replicas }}1{{ else }}{{ int $w.replicas }}{{ end }} + {{- end }} + {{- if not (kindIs "invalid" $w.minReadySeconds) }} + minReadySeconds: {{ int $w.minReadySeconds }} + {{- end }} + selector: + matchLabels: + {{- include "fluxer-api.selectorLabels" . | nindent 6 }} + {{- with include "fluxer-api.pick" (dict "root" $root "w" $w "key" "strategy") }} + strategy: + {{- . | nindent 4 }} + {{- end }} + template: + metadata: + labels: + {{- include "fluxer-api.labels" . | nindent 8 }} + {{- with $podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with include "fluxer-api.pick" (dict "root" $root "w" $w "key" "imagePullSecrets") }} + imagePullSecrets: + {{- . | nindent 8 }} + {{- end }} + {{- with include "fluxer-api.pick" (dict "root" $root "w" $w "key" "podSecurityContext") }} + securityContext: + {{- . | nindent 8 }} + {{- end }} + {{- if not (kindIs "invalid" $w.terminationGracePeriodSeconds) }} + terminationGracePeriodSeconds: {{ int $w.terminationGracePeriodSeconds }} + {{- end }} + {{- with include "fluxer-api.pick" (dict "root" $root "w" $w "key" "nodeSelector") }} + nodeSelector: + {{- . | nindent 8 }} + {{- end }} + {{- with include "fluxer-api.pick" (dict "root" $root "w" $w "key" "affinity") }} + affinity: + {{- . | nindent 8 }} + {{- end }} + {{- with include "fluxer-api.pick" (dict "root" $root "w" $w "key" "tolerations") }} + tolerations: + {{- . | nindent 8 }} + {{- end }} + {{- with include "fluxer-api.topologySpread" . | trim }} + topologySpreadConstraints: + {{- . | nindent 8 }} + {{- end }} + containers: + - name: {{ .name }} + image: {{ include "fluxer-api.image" . }} + imagePullPolicy: {{ ($w.image | default dict).pullPolicy | default ($v.image | default dict).pullPolicy | default "IfNotPresent" }} + {{- with .command }} + command: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with include "fluxer-api.env" . | trim }} + env: + {{- . | nindent 12 }} + {{- end }} + {{- with $envFrom }} + envFrom: + {{- toYaml . | nindent 12 }} + {{- end }} + ports: + - name: http + containerPort: 8080 + {{- with $w.lifecycle }} + lifecycle: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- range $probe := list "startup" "liveness" "readiness" }} + {{- with hasKey $wProbes $probe | ternary (get $wProbes $probe) (get $gProbes $probe) }} + {{ $probe }}Probe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- end }} + {{- with $w.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with include "fluxer-api.pick" (dict "root" $root "w" $w "key" "securityContext") }} + securityContext: + {{- . | nindent 12 }} + {{- end }} + {{- with $w.extraVolumeMounts }} + volumeMounts: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with $w.extraVolumes }} + volumes: + {{- toYaml . | nindent 8 }} + {{- end }} +{{- end }} diff --git a/deploy/helm/fluxer-api/templates/api.yaml b/deploy/helm/fluxer-api/templates/api.yaml new file mode 100644 index 000000000..0758630a4 --- /dev/null +++ b/deploy/helm/fluxer-api/templates/api.yaml @@ -0,0 +1,24 @@ +{{- range $name, $w := .Values.api }} +{{- if not (kindIs "invalid" $w) }} +{{- $ctx := dict "root" $ "name" $name "w" $w "component" "api" "probes" ($.Values.probes | default dict) }} +{{ include "fluxer-api.deployment" $ctx }} +{{ include "fluxer-api.hpa" $ctx }} +{{ include "fluxer-api.pdb" $ctx }} +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ $name }} + namespace: {{ $.Release.Namespace }} + labels: + {{- include "fluxer-api.labels" $ctx | nindent 4 }} +spec: + type: ClusterIP + selector: + {{- include "fluxer-api.selectorLabels" $ctx | nindent 4 }} + ports: + - name: http + port: 8080 + targetPort: http +{{- end }} +{{- end }} diff --git a/deploy/helm/fluxer-api/templates/workers.yaml b/deploy/helm/fluxer-api/templates/workers.yaml new file mode 100644 index 000000000..51091bcba --- /dev/null +++ b/deploy/helm/fluxer-api/templates/workers.yaml @@ -0,0 +1,8 @@ +{{- range $name, $w := .Values.workers }} +{{- if not (kindIs "invalid" $w) }} +{{- $ctx := dict "root" $ "name" $name "w" $w "component" "worker" "command" (list "node" "dist/WorkerEntrypoint.js") "probes" (dict) }} +{{ include "fluxer-api.deployment" $ctx }} +{{ include "fluxer-api.hpa" $ctx }} +{{ include "fluxer-api.pdb" $ctx }} +{{- end }} +{{- end }} diff --git a/deploy/helm/fluxer-api/values.yaml b/deploy/helm/fluxer-api/values.yaml new file mode 100644 index 000000000..e9b37a928 --- /dev/null +++ b/deploy/helm/fluxer-api/values.yaml @@ -0,0 +1,86 @@ +image: + registry: ghcr.io/fluxerapp + tag: v1 + pullPolicy: IfNotPresent + +imagePullSecrets: [] + +env: + NODE_ENV: production + FLUXER_ENV: production + FLUXER_PUBLIC_ORIGIN: https://web.example.com + FLUXER_API_ENDPOINT: https://api.example.com + FLUXER_GATEWAY_ENDPOINT: wss://gateway.example.com + FLUXER_MEDIA_ENDPOINT: https://media.example.com + FLUXER_ADMIN_ENDPOINT: https://admin.example.com + FLUXER_MEDIA_PROXY_UPLOAD_RELAY_ENDPOINT: https://uploads.example.com + FLUXER_INTERNAL_MEDIA_PROXY_ENDPOINT: http://media-proxy:8080 + FLUXER_KV_URL: redis://valkey:6379/0 + FLUXER_NATS_URL: nats://nats:4222 + FLUXER_NATS_JETSTREAM_URL: nats://nats:4222 + +extraEnv: [] + +envFrom: + - secretRef: + name: fluxer-env + +podAnnotations: {} + +podSecurityContext: + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + +securityContext: + allowPrivilegeEscalation: false + +probes: + startup: + httpGet: + path: /_health + port: http + periodSeconds: 10 + failureThreshold: 30 + liveness: + httpGet: + path: /_health + port: http + readiness: + httpGet: + path: /_health + port: http + +strategy: + type: RollingUpdate + +topologySpreadConstraints: [] + +nodeSelector: {} + +tolerations: [] + +affinity: {} + +api: + api: + replicas: 1 + resources: + requests: + cpu: 250m + memory: 1Gi + limits: + memory: 2560Mi + +workers: + worker: + replicas: 1 + env: + FLUXER_API_WORKER_MODE: all_lanes + FLUXER_API_WORKER_ENABLE_CRON_SCHEDULER: "true" + resources: + requests: + cpu: 250m + memory: 1Gi + limits: + memory: 2560Mi diff --git a/deploy/helm/fluxer-gateway/Chart.yaml b/deploy/helm/fluxer-gateway/Chart.yaml new file mode 100644 index 000000000..8da5f7158 --- /dev/null +++ b/deploy/helm/fluxer-gateway/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: fluxer-gateway +description: A Helm chart for the Fluxer realtime gateway. +type: application +version: 0.1.0 +appVersion: "v1" diff --git a/deploy/helm/fluxer-gateway/templates/_helpers.tpl b/deploy/helm/fluxer-gateway/templates/_helpers.tpl new file mode 100644 index 000000000..f720ae8ee --- /dev/null +++ b/deploy/helm/fluxer-gateway/templates/_helpers.tpl @@ -0,0 +1,280 @@ +{{- define "gateway.selectorLabels" -}} +app.kubernetes.io/name: {{ .name }} +app.kubernetes.io/instance: {{ .root.Release.Name }} +{{- end }} + +{{- define "gateway.labels" -}} +{{ include "gateway.selectorLabels" . }} +{{- with .component }} +app.kubernetes.io/component: {{ . }} +{{- end }} +app.kubernetes.io/part-of: fluxer +app.kubernetes.io/managed-by: {{ .root.Release.Service }} +helm.sh/chart: {{ printf "%s-%s" .root.Chart.Name .root.Chart.Version | replace "+" "_" }} +{{- end }} + +{{- define "gateway.headlessName" -}} +{{ printf "%s-headless" .Release.Name }} +{{- end }} + +{{- define "gateway.pick" -}} +{{- $v := get .root.Values .key }} +{{- if hasKey .w .key }} +{{- $v = get .w .key }} +{{- end }} +{{- with $v }} +{{- toYaml . }} +{{- end }} +{{- end }} + +{{- define "gateway.string" -}} +{{- if and (kindIs "float64" .) (eq . (float64 (int64 .))) }} +{{- int64 . | toString }} +{{- else }} +{{- toString . }} +{{- end }} +{{- end }} + +{{- define "gateway.envList" -}} +{{- $env := deepCopy (.root.Values.env | default dict) }} +{{- range $k, $v := .w.env | default dict }} +{{- if kindIs "invalid" $v }} +{{- $_ := unset $env $k }} +{{- else }} +{{- $_ := set $env $k $v }} +{{- end }} +{{- end }} +{{- range $k, $v := $env }} +{{- if not (kindIs "invalid" $v) }} +- name: {{ $k }} + value: {{ include "gateway.string" $v | quote }} +{{- end }} +{{- end }} +{{- with concat (.root.Values.extraEnv | default list) (.w.extraEnv | default list) }} +{{ toYaml . }} +{{- end }} +{{- end }} + +{{- define "gateway.envFrom" -}} +{{- with concat (.root.Values.envFrom | default list) (.w.envFrom | default list) }} +{{- toYaml . }} +{{- end }} +{{- end }} + +{{- define "gateway.podAnnotations" -}} +{{- with merge (deepCopy (.w.podAnnotations | default dict)) (deepCopy (.root.Values.podAnnotations | default dict)) }} +{{- toYaml . }} +{{- end }} +{{- end }} + +{{- define "gateway.probes" -}} +{{- $global := .root.Values.probes | default dict }} +{{- $own := .w.probes | default dict }} +{{- range $probe := list "startup" "liveness" "readiness" }} +{{- $p := get $global $probe }} +{{- if hasKey $own $probe }} +{{- $p = get $own $probe }} +{{- end }} +{{- with $p }} +{{ $probe }}Probe: + {{- toYaml . | nindent 2 }} +{{- end }} +{{- end }} +{{- end }} + +{{- define "gateway.topologySpreadConstraints" -}} +{{- $out := list }} +{{- range include "gateway.pick" (dict "root" .root "w" .w "key" "topologySpreadConstraints") | fromYamlArray }} +{{- $c := deepCopy . }} +{{- if not (hasKey $c "labelSelector") }} +{{- $_ := set $c "labelSelector" (dict "matchLabels" (include "gateway.selectorLabels" $ | fromYaml)) }} +{{- end }} +{{- $out = append $out $c }} +{{- end }} +{{- with $out }} +{{- toYaml . }} +{{- end }} +{{- end }} + +{{- define "gateway.image" -}} +{{- $img := .w.image | default dict }} +{{- $v := .root.Values.image }} +{{- $repo := $img.repository | default (printf "%s/%s" $v.registry ($img.name | default "fluxer-gateway")) }} +{{- $ref := printf "%s:%s" $repo ($img.tag | default $v.tag) }} +{{- with $img.digest }} +{{- $ref = printf "%s@%s" $ref . }} +{{- end }} +{{- $ref | quote }} +{{- end }} + +{{- define "gateway.replicas" -}} +{{- if kindIs "invalid" .w.replicas }}1{{ else }}{{ .w.replicas }}{{ end }} +{{- end }} + +{{- define "gateway.env" -}} +{{- $root := .root }} +{{- $w := .w -}} +{{- with $w.role }} +- name: FLUXER_GATEWAY_ROLE + value: {{ . | quote }} +{{- end }} +{{- if not (kindIs "invalid" $w.buildVersion) }} +- name: BUILD_VERSION + value: {{ include "gateway.string" $w.buildVersion | quote }} +{{- end }} +- name: POD_IP + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: status.podIP +- name: FLUXER_ERLANG_NODE_NAME + value: fluxer_gateway@$(POD_IP) +- name: FLUXER_ERLANG_DIST_PORT + value: "8081" +- name: FLUXER_GATEWAY_CLUSTER_ENABLED + value: "true" +- name: FLUXER_GATEWAY_CLUSTER_DISCOVERY_DNS_NAME + value: {{ printf "%s.%s.svc.%s" (include "gateway.headlessName" $root) $root.Release.Namespace $root.Values.clusterDomain | quote }} +- name: FLUXER_GATEWAY_CLUSTER_DISCOVERY_NODE_BASENAME + value: fluxer_gateway +{{- include "gateway.envList" . }} +{{- end }} + +{{- define "gateway.pod" -}} +{{- $root := .root }} +{{- $w := .w -}} +metadata: + labels: + {{- include "gateway.labels" . | nindent 4 }} + {{- with include "gateway.podAnnotations" . }} + annotations: + {{- . | nindent 4 }} + {{- end }} +spec: + {{- with include "gateway.pick" (dict "root" $root "w" $w "key" "affinity") }} + affinity: + {{- . | nindent 4 }} + {{- end }} + {{- with include "gateway.pick" (dict "root" $root "w" $w "key" "imagePullSecrets") }} + imagePullSecrets: + {{- . | nindent 4 }} + {{- end }} + {{- with include "gateway.pick" (dict "root" $root "w" $w "key" "nodeSelector") }} + nodeSelector: + {{- . | nindent 4 }} + {{- end }} + {{- with include "gateway.pick" (dict "root" $root "w" $w "key" "tolerations") }} + tolerations: + {{- . | nindent 4 }} + {{- end }} + {{- with include "gateway.topologySpreadConstraints" . }} + topologySpreadConstraints: + {{- . | nindent 4 }} + {{- end }} + {{- with include "gateway.pick" (dict "root" $root "w" $w "key" "podSecurityContext") }} + securityContext: + {{- . | nindent 4 }} + {{- end }} + {{- if not (kindIs "invalid" $w.terminationGracePeriodSeconds) }} + terminationGracePeriodSeconds: {{ $w.terminationGracePeriodSeconds }} + {{- end }} + containers: + - name: gateway + image: {{ include "gateway.image" . }} + imagePullPolicy: {{ ($w.image | default dict).pullPolicy | default $root.Values.image.pullPolicy }} + env: + {{- include "gateway.env" . | trim | nindent 6 }} + {{- with include "gateway.envFrom" . }} + envFrom: + {{- . | nindent 6 }} + {{- end }} + {{- with $w.lifecycle }} + lifecycle: + {{- toYaml . | nindent 6 }} + {{- end }} + ports: + - name: http + containerPort: 8080 + protocol: TCP + - name: epmd + containerPort: 4369 + protocol: TCP + - name: erl-dist + containerPort: 8081 + protocol: TCP + {{- with include "gateway.probes" . | trim }} + {{- . | nindent 4 }} + {{- end }} + {{- with $w.resources }} + resources: + {{- toYaml . | nindent 6 }} + {{- end }} + {{- with include "gateway.pick" (dict "root" $root "w" $w "key" "securityContext") }} + securityContext: + {{- . | nindent 6 }} + {{- end }} + {{- with $w.extraVolumeMounts }} + volumeMounts: + {{- toYaml . | nindent 6 }} + {{- end }} + {{- with $w.extraVolumes }} + volumes: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} + +{{- define "gateway.pdb" -}} +{{- with .w.pdb }} +--- +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: {{ $.name }}-pdb + namespace: {{ $.root.Release.Namespace }} + labels: + {{- include "gateway.labels" $ | nindent 4 }} +spec: + {{- if not (kindIs "invalid" .minAvailable) }} + minAvailable: {{ .minAvailable }} + {{- end }} + {{- if not (kindIs "invalid" .maxUnavailable) }} + maxUnavailable: {{ .maxUnavailable }} + {{- end }} + selector: + matchLabels: + {{- include "gateway.selectorLabels" $ | nindent 6 }} +{{- end }} +{{- end }} + +{{- define "gateway.hpa" -}} +{{- with .w.hpa }} +--- +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: {{ $.name }} + namespace: {{ $.root.Release.Namespace }} + labels: + {{- include "gateway.labels" $ | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ $.name }} + minReplicas: {{ required (printf "%s.hpa.minReplicas is required" $.name) .minReplicas }} + maxReplicas: {{ required (printf "%s.hpa.maxReplicas is required" $.name) .maxReplicas }} + {{- if not (kindIs "invalid" .targetCPUUtilizationPercentage) }} + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .targetCPUUtilizationPercentage }} + {{- end }} + {{- with .behavior }} + behavior: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} +{{- end }} diff --git a/deploy/helm/fluxer-gateway/templates/deployments.yaml b/deploy/helm/fluxer-gateway/templates/deployments.yaml new file mode 100644 index 000000000..a051387fe --- /dev/null +++ b/deploy/helm/fluxer-gateway/templates/deployments.yaml @@ -0,0 +1,48 @@ +{{- range $name, $w := .Values.deployments }} +{{- if not (kindIs "invalid" $w) }} +{{- $ctx := dict "root" $ "name" $name "component" $w.role "w" $w }} +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ $name }} + namespace: {{ $.Release.Namespace }} + labels: + {{- include "gateway.labels" $ctx | nindent 4 }} +spec: + {{- if not $w.hpa }} + replicas: {{ include "gateway.replicas" $ctx }} + {{- end }} + {{- if not (kindIs "invalid" $w.minReadySeconds) }} + minReadySeconds: {{ $w.minReadySeconds }} + {{- end }} + selector: + matchLabels: + {{- include "gateway.selectorLabels" $ctx | nindent 6 }} + {{- with include "gateway.pick" (dict "root" $ "w" $w "key" "strategy") }} + strategy: + {{- . | nindent 4 }} + {{- end }} + template: + {{- include "gateway.pod" $ctx | nindent 4 }} +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ $name }} + namespace: {{ $.Release.Namespace }} + labels: + {{- include "gateway.labels" $ctx | nindent 4 }} +spec: + type: ClusterIP + ports: + - name: http + port: 8080 + protocol: TCP + targetPort: http + selector: + {{- include "gateway.selectorLabels" $ctx | nindent 4 }} +{{- include "gateway.hpa" $ctx }} +{{- include "gateway.pdb" $ctx }} +{{- end }} +{{- end }} diff --git a/deploy/helm/fluxer-gateway/templates/headless-service.yaml b/deploy/helm/fluxer-gateway/templates/headless-service.yaml new file mode 100644 index 000000000..74f2cb041 --- /dev/null +++ b/deploy/helm/fluxer-gateway/templates/headless-service.yaml @@ -0,0 +1,26 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "gateway.headlessName" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "gateway.labels" (dict "root" . "name" "gateway" "component" "discovery") | nindent 4 }} +spec: + type: ClusterIP + clusterIP: None + ports: + - name: http + port: 8080 + protocol: TCP + targetPort: http + - name: epmd + port: 4369 + protocol: TCP + targetPort: epmd + - name: erl-dist + port: 8081 + protocol: TCP + targetPort: erl-dist + selector: + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/part-of: fluxer diff --git a/deploy/helm/fluxer-gateway/templates/networkpolicy.yaml b/deploy/helm/fluxer-gateway/templates/networkpolicy.yaml new file mode 100644 index 000000000..a6a8dda5e --- /dev/null +++ b/deploy/helm/fluxer-gateway/templates/networkpolicy.yaml @@ -0,0 +1,53 @@ +{{- $np := .Values.networkPolicy | default dict }} +{{- if $np.enabled }} +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: gateway + namespace: {{ .Release.Namespace }} + labels: + {{- include "gateway.labels" (dict "root" . "name" "gateway") | nindent 4 }} +spec: + podSelector: + matchLabels: + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/part-of: fluxer + policyTypes: + - Ingress + - Egress + egress: + - {} + ingress: + {{- with $np.ingressNamespace }} + - from: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: {{ . }} + ports: + - port: 8080 + protocol: TCP + {{- end }} + {{- with $np.clients }} + - from: + {{- range . }} + - podSelector: + matchLabels: + {{- toYaml . | nindent 10 }} + {{- end }} + ports: + - port: 8080 + protocol: TCP + {{- end }} + - from: + - podSelector: + matchLabels: + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/part-of: fluxer + ports: + - port: 8080 + protocol: TCP + - port: 4369 + protocol: TCP + - port: 8081 + protocol: TCP +{{- end }} diff --git a/deploy/helm/fluxer-gateway/templates/statefulsets.yaml b/deploy/helm/fluxer-gateway/templates/statefulsets.yaml new file mode 100644 index 000000000..433955e71 --- /dev/null +++ b/deploy/helm/fluxer-gateway/templates/statefulsets.yaml @@ -0,0 +1,29 @@ +{{- range $name, $w := .Values.statefulsets }} +{{- if not (kindIs "invalid" $w) }} +{{- $ctx := dict "root" $ "name" $name "component" $w.role "w" $w }} +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: {{ $name }} + namespace: {{ $.Release.Namespace }} + labels: + {{- include "gateway.labels" $ctx | nindent 4 }} +spec: + replicas: {{ include "gateway.replicas" $ctx }} + {{- if not (kindIs "invalid" $w.minReadySeconds) }} + minReadySeconds: {{ $w.minReadySeconds }} + {{- end }} + serviceName: {{ include "gateway.headlessName" $ }} + selector: + matchLabels: + {{- include "gateway.selectorLabels" $ctx | nindent 6 }} + {{- with include "gateway.pick" (dict "root" $ "w" $w "key" "updateStrategy") }} + updateStrategy: + {{- . | nindent 4 }} + {{- end }} + template: + {{- include "gateway.pod" $ctx | nindent 4 }} +{{- include "gateway.pdb" $ctx }} +{{- end }} +{{- end }} diff --git a/deploy/helm/fluxer-gateway/values.yaml b/deploy/helm/fluxer-gateway/values.yaml new file mode 100644 index 000000000..ce9aa676e --- /dev/null +++ b/deploy/helm/fluxer-gateway/values.yaml @@ -0,0 +1,86 @@ +image: + registry: ghcr.io/fluxerapp + tag: v1 + pullPolicy: IfNotPresent + +imagePullSecrets: [] + +clusterDomain: cluster.local + +env: + FLUXER_ENV: production + FLUXER_GATEWAY_PORT: "8080" + FLUXER_GATEWAY_MEDIA_PROXY_ENDPOINT: https://media.example.com + FLUXER_INTERNAL_API_ENDPOINT: http://api:8080 + +extraEnv: [] + +envFrom: +- secretRef: + name: fluxer-env + +podAnnotations: {} + +podSecurityContext: + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + +securityContext: + allowPrivilegeEscalation: false + +probes: + startup: + httpGet: + path: /_health + port: http + failureThreshold: 30 + liveness: + httpGet: + path: /_health + port: http + readiness: + exec: + command: + - curl + - -fsS + - -o + - /dev/null + - --max-time + - "2" + - http://127.0.0.1:8080/_health/ready + timeoutSeconds: 3 + +strategy: {} +updateStrategy: {} + +topologySpreadConstraints: [] +nodeSelector: {} +tolerations: [] +affinity: {} + +networkPolicy: + enabled: false + ingressNamespace: ingress-nginx + clients: + - app.kubernetes.io/part-of: fluxer + +deployments: + gateway: + role: all + replicas: 1 + lifecycle: + preStop: + exec: + command: + - /bin/sh + - -c + - curl -fsS -o /dev/null --max-time 2 http://127.0.0.1:8080/_health/drain; sleep 5 + resources: + requests: + cpu: 100m + memory: 384Mi + limits: + memory: 1Gi + +statefulsets: {} diff --git a/deploy/helm/fluxer-infra/Chart.yaml b/deploy/helm/fluxer-infra/Chart.yaml new file mode 100644 index 000000000..a1b217b04 --- /dev/null +++ b/deploy/helm/fluxer-infra/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: fluxer-infra +description: NATS and Valkey for a Fluxer installation. +type: application +version: 0.1.0 +appVersion: "v1" diff --git a/deploy/helm/fluxer-infra/templates/_helpers.tpl b/deploy/helm/fluxer-infra/templates/_helpers.tpl new file mode 100644 index 000000000..1c87edc88 --- /dev/null +++ b/deploy/helm/fluxer-infra/templates/_helpers.tpl @@ -0,0 +1,282 @@ +{{- define "fluxer-infra.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end }} + +{{- define "fluxer-infra.selectorLabels" -}} +app.kubernetes.io/name: {{ .name }} +app.kubernetes.io/instance: {{ .root.Release.Name }} +{{- end }} + +{{- define "fluxer-infra.labels" -}} +{{ include "fluxer-infra.selectorLabels" . }} +app.kubernetes.io/component: {{ .component }} +app.kubernetes.io/part-of: fluxer +app.kubernetes.io/managed-by: {{ .root.Release.Service }} +helm.sh/chart: {{ include "fluxer-infra.chart" .root }} +{{- end }} + +{{- define "fluxer-infra.pick" -}} +{{- $v := get .root.Values .key }} +{{- if hasKey .w .key }} +{{- $v = get .w .key }} +{{- end }} +{{- with $v }} +{{- toYaml . }} +{{- end }} +{{- end }} + +{{- define "fluxer-infra.string" -}} +{{- if and (kindIs "float64" .) (eq . (float64 (int64 .))) }} +{{- int64 . | toString }} +{{- else }} +{{- toString . }} +{{- end }} +{{- end }} + +{{- define "fluxer-infra.envList" -}} +{{- $env := deepCopy (.root.Values.env | default dict) }} +{{- range $k, $v := .w.env | default dict }} +{{- if kindIs "invalid" $v }} +{{- $_ := unset $env $k }} +{{- else }} +{{- $_ := set $env $k $v }} +{{- end }} +{{- end }} +{{- range $k, $v := $env }} +{{- if not (kindIs "invalid" $v) }} +- name: {{ $k }} + value: {{ include "fluxer-infra.string" $v | quote }} +{{- end }} +{{- end }} +{{- with concat (.root.Values.extraEnv | default list) (.w.extraEnv | default list) }} +{{ toYaml . }} +{{- end }} +{{- end }} + +{{- define "fluxer-infra.envFrom" -}} +{{- with concat (.root.Values.envFrom | default list) (.w.envFrom | default list) }} +{{- toYaml . }} +{{- end }} +{{- end }} + +{{- define "fluxer-infra.probes" -}} +{{- $global := .root.Values.probes | default dict }} +{{- $own := .w.probes | default dict }} +{{- range $probe := list "startup" "liveness" "readiness" }} +{{- $p := get $global $probe }} +{{- if hasKey $own $probe }} +{{- $p = get $own $probe }} +{{- end }} +{{- with $p }} +{{ $probe }}Probe: + {{- toYaml . | nindent 2 }} +{{- end }} +{{- end }} +{{- end }} + +{{- define "fluxer-infra.topologySpreadConstraints" -}} +{{- $out := list }} +{{- range include "fluxer-infra.pick" (dict "root" .root "w" .w "key" "topologySpreadConstraints") | fromYamlArray }} +{{- $c := deepCopy . }} +{{- if not (hasKey $c "labelSelector") }} +{{- $_ := set $c "labelSelector" (dict "matchLabels" (include "fluxer-infra.selectorLabels" $ | fromYaml)) }} +{{- end }} +{{- $out = append $out $c }} +{{- end }} +{{- with $out }} +{{- toYaml . }} +{{- end }} +{{- end }} + +{{- define "fluxer-infra.replicas" -}} +{{- if kindIs "invalid" .w.replicas }}1{{ else }}{{ .w.replicas }}{{ end }} +{{- end }} + +{{- define "fluxer-infra.image" -}} +{{- $ref := printf "%s:%s" .repository .tag }} +{{- with .digest }} +{{- $ref = printf "%s@%s" $ref . }} +{{- end }} +{{- $ref | quote }} +{{- end }} + +{{- define "fluxer-infra.podAnnotations" -}} +{{- with merge (deepCopy (.extra | default dict)) (deepCopy (.w.podAnnotations | default dict)) (deepCopy (.root.Values.podAnnotations | default dict)) }} +annotations: + {{- toYaml . | nindent 2 }} +{{- end }} +{{- end }} + +{{- define "fluxer-infra.podSpec" -}} +{{- $root := .root }} +{{- $w := .w }} +{{- with include "fluxer-infra.pick" (dict "root" $root "w" $w "key" "affinity") }} +affinity: + {{- . | nindent 2 }} +{{- end }} +{{- with include "fluxer-infra.pick" (dict "root" $root "w" $w "key" "imagePullSecrets") }} +imagePullSecrets: + {{- . | nindent 2 }} +{{- end }} +{{- with include "fluxer-infra.pick" (dict "root" $root "w" $w "key" "nodeSelector") }} +nodeSelector: + {{- . | nindent 2 }} +{{- end }} +{{- with include "fluxer-infra.pick" (dict "root" $root "w" $w "key" "tolerations") }} +tolerations: + {{- . | nindent 2 }} +{{- end }} +{{- with include "fluxer-infra.topologySpreadConstraints" . }} +topologySpreadConstraints: + {{- . | nindent 2 }} +{{- end }} +{{- with include "fluxer-infra.pick" (dict "root" $root "w" $w "key" "podSecurityContext") }} +securityContext: + {{- . | nindent 2 }} +{{- end }} +{{- if not (kindIs "invalid" $w.terminationGracePeriodSeconds) }} +terminationGracePeriodSeconds: {{ $w.terminationGracePeriodSeconds }} +{{- end }} +{{- end }} + +{{- define "fluxer-infra.containerCommon" -}} +{{- $root := .root }} +{{- $w := .w }} +{{- $img := $w.image | default dict }} +image: {{ include "fluxer-infra.image" $img }} +imagePullPolicy: {{ $img.pullPolicy }} +{{- $env := include "fluxer-infra.envList" . | trim }} +{{- if or .env $env }} +env: +{{- with .env }} + {{- toYaml . | nindent 2 }} +{{- end }} +{{- with $env }} + {{- . | nindent 2 }} +{{- end }} +{{- end }} +{{- with include "fluxer-infra.envFrom" . }} +envFrom: + {{- . | nindent 2 }} +{{- end }} +{{- with $w.lifecycle }} +lifecycle: + {{- toYaml . | nindent 2 }} +{{- end }} +{{- include "fluxer-infra.probes" . }} +{{- with $w.resources }} +resources: + {{- toYaml . | nindent 2 }} +{{- end }} +{{- with include "fluxer-infra.pick" (dict "root" $root "w" $w "key" "securityContext") }} +securityContext: + {{- . | nindent 2 }} +{{- end }} +{{- with concat .mounts ($w.extraVolumeMounts | default list) }} +volumeMounts: + {{- toYaml . | nindent 2 }} +{{- end }} +{{- end }} + +{{- define "fluxer-infra.statefulSetSpec" -}} +{{- $w := .w }} +{{- with include "fluxer-infra.pick" (dict "root" .root "w" $w "key" "updateStrategy") }} +updateStrategy: + {{- . | nindent 2 }} +{{- end }} +{{- if not (kindIs "invalid" $w.minReadySeconds) }} +minReadySeconds: {{ $w.minReadySeconds }} +{{- end }} +{{- end }} + +{{- define "fluxer-infra.volumeClaim" -}} +- metadata: + name: data + spec: + accessModes: + - ReadWriteOnce + {{- with .storageClassName }} + storageClassName: {{ . | quote }} + {{- end }} + resources: + requests: + storage: {{ .size }} +{{- end }} + +{{- define "fluxer-infra.pdb" -}} +{{- with .w.pdb }} +--- +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: {{ $.name }}-pdb + namespace: {{ $.root.Release.Namespace }} + labels: + {{- include "fluxer-infra.labels" $ | nindent 4 }} +spec: + {{- if not (kindIs "invalid" .minAvailable) }} + minAvailable: {{ .minAvailable }} + {{- end }} + {{- if not (kindIs "invalid" .maxUnavailable) }} + maxUnavailable: {{ .maxUnavailable }} + {{- end }} + selector: + matchLabels: + {{- include "fluxer-infra.selectorLabels" $ | nindent 6 }} +{{- end }} +{{- end }} + +{{- define "fluxer-infra.service" }} +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ .svcName }} + namespace: {{ .root.Release.Namespace }} + labels: + {{- include "fluxer-infra.labels" . | nindent 4 }} +spec: + {{- if .headless }} + clusterIP: None + {{- end }} + {{- if .publishNotReady }} + publishNotReadyAddresses: true + {{- end }} + selector: + {{- include "fluxer-infra.selectorLabels" . | nindent 4 }} + ports: + {{- range .ports }} + - name: {{ index . 0 }} + port: {{ index . 1 }} + targetPort: {{ index . 0 }} + {{- end }} +{{- end }} + +{{- define "fluxer-infra.natsConf" -}} +{{- $w := .Values.nats -}} +{{- with $w.config -}} +listen: 0.0.0.0:4222 +http: 0.0.0.0:8222 +max_payload: {{ .maxPayload }} +max_pending: {{ .maxPending }} +max_connections: {{ .maxConnections }} +{{- if $w.jetstream.enabled }} +server_name: $POD_NAME + +jetstream { + store_dir: /data +} +{{- end }} + +cluster { + name: {{ .clusterName }} + listen: 0.0.0.0:6222 + + routes = [ +{{- range $i := until (int (include "fluxer-infra.replicas" (dict "w" $w))) }} + nats-route://nats-{{ $i }}.nats-headless.{{ $.Release.Namespace }}.svc.{{ $.Values.clusterDomain }}:6222 +{{- end }} + ] +} +{{ end }} +{{- end }} diff --git a/deploy/helm/fluxer-infra/templates/nats.yaml b/deploy/helm/fluxer-infra/templates/nats.yaml new file mode 100644 index 000000000..8ab38c5ec --- /dev/null +++ b/deploy/helm/fluxer-infra/templates/nats.yaml @@ -0,0 +1,71 @@ +{{- with .Values.nats }} +{{- $ctx := dict "root" $ "w" . "name" "nats" "component" "messaging" }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: nats-config + namespace: {{ $.Release.Namespace }} + labels: + {{- include "fluxer-infra.labels" $ctx | nindent 4 }} +data: + nats.conf: {{ include "fluxer-infra.natsConf" $ | toJson }} +{{- include "fluxer-infra.pdb" $ctx }} +{{- include "fluxer-infra.service" (merge (dict "svcName" "nats" "ports" (list (list "client" 4222))) $ctx) }} +{{- include "fluxer-infra.service" (merge (dict "svcName" "nats-headless" "headless" true "ports" (list (list "client" 4222) (list "cluster" 6222) (list "monitor" 8222))) $ctx) }} +{{- $mounts := list (dict "name" "config" "mountPath" "/etc/nats") }} +{{- $env := list }} +{{- if .jetstream.enabled }} +{{- $mounts = append $mounts (dict "name" "data" "mountPath" "/data") }} +{{- $env = append $env (dict "name" "POD_NAME" "valueFrom" (dict "fieldRef" (dict "fieldPath" "metadata.name"))) }} +{{- end }} +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: nats + namespace: {{ $.Release.Namespace }} + labels: + {{- include "fluxer-infra.labels" $ctx | nindent 4 }} +spec: + replicas: {{ include "fluxer-infra.replicas" $ctx }} + serviceName: nats-headless + {{- with include "fluxer-infra.statefulSetSpec" $ctx | trim }} + {{- . | nindent 2 }} + {{- end }} + selector: + matchLabels: + {{- include "fluxer-infra.selectorLabels" $ctx | nindent 6 }} + template: + metadata: + labels: + {{- include "fluxer-infra.labels" $ctx | nindent 8 }} + {{- with include "fluxer-infra.podAnnotations" (merge (dict "extra" (dict "checksum/config" (include "fluxer-infra.natsConf" $ | sha256sum))) $ctx) | trim }} + {{- . | nindent 6 }} + {{- end }} + spec: + {{- include "fluxer-infra.podSpec" $ctx | trim | nindent 6 }} + containers: + - name: nats + {{- include "fluxer-infra.containerCommon" (merge (dict "env" $env "mounts" $mounts) $ctx) | trim | nindent 10 }} + args: + - -c + - /etc/nats/nats.conf + ports: + - name: client + containerPort: 4222 + - name: cluster + containerPort: 6222 + - name: monitor + containerPort: 8222 + volumes: + - name: config + configMap: + name: nats-config + {{- with .extraVolumes }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if .jetstream.enabled }} + volumeClaimTemplates: + {{- include "fluxer-infra.volumeClaim" .jetstream.storage | nindent 4 }} + {{- end }} +{{- end }} diff --git a/deploy/helm/fluxer-infra/templates/valkey.yaml b/deploy/helm/fluxer-infra/templates/valkey.yaml new file mode 100644 index 000000000..e57db863e --- /dev/null +++ b/deploy/helm/fluxer-infra/templates/valkey.yaml @@ -0,0 +1,67 @@ +{{- with .Values.valkey }} +{{- $ctx := dict "root" $ "w" . "name" "valkey" "component" "cache" }} +{{- include "fluxer-infra.pdb" $ctx }} +{{- include "fluxer-infra.service" (merge (dict "svcName" "valkey" "ports" (list (list "valkey" 6379))) $ctx) }} +{{- include "fluxer-infra.service" (merge (dict "svcName" "valkey-headless" "headless" true "publishNotReady" true "ports" (list (list "valkey" 6379))) $ctx) }} +{{- $mounts := list }} +{{- if .persistence.enabled }} +{{- $mounts = append $mounts (dict "name" "data" "mountPath" "/data") }} +{{- end }} +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: valkey + namespace: {{ $.Release.Namespace }} + labels: + {{- include "fluxer-infra.labels" $ctx | nindent 4 }} +spec: + replicas: 1 + serviceName: valkey-headless + {{- with include "fluxer-infra.statefulSetSpec" $ctx | trim }} + {{- . | nindent 2 }} + {{- end }} + selector: + matchLabels: + {{- include "fluxer-infra.selectorLabels" $ctx | nindent 6 }} + template: + metadata: + labels: + {{- include "fluxer-infra.labels" $ctx | nindent 8 }} + {{- with include "fluxer-infra.podAnnotations" $ctx | trim }} + {{- . | nindent 6 }} + {{- end }} + spec: + {{- include "fluxer-infra.podSpec" $ctx | trim | nindent 6 }} + containers: + - name: valkey + {{- include "fluxer-infra.containerCommon" (merge (dict "env" list "mounts" $mounts) $ctx) | trim | nindent 10 }} + command: + - valkey-server + {{- if .persistence.enabled }} + - --appendonly + - "yes" + - --dir + - /data + {{- else }} + - --save + - "" + - --appendonly + - "no" + {{- end }} + - --maxmemory + - {{ .maxmemory | quote }} + - --maxmemory-policy + - {{ .maxmemoryPolicy | quote }} + ports: + - name: valkey + containerPort: 6379 + {{- with .extraVolumes }} + volumes: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if .persistence.enabled }} + volumeClaimTemplates: + {{- include "fluxer-infra.volumeClaim" .persistence | nindent 4 }} + {{- end }} +{{- end }} diff --git a/deploy/helm/fluxer-infra/values.yaml b/deploy/helm/fluxer-infra/values.yaml new file mode 100644 index 000000000..87300f5a8 --- /dev/null +++ b/deploy/helm/fluxer-infra/values.yaml @@ -0,0 +1,108 @@ +imagePullSecrets: [] + +clusterDomain: cluster.local + +env: {} + +extraEnv: [] + +envFrom: [] + +podAnnotations: {} + +podSecurityContext: + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + +securityContext: + allowPrivilegeEscalation: false + +probes: {} + +updateStrategy: {} + +topologySpreadConstraints: [] + +nodeSelector: {} + +tolerations: [] + +affinity: {} + +nats: + image: + repository: nats + tag: 2.14-alpine + pullPolicy: IfNotPresent + replicas: 3 + config: + clusterName: nats + maxPayload: 1MB + maxPending: 64MB + maxConnections: 65536 + jetstream: + enabled: true + storage: + size: 10Gi + storageClassName: "" + podSecurityContext: + fsGroup: 65534 + runAsGroup: 65534 + runAsNonRoot: true + runAsUser: 65534 + seccompProfile: + type: RuntimeDefault + probes: + liveness: + httpGet: + path: /healthz + port: monitor + initialDelaySeconds: 10 + readiness: + httpGet: + path: /healthz?js-enabled-only=true + port: monitor + resources: + requests: + cpu: 50m + memory: 128Mi + limits: + memory: 512Mi + +valkey: + image: + repository: valkey/valkey + tag: 9.1-alpine + pullPolicy: IfNotPresent + maxmemory: 192mb + maxmemoryPolicy: noeviction + persistence: + enabled: true + size: 1Gi + storageClassName: "" + podSecurityContext: + fsGroup: 999 + runAsGroup: 999 + runAsNonRoot: true + runAsUser: 999 + seccompProfile: + type: RuntimeDefault + probes: + liveness: + exec: + command: + - valkey-cli + - ping + initialDelaySeconds: 10 + readiness: + exec: + command: + - valkey-cli + - ping + resources: + requests: + cpu: 50m + memory: 64Mi + limits: + memory: 256Mi diff --git a/deploy/helm/fluxer-ingress/Chart.yaml b/deploy/helm/fluxer-ingress/Chart.yaml new file mode 100644 index 000000000..32c47e5e6 --- /dev/null +++ b/deploy/helm/fluxer-ingress/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: fluxer-ingress +description: Ingress routing for the public Fluxer endpoints. +type: application +version: 0.1.0 +appVersion: "v1" diff --git a/deploy/helm/fluxer-ingress/templates/_helpers.tpl b/deploy/helm/fluxer-ingress/templates/_helpers.tpl new file mode 100644 index 000000000..cdf4c6abb --- /dev/null +++ b/deploy/helm/fluxer-ingress/templates/_helpers.tpl @@ -0,0 +1,27 @@ +{{- define "fluxer-ingress.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end }} + +{{- define "fluxer-ingress.labels" -}} +app.kubernetes.io/name: {{ .Chart.Name }} +app.kubernetes.io/instance: {{ .Release.Name }} +app.kubernetes.io/part-of: fluxer +app.kubernetes.io/managed-by: {{ .Release.Service }} +helm.sh/chart: {{ include "fluxer-ingress.chart" . }} +{{- end }} + +{{- define "fluxer-ingress.annotationKey" -}} +{{- if or (contains "/" .key) (not .prefix) -}} +{{- .key -}} +{{- else -}} +{{- printf "%s/%s" .prefix .key -}} +{{- end -}} +{{- end }} + +{{- define "fluxer-ingress.string" -}} +{{- if and (kindIs "float64" .) (eq . (floor .)) -}} +{{- . | int64 | toString -}} +{{- else -}} +{{- . | toString -}} +{{- end -}} +{{- end }} diff --git a/deploy/helm/fluxer-ingress/templates/clusterissuer.yaml b/deploy/helm/fluxer-ingress/templates/clusterissuer.yaml new file mode 100644 index 000000000..09b5b2262 --- /dev/null +++ b/deploy/helm/fluxer-ingress/templates/clusterissuer.yaml @@ -0,0 +1,20 @@ +{{- with .Values.clusterIssuer }} +{{- if .enabled }} +apiVersion: cert-manager.io/v1 +kind: ClusterIssuer +metadata: + name: {{ required "clusterIssuer.name is required" .name }} + labels: + {{- include "fluxer-ingress.labels" $ | nindent 4 }} +spec: + acme: + email: {{ required "clusterIssuer.email is required" .email | quote }} + privateKeySecretRef: + name: {{ required "clusterIssuer.privateKeySecretName is required" .privateKeySecretName }} + server: {{ required "clusterIssuer.server is required" .server }} + solvers: + - http01: + ingress: + class: {{ required "clusterIssuer.solverIngressClass is required" .solverIngressClass }} +{{- end }} +{{- end }} diff --git a/deploy/helm/fluxer-ingress/templates/ingresses.yaml b/deploy/helm/fluxer-ingress/templates/ingresses.yaml new file mode 100644 index 000000000..7cd647549 --- /dev/null +++ b/deploy/helm/fluxer-ingress/templates/ingresses.yaml @@ -0,0 +1,58 @@ +{{- $v := .Values }} +{{- $presets := $v.annotationPresets | default dict }} +{{- $issuer := $v.clusterIssuer | default dict }} +{{- range $name, $spec := ($v.ingresses | default dict) }} +{{- if not (kindIs "invalid" $spec) }} +{{- $ann := deepCopy ($v.commonAnnotations | default dict) }} +{{- range ($spec.presets | default list) }} +{{- $ann = mergeOverwrite $ann (deepCopy (required (printf "unknown annotation preset %s" .) (index $presets .))) }} +{{- end }} +{{- if and $spec.tls $issuer.enabled }} +{{- $_ := set $ann "cert-manager.io/cluster-issuer" (required "clusterIssuer.name is required" $issuer.name) }} +{{- end }} +{{- $ann = mergeOverwrite $ann (deepCopy ($spec.annotations | default dict)) }} +{{- range $k, $val := $ann }} +{{- if kindIs "invalid" $val }} +{{- $_ := unset $ann $k }} +{{- end }} +{{- end }} +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ $name }} + namespace: {{ $.Release.Namespace }} + labels: + {{- include "fluxer-ingress.labels" $ | nindent 4 }} + {{- with $ann }} + annotations: + {{- range $k, $val := . }} + {{ include "fluxer-ingress.annotationKey" (dict "key" $k "prefix" $v.annotationPrefix) }}: {{ include "fluxer-ingress.string" $val | quote }} + {{- end }} + {{- end }} +spec: + {{- with $spec.ingressClassName | default $v.ingressClassName }} + ingressClassName: {{ . }} + {{- end }} + {{- with $spec.tls }} + tls: + {{- toYaml . | nindent 4 }} + {{- end }} + rules: + {{- range $rule := required (printf "ingress %s needs rules" $name) $spec.rules }} + - host: {{ required (printf "ingress %s has a rule without a host" $name) $rule.host | quote }} + http: + paths: + {{- range $p := $rule.paths | default (list dict) }} + {{- $p = $p | default dict }} + - path: {{ $p.path | default "/" | quote }} + pathType: {{ $p.pathType | default "Prefix" }} + backend: + service: + name: {{ required (printf "ingress %s host %s needs a service" $name $rule.host) ($p.service | default $rule.service) }} + port: + number: {{ required (printf "ingress %s host %s needs a port or servicePort" $name $rule.host) ($p.port | default $rule.port | default $v.servicePort) | int64 }} + {{- end }} + {{- end }} +{{- end }} +{{- end }} diff --git a/deploy/helm/fluxer-ingress/values.yaml b/deploy/helm/fluxer-ingress/values.yaml new file mode 100644 index 000000000..b156c4741 --- /dev/null +++ b/deploy/helm/fluxer-ingress/values.yaml @@ -0,0 +1,53 @@ +ingressClassName: nginx +annotationPrefix: nginx.ingress.kubernetes.io +servicePort: 8080 + +commonAnnotations: {} + +annotationPresets: + websocket: + proxy-read-timeout: "3600" + proxy-send-timeout: "3600" + stripPrefix: + use-regex: "true" + rewrite-target: /$2 + +ingresses: + fluxer: + rules: + - host: web.example.com + service: app-proxy + - host: api.example.com + service: api + - host: admin.example.com + service: admin + - host: media.example.com + service: media-proxy + fluxer-web-api: + presets: [stripPrefix] + rules: + - host: web.example.com + service: api + paths: + - path: /api(/(.*))?$ + pathType: ImplementationSpecific + fluxer-gateway: + presets: [websocket] + rules: + - host: gateway.example.com + service: gateway + fluxer-uploads: + annotations: + proxy-body-size: 100m + proxy-request-buffering: "off" + rules: + - host: uploads.example.com + service: uploads + +clusterIssuer: + enabled: false + name: letsencrypt + email: "" + server: https://acme-v02.api.letsencrypt.org/directory + privateKeySecretName: letsencrypt-account-key + solverIngressClass: nginx diff --git a/deploy/helm/fluxer-media-proxy/Chart.yaml b/deploy/helm/fluxer-media-proxy/Chart.yaml new file mode 100644 index 000000000..4a33850ee --- /dev/null +++ b/deploy/helm/fluxer-media-proxy/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: fluxer-media-proxy +description: Fluxer media proxy and upload relay workloads. +type: application +version: 0.1.0 +appVersion: "v1" diff --git a/deploy/helm/fluxer-media-proxy/templates/_helpers.tpl b/deploy/helm/fluxer-media-proxy/templates/_helpers.tpl new file mode 100644 index 000000000..e2395078c --- /dev/null +++ b/deploy/helm/fluxer-media-proxy/templates/_helpers.tpl @@ -0,0 +1,87 @@ +{{- define "fluxer-media-proxy.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end }} + +{{- define "fluxer-media-proxy.selectorLabels" -}} +app.kubernetes.io/name: {{ .name }} +app.kubernetes.io/instance: {{ .root.Release.Name }} +{{- end }} + +{{- define "fluxer-media-proxy.labels" -}} +{{ include "fluxer-media-proxy.selectorLabels" . }} +app.kubernetes.io/component: {{ include "fluxer-media-proxy.mode" . }} +app.kubernetes.io/part-of: fluxer +app.kubernetes.io/managed-by: {{ .root.Release.Service }} +helm.sh/chart: {{ include "fluxer-media-proxy.chart" .root }} +{{- end }} + +{{- define "fluxer-media-proxy.image" -}} +{{- $g := .root.Values.image -}} +{{- $i := .w.image | default dict -}} +{{- $repo := $i.repository | default (printf "%s/%s" $g.registry ($i.name | default "fluxer-media-proxy")) -}} +{{- $tag := $i.tag | default $g.tag -}} +{{- if $i.digest -}} +{{- printf "%s:%s@%s" $repo $tag $i.digest | quote -}} +{{- else -}} +{{- printf "%s:%s" $repo $tag | quote -}} +{{- end -}} +{{- end }} + +{{- define "fluxer-media-proxy.pick" -}} +{{- $v := ternary (get .w .key) (get .root.Values .key) (hasKey .w .key) -}} +{{- if $v }} +{{- toYaml $v }} +{{- end }} +{{- end }} + +{{- define "fluxer-media-proxy.mode" -}} +{{- $mode := required (printf "workloads.%s.mode is required" .name) .w.mode -}} +{{- if not (has $mode (list "mp" "static" "upload" "relay")) -}} +{{- fail (printf "workloads.%s.mode must be mp, static, upload or relay" .name) -}} +{{- end -}} +{{- $mode -}} +{{- end }} + +{{- define "fluxer-media-proxy.envValue" -}} +{{- if and (kindIs "float64" .) (eq . (float64 (int64 .))) -}} +{{- int64 . | toString -}} +{{- else -}} +{{- toString . -}} +{{- end -}} +{{- end }} + +{{- define "fluxer-media-proxy.mergeEnv" -}} +{{- $out := dict -}} +{{- range $layer := . -}} +{{- range $k, $v := ($layer | default dict) -}} +{{- if kindIs "invalid" $v -}} +{{- $_ := unset $out $k -}} +{{- else -}} +{{- $_ := set $out $k $v -}} +{{- end -}} +{{- end -}} +{{- end -}} +{{- toYaml $out -}} +{{- end }} + +{{- define "fluxer-media-proxy.topologySpreadConstraints" -}} +{{- $out := list -}} +{{- range .constraints -}} +{{- if .labelSelector -}} +{{- $out = append $out . -}} +{{- else -}} +{{- $out = append $out (merge (dict "labelSelector" (dict "matchLabels" $.selector)) .) -}} +{{- end -}} +{{- end -}} +{{- toYaml $out -}} +{{- end }} + +{{- define "fluxer-media-proxy.pdb" -}} +{{- $out := dict -}} +{{- range $k := list "minAvailable" "maxUnavailable" -}} +{{- if and (hasKey $ $k) (not (kindIs "invalid" (index $ $k))) -}} +{{- $_ := set $out $k (index $ $k) -}} +{{- end -}} +{{- end -}} +{{- toYaml $out -}} +{{- end }} diff --git a/deploy/helm/fluxer-media-proxy/templates/workloads.yaml b/deploy/helm/fluxer-media-proxy/templates/workloads.yaml new file mode 100644 index 000000000..8ebc5bdde --- /dev/null +++ b/deploy/helm/fluxer-media-proxy/templates/workloads.yaml @@ -0,0 +1,191 @@ +{{- range $name, $w := .Values.workloads }} +{{- if not (kindIs "invalid" $w) }} +{{- $ctx := dict "root" $ "name" $name "w" $w }} +{{- $mode := include "fluxer-media-proxy.mode" $ctx }} +{{- $sel := include "fluxer-media-proxy.selectorLabels" $ctx | fromYaml }} +{{- $env := include "fluxer-media-proxy.mergeEnv" (list $.Values.env $w.env) | fromYaml }} +{{- $extraEnv := concat ($.Values.extraEnv | default list) ($w.extraEnv | default list) }} +{{- $envFrom := concat ($.Values.envFrom | default list) ($w.envFrom | default list) }} +{{- $podAnnotations := merge (dict) ($w.podAnnotations | default dict) ($.Values.podAnnotations | default dict) }} +{{- $probes := dict }} +{{- range $k, $v := ($.Values.probes | default dict) }} +{{- $_ := set $probes $k $v }} +{{- end }} +{{- range $k, $v := ($w.probes | default dict) }} +{{- $_ := set $probes $k $v }} +{{- end }} +{{- $pick := dict "root" $ "w" $w }} +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ $name }} + namespace: {{ $.Release.Namespace }} + labels: + {{- include "fluxer-media-proxy.labels" $ctx | nindent 4 }} +spec: + {{- if not $w.hpa }} + replicas: {{ ternary $w.replicas 1 (hasKey $w "replicas") | int64 }} + {{- end }} + {{- if not (kindIs "invalid" $w.minReadySeconds) }} + minReadySeconds: {{ $w.minReadySeconds | int64 }} + {{- end }} + selector: + matchLabels: + {{- toYaml $sel | nindent 6 }} + {{- with include "fluxer-media-proxy.pick" (set (deepCopy $pick) "key" "strategy") }} + strategy: + {{- . | nindent 4 }} + {{- end }} + template: + metadata: + {{- with $podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "fluxer-media-proxy.labels" $ctx | nindent 8 }} + spec: + {{- with include "fluxer-media-proxy.pick" (set (deepCopy $pick) "key" "imagePullSecrets") }} + imagePullSecrets: + {{- . | nindent 8 }} + {{- end }} + {{- with include "fluxer-media-proxy.pick" (set (deepCopy $pick) "key" "podSecurityContext") }} + securityContext: + {{- . | nindent 8 }} + {{- end }} + {{- if not (kindIs "invalid" $w.terminationGracePeriodSeconds) }} + terminationGracePeriodSeconds: {{ $w.terminationGracePeriodSeconds | int64 }} + {{- end }} + {{- with include "fluxer-media-proxy.pick" (set (deepCopy $pick) "key" "nodeSelector") }} + nodeSelector: + {{- . | nindent 8 }} + {{- end }} + {{- with include "fluxer-media-proxy.pick" (set (deepCopy $pick) "key" "tolerations") }} + tolerations: + {{- . | nindent 8 }} + {{- end }} + {{- with include "fluxer-media-proxy.pick" (set (deepCopy $pick) "key" "affinity") }} + affinity: + {{- . | nindent 8 }} + {{- end }} + {{- with include "fluxer-media-proxy.pick" (set (deepCopy $pick) "key" "topologySpreadConstraints") | fromYamlArray }} + topologySpreadConstraints: + {{- include "fluxer-media-proxy.topologySpreadConstraints" (dict "constraints" . "selector" $sel) | nindent 8 }} + {{- end }} + containers: + - name: {{ $name }} + image: {{ include "fluxer-media-proxy.image" $ctx }} + imagePullPolicy: {{ ($w.image | default dict).pullPolicy | default $.Values.image.pullPolicy }} + env: + {{- if not (kindIs "invalid" $w.buildVersion) }} + - name: BUILD_VERSION + value: {{ include "fluxer-media-proxy.envValue" $w.buildVersion | quote }} + {{- end }} + - name: FLUXER_MEDIA_PROXY_MODE + value: {{ $mode | quote }} + {{- range $k, $v := $env }} + - name: {{ $k }} + value: {{ include "fluxer-media-proxy.envValue" $v | quote }} + {{- end }} + {{- with $extraEnv }} + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with $envFrom }} + envFrom: + {{- toYaml . | nindent 12 }} + {{- end }} + ports: + - name: http + containerPort: 8080 + protocol: TCP + {{- with $w.lifecycle }} + lifecycle: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- range $k := list "startup" "liveness" "readiness" }} + {{- with get $probes $k }} + {{ $k }}Probe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- end }} + {{- with $w.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with include "fluxer-media-proxy.pick" (set (deepCopy $pick) "key" "securityContext") }} + securityContext: + {{- . | nindent 12 }} + {{- end }} + {{- with $w.extraVolumeMounts }} + volumeMounts: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with $w.extraVolumes }} + volumes: + {{- toYaml . | nindent 8 }} + {{- end }} +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ $name }} + namespace: {{ $.Release.Namespace }} + labels: + {{- include "fluxer-media-proxy.labels" $ctx | nindent 4 }} +spec: + type: ClusterIP + selector: + {{- toYaml $sel | nindent 4 }} + ports: + - name: http + port: 8080 + targetPort: http + protocol: TCP +{{- with include "fluxer-media-proxy.pdb" ($w.pdb | default dict) | fromYaml }} +--- +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: {{ $name }}-pdb + namespace: {{ $.Release.Namespace }} + labels: + {{- include "fluxer-media-proxy.labels" $ctx | nindent 4 }} +spec: + {{- toYaml . | nindent 2 }} + selector: + matchLabels: + {{- toYaml $sel | nindent 6 }} +{{- end }} +{{- with $w.hpa }} +--- +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: {{ $name }} + namespace: {{ $.Release.Namespace }} + labels: + {{- include "fluxer-media-proxy.labels" $ctx | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ $name }} + minReplicas: {{ required (printf "workloads.%s.hpa.minReplicas is required" $name) .minReplicas | int64 }} + maxReplicas: {{ required (printf "workloads.%s.hpa.maxReplicas is required" $name) .maxReplicas | int64 }} + {{- if not (kindIs "invalid" .targetCPUUtilizationPercentage) }} + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .targetCPUUtilizationPercentage | int64 }} + {{- end }} + {{- with .behavior }} + behavior: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/deploy/helm/fluxer-media-proxy/values.yaml b/deploy/helm/fluxer-media-proxy/values.yaml new file mode 100644 index 000000000..7b9c6a1d8 --- /dev/null +++ b/deploy/helm/fluxer-media-proxy/values.yaml @@ -0,0 +1,72 @@ +image: + registry: ghcr.io/fluxerapp + tag: v1 + pullPolicy: IfNotPresent + +imagePullSecrets: [] + +env: {} + +extraEnv: [] + +envFrom: + - secretRef: + name: fluxer-env + +podAnnotations: {} + +podSecurityContext: + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + +securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + +probes: + liveness: + httpGet: + path: /_health + port: http + readiness: + httpGet: + path: /_health + port: http + +strategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 25% + maxUnavailable: 25% + +topologySpreadConstraints: [] + +nodeSelector: {} + +tolerations: [] + +affinity: {} + +workloads: + media-proxy: + mode: mp + replicas: 1 + resources: + requests: + cpu: 100m + memory: 256Mi + limits: + memory: 1Gi + + uploads: + mode: relay + replicas: 1 + resources: + requests: + cpu: 50m + memory: 64Mi + limits: + memory: 512Mi diff --git a/deploy/helm/fluxer-push/Chart.yaml b/deploy/helm/fluxer-push/Chart.yaml new file mode 100644 index 000000000..ba9e01255 --- /dev/null +++ b/deploy/helm/fluxer-push/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: fluxer-push +description: Fluxer push notification delivery service +type: application +version: 0.1.0 +appVersion: "v1" diff --git a/deploy/helm/fluxer-push/templates/_helpers.tpl b/deploy/helm/fluxer-push/templates/_helpers.tpl new file mode 100644 index 000000000..d51f9e0eb --- /dev/null +++ b/deploy/helm/fluxer-push/templates/_helpers.tpl @@ -0,0 +1,71 @@ +{{- define "fluxer-push.selectorLabels" -}} +app.kubernetes.io/name: {{ .name }} +app.kubernetes.io/instance: {{ .root.Release.Name }} +{{- end }} + +{{- define "fluxer-push.labels" -}} +{{ include "fluxer-push.selectorLabels" . }} +app.kubernetes.io/component: {{ include "fluxer-push.mode" . }} +app.kubernetes.io/part-of: fluxer +app.kubernetes.io/managed-by: {{ .root.Release.Service }} +helm.sh/chart: {{ printf "%s-%s" .root.Chart.Name .root.Chart.Version | replace "+" "_" }} +{{- end }} + +{{- define "fluxer-push.mode" -}} +{{- $mode := .w.mode | default "delivery" -}} +{{- if not (has $mode (list "delivery" "relay")) -}} +{{- fail (printf "workloads.%s.mode must be delivery or relay" .name) -}} +{{- end -}} +{{- $mode -}} +{{- end }} + +{{- define "fluxer-push.port" -}} +{{- .w.port | default (ternary 8127 8126 (eq (include "fluxer-push.mode" .) "relay")) -}} +{{- end }} + +{{- define "fluxer-push.image" -}} +{{- $global := .root.Values.image | default dict -}} +{{- $img := .w.image | default dict -}} +{{- $repo := $img.repository -}} +{{- if not $repo -}} +{{- $repo = printf "%s/%s" (required "image.registry is required" $global.registry) ($img.name | default "fluxer-push") -}} +{{- end -}} +{{- $ref := printf "%s:%s" $repo (include "fluxer-push.string" (required "image.tag is required" ($img.tag | default $global.tag))) -}} +{{- with $img.digest }}{{ $ref = printf "%s@%s" $ref . }}{{ end -}} +{{- $ref -}} +{{- end }} + +{{- define "fluxer-push.string" -}} +{{- if and (kindIs "float64" .) (eq . (floor .)) -}} +{{- . | int64 | toString -}} +{{- else -}} +{{- . | toString -}} +{{- end -}} +{{- end }} + +{{- define "fluxer-push.env" -}} +{{- $env := deepCopy (.root.Values.env | default dict) -}} +{{- range $k, $v := (.w.env | default dict) -}} +{{- if kindIs "invalid" $v -}} +{{- $_ := unset $env $k -}} +{{- else -}} +{{- $_ := set $env $k $v -}} +{{- end -}} +{{- end -}} +{{- if not (kindIs "invalid" .w.port) -}} +{{- $_ := set $env "FLUXER_PUSH_SERVICE_PORT" .w.port -}} +{{- end -}} +{{- if not (kindIs "invalid" .w.buildVersion) }} +- name: BUILD_VERSION + value: {{ include "fluxer-push.string" .w.buildVersion | quote }} +{{- end }} +{{- range $k, $v := $env }} +{{- if not (kindIs "invalid" $v) }} +- name: {{ $k }} + value: {{ include "fluxer-push.string" $v | quote }} +{{- end }} +{{- end }} +{{- with concat (.root.Values.extraEnv | default list) (.w.extraEnv | default list) }} +{{ toYaml . }} +{{- end }} +{{- end }} diff --git a/deploy/helm/fluxer-push/templates/workloads.yaml b/deploy/helm/fluxer-push/templates/workloads.yaml new file mode 100644 index 000000000..686971755 --- /dev/null +++ b/deploy/helm/fluxer-push/templates/workloads.yaml @@ -0,0 +1,205 @@ +{{- range $name, $w := .Values.workloads }} +{{- if not (kindIs "invalid" $w) }} +{{- $ctx := dict "root" $ "name" $name "w" $w }} +{{- $mode := include "fluxer-push.mode" $ctx }} +{{- $port := include "fluxer-push.port" $ctx | int }} +{{- $globalProbes := $.Values.probes | default dict }} +{{- $workloadProbes := $w.probes | default dict }} +{{- $probes := dict }} +{{- range $probe := list "startup" "liveness" "readiness" }} +{{- $_ := set $probes $probe (ternary (index $workloadProbes $probe) (index $globalProbes $probe) (hasKey $workloadProbes $probe)) }} +{{- end }} +{{- $annotations := mergeOverwrite (deepCopy ($.Values.podAnnotations | default dict)) (deepCopy ($w.podAnnotations | default dict)) }} +{{- $pullSecrets := ternary $w.imagePullSecrets $.Values.imagePullSecrets (hasKey $w "imagePullSecrets") }} +{{- $podSecurityContext := ternary $w.podSecurityContext $.Values.podSecurityContext (hasKey $w "podSecurityContext") }} +{{- $securityContext := ternary $w.securityContext $.Values.securityContext (hasKey $w "securityContext") }} +{{- $strategy := ternary $w.strategy $.Values.strategy (hasKey $w "strategy") }} +{{- $tsc := ternary $w.topologySpreadConstraints $.Values.topologySpreadConstraints (hasKey $w "topologySpreadConstraints") }} +{{- $nodeSelector := ternary $w.nodeSelector $.Values.nodeSelector (hasKey $w "nodeSelector") }} +{{- $tolerations := ternary $w.tolerations $.Values.tolerations (hasKey $w "tolerations") }} +{{- $affinity := ternary $w.affinity $.Values.affinity (hasKey $w "affinity") }} +{{- $envFrom := concat ($.Values.envFrom | default list) ($w.envFrom | default list) }} +{{- $env := include "fluxer-push.env" $ctx }} +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ $name }} + namespace: {{ $.Release.Namespace }} + labels: + {{- include "fluxer-push.labels" $ctx | nindent 4 }} +spec: + {{- if not $w.hpa }} + replicas: {{ ternary $w.replicas 1 (hasKey $w "replicas") | int }} + {{- end }} + {{- if hasKey $w "minReadySeconds" }} + minReadySeconds: {{ $w.minReadySeconds | int }} + {{- end }} + selector: + matchLabels: + {{- include "fluxer-push.selectorLabels" $ctx | nindent 6 }} + {{- with $strategy }} + strategy: + {{- toYaml . | nindent 4 }} + {{- end }} + template: + metadata: + {{- with $annotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "fluxer-push.labels" $ctx | nindent 8 }} + spec: + {{- with $pullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with $podSecurityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if hasKey $w "terminationGracePeriodSeconds" }} + terminationGracePeriodSeconds: {{ $w.terminationGracePeriodSeconds | int }} + {{- end }} + {{- with $nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with $tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with $affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with $tsc }} + topologySpreadConstraints: + {{- range . }} + {{- $c := deepCopy . }} + {{- if not $c.labelSelector }} + {{- $_ := set $c "labelSelector" (dict "matchLabels" (include "fluxer-push.selectorLabels" $ctx | fromYaml)) }} + {{- end }} + {{- toYaml (list $c) | nindent 8 }} + {{- end }} + {{- end }} + containers: + - name: {{ $name }} + image: {{ include "fluxer-push.image" $ctx | quote }} + imagePullPolicy: {{ ($w.image | default dict).pullPolicy | default ($.Values.image | default dict).pullPolicy | default "IfNotPresent" }} + command: + - /usr/local/bin/fluxer-push + {{- if eq $mode "relay" }} + args: + - --mode + - relay + {{- end }} + {{- with trim $env }} + env: + {{- . | nindent 12 }} + {{- end }} + {{- with $envFrom }} + envFrom: + {{- toYaml . | nindent 12 }} + {{- end }} + ports: + - name: http + containerPort: {{ $port }} + protocol: TCP + {{- with $probes.startup }} + startupProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with $probes.liveness }} + livenessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with $probes.readiness }} + readinessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with $w.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with $securityContext }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with $w.lifecycle }} + lifecycle: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with $w.extraVolumeMounts }} + volumeMounts: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with $w.extraVolumes }} + volumes: + {{- toYaml . | nindent 8 }} + {{- end }} +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ $name }} + namespace: {{ $.Release.Namespace }} + labels: + {{- include "fluxer-push.labels" $ctx | nindent 4 }} +spec: + type: ClusterIP + selector: + {{- include "fluxer-push.selectorLabels" $ctx | nindent 4 }} + ports: + - name: http + port: {{ $port }} + protocol: TCP + targetPort: http +{{- with $w.pdb }} +--- +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: {{ $name }}-pdb + namespace: {{ $.Release.Namespace }} + labels: + {{- include "fluxer-push.labels" $ctx | nindent 4 }} +spec: + {{- toYaml . | nindent 2 }} + selector: + matchLabels: + {{- include "fluxer-push.selectorLabels" $ctx | nindent 6 }} +{{- end }} +{{- with $w.hpa }} +--- +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: {{ $name }} + namespace: {{ $.Release.Namespace }} + labels: + {{- include "fluxer-push.labels" $ctx | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ $name }} + minReplicas: {{ required (printf "workloads.%s.hpa.minReplicas is required" $name) .minReplicas | int }} + maxReplicas: {{ required (printf "workloads.%s.hpa.maxReplicas is required" $name) .maxReplicas | int }} + {{- if not (kindIs "invalid" .targetCPUUtilizationPercentage) }} + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .targetCPUUtilizationPercentage | int }} + {{- end }} + {{- with .behavior }} + behavior: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/deploy/helm/fluxer-push/values.yaml b/deploy/helm/fluxer-push/values.yaml new file mode 100644 index 000000000..78dc6ab87 --- /dev/null +++ b/deploy/helm/fluxer-push/values.yaml @@ -0,0 +1,65 @@ +image: + registry: ghcr.io/fluxerapp + tag: v1 + pullPolicy: IfNotPresent + +imagePullSecrets: [] + +env: {} + +extraEnv: [] + +envFrom: + - secretRef: + name: fluxer-env + +podAnnotations: {} + +podSecurityContext: + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + +securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + +probes: + liveness: + httpGet: + path: /_healthz + port: http + readiness: + httpGet: + path: /_healthz + port: http + +strategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 25% + maxUnavailable: 25% + +topologySpreadConstraints: [] + +nodeSelector: {} + +tolerations: [] + +affinity: {} + +workloads: + push: + mode: delivery + replicas: 1 + env: + FLUXER_INTERNAL_API_ENDPOINT: http://api:8080 + FLUXER_SVC_NATS_URL: nats://nats:4222 + resources: + requests: + cpu: 50m + memory: 64Mi + limits: + memory: 256Mi diff --git a/deploy/helm/fluxer-svc/Chart.yaml b/deploy/helm/fluxer-svc/Chart.yaml new file mode 100644 index 000000000..55e6a2e82 --- /dev/null +++ b/deploy/helm/fluxer-svc/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: fluxer-svc +description: Fluxer internal services, each a router Deployment and a shard StatefulSet +type: application +version: 0.1.0 +appVersion: v1 diff --git a/deploy/helm/fluxer-svc/templates/_helpers.tpl b/deploy/helm/fluxer-svc/templates/_helpers.tpl new file mode 100644 index 000000000..4c65fdfa8 --- /dev/null +++ b/deploy/helm/fluxer-svc/templates/_helpers.tpl @@ -0,0 +1,203 @@ +{{- define "fluxer-svc.chart" -}} +{{ printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" }} +{{- end }} + +{{- define "fluxer-svc.selectorLabels" -}} +app.kubernetes.io/name: {{ .name }} +app.kubernetes.io/instance: {{ .root.Release.Name }} +{{- end }} + +{{- define "fluxer-svc.labels" -}} +{{ include "fluxer-svc.selectorLabels" . }} +app.kubernetes.io/component: {{ .mode }} +app.kubernetes.io/part-of: fluxer +app.kubernetes.io/managed-by: {{ .root.Release.Service }} +helm.sh/chart: {{ include "fluxer-svc.chart" .root }} +{{- end }} + +{{- define "fluxer-svc.envValue" -}} +{{- if and (kindIs "float64" .) (eq . (float64 (int64 .))) -}} +{{- int64 . | toString -}} +{{- else -}} +{{- toString . -}} +{{- end -}} +{{- end }} + +{{- define "fluxer-svc.mergeEnv" -}} +{{- $out := dict -}} +{{- range $layer := . -}} +{{- range $k, $v := ($layer | default dict) -}} +{{- if kindIs "invalid" $v -}} +{{- $_ := unset $out $k -}} +{{- else -}} +{{- $_ := set $out $k $v -}} +{{- end -}} +{{- end -}} +{{- end -}} +{{- toYaml $out -}} +{{- end }} + +{{- define "fluxer-svc.topologySpreadConstraints" -}} +{{- $out := list -}} +{{- range .constraints -}} +{{- if .labelSelector -}} +{{- $out = append $out . -}} +{{- else -}} +{{- $out = append $out (merge (dict "labelSelector" (dict "matchLabels" $.selector)) .) -}} +{{- end -}} +{{- end -}} +{{- toYaml $out -}} +{{- end }} + +{{- define "fluxer-svc.pdb" -}} +{{- $out := dict -}} +{{- range $k := list "minAvailable" "maxUnavailable" -}} +{{- if and (hasKey $ $k) (not (kindIs "invalid" (index $ $k))) -}} +{{- $_ := set $out $k (index $ $k) -}} +{{- end -}} +{{- end -}} +{{- toYaml $out -}} +{{- end }} + +{{- define "fluxer-svc.config" -}} +{{- $v := .root.Values -}} +{{- $levels := list (index $v .mode) (index .svc .mode) -}} +{{- $c := dict "extraEnv" ($v.extraEnv | default list) "envFrom" ($v.envFrom | default list) "podAnnotations" (deepCopy ($v.podAnnotations | default dict)) "probes" (deepCopy ($v.probes | default dict)) "image" (deepCopy (.svc.image | default dict)) -}} +{{- range $k := list "imagePullSecrets" "podSecurityContext" "securityContext" "topologySpreadConstraints" "nodeSelector" "tolerations" "affinity" (ternary "updateStrategy" "strategy" (eq .mode "shard")) -}} +{{- $_ := set $c $k (index $v $k) -}} +{{- end -}} +{{- $envLayers := list $v.env -}} +{{- range $level := $levels -}} +{{- range $k, $x := ($level | default dict) -}} +{{- if eq $k "env" -}} +{{- $envLayers = append $envLayers $x -}} +{{- else if has $k (list "podAnnotations" "image") -}} +{{- $_ := set $c $k (mergeOverwrite (index $c $k) (deepCopy ($x | default dict))) -}} +{{- else if has $k (list "extraEnv" "envFrom") -}} +{{- $_ := set $c $k (concat (index $c $k) ($x | default list)) -}} +{{- else if eq $k "probes" -}} +{{- range $name, $p := ($x | default dict) -}} +{{- $_ := set $c.probes $name $p -}} +{{- end -}} +{{- else -}} +{{- $_ := set $c $k $x -}} +{{- end -}} +{{- end -}} +{{- end -}} +{{- $_ := set $c "env" (include "fluxer-svc.mergeEnv" $envLayers | fromYaml) -}} +{{- toYaml $c }} +{{- end }} + +{{- define "fluxer-svc.image" -}} +{{- $g := .root.Values.image -}} +{{- $i := .c.image -}} +{{- $repo := $i.repository | default (printf "%s/%s" $g.registry ($i.name | default (printf "fluxer-%s" .service))) -}} +{{- $ref := printf "%s:%s" $repo ($i.tag | default $g.tag) -}} +{{- with $i.digest }}{{ $ref = printf "%s@%s" $ref . }}{{ end -}} +{{- $ref -}} +{{- end }} + +{{- define "fluxer-svc.pod" -}} +{{- $v := .root.Values -}} +{{- $c := .c -}} +metadata: + {{- with $c.podAnnotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} + labels: + {{- include "fluxer-svc.labels" . | nindent 4 }} +spec: + {{- with $c.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with $c.podSecurityContext }} + securityContext: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- if not (kindIs "invalid" $c.terminationGracePeriodSeconds) }} + terminationGracePeriodSeconds: {{ $c.terminationGracePeriodSeconds | int64 }} + {{- end }} + {{- with $c.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with $c.tolerations }} + tolerations: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with $c.affinity }} + affinity: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with $c.topologySpreadConstraints }} + topologySpreadConstraints: + {{- include "fluxer-svc.topologySpreadConstraints" (dict "constraints" . "selector" (include "fluxer-svc.selectorLabels" $ | fromYaml)) | nindent 4 }} + {{- end }} + containers: + - name: {{ .mode }} + image: {{ include "fluxer-svc.image" . | quote }} + imagePullPolicy: {{ $c.image.pullPolicy | default $v.image.pullPolicy }} + env: + - name: FLUXER_SVC_MODE + value: {{ .mode | quote }} + - name: FLUXER_SVC_NAME + value: {{ .service | quote }} + - name: FLUXER_SVC_SHARD_COUNT + value: {{ .shardCount | quote }} + - name: FLUXER_SVC_PORT + value: {{ include "fluxer-svc.envValue" $v.port | quote }} + {{- if not (kindIs "invalid" $c.buildVersion) }} + - name: BUILD_VERSION + value: {{ include "fluxer-svc.envValue" $c.buildVersion | quote }} + {{- end }} + {{- if eq .mode "shard" }} + - name: POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + {{- end }} + {{- range $name, $value := $c.env }} + - name: {{ $name }} + value: {{ include "fluxer-svc.envValue" $value | quote }} + {{- end }} + {{- with $c.extraEnv }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with $c.envFrom }} + envFrom: + {{- toYaml . | nindent 8 }} + {{- end }} + ports: + - name: http + containerPort: {{ $v.port }} + protocol: TCP + {{- with $c.lifecycle }} + lifecycle: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- range $name := list "startup" "liveness" "readiness" }} + {{- with index $c.probes $name }} + {{ $name }}Probe: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- end }} + {{- with $c.resources }} + resources: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with $c.securityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with $c.extraVolumeMounts }} + volumeMounts: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with $c.extraVolumes }} + volumes: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/deploy/helm/fluxer-svc/templates/workloads.yaml b/deploy/helm/fluxer-svc/templates/workloads.yaml new file mode 100644 index 000000000..3e3c96efa --- /dev/null +++ b/deploy/helm/fluxer-svc/templates/workloads.yaml @@ -0,0 +1,145 @@ +{{- range $service, $svc := .Values.services }} +{{- if not (kindIs "invalid" $svc) }} +{{- $svc = $svc | default dict }} +{{- $rc := fromYaml (include "fluxer-svc.config" (dict "root" $ "svc" $svc "mode" "router")) }} +{{- $sc := fromYaml (include "fluxer-svc.config" (dict "root" $ "svc" $svc "mode" "shard")) }} +{{- $routerReplicas := ternary $rc.replicas 1 (hasKey $rc "replicas") | int64 }} +{{- $shardCount := ternary $sc.replicas 1 (hasKey $sc "replicas") | int64 }} +{{- if lt $shardCount 1 }} +{{- fail (printf "services.%s shard replicas must be at least 1" $service) }} +{{- end }} +{{- $router := dict "root" $ "service" $service "svc" $svc "mode" "router" "name" $service "c" $rc "shardCount" (toString $shardCount) }} +{{- $shard := dict "root" $ "service" $service "svc" $svc "mode" "shard" "name" (printf "%s-shard" $service) "c" $sc "shardCount" (toString $shardCount) }} +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ $service }} + namespace: {{ $.Release.Namespace }} + labels: + {{- include "fluxer-svc.labels" $router | nindent 4 }} +spec: + {{- if not $rc.hpa }} + replicas: {{ $routerReplicas }} + {{- end }} + {{- if not (kindIs "invalid" $rc.minReadySeconds) }} + minReadySeconds: {{ $rc.minReadySeconds | int64 }} + {{- end }} + selector: + matchLabels: + {{- include "fluxer-svc.selectorLabels" $router | nindent 6 }} + {{- with $rc.strategy }} + strategy: + {{- toYaml . | nindent 4 }} + {{- end }} + template: + {{- include "fluxer-svc.pod" $router | nindent 4 }} +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: {{ $service }}-shard + namespace: {{ $.Release.Namespace }} + labels: + {{- include "fluxer-svc.labels" $shard | nindent 4 }} +spec: + replicas: {{ $shardCount }} + {{- if not (kindIs "invalid" $sc.minReadySeconds) }} + minReadySeconds: {{ $sc.minReadySeconds | int64 }} + {{- end }} + podManagementPolicy: Parallel + serviceName: {{ $service }}-shard-headless + selector: + matchLabels: + {{- include "fluxer-svc.selectorLabels" $shard | nindent 6 }} + {{- with $sc.updateStrategy }} + updateStrategy: + {{- toYaml . | nindent 4 }} + {{- end }} + template: + {{- include "fluxer-svc.pod" $shard | nindent 4 }} +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ $service }} + namespace: {{ $.Release.Namespace }} + labels: + {{- include "fluxer-svc.labels" $router | nindent 4 }} +spec: + type: ClusterIP + selector: + {{- include "fluxer-svc.selectorLabels" $router | nindent 4 }} + ports: + - name: http + port: {{ $.Values.port }} + targetPort: {{ $.Values.port }} + protocol: TCP +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ $service }}-shard-headless + namespace: {{ $.Release.Namespace }} + labels: + {{- include "fluxer-svc.labels" $shard | nindent 4 }} +spec: + type: ClusterIP + clusterIP: None + publishNotReadyAddresses: true + selector: + {{- include "fluxer-svc.selectorLabels" $shard | nindent 4 }} + ports: + - name: http + port: {{ $.Values.port }} + targetPort: {{ $.Values.port }} + protocol: TCP +{{- with $rc.hpa }} +--- +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: {{ $service }} + namespace: {{ $.Release.Namespace }} + labels: + {{- include "fluxer-svc.labels" $router | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ $service }} + minReplicas: {{ required (printf "services.%s router hpa.minReplicas is required" $service) .minReplicas | int64 }} + maxReplicas: {{ required (printf "services.%s router hpa.maxReplicas is required" $service) .maxReplicas | int64 }} + {{- if not (kindIs "invalid" .targetCPUUtilizationPercentage) }} + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .targetCPUUtilizationPercentage | int64 }} + {{- end }} + {{- with .behavior }} + behavior: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} +{{- range $ctx := list $router $shard }} +{{- with include "fluxer-svc.pdb" ($ctx.c.pdb | default dict) | fromYaml }} +--- +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: {{ $ctx.name }}-pdb + namespace: {{ $.Release.Namespace }} + labels: + {{- include "fluxer-svc.labels" $ctx | nindent 4 }} +spec: + {{- toYaml . | nindent 2 }} + selector: + matchLabels: + {{- include "fluxer-svc.selectorLabels" $ctx | nindent 6 }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/deploy/helm/fluxer-svc/values.yaml b/deploy/helm/fluxer-svc/values.yaml new file mode 100644 index 000000000..b4952a2a7 --- /dev/null +++ b/deploy/helm/fluxer-svc/values.yaml @@ -0,0 +1,89 @@ +image: + registry: ghcr.io/fluxerapp + tag: v1 + pullPolicy: IfNotPresent + +imagePullSecrets: [] + +env: + FLUXER_SVC_NATS_URL: nats://nats:4222 + +extraEnv: [] + +envFrom: + - secretRef: + name: fluxer-env + +podAnnotations: {} + +podSecurityContext: + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + +securityContext: + allowPrivilegeEscalation: false + +probes: + liveness: + httpGet: + path: /_healthz + port: http + readiness: + httpGet: + path: /_health + port: http + +strategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 25% + maxUnavailable: 25% + +updateStrategy: + type: RollingUpdate + +topologySpreadConstraints: [] +nodeSelector: {} +tolerations: [] +affinity: {} + +port: 8090 + +router: + replicas: 1 + resources: + requests: + cpu: 50m + memory: 64Mi + limits: + memory: 192Mi + +shard: + replicas: 2 + probes: + startup: + httpGet: + path: /_healthz + port: http + periodSeconds: 10 + failureThreshold: 30 + resources: + requests: + cpu: 50m + memory: 96Mi + limits: + memory: 384Mi + +services: + gifs: + shard: + env: + FLUXER_MEDIA_PROXY_PUBLIC_ENDPOINT: https://media.example.com + messages: {} + snowflakes: {} + unfurl: + shard: + env: + FLUXER_MEDIA_PROXY_ENDPOINT: http://media-proxy:8080 + users: {} diff --git a/deploy/helm/fluxer-web/Chart.yaml b/deploy/helm/fluxer-web/Chart.yaml new file mode 100644 index 000000000..642088db3 --- /dev/null +++ b/deploy/helm/fluxer-web/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: fluxer-web +description: Fluxer web app proxy and admin dashboard. +type: application +version: 0.1.0 +appVersion: "v1" diff --git a/deploy/helm/fluxer-web/templates/_helpers.tpl b/deploy/helm/fluxer-web/templates/_helpers.tpl new file mode 100644 index 000000000..2c009a6ab --- /dev/null +++ b/deploy/helm/fluxer-web/templates/_helpers.tpl @@ -0,0 +1,80 @@ +{{- define "fluxer-web.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end }} + +{{- define "fluxer-web.selectorLabels" -}} +app.kubernetes.io/name: {{ .name }} +app.kubernetes.io/instance: {{ .root.Release.Name }} +{{- end }} + +{{- define "fluxer-web.labels" -}} +{{ include "fluxer-web.selectorLabels" . }} +app.kubernetes.io/component: web +app.kubernetes.io/part-of: fluxer +app.kubernetes.io/managed-by: {{ .root.Release.Service }} +helm.sh/chart: {{ include "fluxer-web.chart" .root }} +{{- end }} + +{{- define "fluxer-web.image" -}} +{{- $g := .root.Values.image | default dict -}} +{{- $i := .w.image | default dict -}} +{{- $repo := $i.repository -}} +{{- if not $repo -}} +{{- $repo = printf "%s/%s" (required "image.registry is required" $g.registry) ($i.name | default (printf "fluxer-%s" .name)) -}} +{{- end -}} +{{- $tag := required "image.tag is required" ($i.tag | default $g.tag) -}} +{{- if $i.digest -}} +{{- printf "%s:%s@%s" $repo $tag $i.digest | quote -}} +{{- else -}} +{{- printf "%s:%s" $repo $tag | quote -}} +{{- end -}} +{{- end }} + +{{- define "fluxer-web.pick" -}} +{{- $v := ternary (get .w .key) (get .root.Values .key) (hasKey .w .key) -}} +{{- if $v }} +{{- toYaml $v }} +{{- end }} +{{- end }} + +{{- define "fluxer-web.str" -}} +{{- if and (kindIs "float64" .) (eq . (floor .)) -}} +{{- int64 . | toString | quote -}} +{{- else -}} +{{- toString . | quote -}} +{{- end -}} +{{- end }} + +{{- define "fluxer-web.env" -}} +{{- $env := dict -}} +{{- range $k, $val := .root.Values.env | default dict }} +{{- $_ := set $env $k $val }} +{{- end }} +{{- range $k, $val := .w.env | default dict }} +{{- $_ := set $env $k $val }} +{{- end }} +{{- range $k, $val := $env }} +{{- if not (kindIs "invalid" $val) }} +- name: {{ $k }} + value: {{ include "fluxer-web.str" $val }} +{{- end }} +{{- end }} +{{- with .w.buildVersion }} +- name: BUILD_VERSION + value: {{ include "fluxer-web.str" . }} +{{- end }} +{{- with concat (.root.Values.extraEnv | default list) (.w.extraEnv | default list) }} +{{ toYaml . }} +{{- end }} +{{- end }} + +{{- define "fluxer-web.topologySpread" -}} +{{- $tscs := ternary .w.topologySpreadConstraints .root.Values.topologySpreadConstraints (hasKey .w "topologySpreadConstraints") -}} +{{- range $tscs }} +{{- $c := deepCopy . }} +{{- if not $c.labelSelector }} +{{- $_ := set $c "labelSelector" (dict "matchLabels" (include "fluxer-web.selectorLabels" $ | fromYaml)) }} +{{- end }} +- {{- toYaml $c | nindent 2 }} +{{- end }} +{{- end }} diff --git a/deploy/helm/fluxer-web/templates/workloads.yaml b/deploy/helm/fluxer-web/templates/workloads.yaml new file mode 100644 index 000000000..3851b0efe --- /dev/null +++ b/deploy/helm/fluxer-web/templates/workloads.yaml @@ -0,0 +1,172 @@ +{{- $v := .Values }} +{{- range $name, $w := .Values.workloads }} +{{- if not (kindIs "invalid" $w) }} +{{- $ctx := dict "root" $ "name" $name "w" $w }} +{{- $envFrom := concat ($v.envFrom | default list) ($w.envFrom | default list) }} +{{- $podAnnotations := merge (dict) ($w.podAnnotations | default dict) ($v.podAnnotations | default dict) }} +{{- $wProbes := $w.probes | default dict }} +{{- $gProbes := $v.probes | default dict }} +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ $name }} + namespace: {{ $.Release.Namespace }} + labels: + {{- include "fluxer-web.labels" $ctx | nindent 4 }} +spec: + {{- if not $w.hpa }} + replicas: {{ if kindIs "invalid" $w.replicas }}1{{ else }}{{ int $w.replicas }}{{ end }} + {{- end }} + {{- if not (kindIs "invalid" $w.minReadySeconds) }} + minReadySeconds: {{ int $w.minReadySeconds }} + {{- end }} + selector: + matchLabels: + {{- include "fluxer-web.selectorLabels" $ctx | nindent 6 }} + {{- with include "fluxer-web.pick" (dict "root" $ "w" $w "key" "strategy") }} + strategy: + {{- . | nindent 4 }} + {{- end }} + template: + metadata: + labels: + {{- include "fluxer-web.labels" $ctx | nindent 8 }} + {{- with $podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with include "fluxer-web.pick" (dict "root" $ "w" $w "key" "imagePullSecrets") }} + imagePullSecrets: + {{- . | nindent 8 }} + {{- end }} + {{- with include "fluxer-web.pick" (dict "root" $ "w" $w "key" "podSecurityContext") }} + securityContext: + {{- . | nindent 8 }} + {{- end }} + {{- if not (kindIs "invalid" $w.terminationGracePeriodSeconds) }} + terminationGracePeriodSeconds: {{ int $w.terminationGracePeriodSeconds }} + {{- end }} + {{- with include "fluxer-web.pick" (dict "root" $ "w" $w "key" "nodeSelector") }} + nodeSelector: + {{- . | nindent 8 }} + {{- end }} + {{- with include "fluxer-web.pick" (dict "root" $ "w" $w "key" "affinity") }} + affinity: + {{- . | nindent 8 }} + {{- end }} + {{- with include "fluxer-web.pick" (dict "root" $ "w" $w "key" "tolerations") }} + tolerations: + {{- . | nindent 8 }} + {{- end }} + {{- with include "fluxer-web.topologySpread" $ctx | trim }} + topologySpreadConstraints: + {{- . | nindent 8 }} + {{- end }} + containers: + - name: {{ $name }} + image: {{ include "fluxer-web.image" $ctx }} + imagePullPolicy: {{ ($w.image | default dict).pullPolicy | default ($v.image | default dict).pullPolicy | default "IfNotPresent" }} + {{- with include "fluxer-web.env" $ctx | trim }} + env: + {{- . | nindent 12 }} + {{- end }} + {{- with $envFrom }} + envFrom: + {{- toYaml . | nindent 12 }} + {{- end }} + ports: + - name: http + containerPort: 8080 + protocol: TCP + {{- with $w.lifecycle }} + lifecycle: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- range $probe := list "startup" "liveness" "readiness" }} + {{- with hasKey $wProbes $probe | ternary (get $wProbes $probe) (get $gProbes $probe) }} + {{ $probe }}Probe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- end }} + {{- with $w.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with include "fluxer-web.pick" (dict "root" $ "w" $w "key" "securityContext") }} + securityContext: + {{- . | nindent 12 }} + {{- end }} + {{- with $w.extraVolumeMounts }} + volumeMounts: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with $w.extraVolumes }} + volumes: + {{- toYaml . | nindent 8 }} + {{- end }} +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ $name }} + namespace: {{ $.Release.Namespace }} + labels: + {{- include "fluxer-web.labels" $ctx | nindent 4 }} +spec: + type: ClusterIP + selector: + {{- include "fluxer-web.selectorLabels" $ctx | nindent 4 }} + ports: + - name: http + port: 8080 + targetPort: http + protocol: TCP +{{- with $w.hpa }} +--- +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: {{ $name }} + namespace: {{ $.Release.Namespace }} + labels: + {{- include "fluxer-web.labels" $ctx | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ $name }} + minReplicas: {{ required (printf "%s.hpa.minReplicas is required" $name) .minReplicas }} + maxReplicas: {{ required (printf "%s.hpa.maxReplicas is required" $name) .maxReplicas }} + {{- with .targetCPUUtilizationPercentage }} + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ . }} + {{- end }} + {{- with .behavior }} + behavior: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} +{{- with $w.pdb }} +--- +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: {{ $name }}-pdb + namespace: {{ $.Release.Namespace }} + labels: + {{- include "fluxer-web.labels" $ctx | nindent 4 }} +spec: + {{- toYaml . | nindent 2 }} + selector: + matchLabels: + {{- include "fluxer-web.selectorLabels" $ctx | nindent 6 }} +{{- end }} +{{- end }} +{{- end }} diff --git a/deploy/helm/fluxer-web/values.yaml b/deploy/helm/fluxer-web/values.yaml new file mode 100644 index 000000000..9c95bcf12 --- /dev/null +++ b/deploy/helm/fluxer-web/values.yaml @@ -0,0 +1,83 @@ +image: + registry: ghcr.io/fluxerapp + tag: v1 + pullPolicy: IfNotPresent + +imagePullSecrets: [] + +env: {} + +extraEnv: [] + +envFrom: + - secretRef: + name: fluxer-env + +podAnnotations: {} + +podSecurityContext: + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + +securityContext: + allowPrivilegeEscalation: false + +probes: + startup: + httpGet: + path: /_health + port: http + periodSeconds: 10 + failureThreshold: 30 + liveness: + httpGet: + path: /_health + port: http + readiness: + httpGet: + path: /_health + port: http + +strategy: + type: RollingUpdate + +topologySpreadConstraints: [] + +nodeSelector: {} + +tolerations: [] + +affinity: {} + +workloads: + admin: + image: + name: fluxer-admin + replicas: 1 + env: + FLUXER_ENV: production + FLUXER_API_ENDPOINT: https://api.example.com + FLUXER_ADMIN_ENDPOINT: https://admin.example.com + FLUXER_MEDIA_ENDPOINT: https://media.example.com + FLUXER_APP_ENDPOINT: https://web.example.com + resources: + requests: + cpu: 50m + memory: 96Mi + limits: + memory: 384Mi + app-proxy: + image: + name: fluxer-app-proxy-self-hosted + replicas: 1 + env: + RELEASE_CHANNEL: stable + PUBLIC_BOOTSTRAP_API_ENDPOINT: /api + PUBLIC_BOOTSTRAP_API_PUBLIC_ENDPOINT: https://web.example.com/api + resources: + requests: + cpu: 50m + memory: 96Mi + limits: + memory: 384Mi