Setting Up Seafile on Kubernetes on Rocky Linux

Seafile 13 Steps

  • Label the green-hills node and say that it has local storage available:
kubectl label nodes green-hills local-storage-available=true
  • Create the namespace:
kubectl create ns seafile
  • Install EPEL:
dnf install -y epel-release
  • Install pwgen:
dnf install -y pwgen
  • To make the jwt private key, run:
pwgen -s 40 1
  • Create the secretMap:
kubectl create secret generic seafile-secret --namespace seafile \
--from-literal=JWT_PRIVATE_KEY='<your-jwt-private-key>' \
--from-literal=SEAFILE_MYSQL_DB_PASSWORD='<your-secure-password>' \
--from-literal=INIT_SEAFILE_ADMIN_PASSWORD='<your-secure-password>' \
--from-literal=INIT_SEAFILE_MYSQL_ROOT_PASSWORD='<your-secure-password>' \
--from-literal=REDIS_PASSWORD='<your-secure-password>' \
--from-literal=S3_SECRET_KEY='' \
--from-literal=S3_SSE_C_KEY=''
  • Create the seafile-k8s-yaml directory:
mkdir -p /opt/seafile-k8s-yaml
  • Create the mysql directory
mkdir -p /opt/mysql-k8s-yaml
  • Create the mysql-data directory to store all of the database data in /root:
mkdir -p /root/mysql-data
mkdir -p /root/redis-data
mkdir -p /root/elasticsearch-data
  • Pull down all of the required yaml files for seafile:
wget -P /opt/seafile-k8s-yaml https://manual.seafile.com/13.0/repo/k8s/cluster/seafile-backend-deployment.yaml
wget -P /opt/seafile-k8s-yaml https://manual.seafile.com/13.0/repo/k8s/cluster/seafile-persistentvolume.yaml
wget -P /opt/seafile-k8s-yaml https://manual.seafile.com/13.0/repo/k8s/cluster/seafile-persistentvolumeclaim.yaml
wget -P /opt/seafile-k8s-yaml https://manual.seafile.com/13.0/repo/k8s/cluster/seafile-service.yaml
wget -P /opt/seafile-k8s-yaml https://manual.seafile.com/13.0/repo/k8s/cluster/seafile-env.yaml
  • Set seafile-persistentvolume.yaml to the following:
cat << "EOF" | tee /opt/seafile-k8s-yaml/seafile-persistentvolume.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: seafile-data
spec:
  capacity:
    storage: 10.9Ti
  volumeMode: Filesystem
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: local-storage
  hostPath:
    path: /mnt/sonic
EOF
  • Set the seafile-persistentvolumeclaim.yaml as the following:
cat << "EOF" | tee /opt/seafile-k8s-yaml/seafile-persistentvolumeclaim.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: seafile-data
  namespace: seafile
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10.9Ti
  storageClassName: local-storage
EOF
  • Set seafile-backend-deployment.yaml as the following:
cat << "EOF" | tee /opt/seafile-k8s-yaml/seafile-backend-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: seafile-backend
  namespace: seafile
spec:
  replicas: 1
  selector:
    matchLabels:
      app: seafile-backend
  template:
    metadata:
      labels:
        app: seafile-backend
    spec:
      enableServiceLinks: false
      initContainers:  
        - name: set-ownership  
          image: busybox  
          command: ['sh', '-c', 'chown -R root:root /shared']  
          volumeMounts:  
          - name: seafile-data  
            mountPath: /shared
      containers:
        - name: seafile-backend
          image: seafileltd/seafile-pro-mc:13.0-latest
          env:
            - name: CLUSTER_SERVER
              value: "true"
            - name: CLUSTER_MODE
              value: "backend"
          envFrom:
            - configMapRef:
                name: seafile-env 
            - secretRef:
                name: seafile-secret
          volumeMounts:
            - name: seafile-data
              mountPath: /shared
      volumes:
        - name: seafile-data
          persistentVolumeClaim:
            claimName: seafile-data
      restartPolicy: Always
      imagePullSecrets:
        - name: regcred
EOF
  • The setup for seafile-env.yaml:
cat << "EOF" | tee /opt/seafile-k8s-yaml/seafile-env.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: seafile-env
  namespace: seafile
