From afd92fd93828a4b560891bd680322b8493654df9 Mon Sep 17 00:00:00 2001 From: Sacha Al Himdani Date: Tue, 17 Feb 2026 15:46:27 +0100 Subject: [PATCH] Migrate from MinIO to SeaweedFS Signed-off-by: Sacha Al Himdani --- cfg/dev.yaml | 2 +- compose.prod.yaml | 34 +++-- compose.yaml | 28 ++-- compose/seaweedfs/s3.json | 21 +++ contrib/helm/README.md | 22 +-- contrib/helm/charts/probo/Chart.lock | 9 -- contrib/helm/charts/probo/Chart.yaml | 4 - contrib/helm/charts/probo/README.md | 2 +- contrib/helm/charts/probo/templates/NOTES.txt | 8 +- .../helm/charts/probo/templates/_helpers.tpl | 16 +-- .../charts/probo/templates/seaweedfs.yaml | 136 ++++++++++++++++++ .../probo/values-production.yaml.example | 6 +- contrib/helm/charts/probo/values.yaml | 45 +++--- e2e/console/testdata/config.yaml | 2 +- 14 files changed, 249 insertions(+), 86 deletions(-) create mode 100644 compose/seaweedfs/s3.json delete mode 100644 contrib/helm/charts/probo/Chart.lock create mode 100644 contrib/helm/charts/probo/templates/seaweedfs.yaml diff --git a/cfg/dev.yaml b/cfg/dev.yaml index cb3857ddc..08e9452be 100644 --- a/cfg/dev.yaml +++ b/cfg/dev.yaml @@ -72,7 +72,7 @@ probod: bucket: "probod" access-key-id: "probod" secret-access-key: "thisisnotasecret" - endpoint: "http://127.0.0.1:9000" + endpoint: "http://127.0.0.1:8333" notifications: mailer: diff --git a/compose.prod.yaml b/compose.prod.yaml index c67f543b6..826dd0b60 100644 --- a/compose.prod.yaml +++ b/compose.prod.yaml @@ -20,12 +20,12 @@ services: PG_DATABASE: "probod" PG_POOL_SIZE: "100" - # AWS/MinIO S3 storage + # S3-compatible storage (SeaweedFS) AWS_REGION: "us-east-1" AWS_BUCKET: "probod" AWS_ACCESS_KEY_ID: "probod" AWS_SECRET_ACCESS_KEY: "thisisnotasecret" - AWS_ENDPOINT: "http://minio:9000" + AWS_ENDPOINT: "http://seaweedfs:8333" # Observability - Metrics & Tracing METRICS_ADDR: "probo:8081" @@ -48,7 +48,7 @@ services: depends_on: postgres: condition: service_healthy - minio: + seaweedfs: condition: service_started chrome: condition: service_started @@ -75,19 +75,23 @@ services: timeout: 5s retries: 5 - minio: - image: "quay.io/minio/minio" - entrypoint: "sh" - command: | - -c 'mkdir -p /var/lib/minio/probod && minio server --json --console-address :9001 /var/lib/minio' + seaweedfs: + image: "chrislusf/seaweedfs:4.13" + command: > + server + -s3 + -s3.config=/etc/seaweedfs/s3.json + -s3.port=8333 + -volume.max=100 + -master.volumeSizeLimitMB=1024 + -dir=/data ports: - - "9000:9000" - - "9001:9001" + - "8333:8333" + - "9333:9333" + - "8888:8888" volumes: - - "minio-data:/var/lib/minio:rw" - environment: - MINIO_ROOT_USER: "probod" - MINIO_ROOT_PASSWORD: "thisisnotasecret" + - "./compose/seaweedfs/s3.json:/etc/seaweedfs/s3.json:ro" + - "seaweedfs-data:/data:rw" chrome: image: "chromedp/headless-shell:140.0.7259.2" @@ -109,4 +113,4 @@ services: volumes: probo-data: postgres-data: - minio-data: + seaweedfs-data: diff --git a/compose.yaml b/compose.yaml index 8b541c9f0..681d6e027 100644 --- a/compose.yaml +++ b/compose.yaml @@ -20,19 +20,23 @@ services: timeout: 5s retries: 5 - minio: - image: "quay.io/minio/minio" - entrypoint: "sh" - command: | - -c 'mkdir -p /var/lib/minio/probod && mkdir -p /var/lib/minio/probod-test && minio server --json --console-address :9001 /var/lib/minio' + seaweedfs: + image: "chrislusf/seaweedfs:latest" + command: > + server + -s3 + -s3.config=/etc/seaweedfs/s3.json + -s3.port=8333 + -volume.max=100 + -master.volumeSizeLimitMB=1024 + -dir=/data ports: - - "9000:9000" - - "9001:9001" + - "8333:8333" + - "9333:9333" + - "8888:8888" volumes: - - "minio-data:/var/lib/minio:rw" - environment: - MINIO_ROOT_USER: "probod" - MINIO_ROOT_PASSWORD: "thisisnotasecret" + - "./compose/seaweedfs/s3.json:/etc/seaweedfs/s3.json:ro" + - "seaweedfs-data:/data:rw" grafana: image: "grafana/grafana:latest" @@ -154,7 +158,7 @@ services: volumes: postgres-data: - minio-data: + seaweedfs-data: grafana-data: prometheus-data: tempo-data: diff --git a/compose/seaweedfs/s3.json b/compose/seaweedfs/s3.json new file mode 100644 index 000000000..37e41b809 --- /dev/null +++ b/compose/seaweedfs/s3.json @@ -0,0 +1,21 @@ +{ + "identities": [ + { + "name": "probod", + "credentials": [ + { + "accessKey": "probod", + "secretKey": "thisisnotasecret" + } + ], + "actions": [ + "Admin", + "Read", + "Write", + "List", + "Tagging", + "Lock" + ] + } + ] +} diff --git a/contrib/helm/README.md b/contrib/helm/README.md index 5b3b13189..b64717dae 100644 --- a/contrib/helm/README.md +++ b/contrib/helm/README.md @@ -14,7 +14,7 @@ Before deploying Probo, ensure you have: 1. **Kubernetes Cluster** - Version 1.23+ 2. **Helm** - Version 3.8+ 3. **PostgreSQL Database** - Managed service (AWS RDS, GCP Cloud SQL, Azure Database, etc.) -4. **S3 Storage** - AWS S3 or S3-compatible storage (GCS, DigitalOcean Spaces, MinIO, etc.) +4. **S3 Storage** - AWS S3 or S3-compatible storage (GCS, DigitalOcean Spaces, SeaweedFS, etc.) ## Install @@ -48,7 +48,7 @@ helm install my-probo ./charts/probo \ --set postgresql.auth.postgresUser="probod" \ --set postgresql.auth.postgresPassword="your-db-password" \ --set postgresql.auth.database="probod" \ - --set minio.enabled=true \ + --set seaweedfs.enabled=true \ --set s3.bucket="your-bucket-name" \ --set s3.accessKeyId="your-access-key" \ --set s3.secretAccessKey="your-secret-key" @@ -431,15 +431,15 @@ spec: | s3.region | string | `"us-east-1"` | S3 region | | s3.bucket | string | `"probod"` | S3 bucket name | | s3.endpoint | string | `""` | S3 endpoint (leave empty for AWS S3, set for S3-compatible storage) | -| s3.accessKeyId | string | `""` | **REQUIRED** (when minio.enabled=false) S3 access key | -| s3.secretAccessKey | string | `""` | **REQUIRED** (when minio.enabled=false) S3 secret key | -| minio.enabled | bool | `true` | Enable included MinIO for demo purposes (NOT for production) | -| minio.mode | string | `"standalone"` | MinIO mode | -| minio.persistence.enabled | bool | `false` | Enable MinIO persistence | -| minio.resources.requests.memory | string | `"128Mi"` | MinIO memory request | -| minio.primary.enabled | bool | `true` | Enable MinIO primary node | -| minio.auth.rootUser | string | `"probod"` | MinIO root username | -| minio.auth.rootPassword | string | `"probod"` | MinIO root password | +| s3.accessKeyId | string | `""` | **REQUIRED** (when seaweedfs.enabled=false) S3 access key | +| s3.secretAccessKey | string | `""` | **REQUIRED** (when seaweedfs.enabled=false) S3 secret key | +| seaweedfs.enabled | bool | `true` | Enable included SeaweedFS for demo purposes (NOT for production) | +| seaweedfs.image.repository | string | `"chrislusf/seaweedfs"` | SeaweedFS container image repository | +| seaweedfs.image.tag | string | `"latest"` | SeaweedFS image tag | +| seaweedfs.persistence.enabled | bool | `false` | Enable SeaweedFS persistence | +| seaweedfs.persistence.size | string | `"10Gi"` | SeaweedFS persistent volume size | +| seaweedfs.auth.accessKey | string | `"probod"` | SeaweedFS S3 access key | +| seaweedfs.auth.secretKey | string | `"probod"` | SeaweedFS S3 secret key | | chrome.enabled | bool | `true` | Deploy Chrome headless in the cluster for PDF generation | | chrome.replicaCount | int | `1` | Number of Chrome replicas | | chrome.image.repository | string | `"chromedp/headless-shell"` | Chrome container image repository | diff --git a/contrib/helm/charts/probo/Chart.lock b/contrib/helm/charts/probo/Chart.lock deleted file mode 100644 index f14358e8d..000000000 --- a/contrib/helm/charts/probo/Chart.lock +++ /dev/null @@ -1,9 +0,0 @@ -dependencies: -- name: minio - repository: https://charts.min.io/ - version: 5.4.0 -- name: kubernetes-ingress - repository: https://haproxytech.github.io/helm-charts - version: 1.45.1 -digest: sha256:86861ee988df3673c6306ff42719f09d12ce22e2d48a0db757ad4365d204bd85 -generated: "2025-10-31T10:17:36.108938+01:00" diff --git a/contrib/helm/charts/probo/Chart.yaml b/contrib/helm/charts/probo/Chart.yaml index fac1c847a..b2c75dae4 100644 --- a/contrib/helm/charts/probo/Chart.yaml +++ b/contrib/helm/charts/probo/Chart.yaml @@ -18,10 +18,6 @@ maintainers: icon: https://github.com/getprobo/probo/blob/main/.github/cover_v3.png dependencies: - - name: minio - condition: minio.enabled - repository: https://charts.min.io/ - version: 5.4.0 # Latest stable version - name: kubernetes-ingress alias: haproxy-ingress condition: haproxy-ingress.enabled diff --git a/contrib/helm/charts/probo/README.md b/contrib/helm/charts/probo/README.md index 992f4f973..826b5cc89 100644 --- a/contrib/helm/charts/probo/README.md +++ b/contrib/helm/charts/probo/README.md @@ -7,7 +7,7 @@ This Helm chart deploys Probo - an open-source SOC-2 compliance platform - on Ku - Kubernetes 1.23+ - Helm 3.8+ - External PostgreSQL database (AWS RDS, GCP Cloud SQL, Azure Database, etc.) -- S3 or S3-compatible object storage (AWS S3, GCS, DigitalOcean Spaces, MinIO, etc.) +- S3 or S3-compatible object storage (AWS S3, GCS, DigitalOcean Spaces, SeaweedFS, etc.) - OpenSSL installed (for generating secrets) ## Installing the Chart diff --git a/contrib/helm/charts/probo/templates/NOTES.txt b/contrib/helm/charts/probo/templates/NOTES.txt index cf5097c4f..e2d0e0c40 100644 --- a/contrib/helm/charts/probo/templates/NOTES.txt +++ b/contrib/helm/charts/probo/templates/NOTES.txt @@ -48,8 +48,8 @@ {{- end }} 💾 Object Storage: -{{- if .Values.minio.enabled }} - Using MinIO: {{ include "probo.fullname" . }}-minio:9000 +{{- if .Values.seaweedfs.enabled }} + Using SeaweedFS: {{ include "probo.fullname" . }}-seaweedfs:8333 Bucket: {{ .Values.s3.bucket }} {{- else }} Using S3 storage @@ -64,8 +64,8 @@ {{- if not .Values.ingress.tls }} ⚠️ TLS is not enabled! Enable it for production by configuring ingress.tls {{- end }} -{{- if or .Values.postgresql.enabled .Values.minio.enabled }} - ⚠️ Using internal PostgreSQL or MinIO - FOR TESTING ONLY! +{{- if or .Values.postgresql.enabled .Values.seaweedfs.enabled }} + ⚠️ Using internal PostgreSQL or SeaweedFS - FOR TESTING ONLY! For production, disable these and use external managed services. {{- end }} diff --git a/contrib/helm/charts/probo/templates/_helpers.tpl b/contrib/helm/charts/probo/templates/_helpers.tpl index f23ad5e9d..dfdfae705 100644 --- a/contrib/helm/charts/probo/templates/_helpers.tpl +++ b/contrib/helm/charts/probo/templates/_helpers.tpl @@ -118,8 +118,8 @@ PostgreSQL password (from subchart or external config) S3 endpoint */}} {{- define "probo.s3.endpoint" -}} -{{- if .Values.minio.enabled }} -{{- printf "http://%s-minio:9000" .Release.Name }} +{{- if .Values.seaweedfs.enabled }} +{{- printf "http://%s-seaweedfs:8333" (include "probo.fullname" .) }} {{- else }} {{- .Values.s3.endpoint }} {{- end }} @@ -129,10 +129,10 @@ S3 endpoint S3 access key */}} {{- define "probo.s3.accessKeyId" -}} -{{- if .Values.minio.enabled }} -{{- .Values.minio.auth.rootUser | required "minio.auth.rootUser is required when minio.enabled=true" }} +{{- if .Values.seaweedfs.enabled }} +{{- .Values.seaweedfs.auth.accessKey | required "seaweedfs.auth.accessKey is required when seaweedfs.enabled=true" }} {{- else }} -{{- .Values.s3.accessKeyId | required "s3.accessKeyId is required when minio.enabled=false" }} +{{- .Values.s3.accessKeyId | required "s3.accessKeyId is required when seaweedfs.enabled=false" }} {{- end }} {{- end }} @@ -140,10 +140,10 @@ S3 access key S3 secret key */}} {{- define "probo.s3.secretAccessKey" -}} -{{- if .Values.minio.enabled }} -{{- .Values.minio.auth.rootPassword | required "minio.auth.rootPassword is required when minio.enabled=true" }} +{{- if .Values.seaweedfs.enabled }} +{{- .Values.seaweedfs.auth.secretKey | required "seaweedfs.auth.secretKey is required when seaweedfs.enabled=true" }} {{- else }} -{{- .Values.s3.secretAccessKey | required "s3.secretAccessKey is required when minio.enabled=false" }} +{{- .Values.s3.secretAccessKey | required "s3.secretAccessKey is required when seaweedfs.enabled=false" }} {{- end }} {{- end }} diff --git a/contrib/helm/charts/probo/templates/seaweedfs.yaml b/contrib/helm/charts/probo/templates/seaweedfs.yaml new file mode 100644 index 000000000..3be5860ca --- /dev/null +++ b/contrib/helm/charts/probo/templates/seaweedfs.yaml @@ -0,0 +1,136 @@ +{{- if .Values.seaweedfs.enabled }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "probo.fullname" . }}-seaweedfs + labels: + {{- include "probo.labels" . | nindent 4 }} + app.kubernetes.io/component: seaweedfs +stringData: + s3.json: | + { + "identities": [ + { + "name": {{ .Values.seaweedfs.auth.accessKey | quote }}, + "credentials": [ + { + "accessKey": {{ .Values.seaweedfs.auth.accessKey | quote }}, + "secretKey": {{ .Values.seaweedfs.auth.secretKey | quote }} + } + ], + "actions": ["Admin", "Read", "Write", "List", "Tagging", "Lock"] + } + ] + } +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "probo.fullname" . }}-seaweedfs + labels: + {{- include "probo.labels" . | nindent 4 }} + app.kubernetes.io/component: seaweedfs +spec: + replicas: 1 + selector: + matchLabels: + {{- include "probo.selectorLabels" . | nindent 6 }} + app.kubernetes.io/component: seaweedfs + template: + metadata: + labels: + {{- include "probo.selectorLabels" . | nindent 8 }} + app.kubernetes.io/component: seaweedfs + spec: + containers: + - name: seaweedfs + image: "{{ .Values.seaweedfs.image.repository }}:{{ .Values.seaweedfs.image.tag }}" + imagePullPolicy: {{ .Values.seaweedfs.image.pullPolicy }} + args: + - server + - -s3 + - -s3.config=/etc/seaweedfs/s3.json + - -s3.port=8333 + - -volume.max=100 + - -master.volumeSizeLimitMB=1024 + - -dir=/data + ports: + - name: s3 + containerPort: 8333 + protocol: TCP + - name: master + containerPort: 9333 + protocol: TCP + - name: filer + containerPort: 8888 + protocol: TCP + livenessProbe: + httpGet: + path: /cluster/status + port: master + initialDelaySeconds: 15 + periodSeconds: 10 + readinessProbe: + httpGet: + path: /cluster/status + port: master + initialDelaySeconds: 5 + periodSeconds: 5 + resources: + {{- toYaml .Values.seaweedfs.resources | nindent 12 }} + volumeMounts: + - name: config + mountPath: /etc/seaweedfs + readOnly: true + - name: data + mountPath: /data + volumes: + - name: config + secret: + secretName: {{ include "probo.fullname" . }}-seaweedfs + - name: data + {{- if .Values.seaweedfs.persistence.enabled }} + persistentVolumeClaim: + claimName: {{ include "probo.fullname" . }}-seaweedfs + {{- else }} + emptyDir: {} + {{- end }} +{{- if .Values.seaweedfs.persistence.enabled }} +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "probo.fullname" . }}-seaweedfs + labels: + {{- include "probo.labels" . | nindent 4 }} + app.kubernetes.io/component: seaweedfs +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: {{ .Values.seaweedfs.persistence.size }} + {{- if .Values.seaweedfs.persistence.storageClass }} + storageClassName: {{ .Values.seaweedfs.persistence.storageClass | quote }} + {{- end }} +{{- end }} +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ include "probo.fullname" . }}-seaweedfs + labels: + {{- include "probo.labels" . | nindent 4 }} + app.kubernetes.io/component: seaweedfs +spec: + type: ClusterIP + ports: + - port: 8333 + targetPort: s3 + protocol: TCP + name: s3 + selector: + {{- include "probo.selectorLabels" . | nindent 4 }} + app.kubernetes.io/component: seaweedfs +{{- end }} diff --git a/contrib/helm/charts/probo/values-production.yaml.example b/contrib/helm/charts/probo/values-production.yaml.example index c2b45560d..194957847 100644 --- a/contrib/helm/charts/probo/values-production.yaml.example +++ b/contrib/helm/charts/probo/values-production.yaml.example @@ -215,9 +215,9 @@ postgresql: # ... # -----END CERTIFICATE----- -# MinIO configuration -# Disable included MinIO for production - use external S3 instead -minio: +# SeaweedFS configuration +# Disable included SeaweedFS for production - use external S3 instead +seaweedfs: enabled: false # S3 storage configuration (REQUIRED) diff --git a/contrib/helm/charts/probo/values.yaml b/contrib/helm/charts/probo/values.yaml index c496e51a7..9c5ddc9a3 100644 --- a/contrib/helm/charts/probo/values.yaml +++ b/contrib/helm/charts/probo/values.yaml @@ -307,37 +307,48 @@ postgresql: # S3 storage configuration # For production: Use external S3 (AWS S3, GCS, etc.) -# For testing: Enable the included MinIO chart +# For testing: Enable the included SeaweedFS chart s3: - # External S3 configuration (used when minio.enabled=false) + # External S3 configuration (used when seaweedfs.enabled=false) region: "us-east-1" bucket: "probod" # For AWS S3, leave endpoint empty - # For S3-compatible storage (MinIO, DigitalOcean Spaces, etc.), set the endpoint + # For S3-compatible storage (SeaweedFS, DigitalOcean Spaces, etc.), set the endpoint endpoint: "" - accessKeyId: "" # REQUIRED when minio.enabled=false: S3 access key - secretAccessKey: "" # REQUIRED when minio.enabled=false: S3 secret key + accessKeyId: "" # REQUIRED when seaweedfs.enabled=false: S3 access key + secretAccessKey: "" # REQUIRED when seaweedfs.enabled=false: S3 secret key # Use path-style URLs (required for Azure Blob Storage and some S3-compatible services) # When true: https://endpoint/bucket/key # When false (default): https://bucket.endpoint/key usePathStyle: false -# MinIO configuration -# Enable included MinIO for testing/development (NOT for production) -minio: - # -- enabled included Minio operator for s3 resource demo purposes +# SeaweedFS configuration +# Enable included SeaweedFS for testing/development (NOT for production) +seaweedfs: + # -- Enable included SeaweedFS for S3-compatible object storage enabled: true - mode: standalone + + image: + repository: chrislusf/seaweedfs + tag: "latest" + pullPolicy: IfNotPresent + + resources: + limits: + cpu: 1000m + memory: 1Gi + requests: + cpu: 100m + memory: 128Mi + persistence: enabled: false - resources: - requests: - memory: 128Mi - primary: - enabled: true + size: 10Gi + storageClass: "" + auth: - rootUser: probod - rootPassword: probod + accessKey: probod + secretKey: probod # Headless Chrome for PDF generation chrome: diff --git a/e2e/console/testdata/config.yaml b/e2e/console/testdata/config.yaml index 77e638b65..a92289933 100644 --- a/e2e/console/testdata/config.yaml +++ b/e2e/console/testdata/config.yaml @@ -48,7 +48,7 @@ probod: bucket: "probod-test" access-key-id: "probod" secret-access-key: "thisisnotasecret" - endpoint: "http://127.0.0.1:9000" + endpoint: "http://127.0.0.1:8333" notifications: mailer: