SeaweedFS (Bootstrap) Installation
SeaweedFS (Bootstrap) is the minimal instance required to break the circular dependency between storage and the database. It provides the initial S3 endpoint for CloudnativePG.
Installation
Section titled “Installation”-
Connect to
🟢 ManagementKubernetes Cluster ; _i.e w/ Kubeconfig File.Set Kubeconfig File
Ensure you have defined and loaded your Global Shell Variables as described in Shell Variables.
Terminal window source $HOME/opstella-installation/shell-values/kubernetes/management_cluster.vars.shTerminal window export KUBECONFIG="$HOME/opstella-installation/kubeconfigs/management_cluster.yaml" -
Export Required Shell Variables
You will need to export several variables that are used within the Helm values and Kubernetes manifest files.
Ensure
K8S_INGRESS_TLS_CERTIFICATE_SECRET_NAMEandK8S_STORAGE_CLASS_NAMEare defined as per the Shell Variables guide.Terminal window # S3 Credentialsexport SEAWEEDFS_BOOTSTRAP_S3_ADMIN_PASSWORD="CHANGEME"export SEAWEEDFS_BOOTSTRAP_S3_POSTGRES_BACKUP_PASSWORD="CHANGEME"# Domain Namesexport SEAWEEDFS_BOOTSTRAP_FILER_DOMAIN="seaweedfs-bootstrap.${BASE_DOMAIN}"export SEAWEEDFS_BOOTSTRAP_API_DOMAIN="seaweedfs-bootstrap-s3.${BASE_DOMAIN}"export SEAWEEDFS_BOOTSTRAP_ADMIN_DOMAIN="seaweedfs-bootstrap-admin.${BASE_DOMAIN}"# Admin Credentialsexport SEAWEEDFS_BOOTSTRAP_ADMIN_PASSWORD="CHANGEME" -
Create Namespace for SeaweedFS Bootstrap
Terminal window kubectl create namespace seaweedfs-bootstrap -
Create SeaweedFS S3 Configuration
Terminal window cat <<EOF > $HOME/opstella-installation/kubernetes-manifests/seaweedfs-bootstrap-s3.yaml---# S3 Credentials for SeaweedFS (Minimal/Bootstrap)apiVersion: v1kind: Secrettype: Opaquemetadata:name: seaweedfs-s3-secretnamespace: seaweedfs-bootstrapstringData:# ----------------------------------------------------------------------------# INPUT CONFIGURATION (YAML)# Run 'scripts/seaweedfs-utils.sh update <this_file>' to generate the JSON below.# ----------------------------------------------------------------------------s3_config_input: |users:- name: adminpassword: "${SEAWEEDFS_BOOTSTRAP_S3_ADMIN_PASSWORD}"actions:- Admin- Read- Write- List- Tagging- name: postgres-backuppassword: "${SEAWEEDFS_BOOTSTRAP_S3_POSTGRES_BACKUP_PASSWORD}"actions:- "Read:postgres-backups"- "Write:postgres-backups"- "List:postgres-backups"- "Tagging:postgres-backups"# ----------------------------------------------------------------------------# GENERATED CONFIGURATION (JSON) - DO NOT EDIT MANUALLY# ----------------------------------------------------------------------------seaweedfs_s3_config: ""EOFTerminal window # Generate proper JSON formatted config into the configuration file$HOME/opstella-installation/assets/scripts/seaweedfs-utils.sh update kubernetes-manifests/seaweedfs-bootstrap-s3.yamlApply the configuration:
Terminal window kubectl apply -f seaweedfs-bootstrap-s3.yaml -
Add SeaweedFS Helm Repository
Terminal window helm repo add seaweedfs https://seaweedfs.github.io/seaweedfs/helmhelm repo update -
Create SeaweedFS Bootstrap Helm Values
Terminal window cat <<EOF > $HOME/opstella-installation/helm-values/seaweedfs-bootstrap-values.yamlglobal:imageName: chrislusf/seaweedfsloggingLevel: 1enableSecurity: false# Disable global replication (Relies on Longhorn)enableReplication: false# "000" = No replication at SeaweedFS levelreplicationPlacement: "000"master:enabled: truereplicas: 1# Default volume size is 1GB (1000MB) in this chart, fitting ~10 volumes in 10GivolumeSizeLimitMB: 1000# Security ContextpodSecurityContext:enabled: truefsGroup: 1000containerSecurityContext:enabled: truerunAsUser: 1000runAsGroup: 1000runAsNonRoot: trueprivileged: falseallowPrivilegeEscalation: falseseccompProfile:type: RuntimeDefaultcapabilities:drop: ["ALL"]# Persistence for Master (Metadata/Sequence)data:type: "persistentVolumeClaim"size: "5Gi"storageClass: "${K8S_STORAGECLASS_NAME}"logs:type: "emptyDir"size: "1Gi"volume:enabled: truereplicas: 1ipBind: "0.0.0.0"minFreeSpacePercent: 5# Security ContextpodSecurityContext:enabled: truefsGroup: 1000containerSecurityContext:enabled: truerunAsUser: 1000runAsGroup: 1000runAsNonRoot: trueprivileged: falseallowPrivilegeEscalation: falseseccompProfile:type: RuntimeDefaultcapabilities:drop: ["ALL"]# Persistence for Volume (Data)dataDirs:- name: datatype: "persistentVolumeClaim"size: "10Gi"storageClass: "${K8S_STORAGECLASS_NAME}"maxVolumes: 10extraArgs: []filer:enabled: truereplicas: 1port: 9001 # MinIO Console compatibility# Security ContextpodSecurityContext:enabled: truefsGroup: 1000containerSecurityContext:enabled: truerunAsUser: 1000runAsGroup: 1000runAsNonRoot: trueprivileged: falseallowPrivilegeEscalation: falseseccompProfile:type: RuntimeDefaultcapabilities:drop: ["ALL"]# Persistence for Filer (Metadata)data:type: "persistentVolumeClaim"size: "5Gi"storageClass: "${K8S_STORAGECLASS_NAME}"logs:type: "emptyDir"size: "1Gi"# Ingress for Filer (SeaweedFS WebUI)ingress:enabled: falseclassName: nginxhost: "${SEAWEEDFS_BOOTSTRAP_FILER_DOMAIN}"path: "/"pathType: Prefixannotations:nginx.ingress.kubernetes.io/proxy-body-size: "0"ingress.kubernetes.io/proxy-body-size: "0"# Basic Auth Configuration# nginx.ingress.kubernetes.io/auth-type: basic# nginx.ingress.kubernetes.io/auth-secret: seaweedfs-filer-basic-auth# nginx.ingress.kubernetes.io/auth-realm: "Authentication Required"tls:- secretName: "${K8S_INGRESS_TLS_CERTIFICATE_SECRET_NAME}"hosts:- "${SEAWEEDFS_BOOTSTRAP_FILER_DOMAIN}"s3:enabled: truereplicas: 1port: 9000 # MinIO API compatibilityenableAuth: trueexistingConfigSecret: seaweedfs-s3-secret# Security ContextpodSecurityContext:enabled: truefsGroup: 1000containerSecurityContext:enabled: truerunAsUser: 1000runAsGroup: 1000runAsNonRoot: trueprivileged: falseallowPrivilegeEscalation: falseseccompProfile:type: RuntimeDefaultcapabilities:drop: ["ALL"]logs:type: "emptyDir"size: "1Gi"# Ingress for S3 APIingress:enabled: trueclassName: nginxhost: "${SEAWEEDFS_BOOTSTRAP_API_DOMAIN}"path: "/"pathType: Prefixannotations:nginx.ingress.kubernetes.io/proxy-body-size: "0"ingress.kubernetes.io/proxy-body-size: "0"tls:- secretName: "${K8S_INGRESS_TLS_CERTIFICATE_SECRET_NAME}"hosts:- "${SEAWEEDFS_BOOTSTRAP_API_DOMAIN}"admin:enabled: truesecret:adminUser: "admin"adminPassword: "${SEAWEEDFS_BOOTSTRAP_ADMIN_PASSWORD}"ingress:enabled: trueclassName: nginxhost: "${SEAWEEDFS_BOOTSTRAP_ADMIN_DOMAIN}"path: "/"pathType: Prefixtls:- secretName: "${K8S_INGRESS_TLS_CERTIFICATE_SECRET_NAME}"hosts:- "${SEAWEEDFS_BOOTSTRAP_ADMIN_DOMAIN}"# Security ContextpodSecurityContext:enabled: truefsGroup: 1000containerSecurityContext:enabled: truerunAsUser: 1000runAsGroup: 1000runAsNonRoot: trueprivileged: falseallowPrivilegeEscalation: falseseccompProfile:type: RuntimeDefaultcapabilities:drop: ["ALL"]EOF -
Install SeaweedFS Bootstrap Helm Release
Terminal window helm upgrade --install seaweedfs-bootstrap seaweedfs/seaweedfs \--version 4.0.407 \--namespace seaweedfs-bootstrap \-f $HOME/opstella-installation/helm-values/seaweedfs-bootstrap-values.yaml -
Provision SeaweedFS Buckets
Terminal window cat <<EOF > $HOME/opstella-installation/kubernetes-manifests/seaweedfs-bootstrap-job-provisioning.yamlapiVersion: batch/v1kind: Jobmetadata:name: seaweedfs-bootstrap-provisioningnamespace: seaweedfs-bootstraplabels:app.kubernetes.io/name: seaweedfs-bootstrapapp.kubernetes.io/component: provisioningspec:ttlSecondsAfterFinished: 300template:metadata:labels:app.kubernetes.io/name: seaweedfs-bootstrapapp.kubernetes.io/component: provisioningspec:restartPolicy: OnFailure# Security ContextsecurityContext:runAsNonRoot: truerunAsUser: 10000fsGroup: 10000seccompProfile:type: RuntimeDefaultcontainers:- name: provisionerimage: chrislusf/seaweedfs:4.07# Security ContextsecurityContext:allowPrivilegeEscalation: falsereadOnlyRootFilesystem: falserunAsNonRoot: truerunAsUser: 10000capabilities:drop: ["ALL"]# Resource Limitsresources:requests:memory: "64Mi"cpu: "100m"limits:memory: "128Mi"cpu: "200m"env:# Target SeaweedFS Master (Minimal/Bootstrap instance)- name: WEED_MASTERvalue: "seaweedfs-master:9333"# Target SeaweedFS Filer (Minimal/Bootstrap instance)- name: WEED_FILERvalue: "seaweedfs-filer:9001"# Comma-separated list of buckets to create- name: SEAWEEDFS_BUCKETSvalue: "postgres-backup"command:- "/bin/sh"- "-c"- |set -eecho "[INFO] Starting SeaweedFS Bootstrap Provisioning..."echo "[INFO] Target Master: \$WEED_MASTER"echo "[INFO] Target Filer: \$WEED_FILER"echo "[INFO] Buckets: \$SEAWEEDFS_BUCKETS"# 1. Wait for Master Leaderecho "[INFO] Waiting for SeaweedFS Master Leader..."START_TIME=\$(date +%s)TIMEOUT=300until echo cluster.ps | weed shell -master=\$WEED_MASTER > /dev/null 2>&1; doCURRENT_TIME=\$(date +%s)ELAPSED_TIME=\$((\$CURRENT_TIME - \$START_TIME))if [ \$ELAPSED_TIME -gt \$TIMEOUT ]; thenecho "[ERROR] Timeout waiting for SeaweedFS Master at \$WEED_MASTER"exit 1fiecho "[WAIT] Connecting to master... (\${ELAPSED_TIME}s)"sleep 5doneecho "[INFO] Connected to Master."# 2. Wait for Filer (required for bucket creation)echo "[INFO] Waiting for SeaweedFS Filer..."until echo fs.ls / | weed shell -master=\$WEED_MASTER -filer=\$WEED_FILER > /dev/null 2>&1; doCURRENT_TIME=\$(date +%s)ELAPSED_TIME=\$((\$CURRENT_TIME - \$START_TIME))if [ \$ELAPSED_TIME -gt \$TIMEOUT ]; thenecho "[ERROR] Timeout waiting for SeaweedFS Filer at \$WEED_FILER"exit 1fiecho "[WAIT] Connecting to filer... (\${ELAPSED_TIME}s)"sleep 5doneecho "[INFO] Connected to Filer."# 3. Create Buckets# Generate weed shell scriptSCRIPT_FILE="/tmp/provision.weed"echo "# Auto-generated provisioning script" > \$SCRIPT_FILE# POSIX compliant way to split comma-separated stringecho "\$SEAWEEDFS_BUCKETS" | tr ',' '\n' | while read -r bucket; do# Trim whitespacebucket=\$(echo "\$bucket" | sed 's/^[[:space:]]*//;s/[[:space:]]*\$//')if [ -n "\$bucket" ]; thenecho "s3.bucket.create -name \$bucket" >> \$SCRIPT_FILEecho "[INFO] Added bucket creation command for: \$bucket"fidoneecho "s3.bucket.list" >> \$SCRIPT_FILE# Executeecho "[INFO] Executing provisioning commands:"cat \$SCRIPT_FILEecho "----------------------------------------"# Run weed shell with better error handlingOUTPUT_FILE="/tmp/output.log"if ! cat \$SCRIPT_FILE | weed shell -master=\$WEED_MASTER -filer=\$WEED_FILER 2>&1 | tee \$OUTPUT_FILE; then# Check if error is just "bucket already exists"if grep -qi "already exist\|AlreadyExists" \$OUTPUT_FILE; thenecho "[WARN] Some buckets already exist (expected, continuing...)"elseecho "[ERROR] Bucket creation failed with unexpected error:"cat \$OUTPUT_FILEexit 1fifiecho "[INFO] Bucket creation completed."# 4. Verify bucket creationecho "[INFO] Verifying bucket creation..."BUCKET_LIST=\$(echo s3.bucket.list | weed shell -master=\$WEED_MASTER -filer=\$WEED_FILER 2>&1)echo "\$SEAWEEDFS_BUCKETS" | tr ',' '\n' | while read -r bucket; dobucket=\$(echo "\$bucket" | sed 's/^[[:space:]]*//;s/[[:space:]]*\$//')if [ -n "\$bucket" ]; thenif echo "\$BUCKET_LIST" | grep -q "\$bucket"; thenecho "[SUCCESS] Bucket '\$bucket' exists"elseecho "[ERROR] Bucket '\$bucket' NOT found!"echo "[DEBUG] Available buckets:"echo "\$BUCKET_LIST"exit 1fifidoneecho "[SUCCESS] All buckets verified successfully!"EOFApply the provisioning job:
Terminal window kubectl apply -f seaweedfs-bootstrap-job-provisioning.yaml
Post-Installation
Section titled “Post-Installation”-
Verify Pod Status
Terminal window kubectl get pods -n seaweedfs-bootstrap💡 All components should be
Running:NAME READY STATUS RESTARTS AGEseaweedfs-admin-0 1/1 Running 0 ...seaweedfs-filer-0 1/1 Running 0 ...seaweedfs-master-0 1/1 Running 0 ...seaweedfs-s3-XXXXXXXXXX-YYYYY 1/1 Running 0 ...seaweedfs-volume-0 1/1 Running 0 ...seaweedfs-bootstrap-provisioning 0/1 Completed 0 ... -
Verify Admin UI and Buckets
- Access the SeaweedFS Bootstrap Admin UI at
https://${SEAWEEDFS_BOOTSTRAP_ADMIN_DOMAIN}. - Login with the admin credentials defined in
${SEAWEEDFS_BOOTSTRAP_ADMIN_PASSWORD}. - Navigate to the Buckets tab and confirm that the
postgres-backupbucket has been successfully created.
- Access the SeaweedFS Bootstrap Admin UI at
Finished?
Use the below navigation to proceed