feat: storage benchmark — local-path PV, vcd-disk-ext4 PV, S3 (10 iter each)

This commit is contained in:
“Naeel”
2026-05-19 07:28:45 +04:00
parent 1d81ca34e1
commit 865d33a0e1
12 changed files with 414 additions and 0 deletions
+105
View File
@@ -0,0 +1,105 @@
#!/bin/bash
# Fission storage benchmark: local-path PV / vcd-disk-ext4 PV / S3
# Каждый вызов = полный путь: curl → Fission Router → Executor → Pod → Storage
set -e
N=${1:-10} # число итераций, по умолчанию 10
OUTDIR="$(cd "$(dirname "$0")/../../test-results" 2>/dev/null && pwd || echo /tmp)"
OUTFILE="$OUTDIR/bench-storage-$(date +%Y-%m-%d_%H-%M-%S).txt"
mkdir -p "$OUTDIR"
# Port-forward router
kubectl port-forward svc/router 8889:80 -n fission &>/tmp/pf-bench.log &
PF_PID=$!
trap "kill $PF_PID 2>/dev/null" EXIT
sleep 3
ROUTER="http://localhost:8889"
# JWT auth
PASSWORD=$(kubectl get secret router -n fission -o jsonpath="{.data.password}" | base64 -d)
USERNAME=$(kubectl get secret router -n fission -o jsonpath="{.data.username}" | base64 -d)
TOKEN=$(curl -s -X POST "$ROUTER/auth/login" \
-H "Content-Type: application/json" \
-d "{\"username\":\"$USERNAME\",\"password\":\"$PASSWORD\"}" \
| python3 -c "import sys,json; print(json.load(sys.stdin)['accesstoken'])")
if [ -z "$TOKEN" ]; then
echo "ERROR: не удалось получить JWT токен" >&2
exit 1
fi
AUTH="-H \"Authorization: Bearer $TOKEN\""
# Функция запуска N итераций и сбора статистики
bench_endpoint() {
local LABEL="$1"
local URL="$2"
local times=()
local errors=0
echo ""
echo "=== $LABEL ==="
printf "%-5s %-12s %s\n" "iter" "ms" "response"
for i in $(seq 1 $N); do
START=$(date +%s%N)
RESP=$(curl -s --max-time 60 -H "Authorization: Bearer $TOKEN" "$URL" 2>/dev/null)
END=$(date +%s%N)
MS=$(( (END - START) / 1000000 ))
if echo "$RESP" | python3 -c "import sys,json; d=json.load(sys.stdin); assert d['status']=='ok'" 2>/dev/null; then
STATUS="ok"
INNER=$(echo "$RESP" | python3 -c "import sys,json; d=json.load(sys.stdin); k=list(d.keys()); print(' '.join(f'{k}={d[k]}' for k in k if k not in ['status']))" 2>/dev/null)
else
STATUS="ERR"
INNER="$RESP"
(( errors++ )) || true
fi
times+=($MS)
printf "%-5s %-12s %s\n" "$i" "${MS}ms" "$STATUS $INNER"
done
# Статистика
local sum=0 min=999999999 max=0
for t in "${times[@]}"; do
sum=$((sum + t))
[ $t -lt $min ] && min=$t
[ $t -gt $max ] && max=$t
done
local avg=$((sum / N))
local ok=$((N - errors))
echo "---"
printf " Успешно: %d/%d | min=%dms avg=%dms max=%dms\n" "$ok" "$N" "$min" "$avg" "$max"
}
# Заголовок отчёта
{
echo "============================================================"
echo " Fission Storage Benchmark"
echo " Дата: $(date)"
echo " Итераций: $N на endpoint"
echo " Стек: curl → Router → Executor → Pod → Storage"
echo "============================================================"
bench_endpoint "local-path PV (100Mi, rawfile CSI, WaitForFirstConsumer)" \
"$ROUTER/check/local"
bench_endpoint "vcd-disk-ext4 PV (10Gi, VMware Cloud Director)" \
"$ROUTER/check/vcd"
bench_endpoint "S3 ngcloud (s3.msk-1.ngcloud.ru, bucket=sless-functions)" \
"$ROUTER/check/s3"
echo ""
echo "============================================================"
echo " Завершено: $(date)"
echo "============================================================"
} | tee "$OUTFILE"
echo ""
echo "Результаты записаны: $OUTFILE"
+31
View File
@@ -0,0 +1,31 @@
import os
import time
def main(event, context):
path = "/mnt/data/check.txt"
test_data = "storage-check-ok"
try:
# Write
t0 = time.time()
with open(path, "w") as f:
f.write(test_data)
write_ms = round((time.time() - t0) * 1000, 2)
# Read
t0 = time.time()
with open(path, "r") as f:
result = f.read()
read_ms = round((time.time() - t0) * 1000, 2)
# Cleanup
os.remove(path)
if result == test_data:
return {"status": "ok", "write_ms": write_ms, "read_ms": read_ms, "mount": path}
else:
return {"status": "error", "detail": "data mismatch"}
except Exception as e:
return {"status": "error", "detail": str(e)}
+48
View File
@@ -0,0 +1,48 @@
import time
import boto3
from botocore.client import Config
def main(event, context):
# Credentials mounted by Fission at /secrets/<namespace>/<secretname>/<key>
try:
with open("/secrets/default/bench-s3-secret/access-key") as f:
access_key = f.read().strip()
with open("/secrets/default/bench-s3-secret/secret-key") as f:
secret_key = f.read().strip()
except Exception as e:
return {"status": "error", "detail": f"secret read: {e}"}
s3 = boto3.client(
"s3",
endpoint_url="https://s3.msk-1.ngcloud.ru",
aws_access_key_id=access_key,
aws_secret_access_key=secret_key,
config=Config(signature_version="s3v4"),
)
bucket = "sless-functions"
key = "storage-check/check.txt"
test_data = b"storage-check-ok"
try:
# Write
t0 = time.time()
s3.put_object(Bucket=bucket, Key=key, Body=test_data)
write_ms = round((time.time() - t0) * 1000, 2)
# Read
t0 = time.time()
result = s3.get_object(Bucket=bucket, Key=key)["Body"].read()
read_ms = round((time.time() - t0) * 1000, 2)
# Cleanup
s3.delete_object(Bucket=bucket, Key=key)
if result == test_data:
return {"status": "ok", "write_ms": write_ms, "read_ms": read_ms, "bucket": bucket}
else:
return {"status": "error", "detail": "data mismatch"}
except Exception as e:
return {"status": "error", "detail": str(e)}
+24
View File
@@ -0,0 +1,24 @@
#!/bin/bash
NS=default
echo "=== Removing HTTP triggers ==="
fission httptrigger delete --name check-local-t --namespace $NS 2>/dev/null || true
fission httptrigger delete --name check-vcd-t --namespace $NS 2>/dev/null || true
fission httptrigger delete --name check-s3-t --namespace $NS 2>/dev/null || true
echo "=== Removing functions ==="
fission fn delete --name check-local --namespace $NS 2>/dev/null || true
fission fn delete --name check-vcd --namespace $NS 2>/dev/null || true
fission fn delete --name check-s3 --namespace $NS 2>/dev/null || true
echo "=== Removing environment ==="
fission env delete --name bench-py --namespace $NS 2>/dev/null || true
echo "=== Removing PVCs ==="
kubectl delete pvc storage-check-local -n $NS 2>/dev/null || true
kubectl delete pvc storage-check-vcd -n $NS 2>/dev/null || true
echo "=== Removing secret ==="
kubectl delete secret bench-s3-secret -n $NS 2>/dev/null || true
echo "Done."
+49
View File
@@ -0,0 +1,49 @@
#!/bin/bash
set -e
cd "$(dirname "$0")"
NS=default
S3_KEY=0GLQRD38H4I6RBDB0EWJ
S3_SECRET=eTFibiHmBd96IApj9PYsboTR6OBoD7osxoarHykw
echo "=== PVCs ==="
kubectl apply -f pvc-local.yaml
kubectl apply -f pvc-vcd.yaml
echo "=== S3 Secret ==="
kubectl create secret generic bench-s3-secret \
--from-literal=access-key="$S3_KEY" \
--from-literal=secret-key="$S3_SECRET" \
-n $NS 2>/dev/null || echo "secret already exists"
echo "=== Environment (no resource limits to avoid quota) ==="
fission env create --name bench-py --image naeel/fission-python-env:v1.1 \
--mincpu 0 --maxcpu 0 --minmemory 0 --maxmemory 0 \
--poolsize 1 --namespace $NS 2>/dev/null || echo "env already exists"
echo "=== Functions ==="
# check-local и check-vcd используют newdeploy: 1 под = 1 PVC (RWO работает)
fission fn create --name check-local --env bench-py --code check_pv.py \
--executortype newdeploy --minscale 1 --maxscale 1 --namespace $NS 2>/dev/null || \
fission fn update --name check-local --code check_pv.py --namespace $NS
fission fn create --name check-vcd --env bench-py --code check_pv.py \
--executortype newdeploy --minscale 1 --maxscale 1 --namespace $NS 2>/dev/null || \
fission fn update --name check-vcd --code check_pv.py --namespace $NS
# check-s3 использует poolmgr
fission fn create --name check-s3 --env bench-py --code check_s3.py \
--namespace $NS --secret bench-s3-secret 2>/dev/null || \
fission fn update --name check-s3 --code check_s3.py --namespace $NS --secret bench-s3-secret
echo "=== Patching podspec (PVC mounts) ==="
kubectl patch function check-local -n $NS --type=merge -p "$(cat patch-local.json)"
kubectl patch function check-vcd -n $NS --type=merge -p "$(cat patch-vcd.json)"
echo "=== HTTP Triggers ==="
fission httptrigger create --name check-local-t --url /check/local --function check-local --method GET --namespace $NS 2>/dev/null || true
fission httptrigger create --name check-vcd-t --url /check/vcd --function check-vcd --method GET --namespace $NS 2>/dev/null || true
fission httptrigger create --name check-s3-t --url /check/s3 --function check-s3 --method GET --namespace $NS 2>/dev/null || true
echo ""
echo "Done! Жди ~60s (newdeploy pods + PVC binding), затем: ./test.sh"
@@ -0,0 +1,30 @@
apiVersion: fission.io/v1
kind: Function
metadata:
name: check-local
namespace: default
spec:
InvokeStrategy:
ExecutionStrategy:
ExecutorType: newdeploy
MinScale: 1
MaxScale: 1
StrategyType: execution
environment:
name: bench-py
namespace: default
package:
functionName: main.main
packageref:
name: check-local-7d087421-7802-4af1-b111-03d71fe738cd
namespace: default
podspec:
volumes:
- name: data
persistentVolumeClaim:
claimName: storage-check-local
containers:
- name: bench-py
volumeMounts:
- name: data
mountPath: /mnt/data
+30
View File
@@ -0,0 +1,30 @@
apiVersion: fission.io/v1
kind: Function
metadata:
name: check-vcd
namespace: default
spec:
InvokeStrategy:
ExecutionStrategy:
ExecutorType: newdeploy
MinScale: 1
MaxScale: 1
StrategyType: execution
environment:
name: bench-py
namespace: default
package:
functionName: main.main
packageref:
name: check-vcd-6e6076b2-2207-49f3-a12d-83fb50633285
namespace: default
podspec:
volumes:
- name: data
persistentVolumeClaim:
claimName: storage-check-vcd
containers:
- name: bench-py
volumeMounts:
- name: data
mountPath: /mnt/data
+25
View File
@@ -0,0 +1,25 @@
{
"spec": {
"podspec": {
"volumes": [
{
"name": "data",
"persistentVolumeClaim": {
"claimName": "storage-check-local"
}
}
],
"containers": [
{
"name": "bench-py",
"volumeMounts": [
{
"name": "data",
"mountPath": "/mnt/data"
}
]
}
]
}
}
}
+25
View File
@@ -0,0 +1,25 @@
{
"spec": {
"podspec": {
"volumes": [
{
"name": "data",
"persistentVolumeClaim": {
"claimName": "storage-check-vcd"
}
}
],
"containers": [
{
"name": "bench-py",
"volumeMounts": [
{
"name": "data",
"mountPath": "/mnt/data"
}
]
}
]
}
}
}
+12
View File
@@ -0,0 +1,12 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: storage-check-local
namespace: default
spec:
accessModes:
- ReadWriteOnce
storageClassName: local-path
resources:
requests:
storage: 100Mi
+12
View File
@@ -0,0 +1,12 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: storage-check-vcd
namespace: default
spec:
accessModes:
- ReadWriteOnce
storageClassName: vcd-disk-ext4
resources:
requests:
storage: 10Gi
+23
View File
@@ -0,0 +1,23 @@
#!/bin/bash
set -e
# Port-forward router to localhost:8888
kubectl port-forward svc/router 8888:80 -n fission &>/tmp/pf.log &
PF_PID=$!
trap "kill $PF_PID 2>/dev/null" EXIT
sleep 3
ROUTER="localhost:8888"
echo "Router via port-forward: $ROUTER"
echo ""
echo "=== [1/3] local-path ==="
curl -sf --max-time 30 "http://$ROUTER/check/local" | python3 -m json.tool || echo "ERROR: no response"
echo ""
echo "=== [2/3] vcd-disk-ext4 ==="
curl -sf --max-time 30 "http://$ROUTER/check/vcd" | python3 -m json.tool || echo "ERROR: no response"
echo ""
echo "=== [3/3] S3 (ngcloud) ==="
curl -sf --max-time 30 "http://$ROUTER/check/s3" | python3 -m json.tool || echo "ERROR: no response"