Ship production deployment and runtime observability

This commit is contained in:
ms2k 2026-07-18 04:22:14 +09:00
commit 775dd61963
16 changed files with 3712 additions and 175 deletions

View file

@ -0,0 +1,7 @@
apiVersion: v2
name: remora
description: Remora API, Worker, and WebClient deployment
type: application
version: 0.1.0
appVersion: 0.0.0
kubeVersion: ">=1.31.0-0"

View file

@ -0,0 +1,31 @@
{{- define "remora.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- define "remora.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- printf "%s-%s" .Release.Name (include "remora.name" .) | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}
{{- define "remora.labels" -}}
app.kubernetes.io/name: {{ include "remora.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
helm.sh/chart: {{ printf "%s-%s" .Chart.Name .Chart.Version | quote }}
{{- end -}}
{{- define "remora.selectorLabels" -}}
app.kubernetes.io/name: {{ include "remora.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end -}}
{{- define "remora.image" -}}
{{- if .digest -}}
{{- printf "%s@%s" .repository .digest -}}
{{- else -}}
{{- printf "%s:%s" .repository .tag -}}
{{- end -}}
{{- end -}}

View file

@ -0,0 +1,178 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "remora.fullname" . }}-api
labels:
{{- include "remora.labels" . | nindent 4 }}
app.kubernetes.io/component: api
spec:
replicas: {{ .Values.api.replicas }}
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0
maxSurge: 1
selector:
matchLabels:
{{- include "remora.selectorLabels" . | nindent 6 }}
app.kubernetes.io/component: api
template:
metadata:
labels:
{{- include "remora.selectorLabels" . | nindent 8 }}
app.kubernetes.io/component: api
annotations:
checksum/config: {{ include (print $.Template.BasePath "/config.yaml") . | sha256sum }}
checksum/vault: {{ include (print $.Template.BasePath "/vault-agent.yaml") . | sha256sum }}
spec:
serviceAccountName: {{ include "remora.fullname" . }}-api
automountServiceAccountToken: false
enableServiceLinks: false
dnsPolicy: {{ .Values.podDnsPolicy }}
{{- with .Values.podDnsConfig }}
dnsConfig:
{{- toYaml . | nindent 8 }}
{{- end }}
terminationGracePeriodSeconds: 60
securityContext:
runAsNonRoot: true
runAsUser: 65532
runAsGroup: 65532
fsGroup: 65532
seccompProfile:
type: RuntimeDefault
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
{{- include "remora.selectorLabels" . | nindent 14 }}
app.kubernetes.io/component: api
containers:
- name: api
image: {{ include "remora.image" .Values.api.image | quote }}
imagePullPolicy: {{ .Values.api.image.pullPolicy }}
command: [sh, -ec]
args:
- until test -s /vault/token/token; do sleep 0.2; done; exec /usr/local/bin/remora-api
envFrom:
- configMapRef:
name: {{ include "remora.fullname" . }}-config
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: {{ .Values.database.urlSecret.name }}
key: {{ .Values.database.urlSecret.key }}
- name: REMORA_COORDINATION_URL
valueFrom:
secretKeyRef:
name: {{ .Values.coordination.urlSecret.name }}
key: {{ .Values.coordination.urlSecret.key }}
- name: REMORA_TARGET_ACCESS_KEY
valueFrom:
secretKeyRef:
name: {{ .Values.target.credentialsSecret.name }}
key: {{ .Values.target.credentialsSecret.accessKey }}
- name: REMORA_TARGET_SECRET_KEY
valueFrom:
secretKeyRef:
name: {{ .Values.target.credentialsSecret.name }}
key: {{ .Values.target.credentialsSecret.secretKey }}
- {name: REMORA_DATABASE_MAX_CONNECTIONS, value: {{ .Values.database.maxApiConnections | quote }}}
- {name: REMORA_PUBLIC_BIND, value: "0.0.0.0:8080"}
- {name: REMORA_WORKER_BIND, value: "0.0.0.0:8443"}
- {name: REMORA_MANAGEMENT_BIND, value: "0.0.0.0:9090"}
- {name: REMORA_CURSOR_SECRET_FILE, value: /run/remora/cursor/cursor-secret}
- {name: REMORA_WORKER_TLS_CERT_FILE, value: /run/remora/tls/server.crt}
- {name: REMORA_WORKER_TLS_KEY_FILE, value: /run/remora/tls/server.key}
- {name: REMORA_WORKER_TLS_CLIENT_CA_FILE, value: /run/remora/tls/ca.crt}
- {name: REMORA_VAULT_TOKEN_FILE, value: /vault/token/token}
ports:
- {name: public, containerPort: 8080}
- {name: worker, containerPort: 8443}
- {name: management, containerPort: 9090}
readinessProbe:
httpGet: {path: /health/ready, port: management}
periodSeconds: 5
timeoutSeconds: 2
failureThreshold: 6
livenessProbe:
httpGet: {path: /health/live, port: management}
periodSeconds: 10
timeoutSeconds: 2
failureThreshold: 3
startupProbe:
httpGet: {path: /health/live, port: management}
periodSeconds: 2
failureThreshold: 90
resources:
{{- toYaml .Values.api.resources | nindent 12 }}
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: [ALL]
volumeMounts:
- {name: cursor, mountPath: /run/remora/cursor, readOnly: true}
- {name: worker-tls, mountPath: /run/remora/tls, readOnly: true}
- {name: vault-token, mountPath: /vault/token, readOnly: true}
- name: vault-agent
image: {{ .Values.vault.image | quote }}
imagePullPolicy: IfNotPresent
args: [agent, -config=/vault/config/config.hcl]
resources:
requests: {cpu: 10m, memory: 32Mi}
limits: {cpu: 250m, memory: 128Mi}
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: [ALL]
volumeMounts:
- {name: vault-config, mountPath: /vault/config, readOnly: true}
- {name: vault-token, mountPath: /vault/token}
- {name: vault-service-account, mountPath: /var/run/secrets/vault, readOnly: true}
{{- if .Values.vault.caSecret.name }}
- {name: vault-ca, mountPath: /vault/ca, readOnly: true}
{{- end }}
{{- if .Values.vault.caSecret.name }}
env:
- {name: VAULT_CACERT, value: /vault/ca/{{ .Values.vault.caSecret.key }}}
{{- end }}
volumes:
- name: cursor
secret:
secretName: {{ include "remora.fullname" . }}-cursor
- name: worker-tls
secret:
secretName: {{ include "remora.fullname" . }}-worker-tls
items:
- {key: ca.crt, path: ca.crt}
- {key: server.crt, path: server.crt}
- {key: server.key, path: server.key}
- name: vault-config
configMap:
name: {{ include "remora.fullname" . }}-vault-api
- name: vault-token
emptyDir:
medium: Memory
- name: vault-service-account
projected:
sources:
- serviceAccountToken:
path: token
audience: {{ .Values.vault.audience | quote }}
expirationSeconds: 3600
{{- if .Values.vault.caSecret.name }}
- name: vault-ca
secret:
secretName: {{ .Values.vault.caSecret.name }}
items:
- {key: {{ .Values.vault.caSecret.key }}, path: {{ .Values.vault.caSecret.key }}}
{{- end }}

View file

@ -0,0 +1,35 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "remora.fullname" . }}-config
labels:
{{- include "remora.labels" . | nindent 4 }}
data:
REMORA_COORDINATION_NAMESPACE: {{ required "coordination.namespace is required" .Values.coordination.namespace | quote }}
REMORA_GLOBAL_ATTEMPT_LIMIT: {{ .Values.coordination.globalAttemptLimit | quote }}
REMORA_LEASE_TTL_SECONDS: {{ .Values.coordination.leaseTtlSeconds | quote }}
REMORA_RESERVE_TTL_SECONDS: {{ .Values.coordination.reserveTtlSeconds | quote }}
REMORA_PROGRESS_TTL_SECONDS: {{ .Values.coordination.progressTtlSeconds | quote }}
REMORA_OIDC_ISSUER: {{ required "oidc.issuer is required" .Values.oidc.issuer | quote }}
REMORA_OIDC_AUDIENCE: {{ required "oidc.audience is required" .Values.oidc.audience | quote }}
REMORA_OIDC_RESOURCE_CLIENT: {{ required "oidc.resourceClient is required" .Values.oidc.resourceClient | quote }}
REMORA_OIDC_PROJECT_ROLE_CLAIMS: {{ required "oidc.projectRoleClaims is required" .Values.oidc.projectRoleClaims | quote }}
REMORA_TARGET_STORE_ID: {{ required "target.storeId is required" .Values.target.storeId | quote }}
REMORA_TARGET_ENDPOINT: {{ required "target.endpoint is required" .Values.target.endpoint | quote }}
REMORA_TARGET_BUCKET: {{ required "target.bucket is required" .Values.target.bucket | quote }}
REMORA_TARGET_REGION: {{ required "target.region is required" .Values.target.region | quote }}
REMORA_TARGET_ROOT: {{ .Values.target.root | quote }}
REMORA_TARGET_EXCLUSIVE_WRITER: {{ required "target.exclusiveWriter must be true" .Values.target.exclusiveWriter | quote }}
REMORA_DEFAULT_TARGET_PREFIX: {{ .Values.target.defaultPrefix | quote }}
REMORA_RESERVED_TARGET_SEGMENTS: {{ required "runtime.reservedTargetSegments is required" .Values.runtime.reservedTargetSegments | quote }}
REMORA_ADMISSION_POLICY_VERSION: {{ required "runtime.admissionPolicyVersion is required" .Values.runtime.admissionPolicyVersion | quote }}
REMORA_MAX_ATTEMPTS: {{ .Values.runtime.maxAttempts | quote }}
REMORA_RECOVERY_INTERVAL_SECONDS: {{ .Values.runtime.recoveryIntervalSeconds | quote }}
REMORA_EVENT_ALLOWED_ORIGINS: {{ required "runtime.eventAllowedOrigins is required" .Values.runtime.eventAllowedOrigins | quote }}
REMORA_EVENT_RETENTION_SECONDS: {{ .Values.runtime.eventRetentionSeconds | quote }}
REMORA_VAULT_ADDR: {{ required "vault.address is required" .Values.vault.address | quote }}
REMORA_VAULT_PATH: {{ required "vault.path is required" .Values.vault.path | quote }}
REMORA_OUTBOUND_ALLOWED_SCHEME_PORTS: {{ required "outbound.allowedSchemePorts is required" .Values.outbound.allowedSchemePorts | quote }}
REMORA_OUTBOUND_ALLOWED_CIDRS: {{ .Values.outbound.allowedCidrs | quote }}
REMORA_OUTBOUND_MAX_REDIRECTS: {{ .Values.outbound.maxRedirects | quote }}
RUST_LOG: {{ .Values.runtime.logFilter | quote }}

View file

@ -0,0 +1,39 @@
{{- if .Values.ingress.enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ include "remora.fullname" . }}
labels:
{{- include "remora.labels" . | nindent 4 }}
{{- with .Values.ingress.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- if .Values.ingress.className }}
ingressClassName: {{ .Values.ingress.className }}
{{- end }}
{{- if .Values.ingress.tlsSecretName }}
tls:
- hosts: [{{ .Values.ingress.host | quote }}]
secretName: {{ .Values.ingress.tlsSecretName }}
{{- end }}
rules:
- host: {{ .Values.ingress.host | quote }}
http:
paths:
- path: /v1
pathType: Prefix
backend:
service:
name: {{ include "remora.fullname" . }}
port:
name: http
- path: /
pathType: Prefix
backend:
service:
name: {{ include "remora.fullname" . }}-web
port:
name: http
{{- end }}

View file

@ -0,0 +1,37 @@
{{- $fullname := include "remora.fullname" . -}}
{{- $tlsName := printf "%s-worker-tls" $fullname -}}
{{- $existingTls := lookup "v1" "Secret" .Release.Namespace $tlsName -}}
apiVersion: v1
kind: Secret
metadata:
name: {{ $tlsName }}
labels:
{{- include "remora.labels" . | nindent 4 }}
type: Opaque
{{ if $existingTls }}
data:
{{- toYaml $existingTls.data | nindent 2 }}
{{ else }}
{{ $ca := genCA (printf "%s-worker-ca" $fullname) 3650 }}
{{ $serverDns := list (printf "%s-worker" $fullname) (printf "%s-worker.%s" $fullname .Release.Namespace) (printf "%s-worker.%s.svc" $fullname .Release.Namespace) }}
{{ $server := genSignedCert (printf "%s-worker" $fullname) nil $serverDns 3650 $ca }}
{{ $client := genSignedCert (printf "%s-worker-client" $fullname) nil nil 3650 $ca }}
data:
ca.crt: {{ $ca.Cert | b64enc }}
server.crt: {{ $server.Cert | b64enc }}
server.key: {{ $server.Key | b64enc }}
client.crt: {{ $client.Cert | b64enc }}
client.key: {{ $client.Key | b64enc }}
{{ end }}
---
{{ $cursorName := printf "%s-cursor" $fullname }}
{{ $existingCursor := lookup "v1" "Secret" .Release.Namespace $cursorName }}
apiVersion: v1
kind: Secret
metadata:
name: {{ $cursorName }}
labels:
{{- include "remora.labels" . | nindent 4 }}
type: Opaque
data:
cursor-secret: {{ if $existingCursor }}{{ index $existingCursor.data "cursor-secret" }}{{ else }}{{ randAlphaNum 64 | b64enc }}{{ end }}

View file

@ -0,0 +1,203 @@
{{- if .Values.networkPolicy.enabled }}
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: {{ include "remora.fullname" . }}-api
labels:
{{- include "remora.labels" . | nindent 4 }}
spec:
podSelector:
matchLabels:
{{- include "remora.selectorLabels" . | nindent 6 }}
app.kubernetes.io/component: api
policyTypes: [Ingress, Egress]
ingress:
- from:
- podSelector:
matchLabels:
{{- include "remora.selectorLabels" . | nindent 14 }}
app.kubernetes.io/component: web
- namespaceSelector:
{{- toYaml .Values.networkPolicy.ingressNamespaceSelector | nindent 12 }}
podSelector:
{{- toYaml .Values.networkPolicy.ingressPodSelector | nindent 12 }}
ports:
- {port: 8080, protocol: TCP}
- from:
- podSelector:
matchLabels:
{{- include "remora.selectorLabels" . | nindent 14 }}
app.kubernetes.io/component: worker
ports:
- {port: 8443, protocol: TCP}
- from:
- namespaceSelector:
{{- toYaml .Values.networkPolicy.monitoringNamespaceSelector | nindent 12 }}
podSelector:
{{- toYaml .Values.networkPolicy.monitoringPodSelector | nindent 12 }}
ports:
- {port: 9090, protocol: TCP}
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- {port: 53, protocol: UDP}
- {port: 53, protocol: TCP}
{{- range .Values.networkPolicy.dnsPodLabels }}
- to:
- podSelector:
matchLabels:
{{- toYaml . | nindent 14 }}
ports:
- {port: 53, protocol: UDP}
- {port: 53, protocol: TCP}
{{- end }}
{{- range .Values.networkPolicy.dependencyPodLabels }}
- to:
- podSelector:
matchLabels:
{{- toYaml . | nindent 14 }}
{{- end }}
{{- range .Values.networkPolicy.externalEgressCidrs }}
- to:
- ipBlock:
cidr: {{ . | quote }}
{{- end }}
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: {{ include "remora.fullname" . }}-worker
labels:
{{- include "remora.labels" . | nindent 4 }}
spec:
podSelector:
matchLabels:
{{- include "remora.selectorLabels" . | nindent 6 }}
app.kubernetes.io/component: worker
policyTypes: [Ingress, Egress]
ingress:
- from:
- namespaceSelector:
{{- toYaml .Values.networkPolicy.monitoringNamespaceSelector | nindent 12 }}
podSelector:
{{- toYaml .Values.networkPolicy.monitoringPodSelector | nindent 12 }}
ports:
- {port: 9090, protocol: TCP}
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- {port: 53, protocol: UDP}
- {port: 53, protocol: TCP}
{{- range .Values.networkPolicy.dnsPodLabels }}
- to:
- podSelector:
matchLabels:
{{- toYaml . | nindent 14 }}
ports:
- {port: 53, protocol: UDP}
- {port: 53, protocol: TCP}
{{- end }}
- to:
- podSelector:
matchLabels:
{{- include "remora.selectorLabels" . | nindent 14 }}
app.kubernetes.io/component: api
ports:
- {port: 8443, protocol: TCP}
{{- range .Values.networkPolicy.dependencyPodLabels }}
- to:
- podSelector:
matchLabels:
{{- toYaml . | nindent 14 }}
{{- end }}
{{- range .Values.networkPolicy.externalEgressCidrs }}
- to:
- ipBlock:
cidr: {{ . | quote }}
{{- end }}
{{- if .Values.networkPolicy.allowPublicSourceEgress }}
- to:
- ipBlock:
cidr: 0.0.0.0/0
except:
- 0.0.0.0/8
- 10.0.0.0/8
- 100.64.0.0/10
- 127.0.0.0/8
- 169.254.0.0/16
- 172.16.0.0/12
- 192.0.0.0/24
- 192.0.2.0/24
- 192.168.0.0/16
- 198.18.0.0/15
- 198.51.100.0/24
- 203.0.113.0/24
- 224.0.0.0/4
- 240.0.0.0/4
ports:
{{- range $entry := splitList "," .Values.outbound.allowedSchemePorts }}
- port: {{ index (splitList ":" $entry) 1 }}
protocol: TCP
{{- end }}
- to:
- ipBlock:
cidr: 2000::/3
except:
- 2001::/23
- 2001:db8::/32
- 3ff0::/12
ports:
{{- range $entry := splitList "," .Values.outbound.allowedSchemePorts }}
- port: {{ index (splitList ":" $entry) 1 }}
protocol: TCP
{{- end }}
{{- end }}
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: {{ include "remora.fullname" . }}-web
labels:
{{- include "remora.labels" . | nindent 4 }}
spec:
podSelector:
matchLabels:
{{- include "remora.selectorLabels" . | nindent 6 }}
app.kubernetes.io/component: web
policyTypes: [Ingress, Egress]
ingress:
- ports:
- {port: 8080, protocol: TCP}
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- {port: 53, protocol: UDP}
- {port: 53, protocol: TCP}
{{- range .Values.networkPolicy.dnsPodLabels }}
- to:
- podSelector:
matchLabels:
{{- toYaml . | nindent 14 }}
ports:
- {port: 53, protocol: UDP}
- {port: 53, protocol: TCP}
{{- end }}
{{- end }}

View file

@ -0,0 +1,56 @@
{{- if .Values.observability.serviceMonitor.enabled }}
{{- range $component := list "api" "worker" "web" }}
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: {{ include "remora.fullname" $ }}-{{ $component }}
labels:
{{- include "remora.labels" $ | nindent 4 }}
spec:
selector:
matchLabels:
{{- include "remora.selectorLabels" $ | nindent 6 }}
app.kubernetes.io/component: {{ $component }}
endpoints:
- port: metrics
path: /metrics
interval: {{ $.Values.observability.serviceMonitor.interval }}
---
{{- end }}
{{- end }}
{{- if .Values.observability.prometheusRule.enabled }}
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: {{ include "remora.fullname" . }}
labels:
{{- include "remora.labels" . | nindent 4 }}
spec:
groups:
- name: remora
rules:
- alert: RemoraComponentNotReady
expr: >-
remora_ready{component=~"remora-api|remora-worker"} == 0
or absent(remora_ready{component="remora-api"})
or absent(remora_ready{component="remora-worker"})
for: 5m
labels:
severity: critical
annotations:
summary: Remora API or Worker is not ready
- alert: RemoraRecoveryFailures
expr: increase(remora_background_failures_total[10m]) > 0
for: 2m
labels:
severity: warning
annotations:
summary: Remora background recovery is failing
- alert: RemoraWebUnavailable
expr: absent(remora_web_build_info)
for: 5m
labels:
severity: critical
annotations:
summary: Remora WebClient has no healthy scrape target
{{- end }}

View file

@ -0,0 +1,17 @@
{{- if .Values.podDisruptionBudget.enabled }}
{{- range $component := list "api" "worker" "web" }}
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: {{ include "remora.fullname" $ }}-{{ $component }}
labels:
{{- include "remora.labels" $ | nindent 4 }}
spec:
minAvailable: {{ $.Values.podDisruptionBudget.minAvailable }}
selector:
matchLabels:
{{- include "remora.selectorLabels" $ | nindent 6 }}
app.kubernetes.io/component: {{ $component }}
---
{{- end }}
{{- end }}

View file

@ -0,0 +1,26 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "remora.fullname" . }}-api
labels:
{{- include "remora.labels" . | nindent 4 }}
app.kubernetes.io/component: api
automountServiceAccountToken: false
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "remora.fullname" . }}-worker
labels:
{{- include "remora.labels" . | nindent 4 }}
app.kubernetes.io/component: worker
automountServiceAccountToken: false
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "remora.fullname" . }}-web
labels:
{{- include "remora.labels" . | nindent 4 }}
app.kubernetes.io/component: web
automountServiceAccountToken: false

