-
Notifications
You must be signed in to change notification settings - Fork 124
Add Helm charts for Vespa multinode HA deployment #1702
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| dependencies: | ||
| - name: config | ||
| repository: "" | ||
| version: 0.1.0 | ||
| - name: services | ||
| repository: "" | ||
| version: 0.1.0 | ||
| digest: sha256:7550b9a2ad831bd383e6bf22c51013efd466f8d287df676433766fd59e9aac29 | ||
| generated: "2025-04-10T10:54:29.929533+01:00" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| apiVersion: v2 | ||
| name: vespa | ||
| version: 0.1.0 | ||
| dependencies: | ||
| - name: config | ||
| alias: config | ||
| version: 0.1.0 | ||
| - name: services | ||
| alias: services | ||
| version: 0.1.0 | ||
| condition: services.enabled |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,170 @@ | ||
| <!-- Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> | ||
|
|
||
| <picture> | ||
| <source media="(prefers-color-scheme: dark)" srcset="https://assets.vespa.ai/logos/Vespa-logo-green-RGB.svg"> | ||
| <source media="(prefers-color-scheme: light)" srcset="https://assets.vespa.ai/logos/Vespa-logo-dark-RGB.svg"> | ||
| <img alt="#Vespa" width="200" src="https://assets.vespa.ai/logos/Vespa-logo-dark-RGB.svg" style="margin-bottom: 25px;"> | ||
| </picture> | ||
|
|
||
| # Multinode-HA using Helm | ||
| This guide uses the multinode-HA configuration and principles and deploys a Vespa application using Kubernetes and Helm. | ||
|
|
||
| ## Overview | ||
| This deployment is designed for high availability and uses the Helm chart consisting of two primary modules: | ||
| 1. `config` - contains and deploys **vespa-configserver**, which must be running successfully before starting other components. | ||
| 2. `services` - starts **admin**, **content**, **feed**, and **query** clusters, depending on a successful `configserver` startup. | ||
|
|
||
| A key mechanism ensures the correct start order of the modules: `initContainers` in `services` waits for the `configserver` to become ready by repeatedly checking its health. Only after the `configserver` successfully initializes, the `services` module will proceed to start. Here’s the command used in the `initContainers`: | ||
|
|
||
| ```bash | ||
| until curl -f http://vespa-configserver-0.vespa-internal.vespa.svc.cluster.local:19071/state/v1/health; do | ||
| echo "Waiting for Vespa ConfigServer to be ready in namespace $CONFIGSERVER_NAMESPACE..."; | ||
| sleep 5; | ||
| done | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| ## Prerequisites | ||
| Make sure the following tools are installed and configured: | ||
| * [Helm](https://helm.sh/docs/intro/install/) | ||
| * A Kubernetes cluster - either local or hosted (e.g., Azure AKS, AWS EKS, etc.) | ||
|
|
||
| --- | ||
|
|
||
| ## Installation | ||
| Clone the repository: | ||
| ```bash | ||
| git clone --depth 1 https://github.com/vespa-engine/sample-apps.git | ||
| cd sample-apps/examples/operations/multinode-HA/helm | ||
| ``` | ||
|
|
||
| Prepare your `values.yaml` with the desired configuration. Here's an example: | ||
| ```yaml | ||
| config: | ||
| serverReplicas: 3 | ||
| services: | ||
| content: | ||
| replicas: 2 | ||
| storage: 25Gi | ||
| ``` | ||
|
|
||
| Deploy Vespa using Helm: | ||
| ```bash | ||
| helm dependency update helm | ||
| helm upgrade --install vespa . -n vespa --create-namespace -f values.yaml | ||
| ``` | ||
|
|
||
| This will create the namespace `vespa` and deploy all components of the application. | ||
|
|
||
| --- | ||
|
|
||
| ## Deploy the application package | ||
|
|
||
| ``` | ||
| kubectl port-forward -n vespa pod/vespa-configserver-0 19071 | ||
| ``` | ||
|
|
||
| ``` | ||
| (cd conf && zip -r - .) | \ | ||
| curl --header Content-Type:application/zip \ | ||
| --data-binary @- \ | ||
| http://localhost:19071/application/v2/tenant/default/prepareandactivate | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| ## Module Details | ||
|
|
||
| ### Config Module | ||
| The `config` module contains the **vespa-configserver**, which is essential for Vespa's operation. This module deploys a StatefulSet with `serverReplicas` to ensure high availability. | ||
|
|
||
| #### ConfigServer Health Check | ||
| The `configserver` health is verified by an HTTP curl to its `/state/v1/health` endpoint. The `services` module will not start until all `configserver` replicas are running and reachable. | ||
|
|
||
| Here’s an example of the expected `configserver` health response: | ||
| ```json | ||
| { | ||
| "time" : 1678268549957, | ||
| "status" : { | ||
| "code" : "up" | ||
| }, | ||
| "metrics" : { | ||
| "snapshot" : { | ||
| "from" : 1.678268489718E9, | ||
| "to" : 1.678268549718E9 | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ### Services Module | ||
| The `services` module contains the following components: | ||
| - **Admin**: Handles Vespa cluster administration. | ||
| - **Feed**: Handles document feeding. | ||
| - **Query**: Handles document queries. | ||
| - **Content**: Stores indexed data. | ||
|
|
||
| This module is configured to depend on the `config` module startup. The `initContainers` logic ensures that no pods in the `services` module are started until the `configserver` reaches a stable, healthy state. | ||
|
|
||
| Below is the typical `initContainers` logic defined for `services`: | ||
| ```bash | ||
| until curl -f http://vespa-configserver-0.vespa-internal.vespa.svc.cluster.local:19071/state/v1/health; do | ||
| echo "Waiting for Vespa ConfigServer to be ready in namespace $CONFIGSERVER_NAMESPACE..."; | ||
| sleep 5; | ||
| done | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| ## Verification | ||
| Once the installation completes, you can test the Vespa application by: | ||
| 1. Feeding a document: | ||
| ```bash | ||
| curl -X POST http://vespa-query-container-0.vespa.svc.cluster.local/document/v1/my-space/my-doc \ | ||
| -d '{"id": "id:my-space:my-doc::1", "fields": {"field1": "value1"}}' | ||
| ``` | ||
|
|
||
| 2. Querying documents: | ||
| ```bash | ||
| curl "http://vespa-query-container-0.vespa.svc.cluster.local/search/?query=my-query" | ||
| ``` | ||
|
|
||
| or | ||
|
|
||
| ``` | ||
| kubectl -n vespa port-forward svc/vespa-query 8080 | ||
| ``` | ||
| ``` | ||
| curl --data-urlencode 'yql=select * from sources * where true' \ | ||
| http://localhost:8080/search/ | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| ## Customization and Scaling | ||
| Values such as `config.serverReplicas`, `services.content.replicas`, and `services.content.storage` can be adjusted in `values.yaml` to match your requirements for scaling and resource configuration. For example: | ||
| ```yaml | ||
| config: | ||
| serverReplicas: 5 | ||
| services: | ||
| content: | ||
| replicas: 4 | ||
| storage: 50Gi | ||
| ``` | ||
|
|
||
| Refer to the official [Vespa documentation](https://docs.vespa.ai/en/) for advanced deployment details and customization options. | ||
|
|
||
| --- | ||
|
|
||
| ## Troubleshooting | ||
| Check Helm release status to confirm all components deployed successfully: | ||
| ```bash | ||
| helm status vespa -n vespa | ||
| ``` | ||
|
|
||
| If pods are stuck, ensure that: | ||
| 1. The `configserver` is running and reachable. | ||
| 2. Kubernetes networking allows communication between the pods. | ||
|
|
||
| For further troubleshooting details, refer to the [Vespa troubleshooting guide](https://docs.vespa.ai/en/operations.html). | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| apiVersion: v2 | ||
| name: config | ||
| version: 0.1.0 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| apiVersion: v1 | ||
| kind: ConfigMap | ||
| metadata: | ||
| name: vespa-config | ||
| data: | ||
| VESPA_CONFIGSERVERS: >- | ||
| {{- $domain := printf "%s.%s.svc.cluster.local" .Values.headlessName .Release.Namespace }} | ||
| {{- $replicas := int .Values.serverReplicas }} | ||
| {{- $serverList := list }} | ||
| {{- range $i := until $replicas }} | ||
| {{- $serverList = append $serverList (printf "vespa-configserver-%d.%s" $i $domain) }} | ||
| {{- end }} | ||
| {{ join "," $serverList }} | ||
| VESPA_CONFIGSERVER_JVMARGS: "{{ .Values.serverJvmArgs }}" | ||
| VESPA_CONFIGPROXY_JVMARGS: "{{ .Values.proxyJvmArgs }}" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| # Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. | ||
|
|
||
| apiVersion: apps/v1 | ||
| kind: StatefulSet | ||
| metadata: | ||
| name: vespa-configserver | ||
| spec: | ||
| # Use three servers for proper ZooKeeper quorum: | ||
| replicas: {{ .Values.serverReplicas }} | ||
| selector: | ||
| matchLabels: | ||
| app: vespa-configserver | ||
| name: {{ .Values.headlessName }} | ||
| serviceName: {{ .Values.headlessName }} | ||
| template: | ||
| metadata: | ||
| labels: | ||
| app: vespa-configserver | ||
| name: {{ .Values.headlessName }} | ||
| spec: | ||
| initContainers: | ||
| - name: chown-var | ||
| securityContext: | ||
| runAsUser: 0 | ||
| image: busybox | ||
| command: ["sh", "-c", "chown -R 1000 /opt/vespa/var"] | ||
| volumeMounts: | ||
| - name: vespa-var | ||
| mountPath: /opt/vespa/var | ||
| - name: chown-logs | ||
| securityContext: | ||
| runAsUser: 0 | ||
| image: busybox | ||
| command: ["sh", "-c", "chown -R 1000 /opt/vespa/logs"] | ||
| volumeMounts: | ||
| - name: vespa-logs | ||
| mountPath: /opt/vespa/logs | ||
| containers: | ||
| - name: vespa-configserver | ||
| image: vespaengine/vespa | ||
| args: ["configserver,services"] | ||
| imagePullPolicy: Always | ||
| securityContext: | ||
| runAsUser: 1000 | ||
| volumeMounts: | ||
| - name: vespa-var | ||
| mountPath: /opt/vespa/var | ||
| - name: vespa-logs | ||
| mountPath: /opt/vespa/logs | ||
| - name: vespa-workspace | ||
| mountPath: /workspace | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is this mount point used for? I can't find any references to it anywhere else in the chart. |
||
| envFrom: | ||
| - configMapRef: | ||
| name: vespa-config | ||
| # Note that the below are minimum resources for demo use. | ||
| # Use 4G or more for real use cases | ||
| resources: | ||
| requests: | ||
| memory: "1.5G" | ||
| limits: | ||
| memory: "1.5G" | ||
|
Comment on lines
+59
to
+61
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Make memory configurable from values for easier override? |
||
| volumeClaimTemplates: | ||
| # The below are tiny volumes for demo purposes. | ||
| - metadata: | ||
| name: vespa-var | ||
| spec: | ||
| accessModes: [ "ReadWriteOnce" ] | ||
| resources: | ||
| requests: | ||
| storage: 5Gi | ||
| - metadata: | ||
| name: vespa-logs | ||
| spec: | ||
| accessModes: [ "ReadWriteOnce" ] | ||
| resources: | ||
| requests: | ||
| storage: 5Gi | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Make volume claim sizes configurable for easier override? |
||
| - metadata: | ||
| name: vespa-workspace | ||
| spec: | ||
| accessModes: [ "ReadWriteOnce" ] | ||
| resources: | ||
| requests: | ||
| storage: 1Gi | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| # Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. | ||
|
|
||
| apiVersion: v1 | ||
| kind: Service | ||
| metadata: | ||
| name: {{ .Values.headlessName }} | ||
| labels: | ||
| name: {{ .Values.headlessName }} | ||
| spec: | ||
| selector: | ||
| name: {{ .Values.headlessName }} | ||
| clusterIP: None |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| headlessName: "vespa-internal" | ||
| serverReplicas: 3 | ||
| serverJvmArgs: "-Xms32M -Xmx128M" | ||
| proxyJvmArgs: "-Xms32M -Xmx32M" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| apiVersion: v2 | ||
| name: services | ||
| version: 0.1.0 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| # Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. | ||
|
|
||
| apiVersion: apps/v1 | ||
| kind: StatefulSet | ||
| metadata: | ||
| name: vespa-admin | ||
| spec: | ||
| replicas: 1 | ||
| selector: | ||
| matchLabels: | ||
| app: vespa-admin | ||
| name: vespa-internal | ||
| serviceName: vespa-internal | ||
| template: | ||
| metadata: | ||
| labels: | ||
| app: vespa-admin | ||
| name: vespa-internal | ||
| spec: | ||
| initContainers: | ||
| - name: wait-for-configserver | ||
| image: curlimages/curl | ||
| env: | ||
| - name: CONFIGSERVER_NAMESPACE | ||
| value: {{ .Release.Namespace }} | ||
| - name: CONFIGSERVER_SERVICE | ||
| value: vespa-configserver-0.vespa-internal | ||
| command: | ||
| - /bin/sh | ||
| - -c | ||
| - | | ||
| until curl -f http://vespa-configserver-0.vespa-internal.vespa.svc.cluster.local:19071/state/v1/health; do | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it possible to get |
||
| echo "Waiting for Vespa ConfigServer to be ready in namespace $CONFIGSERVER_NAMESPACE..."; | ||
| sleep 5; | ||
| done | ||
| containers: | ||
| - name: vespa-admin | ||
| image: vespaengine/vespa | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would be nice to support specifying the Vespa version, defaulting to |
||
| args: ["services"] | ||
| imagePullPolicy: Always | ||
| envFrom: | ||
| - configMapRef: | ||
| name: vespa-config | ||
| securityContext: | ||
| runAsUser: 1000 | ||
| resources: | ||
| requests: | ||
| memory: "1G" | ||
| limits: | ||
| memory: "1G" | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's keep three as the default number of config servers recommended across all documentation. More than that adds extra operational work, with minimal advantages.