data:
  # for Seafile server
  TIME_ZONE: "Asia/Tokyo"
  SEAFILE_LOG_TO_STDOUT: "true"
  SITE_ROOT: "/"
  SEAFILE_SERVER_HOSTNAME: "192.168.1.100"
  SEAFILE_SERVER_PROTOCOL: "http"

  # for database
  SEAFILE_MYSQL_DB_HOST: "mysql"
  SEAFILE_MYSQL_DB_PORT: "3306"
  SEAFILE_MYSQL_DB_USER: "seafile"
  SEAFILE_MYSQL_DB_CCNET_DB_NAME: "ccnet_db"
  SEAFILE_MYSQL_DB_SEAFILE_DB_NAME: "seafile_db"
  SEAFILE_MYSQL_DB_SEAHUB_DB_NAME: "seahub_db"

  # for cached
  CACHE_PROVIDER: "redis" # or "memcached"

  ## for redis
  REDIS_HOST: "redis-leader"
  REDIS_PASSWORD: "<your-secure-password>"
  REDIS_PORT: "6379"

  ## for memcached
  MEMCACHED_HOST: "192.168.1.100"
  MEMCACHED_PORT: "11211"

  # for s3
  SEAF_SERVER_STORAGE_TYPE: "disk"
  S3_COMMIT_BUCKET: ""
  S3_FS_BUCKET: ""
  S3_BLOCK_BUCKET: ""
  S3_KEY_ID: ""
  S3_USE_V4_SIGNATURE: "true"
  S3_AWS_REGION: "us-east-1"
  S3_HOST: ""
  S3_USE_HTTPS: "true"
  S3_PATH_STYLE_REQUEST: "false"

  # for notification
  ENABLE_NOTIFICATION_SERVER: "false"
  NOTIFICATION_SERVER_URL: ""

  # for seadoc
  ENABLE_SEADOC: "true"
  SEADOC_SERVER_URL: "192.168.1.100" # only valid in ENABLE_SEADOC = true

  # initialization (only valid in first-time deployment and CLUSTER_INIT_MODE = true)
  CLUSTER_INIT_MODE: "true"

  ## for Seafile admin
  INIT_SEAFILE_ADMIN_EMAIL: "howard.a.vanderwal@protonmail.com"

  ## for cluster basic service
  CLUSTER_INIT_ES_HOST: "elasticsearch"
  CLUSTER_INIT_ES_PORT: "9200"

  # Seafile AI
  ENABLE_SEAFILE_AI: "yes"
  SEAFILE_AI_SERVER_URL: "192.168.1.100"

  # Matedata server
  MD_FILE_COUNT_LIMIT: "100000"
EOF
chown -R 1000:1000 /root/elasticsearch-data
restorecon -R -v /root/elasticsearch-data
  • seafile-service.yaml configuration:
cat << "EOF" | tee /opt/seafile-k8s-yaml/seafile-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: seafile
  namespace: seafile
spec:
  selector:
    app: seafile-frontend
  type: NodePort
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
      nodePort: 30007
EOF
  • Create the following mysql yaml files:
cat << "EOF" | tee /opt/mysql-k8s-yaml/mysql-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: mysql 
  namespace: seafile
spec:
  ports:
  - port: 3306
    protocol: TCP
    targetPort: 3306
  selector:
    app: mysql
EOF
cat << "EOF" | tee /opt/mysql-k8s-yaml/mysql-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql 
  namespace: seafile
spec:
  selector:
    matchLabels:
      app: mysql
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - image: mysql:9
        name: mysql
        env:
        - name: MYSQL_ROOT_PASSWORD
          value: password
        ports:
        - containerPort: 3306
          name: mysql
        volumeMounts:
        - name: mysql-persistent-storage
          mountPath: /var/lib/mysql
      volumes:
      - name: mysql-persistent-storage
        persistentVolumeClaim:
          claimName: mysql-pv-claim
EOF
cat << "EOF" | tee /opt/mysql-k8s-yaml/mysql-pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: mysql-pv-volume 
  namespace: seafile
  labels:
    type: local
spec:
  storageClassName: manual
  capacity:
    storage: 20Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: "/root/mysql-data"
EOF
cat << "EOF" | tee /opt/mysql-k8s-yaml/mysql-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-pv-claim
  namespace: seafile
spec:
  storageClassName: manual
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 20Gi
EOF
  • Set up Elasticsearch
# 1️⃣ Host directory for persistence
sudo mkdir -p /root/elasticsearch-data

# 2️⃣ Apply PV & PVC
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: PersistentVolume
metadata:
  name: es-pv-volume
  namespace: seafile
spec:
  storageClassName: manual
  capacity:
    storage: 30Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: "/root/elasticsearch-data"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: es-pv-claim
  namespace: seafile
