Skip to content

Commit

Permalink
feat: Horizontal Pod Autoscaler (#15)
Browse files Browse the repository at this point in the history
Co-authored-by: Yusuke Kuoka <[email protected]>
  • Loading branch information
dvanmali and mumoshu authored Jan 28, 2025
1 parent 991915b commit 9ca46e2
Show file tree
Hide file tree
Showing 14 changed files with 208 additions and 13 deletions.
6 changes: 6 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,12 @@ jobs:
with:
version: v1.61

- name: Create kind cluster
uses: helm/[email protected]
with:
node_image: kindest/node:v1.29.2
version: v0.20.0

- name: Run go test
run: go test -v ./...

Expand Down
2 changes: 1 addition & 1 deletion charts/surrealdb/Chart.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
apiVersion: v2
name: surrealdb
type: application
version: 0.3.6
version: 0.3.7
appVersion: 1.0.0
description: SurrealDB is the ultimate cloud database for tomorrow's applications.
keywords:
Expand Down
20 changes: 17 additions & 3 deletions charts/surrealdb/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# SurrealDB Helm Chart

![Version: 0.3.5](https://img.shields.io/badge/Version-0.3.5-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.0.0](https://img.shields.io/badge/AppVersion-1.0.0-informational?style=flat-square)
![Version: 0.3.7](https://img.shields.io/badge/Version-0.3.7-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.0.0](https://img.shields.io/badge/AppVersion-1.0.0-informational?style=flat-square)

SurrealDB is the ultimate cloud database for tomorrow's applications.

Expand Down Expand Up @@ -31,11 +31,15 @@ Read the Kubernetes Deployment guides in https://surrealdb.com/docs/deployment
|-----|------|---------|-------------|
| affinity | object | `{}` | Assign custom [affinity] rules to the deployment |
| args | list | `["start"]` | Command line arguments to pass to SurrealDB |
| horizontalPodAutoscaler.enabled | bool | `false` | Enable the horizontal pod autoscaler for Surrealdb pods |
| horizontalPodAutoscaler.maxReplicas | int | `1` | Max pod replicas |
| horizontalPodAutoscaler.metrics | list | `[]` (See [values.yaml]) | Metrics which the autoscaler reacts to. See [kubernetes autoscale docs](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/) for metric format. |
| horizontalPodAutoscaler.minReplicas | int | `1` | Min pod replicas |
| nodeSelector | object | `{}` | [Node selector] |
| podAnnotations | object | `{}` | Annotations to be added to SurrealDB pods |
| podExtraEnv | list | `[]` | Extra env entries added to the SurrealDB pods |
| podSecurityContext | object | `{}` (See [values.yaml]) | Toggle and define pod-level security context. |
| replicaCount | int | `1` | The number of SurrealDB pods to run |
| replicaCount | int | `1` | The number of SurrealDB pods to run Note that you usually scale this only when the backend supports it. For example, if you specify volumes and volumeMounts to make this SurrealDB instance stateful, you should not scale it, as it will result in two or more instances writing to the same volume or working independently. |
| resources | object | `{}` | Resource limits and requests |
| securityContext | object | `{}` (See [values.yaml]) | SurrealDB container-level security context |
| tolerations | list | `[]` | [Tolerations] for use with node taints |
Expand All @@ -46,7 +50,6 @@ Read the Kubernetes Deployment guides in https://surrealdb.com/docs/deployment

| Key | Type | Default | Description |
|-----|------|---------|-------------|
| surrealdb.auth | string | `"true"` | Authentication enabled |
| surrealdb.log | string | `"info"` | Log configuration |
| surrealdb.path | string | `"memory"` | path: tikv://tikv-pd:2379 |
| surrealdb.port | int | `8000` | SurrealDB container port |
Expand Down Expand Up @@ -77,6 +80,17 @@ Read the Kubernetes Deployment guides in https://surrealdb.com/docs/deployment
| serviceAccount.create | bool | `true` | Specifies whether a service account should be created |
| serviceAccount.name | string | `""` (defaults to the fullname template) | The name of the service account to use. |

## Horizontal Pod Autoscaler parameters

An optional horizontal pod autoscaler that, when defined, will use metrics to scale Surrealdb pods. Note that the replicaCount variable will be ignored when the horizontal pod autoscaler is used and is replaced by the minReplicas and maxReplicas defined here. The HPA can be added or removed at anytime using `helm upgrade`.

| Key | Type | Default | Description |
|-----|------|---------|-------------|
| horizontalPodAutoscaler.enabled | bool | `false` | Enable the horizontal pod autoscaler for Surrealdb pods |
| horizontalPodAutoscaler.maxReplicas | int | `1` | Max pod replicas |
| horizontalPodAutoscaler.metrics | list | `[]` (See [values.yaml]) | Metrics which the autoscaler reacts to. See [kubernetes autoscale docs](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/) for metric format. |
| horizontalPodAutoscaler.minReplicas | int | `1` | Min pod replicas |

## Ingress parameters

| Key | Type | Default | Description |
Expand Down
12 changes: 12 additions & 0 deletions charts/surrealdb/README.md.gotmpl
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,18 @@ Read the Kubernetes Deployment guides in https://surrealdb.com/docs/deployment
{{- end }}
{{- end }}

## Horizontal Pod Autoscaler parameters

An optional horizontal pod autoscaler that, when defined, will use metrics to scale Surrealdb pods. Note that the replicaCount variable will be ignored when the horizontal pod autoscaler is used and is replaced by the minReplicas and maxReplicas defined here. The HPA can be added or removed at anytime using `helm upgrade`.

| Key | Type | Default | Description |
|-----|------|---------|-------------|
{{- range .Values }}
{{- if hasPrefix "horizontalPodAutoscaler" .Key }}
| {{ .Key }} | {{ .Type }} | {{ if .Default }}{{ .Default }}{{ else }}{{ .AutoDefault }}{{ end }} | {{ if .Description }}{{ .Description }}{{ else }}{{ .AutoDescription }}{{ end }} |
{{- end }}
{{- end }}

## Ingress parameters

| Key | Type | Default | Description |
Expand Down
4 changes: 3 additions & 1 deletion charts/surrealdb/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ metadata:
labels:
{{- include "surrealdb.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
{{- if not .Values.horizontalPodAutoscaler.enabled }}
replicas: {{ .Values.replicaCount | default 1 }}
{{- end }}
selector:
matchLabels:
{{- include "surrealdb.selectorLabels" . | nindent 6 }}
Expand Down
21 changes: 21 additions & 0 deletions charts/surrealdb/templates/hpa.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{{- if .Values.horizontalPodAutoscaler.enabled }}
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: {{ include "surrealdb.fullname" . }}
labels:
{{- include "surrealdb.labels" . | nindent 4 }}
spec:
{{- with .Values.horizontalPodAutoscaler }}
minReplicas: {{ .minReplicas | default 1 }}
maxReplicas: {{ .maxReplicas | default 1 }}
{{- end }}
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: {{ include "surrealdb.fullname" . }}
{{- with .Values.horizontalPodAutoscaler.metrics }}
metrics:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- end }}
21 changes: 19 additions & 2 deletions charts/surrealdb/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,23 @@ resources: {}
# requests: {}
# limits: {}

horizontalPodAutoscaler:
# -- Enable the horizontal pod autoscaler for Surrealdb pods
enabled: false
# -- Min pod replicas
minReplicas: 1
# -- Max pod replicas
maxReplicas: 1
# -- Metrics which the autoscaler reacts to. See [kubernetes autoscale docs](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/) for metric format.
# @default -- `[]` (See [values.yaml])
metrics: []
# - type: Resource
# resource:
# name: cpu
# target:
# type: Utilization
# averageUtilization: 80

# -- [Node selector]
nodeSelector: {}

Expand All @@ -149,8 +166,8 @@ tolerations: []
# -- Assign custom [affinity] rules to the deployment
affinity: {}

# Additional volumes for surrealdb pods
# -- Additional volumes for SurrealDB pod
volumes: []

# Additional volumeMounts for the surrealdb container
# -- Additional volume mounts for SurrealDB container
volumeMounts: []
123 changes: 123 additions & 0 deletions tests/e2e_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package tests

import (
"context"
"encoding/json"
"log"
"os"
"os/exec"
"testing"
"time"
)

var (
SurrealDBChartPath = "../charts/surrealdb"
KubectlTimeout = 1 * time.Second
DeploymentReplicasUpdateTimeout = 20 * time.Second
)

func TestMain(m *testing.M) {
var err error
if env := os.Getenv("SURREALDB_CHART_PATH"); env != "" {
SurrealDBChartPath = env
}
if env := os.Getenv("KUBECTL_TIMEOUT"); env != "" {
KubectlTimeout, err = time.ParseDuration(env)
if err != nil {
log.Fatalf("failed to parse KUBECTL_TIMEOUT: %s", err)
}
}
if env := os.Getenv("DEPLOYMENT_REPLICAS_UPDATE_TIMEOUT"); env != "" {
DeploymentReplicasUpdateTimeout, err = time.ParseDuration(env)
if err != nil {
log.Fatalf("failed to parse DEPLOYMENT_REPLICAS_UPDATE_TIMEOUT: %s", err)
}
}
os.Exit(m.Run())
}

// This runs a series of helm-upgrade commands to test the HPA enable/disable functionality end-to-end
// It assumes there are a Kubernetes cluster available, and the helm and kubectl commands installed.
func TestHPAEnableDisable(t *testing.T) {
const (
ReleaseName = "sdb1"
DeploymentName = "sdb1-surrealdb"
)

t.Cleanup(func() {
helmUninstall(t, ReleaseName)
})

helmUpgrade(t, ReleaseName)
waitUntilDeploymentHasReplicas(t, DeploymentName, 1)

helmUpgrade(t, ReleaseName, "--set", "horizontalPodAutoscaler.enabled=true", "--set", "horizontalPodAutoscaler.minReplicas=2", "--set", "horizontalPodAutoscaler.maxReplicas=3")
waitUntilDeploymentHasReplicas(t, DeploymentName, 2)

helmUpgrade(t, ReleaseName, "--set", "horizontalPodAutoscaler.enabled=false")
waitUntilDeploymentHasReplicas(t, DeploymentName, 1)
}

func helmUpgrade(t *testing.T, release string, args ...string) {
c := exec.Command("helm", append([]string{"upgrade", "--install", release, SurrealDBChartPath}, args...)...)
res, err := c.CombinedOutput()
if err != nil {
t.Fatalf("helm upgrade failed: %s", string(res))
}
}

type deployment struct {
Spec *deploymentSpec `json:"spec"`
}

type deploymentSpec struct {
Replicas int `json:"replicas"`
}

func kubectlGetDeployment(t *testing.T, name string) deployment {
ctx, cancel := context.WithTimeout(context.Background(), KubectlTimeout)
defer cancel()
c := exec.CommandContext(ctx, "kubectl", "get", "deployment", name, "-o", "json")
res, err := c.CombinedOutput()
if err != nil {
t.Fatalf("kubectl get deployment failed: %s", string(res))
}

var deployment deployment
err = json.Unmarshal(res, &deployment)
if err != nil {
t.Fatalf("failed to unmarshal deployment: %s", string(res))
}

return deployment
}

func helmUninstall(t *testing.T, release string) {
c := exec.Command("helm", "uninstall", release)
res, err := c.CombinedOutput()
if err != nil {
t.Fatalf("helm uninstall failed: %s", string(res))
}
}

func waitUntilDeploymentHasReplicas(t *testing.T, name string, replicas int) {
done := make(chan struct{})
defer close(done)
go func() {
for {
deployment := kubectlGetDeployment(t, name)
if deployment.Spec.Replicas == replicas {
done <- struct{}{}
return
}
time.Sleep(1 * time.Second)
}
}()

select {
case <-done:
return
case <-time.After(DeploymentReplicasUpdateTimeout):
t.Fatalf("timed out waiting for deployment to have %d replicas", replicas)
}
}
2 changes: 1 addition & 1 deletion tests/testdata/snapshots/deployment.yaml/default
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ kind: Deployment
metadata:
name: testrelease-surrealdb
labels:
helm.sh/chart: surrealdb-0.3.6
helm.sh/chart: surrealdb-0.3.7
app.kubernetes.io/name: surrealdb
app.kubernetes.io/instance: testrelease
app.kubernetes.io/version: "1.0.0"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ kind: Deployment
metadata:
name: testrelease-surrealdb
labels:
helm.sh/chart: surrealdb-0.3.6
helm.sh/chart: surrealdb-0.3.7
app.kubernetes.io/name: surrealdb
app.kubernetes.io/instance: testrelease
app.kubernetes.io/version: "1.0.0"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ kind: Deployment
metadata:
name: testrelease-surrealdb
labels:
helm.sh/chart: surrealdb-0.3.6
helm.sh/chart: surrealdb-0.3.7
app.kubernetes.io/name: surrealdb
app.kubernetes.io/instance: testrelease
app.kubernetes.io/version: "1.0.0"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ kind: Deployment
metadata:
name: testrelease-surrealdb
labels:
helm.sh/chart: surrealdb-0.3.6
helm.sh/chart: surrealdb-0.3.7
app.kubernetes.io/name: surrealdb
app.kubernetes.io/instance: testrelease
app.kubernetes.io/version: "1.0.0"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ kind: Deployment
metadata:
name: testrelease-surrealdb
labels:
helm.sh/chart: surrealdb-0.3.6
helm.sh/chart: surrealdb-0.3.7
app.kubernetes.io/name: surrealdb
app.kubernetes.io/instance: testrelease
app.kubernetes.io/version: "1.0.0"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ kind: Deployment
metadata:
name: testrelease-surrealdb
labels:
helm.sh/chart: surrealdb-0.3.6
helm.sh/chart: surrealdb-0.3.7
app.kubernetes.io/name: surrealdb
app.kubernetes.io/instance: testrelease
app.kubernetes.io/version: "1.0.0"
Expand Down

0 comments on commit 9ca46e2

Please sign in to comment.