View file

@ -0,0 +1,71 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "remora.fullname" . }}
labels:
{{- include "remora.labels" . | nindent 4 }}
app.kubernetes.io/component: api
spec:
selector:
{{- include "remora.selectorLabels" . | nindent 4 }}
app.kubernetes.io/component: api
ports:
- {name: http, port: 80, targetPort: public}
---
apiVersion: v1
kind: Service
metadata:
name: {{ include "remora.fullname" . }}-worker
labels:
{{- include "remora.labels" . | nindent 4 }}
app.kubernetes.io/component: api
spec:
selector:
{{- include "remora.selectorLabels" . | nindent 4 }}
app.kubernetes.io/component: api
ports:
- {name: https, port: 8443, targetPort: worker}
---
apiVersion: v1
kind: Service
metadata:
name: {{ include "remora.fullname" . }}-web
labels:
{{- include "remora.labels" . | nindent 4 }}
app.kubernetes.io/component: web
spec:
selector:
{{- include "remora.selectorLabels" . | nindent 4 }}
app.kubernetes.io/component: web
ports:
- {name: http, port: 80, targetPort: http}
---
{{- range $component := list "api" "worker" }}
apiVersion: v1
kind: Service
metadata:
name: {{ include "remora.fullname" $ }}-{{ $component }}-metrics
labels:
{{- include "remora.labels" $ | nindent 4 }}
app.kubernetes.io/component: {{ $component }}
spec:
selector:
{{- include "remora.selectorLabels" $ | nindent 4 }}
app.kubernetes.io/component: {{ $component }}
ports:
- {name: metrics, port: 9090, targetPort: management}
---
{{- end }}
apiVersion: v1
kind: Service
metadata:
name: {{ include "remora.fullname" . }}-web-metrics
labels:
{{- include "remora.labels" . | nindent 4 }}
app.kubernetes.io/component: web
spec:
selector:
{{- include "remora.selectorLabels" . | nindent 4 }}
app.kubernetes.io/component: web
ports:
- {name: metrics, port: 9090, targetPort: http}