spec:
  storageClassName: manual
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 30Gi
EOF

# 3️⃣ Deploy ES
kubectl apply -f - <<'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
  name: elasticsearch
  namespace: seafile
spec:
  replicas: 1
  selector:
    matchLabels:
      app: elasticsearch
  template:
    metadata:
      labels:
        app: elasticsearch
    spec:
      containers:
        - name: elasticsearch
          image: docker.elastic.co/elasticsearch/elasticsearch:7.17.10
          env:
            - name: discovery.type
              value: "single-node"
            - name: ES_JAVA_OPTS
              value: "-Xms512m -Xmx512m"
          ports:
            - containerPort: 9200
          volumeMounts:
            - name: es-data
              mountPath: /usr/share/elasticsearch/data
      volumes:
        - name: es-data
          persistentVolumeClaim:
            claimName: es-pv-claim
---
apiVersion: v1
kind: Service
metadata:
  name: elasticsearch
  namespace: seafile
spec:
  selector:
    app: elasticsearch
  ports:
    - protocol: TCP
      port: 9200
      targetPort: 9200
  type: ClusterIP
EOF

# 4️⃣ Update Seafile ConfigMap (replace host)
sed -i 's/CLUSTER_INIT_ES_HOST:.*/CLUSTER_INIT_ES_HOST: "elasticsearch"/' /opt/seafile-k8s-yaml/seafile-env.yaml
sed -i 's/CLUSTER_INIT_ES_PORT:.*/CLUSTER_INIT_ES_PORT: "9200"/' /opt/seafile-k8s-yaml/seafile-env.yaml
kubectl apply -f /opt/seafile-k8s-yaml/seafile-env.yaml
  • Deploy the mysql deployment:
kubectl apply -f /opt/mysql-k8s-yaml
  • Deploy the seafile deployment:
kubectl apply -f /opt/seafile-k8s-yaml/
  • Create the Redis directory:
mkdir -p /opt/redis-k8s-yaml
  • Deploy the Redis instance:
cat << "EOF" | tee /opt/redis-k8s-yaml/redis-leader-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis-leader
  namespace: seafile
  labels:
    app: redis
    role: leader
    tier: backend
spec:
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
        role: leader
        tier: backend
    spec:
      containers:
      - name: leader
        image: "registry.k8s.io/redis@sha256:cb111d1bd870a6a471385a4a69ad17469d326e9dd91e0e455350cacf36e1b3ee"
        args: ["redis-server", "--requirepass", "password"]
        ports:
        - containerPort: 6379
EOF
  • Create the Redis Leader Service:
cat << "EOF" | tee /opt/redis-k8s-yaml/redis-leader-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: redis-leader
  namespace: seafile
  labels:
    app: redis
    role: leader
    tier: backend
spec:
  type: ClusterIP
  ports:
    - protocol: TCP
      port: 6379
  selector:
    app: redis
    role: leader
    tier: backend
EOF
  • Apply the Redis yaml files:
kubectl apply -f /opt/redis-k8s-yaml/
  • Download the frontend seafile deployment yaml:
wget -P /opt/seafile-k8s-yaml https://manual.seafile.com/13.0/repo/k8s/cluster/seafile-frontend-deployment.yaml
  • Set the seafile-frontend-deployment.yaml to the following:
cat << "EOF" | tee /opt/seafile-k8s-yaml/seafile-frontend-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: seafile-frontend
  namespace: seafile
spec:
  selector:
    matchLabels:
      app: seafile-frontend
  template:
    metadata:
      labels:
        app: seafile-frontend
    spec:
      enableServiceLinks: false
      initContainers:  
        - name: set-ownership  
          image: busybox  
          command: ['sh', '-c', 'chown -R root:root /shared']  
          volumeMounts:  
          - name: seafile-data  
            mountPath: /shared
      containers:
        - name: seafile-frontend
          image: seafileltd/seafile-pro-mc:13.0-latest
          env:
            - name: CLUSTER_SERVER
              value: "true"
            - name: CLUSTER_MODE
              value: "frontend"
          envFrom:
            - configMapRef:
                name: seafile-env 
            - secretRef:
                name: seafile-secret
          ports:
            - containerPort: 80
          volumeMounts:
            - name: seafile-data
              mountPath: /shared
      volumes:
        - name: seafile-data
          persistentVolumeClaim:
            claimName: seafile-data
      restartPolicy: Always
      imagePullSecrets:
        - name: regcred
EOF
  • Set seafile-env to the following:
