add rustfs helm chart files (#747)

* add rustfs helm chart files

* update readme file with helm chart

* delete helm chart license file

* fix typo in readme file
This commit is contained in:
majinghe
2025-10-29 12:23:21 +08:00
committed by GitHub
parent d2ced233e5
commit 64ba52bc1e
18 changed files with 700 additions and 9 deletions

75
helm/README.md Normal file
View File

@@ -0,0 +1,75 @@
# rustfs-helm
You can use this helm chart to deploy rustfs on k8s cluster.
## Parameters Overview
| parameter | description | default value |
| -- | -- | -- |
| replicaCount | Number of cluster nodes. | Default is `4`. |
| image.repository | docker image repository. | rustfs/rustfs. |
| image.tag | the tag for rustfs docker image | "latest" |
| secret.rustfs.access_key | RustFS Access Key ID | `rustfsadmin` |
| secret.rustfs.secret_key | RustFS Secret Key ID | `rustfsadmin` |
| storageclass.name | The name for StorageClass. | `local-path` |
| ingress.className | Specify the ingress class, traefik or nginx. | `nginx` |
**NOTE**: [`local-path`](https://github.com/rancher/local-path-provisioner) is used by k3s. If you want to use `local-path`, running the command,
```
kubectl apply -f https://raw.githubusercontent.com/rancher/local-path-provisioner/v0.0.32/deploy/local-path-storage.yaml
```
## Requirement
* Helm V3
* RustFS >= 1.0.0-alpha.66
## Installation
If your ingress class is `traefik`, running the command:
```
helm install rustfs -n rustfs --create-namespace ./ --set ingress.className="traefik"
```
If your ingress class is `nginx`, running the command:
```
helm install rustfs -n rustfs --create-namespace ./ --set ingress.className="nginx"
```
> `traefik` or `nginx`, the different is the session sticky/affinity annotations.
Check the pod status
```
kubectl -n rustfs get pods -w
NAME READY STATUS RESTARTS AGE
rustfs-0 1/1 Running 0 2m27s
rustfs-1 1/1 Running 0 2m27s
rustfs-2 1/1 Running 0 2m27s
rustfs-3 1/1 Running 0 2m27s
```
Check the ingress status
```
kubectl -n rustfs get ing
NAME CLASS HOSTS ADDRESS PORTS AGE
rustfs nginx xmg.rustfs.com 10.43.237.152 80, 443 29m
```
Access the rustfs cluster via `https://xmg.rustfs.com` with the default username and password `rustfsadmin`.
> Replace the `xmg.rustfs.com` with your own domain as well as the certificates.
## Uninstall
Uninstalling the rustfs installation with command,
```
helm uninstall rustfs -n rustfs
```

23
helm/rustfs/.helmignore Normal file
View File

@@ -0,0 +1,23 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*.orig
*~
# Various IDEs
.project
.idea/
*.tmproj
.vscode/

24
helm/rustfs/Chart.yaml Normal file
View File

@@ -0,0 +1,24 @@
apiVersion: v2
name: rustfs
description: RustFS helm chart to deploy RustFS on kubernetes cluster.
# A chart can be either an 'application' or a 'library' chart.
#
# Application charts are a collection of templates that can be packaged into versioned archives
# to be deployed.
#
# Library charts provide useful utilities or functions for the chart developer. They're included as
# a dependency of application charts to inject those utilities and functions into the rendering
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
type: application
# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 1.0.0
# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using.
# It is recommended to use it with quotes.
appVersion: "1.16.0"

View File

@@ -0,0 +1,22 @@
1. Get the application URL by running these commands:
{{- if .Values.ingress.enabled }}
{{- range $host := .Values.ingress.hosts }}
{{- range .paths }}
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }}
{{- end }}
{{- end }}
{{- else if contains "NodePort" .Values.service.type }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "rustfs.fullname" . }})
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
{{- else if contains "LoadBalancer" .Values.service.type }}
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
You can watch its status by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "rustfs.fullname" . }}'
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "rustfs.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
echo http://$SERVICE_IP:{{ .Values.service.port }}
{{- else if contains "ClusterIP" .Values.service.type }}
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "rustfs.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
{{- end }}

View File