View file

@ -0,0 +1,53 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "remora.fullname" . }}-vault-api
labels:
{{- include "remora.labels" . | nindent 4 }}
data:
config.hcl: |
vault {
address = {{ .Values.vault.address | quote }}
}
auto_auth {
method "kubernetes" {
mount_path = {{ .Values.vault.kubernetesAuthMount | quote }}
config = {
role = {{ .Values.vault.apiRole | quote }}
token_path = "/var/run/secrets/vault/token"
}
}
sink "file" {
config = {
path = "/vault/token/token"
mode = 0440
}
}
}
---
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "remora.fullname" . }}-vault-worker
labels:
{{- include "remora.labels" . | nindent 4 }}
data:
config.hcl: |
vault {
address = {{ .Values.vault.address | quote }}
}
auto_auth {
method "kubernetes" {
mount_path = {{ .Values.vault.kubernetesAuthMount | quote }}
config = {
role = {{ .Values.vault.workerRole | quote }}
token_path = "/var/run/secrets/vault/token"
}
}
sink "file" {
config = {
path = "/vault/token/token"
mode = 0440
}
}
}

View file

@ -0,0 +1,80 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "remora.fullname" . }}-web
labels:
{{- include "remora.labels" . | nindent 4 }}
app.kubernetes.io/component: web
spec:
replicas: {{ .Values.web.replicas }}
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0
maxSurge: 1
selector:
matchLabels:
{{- include "remora.selectorLabels" . | nindent 6 }}
app.kubernetes.io/component: web
template:
metadata:
labels:
{{- include "remora.selectorLabels" . | nindent 8 }}
app.kubernetes.io/component: web
spec:
serviceAccountName: {{ include "remora.fullname" . }}-web
automountServiceAccountToken: false
enableServiceLinks: false
dnsPolicy: {{ .Values.podDnsPolicy }}
{{- with .Values.podDnsConfig }}
dnsConfig:
{{- toYaml . | nindent 8 }}
{{- end }}
terminationGracePeriodSeconds: 30
securityContext:
runAsNonRoot: true
runAsUser: 65532
runAsGroup: 65532
seccompProfile:
type: RuntimeDefault
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
{{- include "remora.selectorLabels" . | nindent 14 }}
app.kubernetes.io/component: web
containers:
- name: web
image: {{ include "remora.image" .Values.web.image | quote }}
imagePullPolicy: {{ .Values.web.image.pullPolicy }}
env:
- {name: REMORA_WEB_PUBLIC_ORIGIN, value: {{ required "web.publicOrigin is required" .Values.web.publicOrigin | quote }}}
- {name: REMORA_WEB_API_URL, value: {{ .Values.web.apiUrl | quote }}}
- {name: REMORA_WEB_EVENTS_URL, value: {{ .Values.web.eventsUrl | quote }}}
- {name: REMORA_WEB_OIDC_AUTHORITY, value: {{ required "oidc.issuer is required" .Values.oidc.issuer | quote }}}
- {name: REMORA_WEB_OIDC_CLIENT_ID, value: {{ .Values.web.oidcClientId | quote }}}
- {name: REMORA_WEB_OIDC_REDIRECT_URI, value: {{ .Values.web.publicOrigin | quote }}}
- {name: REMORA_WEB_OIDC_POST_LOGOUT_REDIRECT_URI, value: {{ .Values.web.publicOrigin | quote }}}
- {name: REMORA_WEB_OIDC_SCOPE, value: {{ .Values.web.oidcScope | quote }}}
- {name: REMORA_WEB_CSP_CONNECT_SRC, value: {{ .Values.web.cspConnectSrc | quote }}}
ports:
- {name: http, containerPort: 8080}
readinessProbe:
httpGet: {path: /health/ready, port: http}
periodSeconds: 5
livenessProbe:
httpGet: {path: /health/live, port: http}
periodSeconds: 10
resources:
{{- toYaml .Values.web.resources | nindent 12 }}
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: [ALL]

View file

@ -0,0 +1,176 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "remora.fullname" . }}-worker
labels:
{{- include "remora.labels" . | nindent 4 }}
app.kubernetes.io/component: worker
spec:
replicas: {{ .Values.worker.replicas }}
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0
maxSurge: 1
selector:
matchLabels:
{{- include "remora.selectorLabels" . | nindent 6 }}
app.kubernetes.io/component: worker
template:
metadata:
labels:
{{- include "remora.selectorLabels" . | nindent 8 }}
app.kubernetes.io/component: worker
annotations:
checksum/config: {{ include (print $.Template.BasePath "/config.yaml") . | sha256sum }}
checksum/vault: {{ include (print $.Template.BasePath "/vault-agent.yaml") . | sha256sum }}
spec:
serviceAccountName: {{ include "remora.fullname" . }}-worker
automountServiceAccountToken: false
enableServiceLinks: false
dnsPolicy: {{ .Values.podDnsPolicy }}
{{- with .Values.podDnsConfig }}
dnsConfig:
{{- toYaml . | nindent 8 }}
{{- end }}
terminationGracePeriodSeconds: 60
securityContext:
runAsNonRoot: true
runAsUser: 65532
runAsGroup: 65532
fsGroup: 65532
seccompProfile:
type: RuntimeDefault
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
{{- include "remora.selectorLabels" . | nindent 14 }}
app.kubernetes.io/component: worker
containers:
- name: worker
image: {{ include "remora.image" .Values.worker.image | quote }}
imagePullPolicy: {{ .Values.worker.image.pullPolicy }}
command: [sh, -ec]
args:
- until test -s /vault/token/token; do sleep 0.2; done; exec /usr/local/bin/remora-worker
envFrom:
- configMapRef:
name: {{ include "remora.fullname" . }}-config
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: {{ .Values.database.urlSecret.name }}
key: {{ .Values.database.urlSecret.key }}
- name: REMORA_COORDINATION_URL
valueFrom:
secretKeyRef:
name: {{ .Values.coordination.urlSecret.name }}
key: {{ .Values.coordination.urlSecret.key }}
- name: REMORA_TARGET_ACCESS_KEY
valueFrom:
secretKeyRef:
name: {{ .Values.target.credentialsSecret.name }}
key: {{ .Values.target.credentialsSecret.accessKey }}
- name: REMORA_TARGET_SECRET_KEY
valueFrom:
secretKeyRef:
name: {{ .Values.target.credentialsSecret.name }}
key: {{ .Values.target.credentialsSecret.secretKey }}
- {name: REMORA_DATABASE_MAX_CONNECTIONS, value: {{ .Values.database.maxWorkerConnections | quote }}}
- {name: REMORA_WORKER_LOCAL_ATTEMPT_LIMIT, value: {{ .Values.runtime.workerLocalAttemptLimit | quote }}}
- {name: REMORA_WORKER_API_URL, value: "https://{{ include "remora.fullname" . }}-worker:8443"}
- {name: REMORA_MANAGEMENT_BIND, value: "0.0.0.0:9090"}
- {name: REMORA_WORKER_TLS_CERT_FILE, value: /run/remora/tls/client.crt}
- {name: REMORA_WORKER_TLS_KEY_FILE, value: /run/remora/tls/client.key}
- {name: REMORA_WORKER_TLS_SERVER_CA_FILE, value: /run/remora/tls/ca.crt}
- {name: REMORA_VAULT_TOKEN_FILE, value: /vault/token/token}
- {name: TMPDIR, value: /run/remora/tmp}
ports:
- {name: management, containerPort: 9090}
readinessProbe:
httpGet: {path: /health/ready, port: management}
periodSeconds: 5
timeoutSeconds: 2
failureThreshold: 6
livenessProbe:
httpGet: {path: /health/live, port: management}
periodSeconds: 10
timeoutSeconds: 2
failureThreshold: 3
startupProbe:
httpGet: {path: /health/live, port: management}
periodSeconds: 2
failureThreshold: 90
resources:
{{- toYaml .Values.worker.resources | nindent 12 }}
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: [ALL]
volumeMounts:
- {name: worker-tls, mountPath: /run/remora/tls, readOnly: true}
- {name: temporary, mountPath: /run/remora/tmp}
- {name: vault-token, mountPath: /vault/token, readOnly: true}
- name: vault-agent
image: {{ .Values.vault.image | quote }}
imagePullPolicy: IfNotPresent
args: [agent, -config=/vault/config/config.hcl]
resources:
requests: {cpu: 10m, memory: 32Mi}
limits: {cpu: 250m, memory: 128Mi}
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: [ALL]
volumeMounts:
- {name: vault-config, mountPath: /vault/config, readOnly: true}
- {name: vault-token, mountPath: /vault/token}
- {name: vault-service-account, mountPath: /var/run/secrets/vault, readOnly: true}
{{- if .Values.vault.caSecret.name }}
- {name: vault-ca, mountPath: /vault/ca, readOnly: true}
{{- end }}
{{- if .Values.vault.caSecret.name }}
env:
- {name: VAULT_CACERT, value: /vault/ca/{{ .Values.vault.caSecret.key }}}
{{- end }}
volumes:
- name: worker-tls
secret:
secretName: {{ include "remora.fullname" . }}-worker-tls
items:
- {key: ca.crt, path: ca.crt}
- {key: client.crt, path: client.crt}
- {key: client.key, path: client.key}
- name: vault-config
configMap:
name: {{ include "remora.fullname" . }}-vault-worker
- name: vault-token
emptyDir:
medium: Memory
- name: temporary
emptyDir:
sizeLimit: 2Gi
- name: vault-service-account
projected:
sources:
- serviceAccountToken:
path: token
audience: {{ .Values.vault.audience | quote }}
expirationSeconds: 3600
{{- if .Values.vault.caSecret.name }}
- name: vault-ca
secret:
secretName: {{ .Values.vault.caSecret.name }}
items:
- {key: {{ .Values.vault.caSecret.key }}, path: {{ .Values.vault.caSecret.key }}}
{{- end }}