cat << "EOF" | tee /opt/seafile-k8s-yaml/seafile-env.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: seafile-env
  namespace: seafile
data:
  # for Seafile server
  TIME_ZONE: "Asia/Tokyo"
  SEAFILE_LOG_TO_STDOUT: "true"
  SITE_ROOT: "/"
  SEAFILE_SERVER_HOSTNAME: "192.168.1.100"
  SEAFILE_SERVER_PROTOCOL: "http"

  # for database
  SEAFILE_MYSQL_DB_HOST: "mysql"
  SEAFILE_MYSQL_DB_PORT: "3306"
  SEAFILE_MYSQL_DB_USER: "seafile"
  SEAFILE_MYSQL_DB_CCNET_DB_NAME: "ccnet_db"
  SEAFILE_MYSQL_DB_SEAFILE_DB_NAME: "seafile_db"
  SEAFILE_MYSQL_DB_SEAHUB_DB_NAME: "seahub_db"

  # for cached
  CACHE_PROVIDER: "redis" # or "memcached"

  ## for redis
  REDIS_HOST: "redis-leader"
  REDIS_PASSWORD: "<your-secure-password>"
  REDIS_PORT: "6379"

  ## for memcached
  MEMCACHED_HOST: "192.168.1.100"
  MEMCACHED_PORT: "11211"

  # for s3
  SEAF_SERVER_STORAGE_TYPE: "disk"
  S3_COMMIT_BUCKET: ""
  S3_FS_BUCKET: ""
  S3_BLOCK_BUCKET: ""
  S3_KEY_ID: ""
  S3_USE_V4_SIGNATURE: "true"
  S3_AWS_REGION: "us-east-1"
  S3_HOST: ""
  S3_USE_HTTPS: "true"
  S3_PATH_STYLE_REQUEST: "false"

  # for notification
  ENABLE_NOTIFICATION_SERVER: "false"
  NOTIFICATION_SERVER_URL: ""

  # for seadoc
  ENABLE_SEADOC: "true"
  SEADOC_SERVER_URL: "192.168.1.100" # only valid in ENABLE_SEADOC = true

  # initialization (only valid in first-time deployment and CLUSTER_INIT_MODE = true)
  CLUSTER_INIT_MODE: "false"

  ## for Seafile admin
  INIT_SEAFILE_ADMIN_EMAIL: "howard.a.vanderwal@protonmail.com"

  ## for cluster basic service
  CLUSTER_INIT_ES_HOST: "elasticsearch"
  CLUSTER_INIT_ES_PORT: "9200"

  # Seafile AI
  ENABLE_SEAFILE_AI: "yes"
  SEAFILE_AI_SERVER_URL: "192.168.1.100"

  # Matedata server
  MD_FILE_COUNT_LIMIT: "100000"
EOF
  • Apply all of the yaml files again:
kubectl apply -f /opt/seafile-k8s-yaml/
  • To transfer data, install nfs-utils on both the Worker Node and the Client:
sudo dnf install -y nfs-utils
  • If on ZFS and using datasets, do the following instead:
sudo zfs set sharenfs='rw=@192.168.1.102,no_root_squash' tails
  • Check to make sure NFS on ZFS was shared properly:
sudo zfs get sharenfs tails
  • ExportFS:
sudo exportfs -ra
  • Restart the service:
sudo systemctl restart nfs-server rpcbind
  • If on another filesystem or NOT using datasets with ZFS, use this method:

  • Add the following into /etc/exports for the Worker Node:

cat << "EOF" | tee /etc/exports
/mnt/tails 192.168.1.102(rw,sync,no_subtree_check,no_root_squash)
EOF
  • Enable the NFS server on the Worker Node:
sudo systemctl enable --now nfs-server
  • Ensure these services are allowed through the Worker Node’s firewall:
sudo firewall-cmd --zone=public --permanent --add-service=mountd
sudo firewall-cmd --zone=public --permanent --add-service=nfs
sudo firewall-cmd --zone=public --permanent --add-service=rpc-bind
sudo firewall-cmd --reload
  • On the Client machine, create the following directory:
sudo mkdir -p /nfs/green_hills-tails
  • Mount the NFS share on the Client:
sudo mount 192.168.1.100:/mnt/tails /nfs/green_hills-tails
  • Add the following into /etc/fstab on the Client:
cat << "EOF" | sudo tee -a /etc/fstab
192.168.1.100:/mnt/tails /nfs/green_hills-tails nfs auto,nofail,noatime,nolock,intr,tcp,actimeo=1800 0 0
EOF

Updated: