Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Solana Helm chart #302

Merged
merged 4 commits into from
May 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions dysnix/solana/.helmignore
Original file line number Diff line number Diff line change
@@ -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/
23 changes: 23 additions & 0 deletions dysnix/solana/Chart.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
apiVersion: v2
name: solana
description: Solana blockchain node Helm chart
type: application

version: 0.1.25
appVersion: "v1.17.32"

keywords:
- solana
- crypto
- web3
- cryptocurrency
- blockchain

sources:
- https://github.com/dysnix/charts
- https://github.com/dysnix/docker-solana
- https://github.com/solana-labs/solana

maintainers:
- name: VladStarr
email: vlad.derigin@dysnix.com
107 changes: 107 additions & 0 deletions dysnix/solana/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# Solana helm chart

A Helm chart to deploy Solana node inside Kubernetes cluster.

## Parameters

### Global parameters

| Name | Description | Value |
| ---------------------------- | ---------------------------------------------------- | ------------------------------ |
| `replicaCount` | Number of pods to deploy in the Stateful Set | `1` |
| `image.repository` | Solana image repository | `ghcr.io/dysnix/docker-solana` |
| `image.tag` | Solana image tag | `""` |
| `image.pullPolicy` | Solana image pull policy | `IfNotPresent` |
| `imagePullSecrets` | Solana image pull secrets | `[]` |
| `nameOverride` | String to partially override release name | `""` |
| `fullnameOverride` | String to fully override release name | `""` |
| `serviceAccount.create` | Specifies whether a ServiceAccount should be created | `true` |
| `serviceAccount.name` | The name of the ServiceAccount to use | `""` |
| `serviceAccount.automount` | Whether to auto mount the service account token | `true` |
| `serviceAccount.annotations` | Additional custom annotations for the ServiceAccount | `{}` |
| `podLabels` | Extra labels for pods | `{}` |
| `podAnnotations` | Annotations for pods | `{}` |
| `podSecurityContext` | Configure securityContext for entire pod | `{}` |
| `securityContext` | Configure securityContext for Solana container | `{}` |
| `resources` | Set container requests and limits for CPU or memory | `{}` |
| `livenessProbe` | Solana container livenessProbe | `{}` |
| `startupProbe` | Solana container startupProbe | `{}` |
| `readinessProbe` | Solana container readinessProbe | `{}` |
| `affinity` | Affinity for pod assignment | `{}` |
| `nodeSelector` | Node labels for pod assignment | `{}` |
| `tolerations` | Tolerations for pod assignment | `[]` |
| `volumes` | Pod extra volumes | `[]` |
| `volumeMounts` | Container extra volumeMounts | `[]` |

### Services configuration

| Name | Description | Value |
| ------------------------------------------- | ------------------------------------------ | ----------- |
| `services.rpc.enabled` | Enable Solana RPC service | `true` |
| `services.rpc.type` | Solana RPC service type | `ClusterIP` |
| `services.rpc.port` | Solana RPC service port (+1 for websocket) | `8899` |
| `services.rpc.publishNotReadyAddresses` | Route trafic even when pod is not ready | `false` |
| `services.metrics.enabled` | Enable Solana metrics service | `false` |
| `services.metrics.type` | Solana metrics service type | `ClusterIP` |
| `services.metrics.port` | Solana metrics service port | `9122` |
| `services.metrics.publishNotReadyAddresses` | Route trafic even when pod is not ready | `true` |

### Ingress configuration

| Name | Description | Value |
| -------------- | ------------------------------------------------------- | ----- |
| `ingress.http` | Ingress configuration for Solana RPC HTTP endpoint | `{}` |
| `ingress.ws` | Ingress configuration for Solana RPC WebSocket endpoint | `{}` |

### Metrics configuration

| Name | Description | Value |
| -------------------------------------- | ---------------------------------------------- | ----------------------- |
| `metrics.enabled` | Enable Solana node metrics collection | `false` |
| `metrics.target` | Where to push Solana metrics | `exporter` |
| `metrics.exporter` | influxdb-exporter configuration | `{}` |
| `metrics.serviceMonitor.enabled` | Enable Prometheus ServiceMonitor | `false` |
| `metrics.influxdb.existingSecret.name` | Name of secret containing InfluxDB credentials | `solana-metrics-config` |
| `metrics.influxdb.existingSecret.key` | Key name inside the secret | `config` |

