diff --git a/.github/workflows/helm-chart-ci.yml b/.github/workflows/helm-chart-ci.yml new file mode 100644 index 0000000000..0291b4d6ba --- /dev/null +++ b/.github/workflows/helm-chart-ci.yml @@ -0,0 +1,90 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +name: "Helm Chart CI" + +on: + push: + branches: [ master, 'release-*' ] + paths: [ 'helm/**', '.github/workflows/helm-chart-ci.yml' ] + pull_request: + paths: [ 'helm/**', '.github/workflows/helm-chart-ci.yml' ] + +permissions: + contents: read + +jobs: + lint-and-render: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: false + + - uses: azure/setup-helm@v4 + with: + version: v3.16.2 + + - name: helm lint + run: | + helm lint helm/hugegraph + helm lint helm/hugegraph -f helm/hugegraph/values-single.yaml + helm lint helm/hugegraph -f helm/hugegraph/values-cluster.yaml + + - name: helm template + run: | + for preset in "" "-f helm/hugegraph/values-single.yaml" "-f helm/hugegraph/values-cluster.yaml"; do + helm template ci helm/hugegraph $preset > /dev/null + done + + - name: reject invalid values + run: | + # each of these must fail; the schema and helpers are the contract + ! helm template ci helm/hugegraph --set pd.replicas=100 2>/dev/null + ! helm template ci helm/hugegraph --set pd.pdb.minAvailable=3 2>/dev/null + ! helm template ci helm/hugegraph --set server.hpa.enabled=true 2>/dev/null + ! helm template ci helm/hugegraph \ + --set server.hpa.enabled=true \ + --set server.hpa.minReplicas=2 \ + --set server.resources.requests.cpu=100m \ + --set server.pdb.enabled=true \ + --set server.pdb.minAvailable=2 2>/dev/null + ! helm template ci helm/hugegraph --set server.auth.enabled=true 2>/dev/null + + - name: kubeconform + shell: bash + run: | + set -o pipefail + curl -sSLo /tmp/kc.tar.gz https://github.com/yannh/kubeconform/releases/download/v0.6.7/kubeconform-linux-amd64.tar.gz + tar -xzf /tmp/kc.tar.gz -C /tmp + for preset in "" "-f helm/hugegraph/values-single.yaml" "-f helm/hugegraph/values-cluster.yaml"; do + helm template ci helm/hugegraph $preset | /tmp/kubeconform -strict -summary -kubernetes-version 1.23.0 + done + + - name: legacy --reuse-values compatibility + run: | + # A release created before a field existed must still render. This has + # regressed five times, so it is guarded here rather than by review. + # `-f` is NOT equivalent: it merges over the new defaults, whereas + # --reuse-values discards them, so the fixture must become values.yaml. + cp -R helm/hugegraph /tmp/legacy + cp helm/hugegraph/testdata/values-pre-hardening.yaml /tmp/legacy/values.yaml + helm template legacy /tmp/legacy > /dev/null + helm template legacy /tmp/legacy --is-upgrade > /dev/null + + - name: helm package + run: helm package helm/hugegraph -d /tmp/chart diff --git a/README.md b/README.md index adf9792776..a876069740 100644 --- a/README.md +++ b/README.md @@ -207,9 +207,16 @@ For advanced Docker configurations, see: > > **Version Tags**: Use release tags (e.g., `1.7.0`) for stable deployments. The `latest` tag should only be used for testing or development. +### Option 2: Kubernetes with Helm + +The HStore Helm chart deploys HugeGraph PD, Store, and Server as a distributed +Kubernetes cluster. See the [chart documentation](helm/hugegraph/README.md) for +single-node and highly available presets, configuration, and +upgrade guidance. +
-Option 2: Download Binary Package +Option 3: Download Binary Package Download pre-built packages from the [Download Page](https://hugegraph.apache.org/docs/download/download/): @@ -242,7 +249,7 @@ For detailed instructions, see the [Binary Installation Guide](https://hugegraph
-Option 3: Build from Source +Option 4: Build from Source Build from source for development or customization: diff --git a/helm/hugegraph/.helmignore b/helm/hugegraph/.helmignore new file mode 100644 index 0000000000..8016b1b85d --- /dev/null +++ b/helm/hugegraph/.helmignore @@ -0,0 +1,33 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Patterns to ignore when building packages. +.DS_Store +.git/ +.gitignore +*.swp +*.bak +*.tmp +*.orig +*~ +.idea/ +.vscode/ + +# Contributor tooling, if present in a working tree. Never ship it. +scripts/ +testdata/ + +# Never package a chart archive inside a chart. +*.tgz diff --git a/helm/hugegraph/Chart.yaml b/helm/hugegraph/Chart.yaml new file mode 100644 index 0000000000..0b7f7ccde1 --- /dev/null +++ b/helm/hugegraph/Chart.yaml @@ -0,0 +1,36 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apiVersion: v2 +name: hugegraph +description: Helm chart for Apache HugeGraph HStore cluster (PD + Store + Server) +type: application +version: 0.1.0 +appVersion: "latest" +kubeVersion: ">=1.23.0-0" +keywords: + - hugegraph + - graph + - hstore + - raft +home: https://hugegraph.apache.org/ +sources: + - https://github.com/apache/hugegraph +maintainers: + - name: HugeGraph Community + url: https://hugegraph.apache.org/ + email: dev@hugegraph.apache.org diff --git a/helm/hugegraph/README.md b/helm/hugegraph/README.md new file mode 100644 index 0000000000..b68a102893 --- /dev/null +++ b/helm/hugegraph/README.md @@ -0,0 +1,397 @@ +# HugeGraph HStore Helm Chart + +[Apache HugeGraph](https://hugegraph.apache.org/) - an open source, distributed graph database. + +## Documentation + +This chart deploys a distributed HugeGraph cluster - PD, Store, and Server - on +Kubernetes. For HugeGraph itself see . + +Note that this chart requires Helm 3. `--reset-then-reuse-values`, referenced +under Upgrading, requires Helm 3.14 or later. + +## Prerequisites Details + +* Kubernetes 1.23+ (the chart renders `autoscaling/v2` and `policy/v1`) +* PV support on the underlying infrastructure: a default StorageClass, or an + explicit `storageClassName` for PD and Store +* Sufficient memory for nine JVM processes in the default topology. + Insufficient memory causes OOM kills that surface as silent Raft failures + rather than as clear errors. + +## Chart Details + +| Component | Workload | Purpose | +|---|---|---| +| PD | StatefulSet + PVC | Placement driver; Raft group tracking Stores and partitions | +| Store | StatefulSet + PVC | Graph data storage (HStore) | +| Server | Deployment | Gremlin and REST query layer | + +A distributed HugeGraph cluster has a startup contract that this chart encodes +so operators do not have to: + +- **Server does not run `init-store`.** The chart injects + `HG_SERVER_INIT_STORE_ENABLED=false`, and the image's `init-store` exits when + `init_store.enabled=false`, after which Server registers with PD normally. + This matters because nothing serializes Server replicas: without the gate, + every replica would initialize the same backend concurrently. The chart + creates no init Job and does not set `HG_SERVER_SKIP_INIT`. Standalone + behavior is unchanged, because the option defaults to `true` when unset. +- **Store waits for PD quorum** in an init container before starting, so Store + never registers against an incomplete PD Raft group. +- **The Server startup probe allows at least 450 seconds.** The image may spend + 300 seconds waiting for storage and a further 120 seconds in the start + command. A lower configured `failureThreshold` is raised to this floor rather + than being rejected. +- **The image entrypoint keeps ownership of `PASSWORD` handling and + `auth.admin_pa`.** When authentication or a custom port or REST tuning is + configured, the chart's wrapper only ensures `usePD=true` and `pd.peers` are + present, then hands off to the image entrypoint. +- **Resource names reserve their suffix and StatefulSet ordinal before + truncation,** so a long release name cannot produce colliding or over-long + Pod and Service names, and PD/Store identities stay fixed when replicas + change. + +## Installing the Chart + +```bash +helm install hugegraph ./helm/hugegraph --namespace hugegraph --create-namespace +``` + +This deploys 3 PD + 3 Store + 3 Server, preserves the image's automatic JVM +sizing, and sets no resource requests or limits. Set resources before +production use. + +This first chart is version `0.1.0`. While the contribution is a draft, its +component image tags and `appVersion` track `latest` with pull policy `Always`. +Before stable publication, pin all three component tags and `appVersion` to the +next HugeGraph release and switch the component pull policies to +`IfNotPresent`. + +Verify the release: + +```bash +helm test hugegraph --namespace hugegraph +``` + +### Values Presets + +| File | Purpose | +|---|---| +| `values.yaml` | Default 3+3+3 topology | +| `values-single.yaml` | Single-node 1+1+1 example | +| `values-cluster.yaml` | Production 3+3+3 starting point with JVM/resources, PD/Store PDBs, and required anti-affinity | + +`values-cluster.yaml` is a production starting point, not a capacity +guarantee. Recalculate capacity for the graph size, traffic, failure budget, +node topology, and storage class before production use. + +## Upgrading the Chart + +```bash +helm upgrade hugegraph ./helm/hugegraph --namespace hugegraph --reuse-values +``` + +Every optional field stays optional, so a release created by an earlier +revision continues to render under `--reuse-values`. Note that `--reuse-values` +keeps the old release's values as the complete base, so a release created +before a field existed does **not** pick up its new default — including the +hardened `securityContext`, ServiceAccounts, and `terminationGracePeriodSeconds`. +Pod-level token mounting is the one exception: it is disabled unconditionally. +Use `-f` with your own values, or `--reset-then-reuse-values`, to adopt them. + +PD and Store resource names reserve room for their StatefulSet ordinal before truncation, so +identities stay fixed across replica changes and scaling never renames a +PersistentVolumeClaim. + +## Uninstalling the Chart + +```bash +helm uninstall hugegraph --namespace hugegraph +``` + +Helm does not remove PersistentVolumeClaims created by StatefulSets. Delete +them explicitly, and only when the data is no longer needed. + +## Configuration + +The following table lists the configurable parameters of the chart and their +default values. + +### Global + +| Parameter | Description | Default | +|---|---|---| +| `nameOverride` | Override the chart name in generated resource names | `""` | +| `fullnameOverride` | Override the full generated resource name | `""` | +| `imagePullSecrets` | Secrets used to pull the PD, Store, and Server images | `[]` | + +### PD + +| Parameter | Description | Default | +|---|---|---| +| `pd.replicas` | PD StatefulSet replicas. Maximum `99` | `3` | +| `pd.image.repository` | PD image repository | `hugegraph/pd` | +| `pd.image.tag` | PD image tag. Tracks the development image until the next release is pinned | `latest` | +| `pd.image.pullPolicy` | PD image pull policy | `Always` | +| `pd.javaOpts` | Empty preserves the image's automatic JVM sizing | `""` | +| `pd.ports.grpc` | PD gRPC port | `8686` | +| `pd.ports.rest` | PD REST port, also used by probes | `8620` | +| `pd.ports.raft` | PD Raft port | `8610` | +| `pd.dataPath` | PD data directory inside the container | `/hugegraph-pd/pd_data` | +| `pd.storage.size` | PD PersistentVolumeClaim size | `10Gi` | +| `pd.storage.storageClassName` | Empty uses the cluster default StorageClass | `""` | +| `pd.resources` | PD container resources. Set these for production | `{}` | +| `pd.podSecurityContext` | Pod-level securityContext, rendered only when set | `{}` | +| `pd.securityContext` | Container-level securityContext. Hardened by default; `runAsNonRoot` is not set because the published images run as root | `allowPrivilegeEscalation: false`, `capabilities.drop: [ALL]`, `seccompProfile: RuntimeDefault` | +| `pd.antiAffinity` | One of `required`, `preferred`, `disabled` | `required` | +| `pd.nodeSelector` | Node selector for pd Pods | `{}` | +| `pd.tolerations` | Tolerations for pd Pods | `[]` | +| `pd.affinity` | Raw affinity; overrides `pd.antiAffinity` when set | `{}` | +| `pd.topologySpreadConstraints` | Topology spread constraints for pd Pods | `[]` | +| `pd.priorityClassName` | PriorityClass for pd Pods | `""` | +| `pd.podAnnotations` | Extra annotations on pd Pods | `{}` | +| `pd.podLabels` | Extra labels on pd Pods | `{}` | +| `pd.extraEnv` | Extra environment variables for the PD container | `[]` | +| `pd.terminationGracePeriodSeconds` | Shutdown grace period | `300` | +| `pd.serviceAccount.create` | Create a ServiceAccount for pd | `true` | +| `pd.serviceAccount.name` | Use an existing ServiceAccount instead | `""` | +| `pd.serviceAccount.annotations` | Annotations on the created ServiceAccount | `{}` | +| `pd.serviceAccount.automountServiceAccountToken` | Mount an API token. The chart makes no API calls | `false` | +| `pd.pdb.enabled` | Create a PodDisruptionBudget for PD | `true` | +| `pd.pdb.minAvailable` | Must be strictly less than `pd.replicas`. No PDB is rendered when `pd.replicas` is 1 | `2` | +| `pd.probes.*.periodSeconds` | Probe interval | see `values.yaml` | +| `pd.probes.*.failureThreshold` | Probe failure threshold | see `values.yaml` | +| `pd.probes.*.timeoutSeconds` | Probe timeout. Defaults to `5` on readiness/liveness; Kubernetes would otherwise apply `1` | `5` | +| `pd.probes.*.initialDelaySeconds` | Optional probe start delay | unset | +| `pd.probes.*.successThreshold` | Optional probe success threshold | unset | + +### Store + +| Parameter | Description | Default | +|---|---|---| +| `store.replicas` | Store StatefulSet replicas. Maximum `99` | `3` | +| `store.image.repository` | Store image repository | `hugegraph/store` | +| `store.image.tag` | Store image tag. Tracks the development image until the next release is pinned | `latest` | +| `store.image.pullPolicy` | Store image pull policy | `Always` | +| `store.javaOpts` | Empty preserves the image's automatic JVM sizing | `""` | +| `store.ports.grpc` | Store gRPC port | `8500` | +| `store.ports.raft` | Store Raft port | `8510` | +| `store.ports.rest` | Store REST port | `8520` | +| `store.dataPath` | Store data directory | `/hugegraph-store/storage` | +| `store.storage.size` | Store PersistentVolumeClaim size | `50Gi` | +| `store.storage.storageClassName` | Empty uses the cluster default StorageClass | `""` | +| `store.resources` | Store container resources. Set these for production | `{}` | +| `store.podSecurityContext` | Pod-level securityContext, rendered only when set | `{}` | +| `store.securityContext` | Container-level securityContext; also applied to the PD-quorum init container. Hardened by default; `runAsNonRoot` is not set because the published images run as root | `allowPrivilegeEscalation: false`, `capabilities.drop: [ALL]`, `seccompProfile: RuntimeDefault` | +| `store.waitTimeoutSeconds` | Bound on the PD-quorum wait before the init container fails | `900` | +| `store.antiAffinity` | One of `required`, `preferred`, `disabled` | `required` | +| `store.nodeSelector` | Node selector for store Pods | `{}` | +| `store.tolerations` | Tolerations for store Pods | `[]` | +| `store.affinity` | Raw affinity; overrides `store.antiAffinity` when set | `{}` | +| `store.topologySpreadConstraints` | Topology spread constraints for store Pods | `[]` | +| `store.priorityClassName` | PriorityClass for store Pods | `""` | +| `store.podAnnotations` | Extra annotations on store Pods | `{}` | +| `store.podLabels` | Extra labels on store Pods | `{}` | +| `store.extraEnv` | Extra environment variables for the Store container | `[]` | +| `store.terminationGracePeriodSeconds` | Shutdown grace period | `300` | +| `store.serviceAccount.create` | Create a ServiceAccount for store | `true` | +| `store.serviceAccount.name` | Use an existing ServiceAccount instead | `""` | +| `store.serviceAccount.annotations` | Annotations on the created ServiceAccount | `{}` | +| `store.serviceAccount.automountServiceAccountToken` | Mount an API token. The chart makes no API calls | `false` | +| `store.pdb.enabled` | Create a PodDisruptionBudget for Store | `true` | +| `store.pdb.minAvailable` | Must be strictly less than `store.replicas`. No PDB is rendered when `store.replicas` is 1 | `2` | +| `store.waitImage` | Image for the PD-quorum init container | `curlimages/curl:8.5.0` | +| `store.waitResources` | Resources for the init container | `{}` | +| `store.probes.*` | Same probe keys as PD | see `values.yaml` | + +### Server + +| Parameter | Description | Default | +|---|---|---| +| `server.replicas` | Server Deployment replicas. Ignored when `server.hpa.enabled` | `3` | +| `server.image.repository` | Server image repository | `hugegraph/server` | +| `server.image.tag` | Server image tag. Tracks the development image until the next release is pinned | `latest` | +| `server.image.pullPolicy` | Server image pull policy | `Always` | +| `server.javaOpts` | Empty preserves the image's automatic JVM sizing | `""` | +| `server.port` | Server REST port, container port, and Service port | `8080` | +| `server.backend` | Storage backend | `hstore` | +| `server.resources` | Server resources. `requests.cpu` is required when HPA is enabled | `{}` | +| `server.podSecurityContext` | Pod-level securityContext, rendered only when set | `{}` | +| `server.securityContext` | Container-level securityContext. Hardened by default; `runAsNonRoot` is not set because the published images run as root | `allowPrivilegeEscalation: false`, `capabilities.drop: [ALL]`, `seccompProfile: RuntimeDefault` | +| `server.pdb.enabled` | Create a PodDisruptionBudget for Server. Off by default: Server holds no quorum | `false` | +| `server.pdb.minAvailable` | Must be less than `server.hpa.minReplicas` when HPA is enabled, otherwise less than `server.replicas` | `2` | +| `server.antiAffinity` | One of `required`, `preferred`, `disabled`. Defaults to `preferred` rather than `required` because HPA may scale Server past the node count; set `required` when replicas always stay below it | `preferred` | +| `server.nodeSelector` | Node selector for server Pods | `{}` | +| `server.tolerations` | Tolerations for server Pods | `[]` | +| `server.affinity` | Raw affinity; overrides `server.antiAffinity` when set | `{}` | +| `server.topologySpreadConstraints` | Topology spread constraints for server Pods | `[]` | +| `server.priorityClassName` | PriorityClass for server Pods | `""` | +| `server.podAnnotations` | Extra annotations on server Pods | `{}` | +| `server.podLabels` | Extra labels on server Pods | `{}` | +| `server.extraEnv` | Extra environment variables for the Server container | `[]` | +| `server.terminationGracePeriodSeconds` | Shutdown grace period | `60` | +| `server.serviceAccount.create` | Create a ServiceAccount for server | `true` | +| `server.serviceAccount.name` | Use an existing ServiceAccount instead | `""` | +| `server.serviceAccount.annotations` | Annotations on the created ServiceAccount | `{}` | +| `server.serviceAccount.automountServiceAccountToken` | Mount an API token. The chart makes no API calls | `false` | +| `server.waitImage` | Image for the Helm test hook | `curlimages/curl:8.5.0` | +| `server.testResources` | Resources for the Helm test hook container | `{}` | +| `server.restServer.minFreeMemory` | Empty preserves the image default | `""` | +| `server.restServer.batchMaxWriteThreads` | Empty preserves the image default | `""` | +| `server.initStoreEnabled` | Must remain `false` for distributed HStore | `false` | +| `server.auth.enabled` | Enable admin authentication | `false` | +| `server.auth.existingSecret` | Required when auth is enabled; must contain key `password` | `""` | +| `server.ingress.enabled` | Create an Ingress for the Server Service | `false` | +| `server.ingress.className` | IngressClass name | `""` | +| `server.ingress.annotations` | Ingress annotations (cert-manager, nginx, ALB) | `{}` | +| `server.service.type` | Server Service type | `ClusterIP` | +| `server.service.annotations` | Server Service annotations | `{}` | +| `server.ingress.hosts` | Ingress hosts and paths | see `values.yaml` | +| `server.ingress.tls` | Ingress TLS configuration | `[]` | +| `server.hpa.enabled` | Create a HorizontalPodAutoscaler | `false` | +| `server.hpa.minReplicas` | HPA minimum replicas | `3` | +| `server.hpa.maxReplicas` | HPA maximum replicas | `10` | +| `server.hpa.targetCPUUtilizationPercentage` | HPA CPU utilization target | `70` | +| `server.probes.startup.failureThreshold` | Raised automatically so the budget is at least 450s | `90` | +| `server.probes.startup.periodSeconds` | Startup probe interval | `5` | +| `server.probes.*` | Same optional probe keys as PD | see `values.yaml` | + +When `server.hpa.enabled` is `true` the Deployment omits `spec.replicas`, so a +Helm upgrade does not overwrite the autoscaler's live replica count. Enabling +utilization-based HPA requires a strictly positive +`server.resources.requests.cpu`. + +Specify each parameter with `--set`, or supply a YAML file with `-f`: + +```bash +helm install hugegraph ./helm/hugegraph --set server.replicas=5 +``` + +`values.schema.json` and template helpers reject invalid input at render time, +before anything reaches the cluster: + +- Unknown keys and wrong types are rejected. +- `server.initStoreEnabled` must remain `false` for a distributed deployment. +- With authentication enabled, `server.auth.existingSecret` must name a Secret + containing a `password` key. With authentication disabled it must be empty, + so a configured but inactive Secret reference cannot be overlooked. A missing + Secret fails when Kubernetes configures the container; an empty `password` + fails in the Server startup wrapper. +- `server.hpa.minReplicas` must not exceed `maxReplicas`, and enabling + utilization-based HPA requires a strictly positive + `server.resources.requests.cpu`. +- `pdb.minAvailable` must be less than the matching `replicas`, so a + PodDisruptionBudget cannot permanently block node drains. +- `pd.replicas` and `store.replicas` are capped at 99. + +## Deep Dive + +### Connecting to the Cluster + +```bash +kubectl port-forward -n hugegraph svc/hugegraph-server 8080:8080 +curl http://127.0.0.1:8080/versions +curl http://127.0.0.1:8080/graphs +``` + +### Cluster Health + +| Component | Port | Purpose | +|------|-------------|---------| +| PD | `8686` | gRPC (Store and Server clients) | +| PD | `8620` | REST / health probes | +| PD | `8610` | Raft | +| Store | `8500` | gRPC | +| Store | `8510` | Raft | +| Store | `8520` | REST / health probes | +| Server | `8080` | Gremlin and REST API | + +All ports are configurable through `values.yaml`. Changing `server.port` updates +the listener, container port, and Service together. + +--- + +### Scaling + +PD and Store reserve the maximum StatefulSet ordinal in their resource names, +so scaling never renames a PersistentVolumeClaim or shifts a Pod identity. +Both are capped at 99 replicas. + +Server scales through `server.replicas`, or by enabling `server.hpa`. With HPA +enabled the Deployment omits `spec.replicas`, so a Helm upgrade does not +overwrite the autoscaler's live replica count. + +## Troubleshooting + +### Store Pods Stuck in `Init:0/1` + +The Store init container waits for PD to reach Raft quorum. Check PD first: + +```bash +kubectl get pods -l app.kubernetes.io/component=pd +kubectl logs -c wait-for-pd +``` + +The wait is bounded by `store.waitTimeoutSeconds` (default 900). On timeout the +init container exits with a message naming the peers it polled, so the failure +appears in `kubectl describe pod` instead of hanging silently. + +### PersistentVolumeClaims Stay `Pending` + +No default StorageClass, or the provisioner is unhealthy: + +```bash +kubectl get sc +kubectl get pvc -l app.kubernetes.io/instance= +kubectl -n get pods +``` + +### Server Ready but Queries Fail + +The Server readiness probe uses `/versions`, which can report ready before the +graph is fully able to serve index-backed queries. Confirm the graph is live: + +```bash +kubectl exec -c server -- curl -s localhost:8080/graphs +``` + +### Pods OOM Killed or Restarting + +The default `values.yaml` sets **no** resource requests or limits and preserves +the image's automatic JVM sizing. Set resources explicitly before production +use; see `values-cluster.yaml`. + +```bash +kubectl get pods -o wide +kubectl describe pod | grep -A5 "Last State" +``` + +### Release Name Too Long + +Helm itself rejects release names longer than 53 characters, before this chart +renders anything: + +```text +invalid release name ... the length must not be longer than 53 +``` + +Within that limit the chart is safe: resource names reserve their suffix and +StatefulSet ordinal before truncation, so every generated Service and Pod name +stays inside the 63-character DNS label limit, and PD/Store identities do not +shift when replicas change. Use `fullnameOverride` to shorten generated names +independently of the release name. + +--- + +## Limitations + +- No TLS, backups, Operator, multi-cluster support, automatic leader transfer, + or a complete monitoring stack. +- The published images run as root, so `runAsNonRoot` and + `readOnlyRootFilesystem` are not chart defaults. The container + `securityContext` does default to `allowPrivilegeEscalation: false`, + `capabilities.drop: [ALL]`, and `seccompProfile: RuntimeDefault`, which are + valid for a root image; `podSecurityContext` and `securityContext` are fully + configurable per component. +- `values-cluster.yaml` is a starting point, not a capacity guarantee. diff --git a/helm/hugegraph/templates/NOTES.txt b/helm/hugegraph/templates/NOTES.txt new file mode 100644 index 0000000000..a0d410951d --- /dev/null +++ b/helm/hugegraph/templates/NOTES.txt @@ -0,0 +1,51 @@ +{{/* +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You under the Apache License, Version 2.0 +(the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/}} +{{ .Release.Name }} installed in namespace {{ .Release.Namespace }}. + + PD {{ .Values.pd.replicas }} replica(s) + Store {{ .Values.store.replicas }} replica(s) + Server {{ if .Values.server.hpa.enabled }}HPA {{ .Values.server.hpa.minReplicas }}-{{ .Values.server.hpa.maxReplicas }}{{ else }}{{ .Values.server.replicas }} replica(s){{ end }} + +Watch the cluster come up: + + kubectl get pods -n {{ .Release.Namespace }} -l app.kubernetes.io/instance={{ .Release.Name }} -w + +PD reaches Raft quorum before Store registers, so Store Pods stay in Init until +PD is ready. + +Verify the release: + + helm test {{ .Release.Name }} --namespace {{ .Release.Namespace }} + +Reach the Server API: + + kubectl port-forward -n {{ .Release.Namespace }} svc/{{ include "hugegraph.server.name" . }} {{ .Values.server.port }}:{{ .Values.server.port }} +{{- if .Values.server.auth.enabled }} + PASSWORD="$(kubectl get secret -n {{ .Release.Namespace }} {{ .Values.server.auth.existingSecret }} -o jsonpath='{.data.password}' | base64 --decode)" + curl --user "admin:${PASSWORD}" http://127.0.0.1:{{ .Values.server.port }}/versions +{{- else }} + curl http://127.0.0.1:{{ .Values.server.port }}/versions +{{- end }} +{{- if not .Values.server.auth.enabled }} + +Authentication is disabled. Do not expose this release to untrusted networks. +{{- end }} +{{- if or (empty .Values.pd.resources) (empty .Values.store.resources) (empty .Values.server.resources) }} + +One or more components have no resource requests or limits. Set them before +production use; see values-cluster.yaml. +{{- end }} diff --git a/helm/hugegraph/templates/_helpers.tpl b/helm/hugegraph/templates/_helpers.tpl new file mode 100644 index 0000000000..cc9a24b59f --- /dev/null +++ b/helm/hugegraph/templates/_helpers.tpl @@ -0,0 +1,295 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +{{/* +Expand the name of the chart. +*/}} +{{- define "hugegraph.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +*/}} +{{- define "hugegraph.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 }} + +{{- define "hugegraph.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{- define "hugegraph.labels" -}} +helm.sh/chart: {{ include "hugegraph.chart" . }} +{{ include "hugegraph.selectorLabels" . }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{- define "hugegraph.selectorLabels" -}} +app.kubernetes.io/name: {{ include "hugegraph.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{- define "hugegraph.pd.name" -}} +{{- printf "%s-pd" (include "hugegraph.fullname" . | trunc 57 | trimSuffix "-") }} +{{- end }} + +{{- define "hugegraph.pd.clientName" -}} +{{- printf "%s-pd-client" (include "hugegraph.fullname" . | trunc 53 | trimSuffix "-") }} +{{- end }} + +{{- define "hugegraph.store.name" -}} +{{- printf "%s-store" (include "hugegraph.fullname" . | trunc 54 | trimSuffix "-") }} +{{- end }} + +{{- define "hugegraph.server.name" -}} +{{- printf "%s-server" (include "hugegraph.fullname" . | trunc 56 | trimSuffix "-") }} +{{- end }} + +{{- define "hugegraph.test.name" -}} +{{- printf "%s-test-connection" (include "hugegraph.fullname" . | trunc 47 | trimSuffix "-") }} +{{- end }} + +{{/* +PD Raft peers list: pod-0.svc.ns.svc:8610,... +Uses short headless DNS (cluster.local optional) resolvable inside the namespace. +*/}} +{{- define "hugegraph.pd.raftPeersList" -}} +{{- $peers := list -}} +{{- $replicas := int .Values.pd.replicas -}} +{{- $name := include "hugegraph.pd.name" . -}} +{{- $ns := .Release.Namespace -}} +{{- $port := int .Values.pd.ports.raft -}} +{{- range $i := until $replicas -}} + {{- $peers = append $peers (printf "%s-%d.%s.%s.svc:%d" $name $i $name $ns $port) -}} +{{- end -}} +{{- join "," $peers -}} +{{- end }} + +{{/* +PD gRPC peers for Store/Server. +*/}} +{{- define "hugegraph.pd.grpcPeersList" -}} +{{- $peers := list -}} +{{- $replicas := int .Values.pd.replicas -}} +{{- $name := include "hugegraph.pd.name" . -}} +{{- $ns := .Release.Namespace -}} +{{- $port := int .Values.pd.ports.grpc -}} +{{- range $i := until $replicas -}} + {{- $peers = append $peers (printf "%s-%d.%s.%s.svc:%d" $name $i $name $ns $port) -}} +{{- end -}} +{{- join "," $peers -}} +{{- end }} + +{{/* +PD REST endpoints for Server storage-readiness checks. +*/}} +{{- define "hugegraph.pd.restPeersList" -}} +{{- $peers := list -}} +{{- $replicas := int .Values.pd.replicas -}} +{{- $name := include "hugegraph.pd.name" . -}} +{{- $ns := .Release.Namespace -}} +{{- $port := int .Values.pd.ports.rest -}} +{{- range $i := until $replicas -}} + {{- $peers = append $peers (printf "%s-%d.%s.%s.svc:%d" $name $i $name $ns $port) -}} +{{- end -}} +{{- join "," $peers -}} +{{- end }} + +{{/* +Initial store list for PD bootstrap: store-0.svc.ns.svc:8500,... +*/}} +{{- define "hugegraph.store.initialStoreList" -}} +{{- $peers := list -}} +{{- $replicas := int .Values.store.replicas -}} +{{- $name := include "hugegraph.store.name" . -}} +{{- $ns := .Release.Namespace -}} +{{- $port := int .Values.store.ports.grpc -}} +{{- range $i := until $replicas -}} + {{- $peers = append $peers (printf "%s-%d.%s.%s.svc:%d" $name $i $name $ns $port) -}} +{{- end -}} +{{- join "," $peers -}} +{{- end }} + +{{/* +First store REST endpoint for STORE_REST / wait-partition. +*/}} +{{- define "hugegraph.store.restPrimary" -}} +{{- $name := include "hugegraph.store.name" . -}} +{{- $ns := .Release.Namespace -}} +{{- printf "%s-0.%s.%s.svc:%d" $name $name $ns (int .Values.store.ports.rest) -}} +{{- end }} + +{{/* +Quorum size: floor(replicas/2)+1 +*/}} +{{- define "hugegraph.pd.quorum" -}} +{{- add (div (int .Values.pd.replicas) 2) 1 -}} +{{- end }} + +{{/* +Render JAVA_OPTS only when explicitly configured. An empty value preserves the +image entrypoint's existing automatic JVM sizing behavior. +*/}} +{{- define "hugegraph.javaOptsEnv" -}} +{{- $javaOpts := default "" . -}} +{{- if ne (trim $javaOpts) "" -}} +- name: JAVA_OPTS + value: {{ $javaOpts | quote }} +{{- end -}} +{{- end }} + +{{/* +Keep the startup probe alive for the 300-second storage wait, the Server's +120-second start timeout, and 30 seconds of process overhead. Older stored +values remain accepted, but their rendered threshold is raised to this floor. +*/}} +{{- define "hugegraph.server.startupFailureThreshold" -}} +{{- $period := int .Values.server.probes.startup.periodSeconds -}} +{{- $configured := int .Values.server.probes.startup.failureThreshold -}} +{{- $minimum := div (add 449 $period) $period -}} +{{- max $configured $minimum -}} +{{- end }} + +{{/* +Optional probe tunables, emitted only when explicitly set. Kubernetes defaults +timeoutSeconds to 1 second, which a garbage-collection pause can exceed on a +loaded Server; operators need a supported way to raise it without forking the +chart. Only explicitly configured fields are rendered. +*/}} +{{- define "hugegraph.probeTuning" -}} +{{- if hasKey . "timeoutSeconds" }} +timeoutSeconds: {{ .timeoutSeconds }} +{{- end }} +{{- if hasKey . "initialDelaySeconds" }} +initialDelaySeconds: {{ .initialDelaySeconds }} +{{- end }} +{{- if hasKey . "successThreshold" }} +successThreshold: {{ .successThreshold }} +{{- end }} +{{- end }} + +{{/* +Resolve the ServiceAccount name for a component: an explicit name wins, +otherwise the generated one when create is true, otherwise "default". +*/}} +{{- define "hugegraph.serviceAccountName" -}} +{{- $sa := get .component "serviceAccount" | default dict -}} +{{- if get $sa "name" -}} +{{- get $sa "name" -}} +{{- else if (get $sa "create" | default false) -}} +{{- .name -}} +{{- else -}} +default +{{- end -}} +{{- end }} + +{{/* +The minimum Server replica count that a PDB must remain valid against. +*/}} +{{- define "hugegraph.server.replicaFloor" -}} +{{- if .Values.server.hpa.enabled -}} +{{- .Values.server.hpa.minReplicas -}} +{{- else -}} +{{- .Values.server.replicas -}} +{{- end -}} +{{- end }} + +{{/* +Cross-field validation that JSON Schema draft-07 cannot express. +*/}} +{{- define "hugegraph.validateValues" -}} +{{- $networkPolicy := get .Values "networkPolicy" | default dict -}} +{{- if (get $networkPolicy "enabled" | default false) -}} +{{- fail "networkPolicy.enabled=true is unsupported because this chart does not implement NetworkPolicy resources" -}} +{{- end -}} +{{- if and .Values.server.hpa.enabled (gt (int .Values.server.hpa.minReplicas) (int .Values.server.hpa.maxReplicas)) -}} +{{- fail "server.hpa.minReplicas must be less than or equal to server.hpa.maxReplicas" -}} +{{- end -}} +{{- if .Values.server.hpa.enabled -}} +{{- $serverResources := .Values.server.resources | default dict -}} +{{- $serverRequests := get $serverResources "requests" | default dict -}} +{{- if not (hasKey $serverRequests "cpu") -}} +{{- fail "server.resources.requests.cpu is required when server.hpa.enabled=true" -}} +{{- end -}} +{{- $cpuRequest := trim (toString (get $serverRequests "cpu")) -}} +{{- if or (eq $cpuRequest "") (hasPrefix "-" $cpuRequest) (regexMatch "^[+]?((0+([.]0*)?)|([.]0+))(([KMGTPE]i)|[numkMGTPE]|[eE][+-]?[0-9]+)?$" $cpuRequest) -}} +{{- fail "server.resources.requests.cpu must be strictly positive when server.hpa.enabled=true" -}} +{{- end -}} +{{- end -}} +{{/* +Only validate minAvailable where a PDB is actually rendered. The pd/store PDB +templates require replicas > 1, so a single-replica release never creates one +and must not be failed for a value that has no effect. +*/}} +{{- if and .Values.pd.pdb.enabled (gt (int .Values.pd.replicas) 1) (ge (int .Values.pd.pdb.minAvailable) (int .Values.pd.replicas)) -}} +{{- fail "pd.pdb.minAvailable must be less than pd.replicas, otherwise the PDB permanently blocks voluntary disruptions such as node drains" -}} +{{- end -}} +{{- if and .Values.store.pdb.enabled (gt (int .Values.store.replicas) 1) (ge (int .Values.store.pdb.minAvailable) (int .Values.store.replicas)) -}} +{{- fail "store.pdb.minAvailable must be less than store.replicas, otherwise the PDB permanently blocks voluntary disruptions such as node drains" -}} +{{- end -}} +{{- $svc := get .Values.server "service" | default dict -}} +{{- if and (get $svc "nodePort") (not (has (get $svc "type" | default "ClusterIP") (list "NodePort" "LoadBalancer"))) -}} +{{- fail "server.service.nodePort requires server.service.type to be NodePort or LoadBalancer" -}} +{{- end -}} +{{- $serverPdb := get .Values.server "pdb" | default dict -}} +{{- $serverReplicaFloor := include "hugegraph.server.replicaFloor" . | int -}} +{{- if and (get $serverPdb "enabled" | default false) (gt $serverReplicaFloor 1) (ge (int (get $serverPdb "minAvailable" | default 1)) $serverReplicaFloor) -}} +{{- fail "server.pdb.minAvailable must be less than the active Server replica floor (server.hpa.minReplicas when HPA is enabled, otherwise server.replicas), otherwise the PDB permanently blocks voluntary disruptions such as node drains" -}} +{{- end -}} +{{- end }} + +{{/* +podAntiAffinity snippet for a component label key. +mode: required | preferred | disabled +*/}} +{{- define "hugegraph.antiAffinity" -}} +{{- $mode := .mode -}} +{{- $component := .component -}} +{{- $labels := .labels -}} +{{- if eq $mode "required" }} +affinity: + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchLabels: + {{- toYaml $labels | nindent 12 }} + app.kubernetes.io/component: {{ $component }} + topologyKey: kubernetes.io/hostname +{{- else if eq $mode "preferred" }} +affinity: + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 100 + podAffinityTerm: + labelSelector: + matchLabels: + {{- toYaml $labels | nindent 14 }} + app.kubernetes.io/component: {{ $component }} + topologyKey: kubernetes.io/hostname +{{- end }} +{{- end }} diff --git a/helm/hugegraph/templates/pd-pdb.yaml b/helm/hugegraph/templates/pd-pdb.yaml new file mode 100644 index 0000000000..a0d93a098e --- /dev/null +++ b/helm/hugegraph/templates/pd-pdb.yaml @@ -0,0 +1,32 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +{{- if and .Values.pd.pdb.enabled (gt (int .Values.pd.replicas) 1) }} +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: {{ include "hugegraph.pd.name" . }} + labels: + {{- include "hugegraph.labels" . | nindent 4 }} + app.kubernetes.io/component: pd +spec: + minAvailable: {{ .Values.pd.pdb.minAvailable }} + selector: + matchLabels: + {{- include "hugegraph.selectorLabels" . | nindent 6 }} + app.kubernetes.io/component: pd +{{- end }} diff --git a/helm/hugegraph/templates/pd-service-client.yaml b/helm/hugegraph/templates/pd-service-client.yaml new file mode 100644 index 0000000000..d40cc9325c --- /dev/null +++ b/helm/hugegraph/templates/pd-service-client.yaml @@ -0,0 +1,36 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apiVersion: v1 +kind: Service +metadata: + name: {{ include "hugegraph.pd.clientName" . }} + labels: + {{- include "hugegraph.labels" . | nindent 4 }} + app.kubernetes.io/component: pd +spec: + type: ClusterIP + selector: + {{- include "hugegraph.selectorLabels" . | nindent 4 }} + app.kubernetes.io/component: pd + ports: + - name: rest + port: {{ .Values.pd.ports.rest }} + targetPort: rest + - name: grpc + port: {{ .Values.pd.ports.grpc }} + targetPort: grpc diff --git a/helm/hugegraph/templates/pd-service-headless.yaml b/helm/hugegraph/templates/pd-service-headless.yaml new file mode 100644 index 0000000000..3b724f9dcf --- /dev/null +++ b/helm/hugegraph/templates/pd-service-headless.yaml @@ -0,0 +1,41 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apiVersion: v1 +kind: Service +metadata: + name: {{ include "hugegraph.pd.name" . }} + labels: + {{- include "hugegraph.labels" . | nindent 4 }} + app.kubernetes.io/component: pd +spec: + clusterIP: None + # Mandatory: Raft bootstrap needs DNS for not-yet-ready pods + publishNotReadyAddresses: true + selector: + {{- include "hugegraph.selectorLabels" . | nindent 4 }} + app.kubernetes.io/component: pd + ports: + - name: grpc + port: {{ .Values.pd.ports.grpc }} + targetPort: grpc + - name: rest + port: {{ .Values.pd.ports.rest }} + targetPort: rest + - name: raft + port: {{ .Values.pd.ports.raft }} + targetPort: raft diff --git a/helm/hugegraph/templates/pd-statefulset.yaml b/helm/hugegraph/templates/pd-statefulset.yaml new file mode 100644 index 0000000000..01ef705006 --- /dev/null +++ b/helm/hugegraph/templates/pd-statefulset.yaml @@ -0,0 +1,158 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: {{ include "hugegraph.pd.name" . }} + labels: + {{- include "hugegraph.labels" . | nindent 4 }} + app.kubernetes.io/component: pd +spec: + serviceName: {{ include "hugegraph.pd.name" . }} + replicas: {{ .Values.pd.replicas }} + podManagementPolicy: Parallel + selector: + matchLabels: + {{- include "hugegraph.selectorLabels" . | nindent 6 }} + app.kubernetes.io/component: pd + template: + metadata: + labels: + {{- include "hugegraph.selectorLabels" . | nindent 8 }} + app.kubernetes.io/component: pd + {{- with .Values.pd.podLabels }}{{ toYaml . | nindent 8 }}{{- end }} + {{- with .Values.pd.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + automountServiceAccountToken: {{ get (get .Values.pd "serviceAccount" | default dict) "automountServiceAccountToken" | default false }} + serviceAccountName: {{ include "hugegraph.serviceAccountName" (dict "component" .Values.pd "name" (include "hugegraph.pd.name" .) ) }} + {{- with .Values.pd.terminationGracePeriodSeconds }} + terminationGracePeriodSeconds: {{ . }} + {{- end }} + {{- with .Values.pd.priorityClassName }} + priorityClassName: {{ . | quote }} + {{- end }} + {{- with .Values.pd.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.pd.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.pd.topologySpreadConstraints }} + topologySpreadConstraints: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.pd.podSecurityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if .Values.pd.affinity }} + affinity: + {{- toYaml .Values.pd.affinity | nindent 8 }} + {{- else }} + {{- with include "hugegraph.antiAffinity" (dict "mode" .Values.pd.antiAffinity "component" "pd" "labels" (include "hugegraph.selectorLabels" . | fromYaml)) }}{{ . | trim | nindent 6 }}{{- end }} + {{- end }} + containers: + - name: pd + image: "{{ .Values.pd.image.repository }}:{{ .Values.pd.image.tag | default $.Chart.AppVersion }}" + imagePullPolicy: {{ .Values.pd.image.pullPolicy }} + {{- with .Values.pd.securityContext }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} + ports: + - name: grpc + containerPort: {{ .Values.pd.ports.grpc }} + - name: rest + containerPort: {{ .Values.pd.ports.rest }} + - name: raft + containerPort: {{ .Values.pd.ports.raft }} + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: HG_PD_GRPC_HOST + value: "$(POD_NAME).{{ include "hugegraph.pd.name" . }}.$(NAMESPACE).svc" + - name: HG_PD_GRPC_PORT + value: {{ .Values.pd.ports.grpc | quote }} + - name: HG_PD_REST_PORT + value: {{ .Values.pd.ports.rest | quote }} + - name: HG_PD_RAFT_ADDRESS + value: "$(POD_NAME).{{ include "hugegraph.pd.name" . }}.$(NAMESPACE).svc:{{ .Values.pd.ports.raft }}" + - name: HG_PD_RAFT_PEERS_LIST + value: {{ include "hugegraph.pd.raftPeersList" . | quote }} + - name: HG_PD_INITIAL_STORE_LIST + value: {{ include "hugegraph.store.initialStoreList" . | quote }} + - name: HG_PD_INITIAL_STORE_COUNT + value: {{ .Values.store.replicas | quote }} + - name: HG_PD_DATA_PATH + value: {{ .Values.pd.dataPath | quote }} + {{- with .Values.pd.extraEnv }}{{ toYaml . | nindent 12 }}{{- end }} + {{- with include "hugegraph.javaOptsEnv" .Values.pd.javaOpts }}{{ . | trim | nindent 12 }}{{- end }} + volumeMounts: + - name: pd-data + mountPath: {{ .Values.pd.dataPath }} + startupProbe: + httpGet: + path: /v1/health + port: rest + failureThreshold: {{ .Values.pd.probes.startup.failureThreshold }} + periodSeconds: {{ .Values.pd.probes.startup.periodSeconds }} + {{- with include "hugegraph.probeTuning" .Values.pd.probes.startup }}{{ . | trim | nindent 12 }}{{- end }} + readinessProbe: + httpGet: + path: /v1/health + port: rest + periodSeconds: {{ .Values.pd.probes.readiness.periodSeconds }} + failureThreshold: {{ .Values.pd.probes.readiness.failureThreshold }} + {{- with include "hugegraph.probeTuning" .Values.pd.probes.readiness }}{{ . | trim | nindent 12 }}{{- end }} + livenessProbe: + httpGet: + path: /v1/health + port: rest + periodSeconds: {{ .Values.pd.probes.liveness.periodSeconds }} + failureThreshold: {{ .Values.pd.probes.liveness.failureThreshold }} + {{- with include "hugegraph.probeTuning" .Values.pd.probes.liveness }}{{ . | trim | nindent 12 }}{{- end }} + {{- with .Values.pd.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + volumeClaimTemplates: + - metadata: + name: pd-data + spec: + accessModes: ["ReadWriteOnce"] + {{- if .Values.pd.storage.storageClassName }} + storageClassName: {{ .Values.pd.storage.storageClassName | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.pd.storage.size }} diff --git a/helm/hugegraph/templates/server-deployment.yaml b/helm/hugegraph/templates/server-deployment.yaml new file mode 100644 index 0000000000..6445c80c75 --- /dev/null +++ b/helm/hugegraph/templates/server-deployment.yaml @@ -0,0 +1,242 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +{{- include "hugegraph.validateValues" . }} +{{- $restServer := .Values.server.restServer | default dict }} +{{- $minFreeMemory := "" }} +{{- $batchMaxWriteThreads := "" }} +{{- if hasKey $restServer "minFreeMemory" }} +{{- $minFreeMemory = toString (get $restServer "minFreeMemory") }} +{{- end }} +{{- if hasKey $restServer "batchMaxWriteThreads" }} +{{- $batchMaxWriteThreads = toString (get $restServer "batchMaxWriteThreads") }} +{{- end }} +{{- $customPort := ne (int .Values.server.port) 8080 }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "hugegraph.server.name" . }} + labels: + {{- include "hugegraph.labels" . | nindent 4 }} + app.kubernetes.io/component: server +spec: + {{- if not .Values.server.hpa.enabled }} + replicas: {{ .Values.server.replicas }} + {{- end }} + selector: + matchLabels: + {{- include "hugegraph.selectorLabels" . | nindent 6 }} + app.kubernetes.io/component: server + template: + metadata: + labels: + {{- include "hugegraph.selectorLabels" . | nindent 8 }} + app.kubernetes.io/component: server + {{- with .Values.server.podLabels }}{{ toYaml . | nindent 8 }}{{- end }} + {{- with .Values.server.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + automountServiceAccountToken: {{ get (get .Values.server "serviceAccount" | default dict) "automountServiceAccountToken" | default false }} + serviceAccountName: {{ include "hugegraph.serviceAccountName" (dict "component" .Values.server "name" (include "hugegraph.server.name" .) ) }} + {{- with .Values.server.terminationGracePeriodSeconds }} + terminationGracePeriodSeconds: {{ . }} + {{- end }} + {{- with .Values.server.priorityClassName }} + priorityClassName: {{ . | quote }} + {{- end }} + {{- with .Values.server.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.server.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.server.topologySpreadConstraints }} + topologySpreadConstraints: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.server.podSecurityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if .Values.server.affinity }} + affinity: + {{- toYaml .Values.server.affinity | nindent 8 }} + {{- else }} + {{- with include "hugegraph.antiAffinity" (dict "mode" (.Values.server.antiAffinity | default "preferred") "component" "server" "labels" (include "hugegraph.selectorLabels" . | fromYaml)) }}{{ . | trim | nindent 6 }}{{- end }} + {{- end }} + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + containers: + - name: server + image: "{{ .Values.server.image.repository }}:{{ .Values.server.image.tag | default $.Chart.AppVersion }}" + imagePullPolicy: {{ .Values.server.image.pullPolicy }} + {{- with .Values.server.securityContext }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- if or .Values.server.auth.enabled + $customPort + (ne $minFreeMemory "") + (ne $batchMaxWriteThreads "") }} + command: + - /usr/bin/dumb-init + - -- + - /bin/bash + - -c + args: + - | + set -euo pipefail + {{- if .Values.server.auth.enabled }} + : "${PASSWORD:?auth Secret key 'password' must not be empty}" + {{- end }} + CONF=./conf/rest-server.properties + TMP=$(mktemp) + {{- if .Values.server.auth.enabled }} + FOUND_USE_PD=false + FOUND_PD_PEERS=false + {{- end }} + {{- if $customPort }} + FOUND_RESTSERVER_URL=false + {{- end }} + {{- if ne $minFreeMemory "" }} + FOUND_MIN_FREE_MEMORY=false + {{- end }} + {{- if ne $batchMaxWriteThreads "" }} + FOUND_BATCH_MAX_WRITE_THREADS=false + {{- end }} + while IFS= read -r LINE || [[ -n "${LINE}" ]]; do + case "${LINE}" in + {{- if .Values.server.auth.enabled }} + usePD=*) + printf 'usePD=true\n' >>"${TMP}" + FOUND_USE_PD=true + ;; + pd.peers=*) + printf 'pd.peers=%s\n' "${HG_SERVER_PD_PEERS}" >>"${TMP}" + FOUND_PD_PEERS=true + ;; + {{- end }} + {{- if $customPort }} + restserver.url=*) + printf 'restserver.url=http://0.0.0.0:%s\n' \ + {{ .Values.server.port | quote }} >>"${TMP}" + FOUND_RESTSERVER_URL=true + ;; + {{- end }} + {{- if ne $minFreeMemory "" }} + restserver.min_free_memory=*) + printf 'restserver.min_free_memory=%s\n' \ + {{ $minFreeMemory | quote }} >>"${TMP}" + FOUND_MIN_FREE_MEMORY=true + ;; + {{- end }} + {{- if ne $batchMaxWriteThreads "" }} + batch.max_write_threads=*) + printf 'batch.max_write_threads=%s\n' \ + {{ $batchMaxWriteThreads | quote }} >>"${TMP}" + FOUND_BATCH_MAX_WRITE_THREADS=true + ;; + {{- end }} + *) + printf '%s\n' "${LINE}" >>"${TMP}" + ;; + esac + done <"${CONF}" + {{- if .Values.server.auth.enabled }} + if [[ "${FOUND_USE_PD}" == false ]]; then + printf 'usePD=true\n' >>"${TMP}" + fi + if [[ "${FOUND_PD_PEERS}" == false ]]; then + printf 'pd.peers=%s\n' "${HG_SERVER_PD_PEERS}" >>"${TMP}" + fi + {{- end }} + {{- if $customPort }} + if [[ "${FOUND_RESTSERVER_URL}" == false ]]; then + printf 'restserver.url=http://0.0.0.0:%s\n' \ + {{ .Values.server.port | quote }} >>"${TMP}" + fi + {{- end }} + {{- if ne $minFreeMemory "" }} + if [[ "${FOUND_MIN_FREE_MEMORY}" == false ]]; then + printf 'restserver.min_free_memory=%s\n' \ + {{ $minFreeMemory | quote }} >>"${TMP}" + fi + {{- end }} + {{- if ne $batchMaxWriteThreads "" }} + if [[ "${FOUND_BATCH_MAX_WRITE_THREADS}" == false ]]; then + printf 'batch.max_write_threads=%s\n' \ + {{ $batchMaxWriteThreads | quote }} >>"${TMP}" + fi + {{- end }} + chmod 600 "${TMP}" + mv "${TMP}" "${CONF}" + exec ./docker-entrypoint.sh + {{- end }} + ports: + - name: http + containerPort: {{ .Values.server.port }} + env: + - name: HG_SERVER_BACKEND + value: {{ .Values.server.backend | quote }} + - name: HG_SERVER_PD_PEERS + value: {{ include "hugegraph.pd.grpcPeersList" . | quote }} + - name: HG_SERVER_PD_REST_ENDPOINT + value: {{ include "hugegraph.pd.restPeersList" . | quote }} + - name: STORE_REST + value: {{ include "hugegraph.store.restPrimary" . | quote }} + - name: HG_SERVER_INIT_STORE_ENABLED + value: {{ .Values.server.initStoreEnabled | quote }} + {{- with .Values.server.extraEnv }}{{ toYaml . | nindent 12 }}{{- end }} + {{- with include "hugegraph.javaOptsEnv" .Values.server.javaOpts }}{{ . | trim | nindent 12 }}{{- end }} + {{- if and .Values.server.auth.enabled .Values.server.auth.existingSecret }} + - name: PASSWORD + valueFrom: + secretKeyRef: + name: {{ .Values.server.auth.existingSecret | quote }} + key: password + {{- end }} + startupProbe: + httpGet: + path: /versions + port: http + failureThreshold: {{ include "hugegraph.server.startupFailureThreshold" . }} + periodSeconds: {{ .Values.server.probes.startup.periodSeconds }} + {{- with include "hugegraph.probeTuning" .Values.server.probes.startup }}{{ . | trim | nindent 12 }}{{- end }} + readinessProbe: + httpGet: + path: /versions + port: http + periodSeconds: {{ .Values.server.probes.readiness.periodSeconds }} + failureThreshold: {{ .Values.server.probes.readiness.failureThreshold }} + {{- with include "hugegraph.probeTuning" .Values.server.probes.readiness }}{{ . | trim | nindent 12 }}{{- end }} + livenessProbe: + httpGet: + path: /versions + port: http + periodSeconds: {{ .Values.server.probes.liveness.periodSeconds }} + failureThreshold: {{ .Values.server.probes.liveness.failureThreshold }} + {{- with include "hugegraph.probeTuning" .Values.server.probes.liveness }}{{ . | trim | nindent 12 }}{{- end }} + {{- with .Values.server.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} diff --git a/helm/hugegraph/templates/server-hpa.yaml b/helm/hugegraph/templates/server-hpa.yaml new file mode 100644 index 0000000000..40e95c0d99 --- /dev/null +++ b/helm/hugegraph/templates/server-hpa.yaml @@ -0,0 +1,40 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +{{- if .Values.server.hpa.enabled }} +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "hugegraph.server.name" . }} + labels: + {{- include "hugegraph.labels" . | nindent 4 }} + app.kubernetes.io/component: server +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "hugegraph.server.name" . }} + minReplicas: {{ .Values.server.hpa.minReplicas }} + maxReplicas: {{ .Values.server.hpa.maxReplicas }} + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .Values.server.hpa.targetCPUUtilizationPercentage }} +{{- end }} diff --git a/helm/hugegraph/templates/server-ingress.yaml b/helm/hugegraph/templates/server-ingress.yaml new file mode 100644 index 0000000000..ad473fde73 --- /dev/null +++ b/helm/hugegraph/templates/server-ingress.yaml @@ -0,0 +1,53 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +{{- if .Values.server.ingress.enabled }} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "hugegraph.server.name" . }} + labels: + {{- include "hugegraph.labels" . | nindent 4 }} + app.kubernetes.io/component: server + {{- with (get .Values.server.ingress "annotations") }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if .Values.server.ingress.className }} + ingressClassName: {{ .Values.server.ingress.className | quote }} + {{- end }} + {{- with .Values.server.ingress.tls }} + tls: + {{- toYaml . | nindent 4 }} + {{- end }} + rules: + {{- range .Values.server.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range .paths }} + - path: {{ .path | quote }} + pathType: {{ .pathType }} + backend: + service: + name: {{ include "hugegraph.server.name" $ }} + port: + number: {{ $.Values.server.port }} + {{- end }} + {{- end }} +{{- end }} diff --git a/helm/hugegraph/templates/server-pdb.yaml b/helm/hugegraph/templates/server-pdb.yaml new file mode 100644 index 0000000000..7fb4826aec --- /dev/null +++ b/helm/hugegraph/templates/server-pdb.yaml @@ -0,0 +1,34 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +{{- $pdb := get .Values.server "pdb" | default dict }} +{{- $replicaFloor := include "hugegraph.server.replicaFloor" . | int }} +{{- if and (get $pdb "enabled" | default false) (gt $replicaFloor 1) }} +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: {{ include "hugegraph.server.name" . }} + labels: + {{- include "hugegraph.labels" . | nindent 4 }} + app.kubernetes.io/component: server +spec: + minAvailable: {{ get $pdb "minAvailable" | default 1 }} + selector: + matchLabels: + {{- include "hugegraph.selectorLabels" . | nindent 6 }} + app.kubernetes.io/component: server +{{- end }} diff --git a/helm/hugegraph/templates/server-service.yaml b/helm/hugegraph/templates/server-service.yaml new file mode 100644 index 0000000000..b4053dc4ba --- /dev/null +++ b/helm/hugegraph/templates/server-service.yaml @@ -0,0 +1,41 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apiVersion: v1 +kind: Service +metadata: + name: {{ include "hugegraph.server.name" . }} + labels: + {{- include "hugegraph.labels" . | nindent 4 }} + app.kubernetes.io/component: server + {{- $svc := get .Values.server "service" | default dict }} + {{- with (get $svc "annotations") }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + type: {{ get $svc "type" | default "ClusterIP" }} + selector: + {{- include "hugegraph.selectorLabels" . | nindent 4 }} + app.kubernetes.io/component: server + ports: + - name: http + port: {{ .Values.server.port }} + targetPort: http + {{- with (get $svc "nodePort") }} + nodePort: {{ . }} + {{- end }} diff --git a/helm/hugegraph/templates/serviceaccount.yaml b/helm/hugegraph/templates/serviceaccount.yaml new file mode 100644 index 0000000000..9889414d4b --- /dev/null +++ b/helm/hugegraph/templates/serviceaccount.yaml @@ -0,0 +1,35 @@ +{{- /* +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You under the Apache License, Version 2.0 +(the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ -}} +{{- range $component := list "pd" "store" "server" }} +{{- $values := index $.Values $component }} +{{- $sa := get $values "serviceAccount" | default dict }} +{{- if and (get $sa "create" | default false) (not (get $sa "name")) }} +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include (printf "hugegraph.%s.name" $component) $ }} + labels: + {{- include "hugegraph.labels" $ | nindent 4 }} + app.kubernetes.io/component: {{ $component }} + {{- with (get $sa "annotations") }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +automountServiceAccountToken: {{ get $sa "automountServiceAccountToken" | default false }} +{{- end }} +{{- end }} diff --git a/helm/hugegraph/templates/store-pdb.yaml b/helm/hugegraph/templates/store-pdb.yaml new file mode 100644 index 0000000000..9fb1e536da --- /dev/null +++ b/helm/hugegraph/templates/store-pdb.yaml @@ -0,0 +1,32 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +{{- if and .Values.store.pdb.enabled (gt (int .Values.store.replicas) 1) }} +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: {{ include "hugegraph.store.name" . }} + labels: + {{- include "hugegraph.labels" . | nindent 4 }} + app.kubernetes.io/component: store +spec: + minAvailable: {{ .Values.store.pdb.minAvailable }} + selector: + matchLabels: + {{- include "hugegraph.selectorLabels" . | nindent 6 }} + app.kubernetes.io/component: store +{{- end }} diff --git a/helm/hugegraph/templates/store-service-headless.yaml b/helm/hugegraph/templates/store-service-headless.yaml new file mode 100644 index 0000000000..4c82023b5d --- /dev/null +++ b/helm/hugegraph/templates/store-service-headless.yaml @@ -0,0 +1,41 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apiVersion: v1 +kind: Service +metadata: + name: {{ include "hugegraph.store.name" . }} + labels: + {{- include "hugegraph.labels" . | nindent 4 }} + app.kubernetes.io/component: store +spec: + clusterIP: None + # Mandatory: Raft / self-FQDN resolution before readiness + publishNotReadyAddresses: true + selector: + {{- include "hugegraph.selectorLabels" . | nindent 4 }} + app.kubernetes.io/component: store + ports: + - name: grpc + port: {{ .Values.store.ports.grpc }} + targetPort: grpc + - name: raft + port: {{ .Values.store.ports.raft }} + targetPort: raft + - name: rest + port: {{ .Values.store.ports.rest }} + targetPort: rest diff --git a/helm/hugegraph/templates/store-statefulset.yaml b/helm/hugegraph/templates/store-statefulset.yaml new file mode 100644 index 0000000000..973ecacdff --- /dev/null +++ b/helm/hugegraph/templates/store-statefulset.yaml @@ -0,0 +1,193 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: {{ include "hugegraph.store.name" . }} + labels: + {{- include "hugegraph.labels" . | nindent 4 }} + app.kubernetes.io/component: store +spec: + serviceName: {{ include "hugegraph.store.name" . }} + replicas: {{ .Values.store.replicas }} + podManagementPolicy: Parallel + selector: + matchLabels: + {{- include "hugegraph.selectorLabels" . | nindent 6 }} + app.kubernetes.io/component: store + template: + metadata: + labels: + {{- include "hugegraph.selectorLabels" . | nindent 8 }} + app.kubernetes.io/component: store + {{- with .Values.store.podLabels }}{{ toYaml . | nindent 8 }}{{- end }} + {{- with .Values.store.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + automountServiceAccountToken: {{ get (get .Values.store "serviceAccount" | default dict) "automountServiceAccountToken" | default false }} + serviceAccountName: {{ include "hugegraph.serviceAccountName" (dict "component" .Values.store "name" (include "hugegraph.store.name" .) ) }} + {{- with .Values.store.terminationGracePeriodSeconds }} + terminationGracePeriodSeconds: {{ . }} + {{- end }} + {{- with .Values.store.priorityClassName }} + priorityClassName: {{ . | quote }} + {{- end }} + {{- with .Values.store.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.store.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.store.topologySpreadConstraints }} + topologySpreadConstraints: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.store.podSecurityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if .Values.store.affinity }} + affinity: + {{- toYaml .Values.store.affinity | nindent 8 }} + {{- else }} + {{- with include "hugegraph.antiAffinity" (dict "mode" .Values.store.antiAffinity "component" "store" "labels" (include "hugegraph.selectorLabels" . | fromYaml)) }}{{ . | trim | nindent 6 }}{{- end }} + {{- end }} + initContainers: + - name: wait-for-pd + image: {{ .Values.store.waitImage | quote }} + {{- with .Values.store.securityContext }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} + command: + - sh + - -c + - | + set -eu + REQUIRED={{ include "hugegraph.pd.quorum" . }} + HEALTH_PEERS=$(echo "{{ include "hugegraph.pd.restPeersList" . }}" | tr ',' ' ') + TIMEOUT={{ .Values.store.waitTimeoutSeconds | default 900 }} + DEADLINE=$(( $(date +%s) + TIMEOUT )) + echo "Waiting for PD quorum (${REQUIRED}) among: ${HEALTH_PEERS}" + until [ "$( + ok=0 + for peer in ${HEALTH_PEERS}; do + if curl -fsS "http://${peer}/v1/health" >/dev/null 2>&1; then + ok=$((ok+1)) + fi + done + echo "$ok" + )" -ge "${REQUIRED}" ]; do + if [ "$(date +%s)" -ge "${DEADLINE}" ]; then + echo "Timed out after ${TIMEOUT}s waiting for PD quorum (${REQUIRED}) among: ${HEALTH_PEERS}" >&2 + echo "Check PD Pods: kubectl get pods -l app.kubernetes.io/component=pd" >&2 + exit 1 + fi + echo "Waiting for PD quorum..." + sleep 5 + done + echo "PD quorum reached." + {{- with .Values.store.waitResources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + containers: + - name: store + image: "{{ .Values.store.image.repository }}:{{ .Values.store.image.tag | default $.Chart.AppVersion }}" + imagePullPolicy: {{ .Values.store.image.pullPolicy }} + {{- with .Values.store.securityContext }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} + ports: + - name: grpc + containerPort: {{ .Values.store.ports.grpc }} + - name: raft + containerPort: {{ .Values.store.ports.raft }} + - name: rest + containerPort: {{ .Values.store.ports.rest }} + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: HG_STORE_PD_ADDRESS + value: {{ include "hugegraph.pd.grpcPeersList" . | quote }} + - name: HG_STORE_GRPC_HOST + value: "$(POD_NAME).{{ include "hugegraph.store.name" . }}.$(NAMESPACE).svc" + - name: HG_STORE_GRPC_PORT + value: {{ .Values.store.ports.grpc | quote }} + - name: HG_STORE_REST_PORT + value: {{ .Values.store.ports.rest | quote }} + - name: HG_STORE_RAFT_ADDRESS + value: "$(POD_NAME).{{ include "hugegraph.store.name" . }}.$(NAMESPACE).svc:{{ .Values.store.ports.raft }}" + - name: HG_STORE_DATA_PATH + value: {{ .Values.store.dataPath | quote }} + {{- with .Values.store.extraEnv }}{{ toYaml . | nindent 12 }}{{- end }} + {{- with include "hugegraph.javaOptsEnv" .Values.store.javaOpts }}{{ . | trim | nindent 12 }}{{- end }} + volumeMounts: + - name: store-data + mountPath: {{ .Values.store.dataPath }} + startupProbe: + httpGet: + path: /v1/health + port: rest + failureThreshold: {{ .Values.store.probes.startup.failureThreshold }} + periodSeconds: {{ .Values.store.probes.startup.periodSeconds }} + {{- with include "hugegraph.probeTuning" .Values.store.probes.startup }}{{ . | trim | nindent 12 }}{{- end }} + readinessProbe: + httpGet: + path: /v1/health + port: rest + periodSeconds: {{ .Values.store.probes.readiness.periodSeconds }} + failureThreshold: {{ .Values.store.probes.readiness.failureThreshold }} + {{- with include "hugegraph.probeTuning" .Values.store.probes.readiness }}{{ . | trim | nindent 12 }}{{- end }} + livenessProbe: + httpGet: + path: /v1/health + port: rest + periodSeconds: {{ .Values.store.probes.liveness.periodSeconds }} + failureThreshold: {{ .Values.store.probes.liveness.failureThreshold }} + {{- with include "hugegraph.probeTuning" .Values.store.probes.liveness }}{{ . | trim | nindent 12 }}{{- end }} + {{- with .Values.store.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + volumeClaimTemplates: + - metadata: + name: store-data + spec: + accessModes: ["ReadWriteOnce"] + {{- if .Values.store.storage.storageClassName }} + storageClassName: {{ .Values.store.storage.storageClassName | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.store.storage.size }} diff --git a/helm/hugegraph/templates/tests/test-connection.yaml b/helm/hugegraph/templates/tests/test-connection.yaml new file mode 100644 index 0000000000..beef5c1d47 --- /dev/null +++ b/helm/hugegraph/templates/tests/test-connection.yaml @@ -0,0 +1,83 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Official Helm chart test hook — only runs with: helm test +# Not an install-time hook. Safe with --wait (no init Job in no-init design). +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "hugegraph.test.name" . }}" + labels: + {{- include "hugegraph.labels" . | nindent 4 }} + app.kubernetes.io/component: test + annotations: + "helm.sh/hook": test + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded +spec: + automountServiceAccountToken: false + restartPolicy: Never + securityContext: + seccompProfile: + type: RuntimeDefault + containers: + - name: curl + image: {{ .Values.server.waitImage | quote }} + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] + readOnlyRootFilesystem: true + runAsGroup: 101 + runAsNonRoot: true + runAsUser: 100 + {{- with .Values.server.testResources }} + resources: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if .Values.server.auth.enabled }} + env: + - name: PASSWORD + valueFrom: + secretKeyRef: + name: {{ .Values.server.auth.existingSecret | quote }} + key: password + {{- end }} + command: + - sh + - -c + - | + set -eu + SVC="{{ include "hugegraph.server.name" . }}" + PORT="{{ .Values.server.port }}" + {{- if .Values.server.auth.enabled }} + AUTH_HEADER=$(printf 'admin:%s' "${PASSWORD}" | base64 | tr -d '\r\n') + request() { + printf 'header = "Authorization: Basic %s"\n' "${AUTH_HEADER}" | + curl --config - -fsS "$1" + } + {{- else }} + request() { + curl -fsS "$1" + } + {{- end }} + echo "helm test: GET http://${SVC}:${PORT}/versions" + request "http://${SVC}:${PORT}/versions" + echo + echo "helm test: GET http://${SVC}:${PORT}/graphs" + request "http://${SVC}:${PORT}/graphs" + echo + echo "helm test: OK" diff --git a/helm/hugegraph/testdata/values-pre-hardening.yaml b/helm/hugegraph/testdata/values-pre-hardening.yaml new file mode 100644 index 0000000000..d3d34cea1f --- /dev/null +++ b/helm/hugegraph/testdata/values-pre-hardening.yaml @@ -0,0 +1,128 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Values shape shipped before the production-hardening fields were added. +# validate-chart.sh installs this as the chart default values to emulate a +# Helm --reuse-values upgrade whose stored release values lack all new keys. + +fullnameOverride: "" +nameOverride: "" + +imagePullSecrets: [] + +pd: + replicas: 3 + image: + repository: hugegraph/pd + tag: latest + pullPolicy: IfNotPresent + ports: + grpc: 8686 + rest: 8620 + raft: 8610 + dataPath: /hugegraph-pd/pd_data + storage: + size: 10Gi + storageClassName: "" + resources: {} + antiAffinity: required + pdb: + enabled: true + minAvailable: 2 + probes: + startup: + failureThreshold: 30 + periodSeconds: 10 + readiness: + periodSeconds: 10 + failureThreshold: 3 + liveness: + periodSeconds: 20 + failureThreshold: 3 + +store: + replicas: 3 + image: + repository: hugegraph/store + tag: latest + pullPolicy: IfNotPresent + ports: + grpc: 8500 + raft: 8510 + rest: 8520 + dataPath: /hugegraph-store/storage + storage: + size: 50Gi + storageClassName: "" + resources: {} + antiAffinity: required + pdb: + enabled: true + minAvailable: 2 + waitImage: curlimages/curl:8.5.0 + probes: + startup: + failureThreshold: 40 + periodSeconds: 10 + readiness: + periodSeconds: 10 + failureThreshold: 3 + liveness: + periodSeconds: 20 + failureThreshold: 3 + +server: + replicas: 3 + image: + repository: hugegraph/server + tag: latest + pullPolicy: IfNotPresent + port: 8080 + backend: hstore + resources: {} + waitImage: curlimages/curl:8.5.0 + initStoreEnabled: false + auth: + enabled: false + existingSecret: "" + ingress: + enabled: false + className: "" + hosts: + - host: hugegraph.local + paths: + - path: / + pathType: Prefix + tls: [] + hpa: + enabled: false + minReplicas: 3 + maxReplicas: 10 + targetCPUUtilizationPercentage: 70 + probes: + startup: + failureThreshold: 30 + periodSeconds: 5 + readiness: + periodSeconds: 10 + failureThreshold: 3 + liveness: + periodSeconds: 20 + failureThreshold: 3 + +networkPolicy: + enabled: false diff --git a/helm/hugegraph/values-cluster.yaml b/helm/hugegraph/values-cluster.yaml new file mode 100644 index 0000000000..91ae09a9c6 --- /dev/null +++ b/helm/hugegraph/values-cluster.yaml @@ -0,0 +1,86 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Production starting point: 3 Server + 3 PD + 3 Store. +# Size this profile again for the real graph, traffic, and failure budget. +# Usage: helm install hg ./helm/hugegraph -f values-cluster.yaml + +pd: + replicas: 3 + javaOpts: >- + -Xms256m -Xmx512m + -XX:MaxMetaspaceSize=256m + -XX:MaxDirectMemorySize=256m + -XX:+UseContainerSupport + resources: + requests: + cpu: 500m + memory: 1Gi + limits: + cpu: "2" + memory: 2Gi + antiAffinity: required + pdb: + enabled: true + minAvailable: 2 + storage: + size: 10Gi + +store: + replicas: 3 + javaOpts: >- + -Xms512m -Xmx1024m + -XX:MaxMetaspaceSize=256m + -XX:MaxDirectMemorySize=512m + -XX:+UseContainerSupport + resources: + requests: + cpu: "1" + memory: 2Gi + limits: + cpu: "4" + memory: 4Gi + waitResources: + requests: + cpu: 25m + memory: 32Mi + limits: + cpu: 250m + memory: 64Mi + antiAffinity: required + pdb: + enabled: true + minAvailable: 2 + storage: + size: 50Gi + +server: + replicas: 3 + javaOpts: >- + -Xms512m -Xmx1024m + -XX:MaxMetaspaceSize=256m + -XX:MaxDirectMemorySize=256m + -XX:+UseContainerSupport + resources: + requests: + cpu: "1" + memory: 1Gi + limits: + cpu: "4" + memory: 2Gi + hpa: + enabled: false diff --git a/helm/hugegraph/values-single.yaml b/helm/hugegraph/values-single.yaml new file mode 100644 index 0000000000..86dd71abd6 --- /dev/null +++ b/helm/hugegraph/values-single.yaml @@ -0,0 +1,40 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Convenience preset: single-node / local kind-minikube development. +# Usage: helm install hg ./helm/hugegraph -f values-single.yaml + +pd: + replicas: 1 + antiAffinity: disabled + pdb: + enabled: false + storage: + size: 5Gi + +store: + replicas: 1 + antiAffinity: disabled + pdb: + enabled: false + storage: + size: 10Gi + +server: + replicas: 1 + hpa: + enabled: false diff --git a/helm/hugegraph/values.schema.json b/helm/hugegraph/values.schema.json new file mode 100644 index 0000000000..56c1284bca --- /dev/null +++ b/helm/hugegraph/values.schema.json @@ -0,0 +1,761 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "HugeGraph HStore Helm values", + "type": "object", + "required": [ + "pd", + "store", + "server" + ], + "properties": { + "fullnameOverride": { + "type": "string" + }, + "nameOverride": { + "type": "string" + }, + "imagePullSecrets": { + "type": "array", + "items": { + "type": "object" + } + }, + "pd": { + "$ref": "#/definitions/pd" + }, + "store": { + "$ref": "#/definitions/store" + }, + "server": { + "$ref": "#/definitions/server" + }, + "global": { + "type": "object" + } + }, + "definitions": { + "image": { + "type": "object", + "additionalProperties": false, + "required": [ + "repository", + "tag", + "pullPolicy" + ], + "properties": { + "repository": { + "type": "string", + "minLength": 1 + }, + "tag": { + "type": "string" + }, + "pullPolicy": { + "type": "string", + "enum": [ + "Always", + "IfNotPresent", + "Never" + ] + } + } + }, + "resources": { + "type": "object", + "additionalProperties": false, + "properties": { + "requests": { + "$ref": "#/definitions/resourceList" + }, + "limits": { + "$ref": "#/definitions/resourceList" + } + } + }, + "resourceList": { + "type": "object", + "additionalProperties": { + "type": [ + "string", + "number" + ] + }, + "properties": { + "cpu": { + "type": [ + "string", + "number" + ] + }, + "memory": { + "type": [ + "string", + "number" + ] + }, + "ephemeral-storage": { + "type": [ + "string", + "number" + ] + } + } + }, + "storage": { + "type": "object", + "additionalProperties": false, + "required": [ + "size", + "storageClassName" + ], + "properties": { + "size": { + "type": "string", + "minLength": 1 + }, + "storageClassName": { + "type": "string" + } + } + }, + "pdb": { + "type": "object", + "additionalProperties": false, + "required": [ + "enabled", + "minAvailable" + ], + "properties": { + "enabled": { + "type": "boolean" + }, + "minAvailable": { + "type": "integer", + "minimum": 1 + } + } + }, + "probe": { + "type": "object", + "additionalProperties": false, + "required": [ + "periodSeconds", + "failureThreshold" + ], + "properties": { + "periodSeconds": { + "type": "integer", + "minimum": 1 + }, + "failureThreshold": { + "type": "integer", + "minimum": 1 + }, + "timeoutSeconds": { + "type": "integer", + "minimum": 1 + }, + "initialDelaySeconds": { + "type": "integer", + "minimum": 0 + }, + "successThreshold": { + "type": "integer", + "minimum": 1 + } + } + }, + "probes": { + "type": "object", + "additionalProperties": false, + "required": [ + "startup", + "readiness", + "liveness" + ], + "properties": { + "startup": { + "$ref": "#/definitions/probe" + }, + "readiness": { + "$ref": "#/definitions/probe" + }, + "liveness": { + "$ref": "#/definitions/probe" + } + } + }, + "ports": { + "type": "object", + "additionalProperties": false, + "required": [ + "grpc", + "rest", + "raft" + ], + "properties": { + "grpc": { + "type": "integer", + "minimum": 1, + "maximum": 65535 + }, + "rest": { + "type": "integer", + "minimum": 1, + "maximum": 65535 + }, + "raft": { + "type": "integer", + "minimum": 1, + "maximum": 65535 + } + } + }, + "pd": { + "type": "object", + "additionalProperties": false, + "required": [ + "replicas", + "image", + "ports", + "dataPath", + "storage", + "resources", + "antiAffinity", + "pdb", + "probes" + ], + "properties": { + "replicas": { + "type": "integer", + "minimum": 1, + "maximum": 99 + }, + "image": { + "$ref": "#/definitions/image" + }, + "javaOpts": { + "type": "string" + }, + "ports": { + "$ref": "#/definitions/ports" + }, + "dataPath": { + "type": "string", + "minLength": 1 + }, + "storage": { + "$ref": "#/definitions/storage" + }, + "resources": { + "$ref": "#/definitions/resources" + }, + "antiAffinity": { + "type": "string", + "enum": [ + "required", + "preferred", + "disabled" + ] + }, + "pdb": { + "$ref": "#/definitions/pdb" + }, + "probes": { + "$ref": "#/definitions/probes" + }, + "podSecurityContext": { + "type": "object" + }, + "securityContext": { + "type": "object" + }, + "nodeSelector": { + "type": "object" + }, + "tolerations": { + "type": "array" + }, + "affinity": { + "type": "object" + }, + "topologySpreadConstraints": { + "type": "array" + }, + "priorityClassName": { + "type": "string" + }, + "podAnnotations": { + "type": "object" + }, + "podLabels": { + "type": "object" + }, + "extraEnv": { + "type": "array" + }, + "terminationGracePeriodSeconds": { + "type": "integer", + "minimum": 0 + }, + "serviceAccount": { + "type": "object", + "properties": { + "create": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "annotations": { + "type": "object" + }, + "automountServiceAccountToken": { + "type": "boolean" + } + } + } + } + }, + "store": { + "type": "object", + "additionalProperties": false, + "required": [ + "replicas", + "image", + "ports", + "dataPath", + "storage", + "resources", + "antiAffinity", + "pdb", + "waitImage", + "probes" + ], + "properties": { + "replicas": { + "type": "integer", + "minimum": 1, + "maximum": 99 + }, + "image": { + "$ref": "#/definitions/image" + }, + "javaOpts": { + "type": "string" + }, + "ports": { + "$ref": "#/definitions/ports" + }, + "dataPath": { + "type": "string", + "minLength": 1 + }, + "storage": { + "$ref": "#/definitions/storage" + }, + "resources": { + "$ref": "#/definitions/resources" + }, + "antiAffinity": { + "type": "string", + "enum": [ + "required", + "preferred", + "disabled" + ] + }, + "pdb": { + "$ref": "#/definitions/pdb" + }, + "waitImage": { + "type": "string", + "minLength": 1 + }, + "waitResources": { + "$ref": "#/definitions/resources" + }, + "probes": { + "$ref": "#/definitions/probes" + }, + "podSecurityContext": { + "type": "object" + }, + "securityContext": { + "type": "object" + }, + "waitTimeoutSeconds": { + "type": "integer", + "minimum": 1 + }, + "nodeSelector": { + "type": "object" + }, + "tolerations": { + "type": "array" + }, + "affinity": { + "type": "object" + }, + "topologySpreadConstraints": { + "type": "array" + }, + "priorityClassName": { + "type": "string" + }, + "podAnnotations": { + "type": "object" + }, + "podLabels": { + "type": "object" + }, + "extraEnv": { + "type": "array" + }, + "terminationGracePeriodSeconds": { + "type": "integer", + "minimum": 0 + }, + "serviceAccount": { + "type": "object", + "properties": { + "create": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "annotations": { + "type": "object" + }, + "automountServiceAccountToken": { + "type": "boolean" + } + } + } + } + }, + "server": { + "type": "object", + "additionalProperties": false, + "required": [ + "replicas", + "image", + "port", + "backend", + "resources", + "waitImage", + "initStoreEnabled", + "auth", + "ingress", + "hpa", + "probes" + ], + "properties": { + "replicas": { + "type": "integer", + "minimum": 1 + }, + "image": { + "$ref": "#/definitions/image" + }, + "javaOpts": { + "type": "string" + }, + "port": { + "type": "integer", + "minimum": 1, + "maximum": 65535 + }, + "backend": { + "type": "string", + "const": "hstore" + }, + "resources": { + "$ref": "#/definitions/resources" + }, + "waitImage": { + "type": "string", + "minLength": 1, + "description": "Image used by the Helm test hook" + }, + "waitResources": { + "$ref": "#/definitions/resources" + }, + "restServer": { + "type": "object", + "additionalProperties": false, + "properties": { + "minFreeMemory": { + "oneOf": [ + { + "type": "integer", + "minimum": 0 + }, + { + "type": "string", + "const": "" + } + ] + }, + "batchMaxWriteThreads": { + "oneOf": [ + { + "type": "integer", + "minimum": 0 + }, + { + "type": "string", + "const": "" + } + ] + } + } + }, + "initStoreEnabled": { + "type": "boolean", + "const": false + }, + "auth": { + "type": "object", + "additionalProperties": false, + "required": [ + "enabled", + "existingSecret" + ], + "properties": { + "enabled": { + "type": "boolean" + }, + "existingSecret": { + "type": "string" + } + }, + "allOf": [ + { + "if": { + "properties": { + "enabled": { + "const": true + } + } + }, + "then": { + "properties": { + "existingSecret": { + "minLength": 1 + } + } + } + }, + { + "if": { + "properties": { + "enabled": { + "const": false + } + } + }, + "then": { + "properties": { + "existingSecret": { + "const": "" + } + } + } + } + ] + }, + "ingress": { + "type": "object", + "additionalProperties": false, + "required": [ + "enabled", + "className", + "hosts", + "tls" + ], + "properties": { + "enabled": { + "type": "boolean" + }, + "className": { + "type": "string" + }, + "hosts": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "host", + "paths" + ], + "properties": { + "host": { + "type": "string", + "minLength": 1 + }, + "paths": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "path", + "pathType" + ], + "properties": { + "path": { + "type": "string" + }, + "pathType": { + "type": "string", + "enum": [ + "Exact", + "Prefix", + "ImplementationSpecific" + ] + } + } + } + } + } + } + }, + "tls": { + "type": "array", + "items": { + "type": "object" + } + }, + "annotations": { + "type": "object" + } + } + }, + "hpa": { + "type": "object", + "additionalProperties": false, + "required": [ + "enabled", + "minReplicas", + "maxReplicas", + "targetCPUUtilizationPercentage" + ], + "properties": { + "enabled": { + "type": "boolean" + }, + "minReplicas": { + "type": "integer", + "minimum": 1 + }, + "maxReplicas": { + "type": "integer", + "minimum": 1 + }, + "targetCPUUtilizationPercentage": { + "type": "integer", + "minimum": 1, + "maximum": 100 + } + } + }, + "probes": { + "$ref": "#/definitions/probes" + }, + "antiAffinity": { + "type": "string", + "enum": [ + "required", + "preferred", + "disabled" + ] + }, + "podSecurityContext": { + "type": "object" + }, + "securityContext": { + "type": "object" + }, + "pdb": { + "$ref": "#/definitions/pdb" + }, + "nodeSelector": { + "type": "object" + }, + "tolerations": { + "type": "array" + }, + "affinity": { + "type": "object" + }, + "topologySpreadConstraints": { + "type": "array" + }, + "priorityClassName": { + "type": "string" + }, + "podAnnotations": { + "type": "object" + }, + "podLabels": { + "type": "object" + }, + "extraEnv": { + "type": "array" + }, + "terminationGracePeriodSeconds": { + "type": "integer", + "minimum": 0 + }, + "serviceAccount": { + "type": "object", + "properties": { + "create": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "annotations": { + "type": "object" + }, + "automountServiceAccountToken": { + "type": "boolean" + } + } + }, + "testResources": { + "$ref": "#/definitions/resources" + }, + "service": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "ClusterIP", + "NodePort", + "LoadBalancer" + ] + }, + "annotations": { + "type": "object" + }, + "nodePort": { + "type": [ + "integer", + "null" + ], + "minimum": 30000, + "maximum": 32767 + } + } + } + } + } + } +} diff --git a/helm/hugegraph/values.yaml b/helm/hugegraph/values.yaml new file mode 100644 index 0000000000..83bb1f3c74 --- /dev/null +++ b/helm/hugegraph/values.yaml @@ -0,0 +1,258 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Default values for HugeGraph HStore Helm chart. +# See values-single.yaml and values-cluster.yaml for presets. + +fullnameOverride: "" +nameOverride: "" + +imagePullSecrets: [] + +pd: + replicas: 3 + image: + repository: hugegraph/pd + # The draft tracks latest until the next HugeGraph release tag is available. + # Pin the release tag and switch to IfNotPresent before stable publication. + tag: latest + pullPolicy: Always + # Empty preserves the image entrypoint's automatic JVM sizing. + javaOpts: "" + ports: + grpc: 8686 + rest: 8620 + raft: 8610 + dataPath: /hugegraph-pd/pd_data + storage: + size: 10Gi + storageClassName: "" + resources: {} + # Pod-level and container-level securityContext. Empty by default because the + # published images run as root; set these to satisfy a restricted namespace. + podSecurityContext: {} + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] + seccompProfile: + type: RuntimeDefault + # required | preferred | disabled + antiAffinity: required + # Scheduling. `affinity` takes precedence over the antiAffinity preset. + nodeSelector: {} + tolerations: [] + affinity: {} + topologySpreadConstraints: [] + priorityClassName: "" + podAnnotations: {} + podLabels: {} + # Extra environment variables appended to the pd container. + extraEnv: [] + # Gives a JVM with an on-disk store time to shut down cleanly on drain. + terminationGracePeriodSeconds: 300 + serviceAccount: + create: true + name: "" + annotations: {} + # This chart makes no Kubernetes API calls, so no token is mounted. + automountServiceAccountToken: false + pdb: + enabled: true + minAvailable: 2 + # Startup can take a while during Raft bootstrap + probes: + startup: + failureThreshold: 30 + periodSeconds: 10 + timeoutSeconds: 5 + readiness: + periodSeconds: 10 + failureThreshold: 3 + timeoutSeconds: 5 + liveness: + periodSeconds: 20 + failureThreshold: 3 + timeoutSeconds: 5 + +store: + replicas: 3 + image: + repository: hugegraph/store + # The draft tracks latest until the next HugeGraph release tag is available. + # Pin the release tag and switch to IfNotPresent before stable publication. + tag: latest + pullPolicy: Always + # Empty preserves the image entrypoint's automatic JVM sizing. + javaOpts: "" + ports: + grpc: 8500 + raft: 8510 + rest: 8520 + dataPath: /hugegraph-store/storage + storage: + size: 50Gi + storageClassName: "" + resources: {} + podSecurityContext: {} + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] + seccompProfile: + type: RuntimeDefault + antiAffinity: required + # Scheduling. `affinity` takes precedence over the antiAffinity preset. + nodeSelector: {} + tolerations: [] + affinity: {} + topologySpreadConstraints: [] + priorityClassName: "" + podAnnotations: {} + podLabels: {} + # Extra environment variables appended to the store container. + extraEnv: [] + # Gives a JVM with an on-disk store time to shut down cleanly on drain. + terminationGracePeriodSeconds: 300 + serviceAccount: + create: true + name: "" + annotations: {} + # This chart makes no Kubernetes API calls, so no token is mounted. + automountServiceAccountToken: false + pdb: + enabled: true + minAvailable: 2 + waitImage: curlimages/curl:8.5.0 + # Bound the PD-quorum wait so a cluster that never reaches quorum fails + # visibly instead of sitting in Init:0/1 forever. + waitTimeoutSeconds: 900 + # Optional bounds for the PD-quorum wait init container. + waitResources: {} + probes: + startup: + failureThreshold: 40 + periodSeconds: 10 + timeoutSeconds: 5 + readiness: + periodSeconds: 10 + failureThreshold: 3 + timeoutSeconds: 5 + liveness: + periodSeconds: 20 + failureThreshold: 3 + timeoutSeconds: 5 + +server: + replicas: 3 + image: + repository: hugegraph/server + # The draft tracks latest until the next HugeGraph release tag is available. + # Pin the release tag and switch to IfNotPresent before stable publication. + tag: latest + pullPolicy: Always + # Empty preserves the image entrypoint's automatic JVM sizing. + javaOpts: "" + port: 8080 + backend: hstore + resources: {} + # Server is stateless and may scale past the node count via HPA, so the + # default only prefers spreading. Use "required" when replicas are always + # fewer than schedulable nodes. + # required | preferred | disabled + antiAffinity: preferred + # Scheduling. `affinity` takes precedence over the antiAffinity preset. + nodeSelector: {} + tolerations: [] + affinity: {} + topologySpreadConstraints: [] + priorityClassName: "" + podAnnotations: {} + podLabels: {} + # Extra environment variables appended to the server container. + extraEnv: [] + # Allows in-flight requests to drain before the Server is stopped. + terminationGracePeriodSeconds: 60 + serviceAccount: + create: true + name: "" + annotations: {} + # This chart makes no Kubernetes API calls, so no token is mounted. + automountServiceAccountToken: false + podSecurityContext: {} + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] + seccompProfile: + type: RuntimeDefault + # Server has no quorum to preserve, so no PodDisruptionBudget by default. + pdb: + enabled: false + minAvailable: 2 + # Image used by the Helm test hook. + waitImage: curlimages/curl:8.5.0 + # Optional resources for the Helm test hook container. + testResources: {} + restServer: + # Empty preserves the image's restserver.min_free_memory default. + minFreeMemory: "" + # Empty preserves the image's batch.max_write_threads default. + batchMaxWriteThreads: "" + # Distributed HStore: the init-store gate must be explicitly false, so that + # concurrent Server replicas never initialize the same backend. No + # HG_SERVER_SKIP_INIT and no init Job. + initStoreEnabled: false + auth: + enabled: false + # Secret must contain key "password" + existingSecret: "" + service: + type: ClusterIP + annotations: {} + ingress: + enabled: false + className: "" + annotations: {} + hosts: + - host: hugegraph.local + paths: + - path: / + pathType: Prefix + tls: [] + hpa: + enabled: false + minReplicas: 3 + maxReplicas: 10 + targetCPUUtilizationPercentage: 70 + probes: + startup: + # 450s covers the 300s storage wait and Server process startup. + failureThreshold: 90 + periodSeconds: 5 + timeoutSeconds: 5 + readiness: + periodSeconds: 10 + failureThreshold: 3 + timeoutSeconds: 5 + liveness: + periodSeconds: 20 + failureThreshold: 3 + timeoutSeconds: 5 + +# No init Job; see README.md for the HStore initialization contract. +# Install with: helm install ... --wait (no --wait-for-jobs)