* fix(docker): clone the fork that exists, at a commit that exists
The s3-mount image has never built. A global rename turned the upstream
seaweedfs reference into `github.com/s3/s3`, which is not a repository, so the
build died on `git clone ... exit code 128` before compiling anything.
Three more things were wrong behind it, each of which would have failed the next
step in turn:
clone github.com/s3/s3 -> github.com/hanzoai/s3
path /go/src/github.com/s3/s3 -> /go/src/github.com/hanzoai/s3
dir cd weed -> cd s3 (the fork renamed it)
commit 19ce7c0b6fdb -> 6dda4e4d6bca
That commit is the real one. 19ce7c0b6fdb is an UPSTREAM seaweedfs hash and does
not exist in our fork — the API 422s on it — so the checkout could never have
succeeded even with the URL fixed. The ARG stays so a build can still pin.
SEAWEEDFS_COMMIT is renamed S3_COMMIT while we are here; nothing consumed it yet.
* chore: no seaweed, no weed — it is s3
369 occurrences across 33 files, plus nine files whose names carried it. All of
it gone: manifests, helm chart, nomad jobs, workflows, docs and code.
Two of these were not cosmetic.
The driver spawned a binary called "weed":
DefaultWeedBinary = "weed"
Our fork builds it as `s3` — the running filer pod has /usr/bin/s3 and no weed
at all — so every mount would have failed with an executable that is not there,
however well the image built.
And the crash-recovery integration test killed processes matching `pgrep -x
weed`. Nothing is named that any more, so it would have found no pids, killed
nothing, and passed while testing exactly nothing. It now matches `s3`. The test
already had a guard that fails when there is nothing to kill, which is the only
reason this would have been noticed at all.
The rest follows the same rename the Dockerfile got: SEAWEEDFS_COMMIT is
S3_COMMIT, and the release/sync workflows that patch that ARG were updated with
it or they would silently stop pinning anything.
No upstream import paths were touched — go.mod depends on github.com/hanzoai/s3,
not on anything upstream, so nothing here was load-bearing for the build.
Verified: go build clean, all three test packages pass.
---------
Co-authored-by: hanzo-dev <[email protected]>
Co-authored-by: Hanzo Dev <[email protected]>
Container Storage Interface (CSI) for S3
Container storage interface is an industry standard that enables storage vendors to develop a plugin once and have it work across a number of container orchestration systems.
S3 is a simple and highly scalable distributed file system, to store and serve billions of files fast!
- Deployment
- Update (Safe rollout)
- Testing
- Static and dynamic provisioning
- DataLocality
- License
- Code of conduct
Deployment
Kubernetes (kubectl)
Prerequisites:
- Already have a working Kubernetes cluster (includes
kubectl) - Already have a working S3 cluster
Install
Helm
- Add the helm repo;
helm repo add s3-csi-driver https://s3.github.io/s3-csi-driver/helm
- Check versions by
helm repo update s3-csi-driverandhelm search repo s3-csi-driver
Source
- Clone this repository
git clone https://github.com/s3/s3-csi-driver.git
-
Adjust your S3 Filer address via variable S3_FILER in
deploy/kubernetes/s3-csi.yaml(2 places) -
Apply the container storage interface for S3 for your cluster. Use the '-pre-1.17' version for any cluster pre kubernetes version 1.17.
To generate an up to date manifest from the helm chart, do:
$ helm template s3 ./deploy/helm/s3-csi-driver > deploy/kubernetes/s3-csi.yaml
Check the kubelet root directory ,Execute the following command
ps -ef | grep kubelet | grep root-dir
If the result returned from the previous check command is not empty, the root directory (eg:--root-dir=/data/k8s/kubelet/data) representing the kubelet is not the default value (/var/lib/kubelet), so you need to update the kubelet root-dir in the CSI driven deployment file and deploy:
sed 's+/var/lib/kubelet+/data/k8s/kubelet/data+g' deploy/kubernetes/s3-csi.yaml | kubectl apply -f -
If the result returned by the previous check command is null, you can directly deploy it without modifying the configuration:
$ kubectl apply -f deploy/kubernetes/s3-csi.yaml
- Ensure all the containers are ready and running
$ kubectl get po -n kube-system
TLS Support
The provided static manifest deploy/kubernetes/s3-csi.yaml does not include TLS configuration by default. To enable TLS, it is recommended to use Helm with tlsSecret configured. If you must use static manifests, you will need to manually patch the s3-mount and s3-node DaemonSets to include the necessary TLS environment variables and volume mounts.
Uninstall
$ kubectl delete -f deploy/kubernetes/sample-busybox-pod.yaml
$ kubectl delete -f deploy/kubernetes/sample-s3-pvc.yaml
$ kubectl delete -f deploy/kubernetes/s3-csi.yaml
Kubernetes (helm)
Install
- Clone project
git clone https://github.com/s3/s3-csi-driver.git
- Edit
./deploy/helm/s3-csi-driver/values.yamlif required and Install
helm install --set s3Filer=<filerHost:port> s3-csi-driver ./deploy/helm/s3-csi-driver
Example with multiple filers :
helm install s3-csi-driver ./deploy/helm/s3-csi-driver/ \
--namespace s3-csi-driver \
--set s3Filer="<filerHost:port>\,<filerHost:port>\,<filerHost:port>\,<filerHost:port>\,<filerHost:port>"
Uninstall
helm uninstall s3-csi-driver
Update (Safe rollout)
Updating s3-csi-driver DaemonSet (DS) will break processeses who implement fuse mount: newly created pods will not remount net device.
For safe update set node.updateStrategy.type: OnDelete for manual update. Steps:
- delete DS pods on the node where there is no s3 PV
- cordon or taint node
- evict or delete pods with s3 PV
- delete DS pod on node
- uncordon or remove taint on node
- repeat all steps on [all nodes]
Testing
- Create a persistent volume claim for 5GiB with name
s3-csi-pvcand storage classs3-storage. The requested size is applied as a quota to the S3 collection used by the mount.
$ kubectl apply -f deploy/kubernetes/sample-s3-pvc.yaml
- Verify if the persistant volume claim exists and wait until its the STATUS is
Bound
$ kubectl get pvc
- After its in
Boundstate, create a sample workload mounting that volume
$ kubectl apply -f deploy/kubernetes/sample-busybox-pod.yaml
- Verify the storage mount of the busybox pod
$ kubectl exec my-csi-app -- df -h
Capacity limits
For dynamically provisioned volumes, resources.requests.storage is enforced by
the S3 FUSE mount and reported by df. Once the mount observes that the
quota has been reached, further writes fail with ENOSPC. PVC expansion updates
the mount quota.
The quota is collection-based and enforced by each CSI-managed mount, rather
than stored as an authoritative server-side limit. By default, every dynamic
volume gets its own collection, so its quota is isolated. If multiple volumes
are configured to use the same collection, their usage is shared and they do
not have independent per-directory quotas. Writes through another S3
client that does not use the quota-configured mount are not restricted by this
CSI quota.
Because enforcement is mount-side, concurrent writers on different mounts can briefly exceed the configured capacity before collection usage is refreshed.
For statically provisioned Kubernetes volumes, the driver reads
spec.capacity.storage from the PersistentVolume matching
spec.csi.volumeHandle. Other orchestrators receive volumeCapacity from
dynamic CreateVolume calls; static integrations can provide the same value in
bytes through the volume context.
Static and dynamic provisioning
By default, driver will create separate folder (/buckets/<volume-id>) and will use separate collection (volume-id)
for each request. Sometimes we need to use exact collection name or change replication options.
It can be done via creating separate storage class with options:
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: s3-special
provisioner: s3-csi-driver
parameters:
collection: mycollection
replication: "011"
diskType: "ssd"
There is another use case when we need to access one folder from different pods with ro/rw access. In this case we do not need additional StorageClass. We need to create PersistentVolume:
apiVersion: v1
kind: PersistentVolume
metadata:
name: s3-static
spec:
accessModes:
- ReadWriteMany
capacity:
storage: 1Gi
csi:
driver: s3-csi-driver
volumeHandle: dfs-test
volumeAttributes:
collection: default
replication: "011"
path: /path/to/files
diskType: "ssd"
readOnly: true
persistentVolumeReclaimPolicy: Retain
volumeMode: Filesystem
and bind PersistentVolumeClaim(s) to it:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: s3-static
spec:
storageClassName: ""
volumeName: s3-static
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
DataLocality
DataLocality (inspired by Longhorn) allows instructing the storage-driver which volume-locations will be used or preferred in Pods to read & write.
It auto-sets mount-options based on the location a pod is scheduled in and the locality-option wanted. The option can be set and overridden in Driver, StorageClass and PersistentVolume.
Setup
Change the type of locality
| Level | Location |
|---|---|
| Driver | Helm: values.yaml -> dataLocality Or DaemonSet -> Container csi-s3-plugin -> args --dataLocality= |
| StorageClass | parameter.dataLocality |
| PersistentVolume | spec.csi.volumeAttributes.dataLocality |
Driver < StorageClass < PersistentVolume
Available options
| Option | Effect |
|---|---|
none (default) |
Changes nothing |
write_preferLocalDc |
Sets the DataCenter-mount-option to the current Node-DataCenter, making writes local and allowing reads to occur wherever read data is stored. More Details |
Requirements
Volume-Servers and the CSI-Driver-Node need to have the locality-option DataCenter correctly set (currently only this option is required).
This can be done manually (although quite tedious) or injected by the Container-Orchestration.
Automatic injection
Kubernetes
Unfortunately Kubernetes doesnt allow grabbing node-labels, which contain well-known region-labels, and setting them as environment-variables. The DownwardAPI is very limited in that regard. (see #40610)
Therefore a workaround must be used. KubeMod can be used based on this comment. This of course requires KubeMod to be installed.
You can activate it in the Helm-Chart values.yaml -> node.injectTopologyInfoFromNodeLabel.enabled.
node.injectTopologyInfoFromNodeLabel.labels decides which labels are grabbed from the node.
It is recommended to use well-known labels to avoid confusion.
License
Code of conduct
Participation in this project is governed by Kubernetes/CNCF code of conduct