### Solana node configuration

| Name | Description | Value |
| -------------------------------------------- | ------------------------------------------------------------------ | --------------------------------- |
| `solanaArgs` | `solana-validator` arguments | `{}` |
| `gracefulShutdown.timeout` | Seconds to wait for graceful shutdown | `120` |
| `gracefulShutdown.options` | `solana-validator exit` arguments | `{}` |
| `gracefulShutdown.options.force` | Do not wait for restart-window, useful for non-validators | `false` |
| `gracefulShutdown.options.skip-health-check` | Skip health check before exit | `false` |
| `gracefulShutdown.options.skip-health-check` | Skip check for a new snapshot before exit | `false` |
| `rustLog` | Logging configuration | `solana=info,solana_metrics=warn` |
| `identity.validatorKeypair` | Validator keypair string (required) | `""` |
| `identity.voteKeypair` | Vote keypair string (required only for validator) | `""` |
| `identity.existingSecret` | Use existing secret with keypairs instead of specifying them above | `""` |
| `identity.mountPath` | Keypair files mount path | `/secrets` |

### Solana ledger db persistence config

| Name | Description | Value |
| --------------------------------------- | ------------------------------- | --------------------------- |
| `persistence.ledger.type` | Ledger persistence type | `pvc` |
| `persistence.ledger.pvc.annotations` | PVC volume annotations | `{}` |
| `persistence.ledger.pvc.accessMode` | PVC volume access mode | `ReadWriteOnce` |
| `persistence.ledger.pvc.storageClass` | PVC volume storage class name | `""` |
| `persistence.ledger.pvc.size` | PVC volume size | `2Ti` |
| `persistence.ledger.existingClaim.name` | Existing PVC configuration | `solana-ledger-volume` |
| `persistence.ledger.hostPath.type` | hostPath volume type | `Directory` |
| `persistence.ledger.hostPath.path` | hostPath directory on host node | `/blockchain/solana-ledger` |

### Solana accounts db persistence config

| Name | Description | Value |
| ----------------------------------------- | ------------------------------- | ----------------------------- |
| `persistence.accounts.type` | Accounts persistence type | `pvc` |
| `persistence.accounts.pvc.annotations` | PVC volume annotations | `{}` |
| `persistence.accounts.pvc.accessMode` | PVC volume access mode | `ReadWriteOnce` |
| `persistence.accounts.pvc.storageClass` | PVC volume storage class name | `""` |
| `persistence.accounts.pvc.size` | PVC volume size | `2Ti` |
| `persistence.accounts.existingClaim.name` | Existing PVC configuration | `solana-accounts-volume` |
| `persistence.accounts.hostPath.type` | hostPath volume type | `Directory` |
| `persistence.accounts.hostPath.path` | hostPath directory on host node | `/blockchain/solana-accounts` |
14 changes: 14 additions & 0 deletions dysnix/solana/ci/ct-values.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
identity:
validatorKeypair: "[83,14,17,234,154,118,131,15,148,222,164,240,217,177,178,62,27,15,138,43,9,91,110,197,8,2,78,43,207,188,61,233,43,161,169,18,238,213,255,243,237,121,111,151,144,122,230,225,113,224,121,179,4,232,179,183,82,60,135,13,4,52,66,199]"

readinessProbe:
enabled: false

startupProbe:
enabled: false

gracefulShutdown:
options:
force: true
skip-health-check: true
skip-new-snapshot-check: true
62 changes: 62 additions & 0 deletions dysnix/solana/templates/_helpers.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "solana.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 "solana.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 "solana.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}