@@ -0,0 +1,62 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "rustfs.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "rustfs.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "rustfs.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "rustfs.labels" -}}
helm.sh/chart: {{ include "rustfs.chart" . }}
{{ include "rustfs.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "rustfs.selectorLabels" -}}
app.kubernetes.io/name: {{ include "rustfs.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
Create the name of the service account to use
*/}}
{{- define "rustfs.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "rustfs.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,17 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "rustfs.fullname" . }}-config
data:
RUSTFS_ADDRESS: {{ .Values.config.rustfs.address | quote }}
RUSTFS_CONSOLE_ADDRESS: {{ .Values.config.rustfs.console_address | quote }}
RUSTFS_OBS_LOG_DIRECTORY: {{ .Values.config.rustfs.obs_log_directory | quote }}
RUSTFS_SINKS_FILE_PATH: {{ .Values.config.rustfs.sinks_file_path | quote }}
RUSTFS_CONSOLE_ENABLE: {{ .Values.config.rustfs.console_enable | quote }}
RUSTFS_LOG_LEVEL: {{ .Values.config.rustfs.log_level | quote }}
{{- if eq (int .Values.replicaCount) 4 }}
RUSTFS_VOLUMES: "http://rustfs-{0...3}.rustfs-headless.rustfs.svc.cluster.local:9000/data/rustfs{0...3}"
{{- else if eq (int .Values.replicaCount) 16 }}
RUSTFS_VOLUMES: "http://rustfs-{0...15}.rustfs-headless.rustfs.svc.cluster.local:9000/data"
{{- end }}
RUSTFS_OBS_ENVIRONMENT: "develop"

View File

@@ -0,0 +1,45 @@
{{- if .Values.ingress.enabled -}}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ include "rustfs.fullname" . }}
labels:
{{- include "rustfs.labels" . | nindent 4 }}
{{- if eq .Values.ingress.className "nginx" }}
{{- with .Values.ingress.nginxAnnotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- end }}
spec:
{{- with .Values.ingress.className }}
ingressClassName: {{ . }}
{{- end }}
{{- if .Values.ingress.tls }}
tls:
{{- range .Values.ingress.tls }}
- hosts:
{{- range .hosts }}
- {{ . | quote }}
{{- end }}
secretName: {{ .secretName }}
{{- end }}
{{- end }}
rules:
{{- range .Values.ingress.hosts }}
- host: {{ .host | quote }}
http:
paths:
{{- range .paths }}
- path: {{ .path }}
{{- with .pathType }}
pathType: {{ . }}
{{- end }}
backend:
service:
name: {{ include "rustfs.fullname" $ }}-svc
port:
name: console
{{- end }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,10 @@
{{- if .Values.ingress.enabled }}
apiVersion: v1
kind: Secret
metadata:
name: {{ include "rustfs.fullname" . }}-tls
type: Opaque
data:
tls.crt : {{ .Files.Get "tls/tls.crt" | b64enc | quote }}
tls.key : {{ .Files.Get "tls/tls.key" | b64enc | quote }}
{{- end }}

View File

@@ -0,0 +1,9 @@
apiVersion: v1
kind: Secret
metadata:
name: {{ include "rustfs.fullname" . }}-secret
type: Opaque
data:
RUSTFS_ACCESS_KEY: {{ .Values.secret.rustfs.access_key | b64enc | quote }}
RUSTFS_SECRET_KEY: {{ .Values.secret.rustfs.secret_key | b64enc | quote }}

View File

@@ -0,0 +1,59 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "rustfs.fullname" . }}-headless
labels:
{{- include "rustfs.labels" . | nindent 4 }}
spec:
clusterIP: None
publishNotReadyAddresses: true
ports:
{{- if .Values.ingress.enabled }}
- port: 9000
{{- else }}
- port: {{ .Values.service.ep_port }}
{{- end }}
targetPort: {{ .Values.service.ep_port }}
protocol: TCP
name: endpoint
- port: {{ .Values.service.console_port }}
targetPort: 9001
protocol: TCP
name: console
selector:
app: {{ include "rustfs.name" . }}
---
apiVersion: v1
kind: Service
metadata:
name: {{ include "rustfs.fullname" . }}-svc
{{- if eq .Values.ingress.className "traefik" }}
{{- with .Values.ingress.traefikAnnotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- end }}
labels:
{{- include "rustfs.labels" . | nindent 4 }}
spec:
{{- if .Values.ingress.enabled }}
type: ClusterIP
{{- else }}
type: NodePort
sessionAffinity: ClientIP
sessionAffinityConfig:
clientIP:
timeoutSeconds: 10800
{{- end }}
ports:
- port: {{ .Values.service.ep_port }}
targetPort: {{ .Values.service.ep_port }}
protocol: TCP
name: endpoint
- port: {{ .Values.service.console_port }}
targetPort: {{ .Values.service.console_port }}
protocol: TCP
name: console
selector:
app: {{ include "rustfs.name" . }}