View file

@ -0,0 +1,99 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": ["api", "worker", "web", "database", "coordination", "oidc", "target", "vault"],
"properties": {
"api": {"$ref": "#/$defs/workload"},
"worker": {"$ref": "#/$defs/workload"},
"podDnsPolicy": {"enum": ["ClusterFirst", "ClusterFirstWithHostNet", "Default", "None"]},
"podDnsConfig": {"type": "object"},
"web": {
"allOf": [{"$ref": "#/$defs/workload"}],
"type": "object",
"required": ["publicOrigin", "oidcClientId"],
"properties": {
"publicOrigin": {"type": "string", "format": "uri"},
"oidcClientId": {"type": "string", "minLength": 1}
}
},
"database": {
"type": "object",
"required": ["urlSecret"],
"properties": {"urlSecret": {"$ref": "#/$defs/secretRef"}}
},
"coordination": {
"type": "object",
"required": ["urlSecret", "namespace", "globalAttemptLimit"],
"properties": {
"urlSecret": {"$ref": "#/$defs/secretRef"},
"namespace": {"type": "string", "minLength": 1},
"globalAttemptLimit": {"type": "integer", "minimum": 1}
}
},
"oidc": {
"type": "object",
"required": ["issuer", "audience", "resourceClient"],
"properties": {
"issuer": {"type": "string", "format": "uri"},
"audience": {"type": "string", "minLength": 1},
"resourceClient": {"type": "string", "minLength": 1}
}
},
"target": {
"type": "object",
"required": ["storeId", "endpoint", "bucket", "exclusiveWriter", "credentialsSecret"],
"properties": {
"storeId": {"type": "string", "minLength": 1},
"endpoint": {"type": "string", "format": "uri"},
"bucket": {"type": "string", "minLength": 1},
"exclusiveWriter": {"const": true},
"credentialsSecret": {
"type": "object",
"required": ["name", "accessKey", "secretKey"],
"properties": {
"name": {"type": "string", "minLength": 1},
"accessKey": {"type": "string", "minLength": 1},
"secretKey": {"type": "string", "minLength": 1}
}
}
}
},
"vault": {
"type": "object",
"required": ["address", "path", "apiRole", "workerRole"],
"properties": {
"address": {"type": "string", "format": "uri"},
"path": {"type": "string", "minLength": 1},
"apiRole": {"type": "string", "minLength": 1},
"workerRole": {"type": "string", "minLength": 1}
}
}
},
"$defs": {
"workload": {
"type": "object",
"required": ["replicas", "image"],
"properties": {
"replicas": {"type": "integer", "minimum": 1},
"image": {
"type": "object",
"required": ["repository", "tag", "pullPolicy"],
"properties": {
"repository": {"type": "string", "minLength": 1},
"tag": {"type": "string", "minLength": 1},
"digest": {"type": "string", "pattern": "^(|sha256:[0-9a-f]{64})$"},
"pullPolicy": {"enum": ["Always", "IfNotPresent", "Never"]}
}
}
}
},
"secretRef": {
"type": "object",
"required": ["name", "key"],
"properties": {
"name": {"type": "string", "minLength": 1},
"key": {"type": "string", "minLength": 1}
}
}
}
}