{{/*
Common labels
*/}}
{{- define "solana.labels" -}}
helm.sh/chart: {{ include "solana.chart" . }}
{{ include "solana.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}

{{/*
Selector labels
*/}}
{{- define "solana.selectorLabels" -}}
app.kubernetes.io/name: {{ include "solana.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}

{{/*
Create the name of the service account to use
*/}}
{{- define "solana.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "solana.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}
46 changes: 46 additions & 0 deletions dysnix/solana/templates/ingress-http.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{{- if .Values.ingress.http.enabled -}}
{{- $fullName := include "solana.fullname" . -}}
{{- $svcPort := .Values.services.rpc.port -}}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ $fullName }}-http
labels:
{{- include "solana.labels" . | nindent 4 }}
type: http
{{- with .Values.ingress.http.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- if .Values.ingress.http.className }}
ingressClassName: {{ .Values.ingress.http.className }}
{{- end }}
{{- if .Values.ingress.http.tls }}
tls:
{{- range .Values.ingress.http.tls }}
- hosts:
{{- range .hosts }}
- {{ . | quote }}
{{- end }}
secretName: {{ .secretName }}
{{- end }}
{{- end }}
rules:
{{- range .Values.ingress.http.hosts }}
- host: {{ .host | quote }}
http:
paths:
{{- range .paths }}
- path: {{ .path }}
{{- if .pathType }}
pathType: {{ .pathType }}
{{- end }}
backend:
service:
name: {{ $fullName }}
port:
number: {{ $svcPort }}
{{- end }}
{{- end }}
{{- end }}
46 changes: 46 additions & 0 deletions dysnix/solana/templates/ingress-ws.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{{- if .Values.ingress.ws.enabled -}}
{{- $fullName := include "solana.fullname" . -}}
{{- $svcPort := add .Values.services.rpc.port 1 -}}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ $fullName }}-ws
labels:
{{- include "solana.labels" . | nindent 4 }}
type: ws
{{- with .Values.ingress.ws.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- if .Values.ingress.ws.className }}
ingressClassName: {{ .Values.ingress.ws.className }}
{{- end }}
{{- if .Values.ingress.ws.tls }}
tls:
{{- range .Values.ingress.ws.tls }}
- hosts:
{{- range .hosts }}
- {{ . | quote }}
{{- end }}
secretName: {{ .secretName }}
{{- end }}
{{- end }}
rules:
{{- range .Values.ingress.ws.hosts }}
- host: {{ .host | quote }}
http:
paths:
{{- range .paths }}
- path: {{ .path }}
{{- if .pathType }}
pathType: {{ .pathType }}
{{- end }}
backend:
service:
name: {{ $fullName }}
port:
number: {{ $svcPort }}
{{- end }}
{{- end }}
{{- end }}
11 changes: 11 additions & 0 deletions dysnix/solana/templates/scripts.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "solana.fullname" . }}-scripts
labels:
{{- include "solana.labels" . | nindent 4 }}
data:
start.sh: |-
{{- include (print $.Template.BasePath "/scripts/_start.tpl") . | nindent 4 }}
health.sh: |-
{{- include (print $.Template.BasePath "/scripts/_health.tpl") . | nindent 4 }}
21 changes: 21 additions & 0 deletions dysnix/solana/templates/scripts/_health.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/usr/bin/env sh
# shellcheck disable=SC3040,SC2046

set -e

HTTP_PORT='{{ get .Values.solanaArgs "rpc-port" }}'

# expected outputs:
# - {"jsonrpc":"2.0","result":"ok","id":1}
# - {"jsonrpc":"2.0","error":{"code":-32005,"message":"Node is unhealthy","data":{}},"id":1}
get_health() {
curl -s "http://$MY_POD_IP:$HTTP_PORT" \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","method":"getHealth","id":1}'
}

if get_health | jq -r --exit-status '.error.message'; then
exit 1
fi

exit 0
20 changes: 20 additions & 0 deletions dysnix/solana/templates/scripts/_start.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/bin/sh

exec solana-validator
{{- range $arg, $val := .Values.solanaArgs }}
{{- if and $arg $val }} \{{ end }}
{{- if kindIs "float64" $val }}
--{{ $arg }}={{ int $val }}
{{- else if kindIs "bool" $val }}
{{- if $val }}
--{{ $arg }}
{{- end }}
{{- else if kindIs "slice" $val }}
{{- range $key, $nestedVal := $val }}
{{- if $key }} \{{ end }}
--{{ $arg }}={{ $nestedVal }}
{{- end }}
{{- else }}
--{{ $arg }}={{ $val }}
{{- end }}
{{- end }}
Loading
Loading