View File

@@ -0,0 +1,13 @@
{{- if .Values.serviceAccount.create -}}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "rustfs.serviceAccountName" . }}
labels:
{{- include "rustfs.labels" . | nindent 4 }}
{{- with .Values.serviceAccount.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
automountServiceAccountToken: {{ .Values.serviceAccount.automount }}
{{- end }}

View File

@@ -0,0 +1,132 @@
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: {{ include "rustfs.fullname" . }}
spec:
serviceName: {{ include "rustfs.fullname" . }}-headless
replicas: {{ .Values.replicaCount }}
podManagementPolicy: Parallel
selector:
matchLabels:
app: {{ include "rustfs.name" . }}
template:
metadata:
labels:
app: {{ include "rustfs.name" . }}
spec:
initContainers:
- name: init-step
image: busybox
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:
- name: REPLICA_COUNT
value: "{{ .Values.replicaCount }}"
command:
- sh
- -c
- |
if [ "$REPLICA_COUNT" -eq 4 ]; then
for i in $(seq 0 $(($REPLICA_COUNT - 1))); do
mkdir -p /data/rustfs$i
done;
elif [ "$REPLICA_COUNT" -eq 16 ]; then
mkdir -p /data
fi
chown -R 1000:1000 /data
chown -R 1000:1000 /logs
volumeMounts:
{{- if eq (int .Values.replicaCount) 4 }}
{{- range $i := until (int .Values.replicaCount) }}
- name: data-rustfs-{{ $i }}
mountPath: /data/rustfs{{ $i }}
{{- end }}
{{- else if eq (int .Values.replicaCount) 16 }}
- name: data
mountPath: /data
{{- end }}
- name: logs
mountPath: /logs
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
command: ["/usr/bin/rustfs"]
imagePullPolicy: {{ .Values.image.pullPolicy }}
securityContext:
runAsUser: 1000
ports:
- containerPort: {{ .Values.service.ep_port }}
name: endpoint
- containerPort: {{ .Values.service.console_port }}
name: console
env:
- name: REPLICA_COUNT
value: "{{ .Values.replicaCount }}"
envFrom:
- configMapRef:
name: {{ include "rustfs.fullname" . }}-config
- secretRef:
name: {{ include "rustfs.fullname" . }}-secret
resources:
requests:
memory: {{ .Values.resources.requests.memory }}
cpu: {{ .Values.resources.requests.cpu }}
limits:
memory: {{ .Values.resources.limits.memory }}
cpu: {{ .Values.resources.limits.cpu }}
livenessProbe:
httpGet:
path: /health
port: 9000
initialDelaySeconds: 10
periodSeconds: 5
timeoutSeconds: 3
successThreshold: 1
failureThreshold: 3
readinessProbe:
httpGet:
path: /health
port: 9000
exec:
initialDelaySeconds: 30
periodSeconds: 5
timeoutSeconds: 3
successThreshold: 1
failureThreshold: 3
volumeMounts:
- name: logs
mountPath: /logs
{{- if eq (int .Values.replicaCount) 4 }}
{{- range $i := until (int .Values.replicaCount) }}
- name: data-rustfs-{{ $i }}
mountPath: /data/rustfs{{ $i }}
{{- end }}
{{- else if eq (int .Values.replicaCount) 16 }}
- name: data
mountPath: /data
{{- end }}
volumes:
- name: logs
emptyDir: {}
volumeClaimTemplates:
{{- if eq (int .Values.replicaCount) 4 }}
{{- range $i := until (int .Values.replicaCount) }}
- metadata:
name: data-rustfs-{{ $i }}
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: {{ $.Values.storageclass.name }}
resources:
requests:
storage: {{ $.Values.storageclass.size}}
{{- end }}
{{- else if eq (int .Values.replicaCount) 16 }}
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: {{ $.Values.storageclass.name }}
resources:
requests:
storage: {{ $.Values.storageclass.size}}
{{- end }}

View File

@@ -0,0 +1,15 @@
apiVersion: v1
kind: Pod
metadata:
name: "{{ include "rustfs.fullname" . }}-test-connection"
labels:
{{- include "rustfs.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": test
spec:
containers:
- name: wget
image: busybox
command: ['wget']
args: ['{{ include "rustfs.fullname" . }}:{{ .Values.service.port }}']
restartPolicy: Never

3
helm/rustfs/tls/tls.crt Normal file
View File

@@ -0,0 +1,3 @@
-----BEGIN CERTIFICATE-----
Please input your cert file content.
-----END CERTIFICATE-----

3
helm/rustfs/tls/tls.key Normal file
View File

@@ -0,0 +1,3 @@
-----BEGIN PRIVATE KEY-----
Please input your key file content
-----END PRIVATE KEY-----

139
helm/rustfs/values.yaml Normal file
View File

@@ -0,0 +1,139 @@
# Default values for rustfs.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
# This will set the replicaset count more information can be found here: https://kubernetes.io/docs/concepts/workloads/controllers/replicaset/
replicaCount: 4
# This sets the container image more information can be found here: https://kubernetes.io/docs/concepts/containers/images/
image:
repository: rustfs/rustfs
# This sets the pull policy for images.
pullPolicy: Always
# Overrides the image tag whose default is the chart appVersion.
tag: "1.0.0-alpha.66"
# This is for the secrets for pulling an image from a private repository more information can be found here: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/
imagePullSecrets: []
# This is to override the chart name.
nameOverride: ""
fullnameOverride: ""
secret:
rustfs:
access_key: rustfsadmin
secret_key: rustfsadmin
config:
rustfs:
volume: "/data/rustfs0,/data/rustfs1,/data/rustfs2,/data/rustfs3"
address: "0.0.0.0:9000"
console_address: "0.0.0.0:9001"
log_level: "debug"
rust_log: "debug"
console_enable: "true"
sinks_file_path: "/logs"
obs_log_directory: "/logs"
# This section builds out the service account more information can be found here: https://kubernetes.io/docs/concepts/security/service-accounts/
serviceAccount:
# Specifies whether a service account should be created
create: true
# Automatically mount a ServiceAccount's API credentials?
automount: true
# Annotations to add to the service account
annotations: {}
# The name of the service account to use.
# If not set and create is true, a name is generated using the fullname template
name: ""
# This is for setting Kubernetes Annotations to a Pod.
# For more information checkout: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/
podAnnotations: {}
# This is for setting Kubernetes Labels to a Pod.
# For more information checkout: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/
podLabels: {}
podSecurityContext:
{}
# fsGroup: 2000
securityContext:
{}
# capabilities:
# drop:
# - ALL
# readOnlyRootFilesystem: true
# runAsNonRoot: true
# runAsUser: 1000
service:
type: NodePort
ep_port: 9000
console_port: 9001
# This block is for setting up the ingress for more information can be found here: https://kubernetes.io/docs/concepts/services-networking/ingress/
ingress:
enabled: true
className: "" # Specify the classname, traefik or nginx. Different classname has different annotations for session sticky.
traefikAnnotations:
traefik.ingress.kubernetes.io/service.sticky.cookie: "true"
traefik.ingress.kubernetes.io/service.sticky.cookie.httponly: "true"
traefik.ingress.kubernetes.io/service.sticky.cookie.name: rustfs
traefik.ingress.kubernetes.io/service.sticky.cookie.samesite: none
traefik.ingress.kubernetes.io/service.sticky.cookie.secure: "true"
nginxAnnotations:
nginx.ingress.kubernetes.io/affinity: cookie
nginx.ingress.kubernetes.io/session-cookie-expires: "3600"
nginx.ingress.kubernetes.io/session-cookie-hash: sha1
nginx.ingress.kubernetes.io/session-cookie-max-age: "3600"
nginx.ingress.kubernetes.io/session-cookie-name: rustfs
hosts:
- host: xmg.rustfs.com
paths:
- path: /
pathType: ImplementationSpecific
tls:
- secretName: rustfs-tls
hosts:
- xmg.rustfs.com
resources:
# We usually recommend not to specify default resources and to leave this as a conscious
# choice for the user. This also increases chances charts run on environments with little
# resources, such as Minikube. If you do want to specify resources, uncomment the following
# lines, adjust them as necessary, and remove the curly braces after 'resources:'.
limits:
cpu: 200m
memory: 512Mi
requests:
cpu: 100m
memory: 128Mi
# This is to setup the liveness and readiness probes more information can be found here: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/
livenessProbe:
httpGet:
path: /health
port: http
readinessProbe:
httpGet:
path: /health
port: http
# This section is for setting up autoscaling more information can be found here: https://kubernetes.io/docs/concepts/workloads/autoscaling/
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 100
targetCPUUtilizationPercentage: 80
# targetMemoryUtilizationPercentage: 80
nodeSelector: {}
tolerations: []
affinity: {}
storageclass:
name: local-path
size: 256Mi