View file

@ -0,0 +1,156 @@
nameOverride: ""
fullnameOverride: ""
imagePullSecrets: []
podDnsPolicy: ClusterFirst
podDnsConfig: {}
api:
replicas: 2
image:
repository: remora-api
tag: 0.0.0
digest: ""
pullPolicy: IfNotPresent
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: "2"
memory: 1Gi
worker:
replicas: 2
image:
repository: remora-worker
tag: 0.0.0
digest: ""
pullPolicy: IfNotPresent
resources:
requests:
cpu: 250m
memory: 256Mi
limits:
cpu: "4"
memory: 2Gi
web:
replicas: 2
image:
repository: remora-web
tag: 0.0.0
digest: ""
pullPolicy: IfNotPresent
publicOrigin: https://remora.example.com
apiUrl: /v1/
eventsUrl: ""
oidcClientId: remora-web
oidcScope: openid profile
cspConnectSrc: "'self' https://identity.example.com"
resources:
requests:
cpu: 25m
memory: 64Mi
limits:
cpu: 500m
memory: 256Mi
database:
urlSecret:
name: remora-database
key: url
maxApiConnections: 32
maxWorkerConnections: 16
coordination:
urlSecret:
name: remora-coordination
key: url
namespace: "remora:"
globalAttemptLimit: 64
leaseTtlSeconds: 30
reserveTtlSeconds: 30
progressTtlSeconds: 60
oidc:
issuer: https://identity.example.com
audience: remora-project
resourceClient: remora-web
projectRoleClaims: urn:zitadel:iam:org:project:roles
target:
storeId: primary
endpoint: https://objects.example.com
bucket: remora-results
region: us-east-1
root: ""
defaultPrefix: ""
exclusiveWriter: true
credentialsSecret:
name: remora-target
accessKey: access-key
secretKey: secret-key
vault:
image: hashicorp/vault@sha256:db6a5f78749204d0a8d002be1cc6e9a2a6de53dffce41a894c4a06d309dca360
address: https://vault.example.com
path: remora
kubernetesAuthMount: auth/kubernetes
audience: vault
apiRole: remora-api
workerRole: remora-worker
caSecret:
name: ""
key: ca.crt
runtime:
reservedTargetSegments: _remora
admissionPolicyVersion: deployment-v1
maxAttempts: 3
workerLocalAttemptLimit: 4
recoveryIntervalSeconds: 2
eventAllowedOrigins: https://remora.example.com
eventRetentionSeconds: 3600
logFilter: info
outbound:
allowedSchemePorts: https:443
allowedCidrs: ""
maxRedirects: 5
ingress:
enabled: false
className: ""
annotations: {}
host: remora.example.com
tlsSecretName: ""
networkPolicy:
enabled: true
ingressNamespaceSelector: {}
ingressPodSelector: {}
monitoringNamespaceSelector:
matchLabels:
kubernetes.io/metadata.name: monitoring
monitoringPodSelector: {}
dnsPodLabels: []
dependencyPodLabels:
- fixture.remora.dev/component: postgres
- fixture.remora.dev/component: dragonfly
- fixture.remora.dev/component: vault
- fixture.remora.dev/component: zitadel
- fixture.remora.dev/component: versitygw
externalEgressCidrs: []
allowPublicSourceEgress: true
observability:
serviceMonitor:
enabled: false
interval: 30s
prometheusRule:
enabled: false
podDisruptionBudget:
enabled: true
minAvailable: 1