imbajin commented on code in PR #494:
URL: https://github.com/apache/hugegraph-doc/pull/494#discussion_r4191812768


##########
content/en/docs/quickstart/hugegraph/hugegraph-helm-operations.md:
##########
@@ -0,0 +1,523 @@
+---
+title: "Operate HugeGraph on Kubernetes"
+linkTitle: "Operations on Kubernetes (Helm)"
+weight: 5
+search_keywords:
+  - helm
+  - kubernetes
+  - operations
+  - disaster recovery
+  - networkpolicy
+---
+
+### 1 Scope
+
+This page is for operating a HugeGraph cluster that the Helm chart already
+deployed. Installing, upgrading, and uninstalling are on the
+[deployment page](/docs/quickstart/hugegraph/hugegraph-helm/); the full
+parameter reference stays in the
+[chart 
README](https://github.com/apache/hugegraph/tree/master/helm/hugegraph#configuration).
+
+Commands below assume the release is named `hugegraph` in namespace
+`hugegraph`; substitute your own names. Two credentials come up repeatedly,
+both read from chart-managed Secrets:
+
+```bash
+PASSWORD="$(kubectl get secret -n hugegraph hugegraph-admin \
+  -o jsonpath='{.data.password}' | base64 --decode)"
+PD_SECRET="$(kubectl get secret -n hugegraph hugegraph-pd-auth \
+  -o jsonpath='{.data.secret-key}' | base64 --decode)"
+```
+
+### 2 Ports and 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; changing `server.port` updates the
+listener, container port, and Service together. A stalled component (process
+alive but frozen) is ended by its liveness probe: the default 20 s period and
+3-failure threshold bound the blast radius of a stalled Store at roughly one
+minute, and raft moves its partition leaders within seconds of the restart.
+
+Reach the API through a port-forward. It runs in the foreground, so keep
+it in one terminal:
+
+```bash
+kubectl port-forward -n hugegraph svc/hugegraph-server 8080:8080
+```
+
+and call the API from a second terminal, with `PASSWORD` set as above:
+
+```bash
+curl --user "admin:${PASSWORD}" http://127.0.0.1:8080/versions
+curl --user "admin:${PASSWORD}" http://127.0.0.1:8080/graphs
+```
+
+### 3 Scheduling
+
+Every component (`pd`, `store`, `server`, `hubble`) exposes `nodeSelector`,
+`tolerations`, `affinity`, `topologySpreadConstraints`, and
+`priorityClassName`. Pinning Store to labeled nodes is just:
+
+```yaml
+store:
+  nodeSelector:
+    hugegraph/role: storage
+```
+
+`antiAffinity` (`required` | `preferred` | `disabled`) renders a hostname
+pod-anti-affinity preset for `pd`, `store`, and `server`; Hubble is
+single-replica by design and has no such key. Setting a raw `affinity`
+replaces the preset entirely. All three default to `preferred`, so the chart
+schedules on clusters with fewer nodes than replicas. The trade: under node
+pressure the scheduler may co-locate replicas, and a single node failure can
+then take more than one PD or Store replica with it. Production clusters with
+enough nodes should pin `pd.antiAffinity` and `store.antiAffinity` to
+`required`, as `values-cluster.yaml` does.
+
+### 4 Partition sharding
+
+A fresh install seeds PD with a partition shard count of 3 when
+`store.replicas` is at least 3, and 1 otherwise; without the seed, the PD
+image pins `partition.default-shard-count` to 1 and chart-deployed clusters
+would run without store-level HA. The chart passes the value as
+`-Dpartition.default-shard-count` in PD's `JAVA_OPTS`, so the image's JVM
+auto-sizing is unaffected.
+
+**The seed applies at first bootstrap only.** PD persists the shard count the
+first time it starts with empty storage; from then on the stored value is
+authoritative, and changing `pd.partition.defaultShardCount` or scaling
+`store.replicas` later has no effect on an initialized cluster. To change
+the shard count of a running cluster, use PD's own config API (odd values
+only, at most the live store count), then trigger
+`GET /v1/task/patrolPartitions` and expect shard-group reallocation.
+
+The shard count also fixes the initial partition count:
+`store.replicas x storeMaxShardCount / shardCount`, computed once at
+bootstrap. With the image default `store-max-shard-count` of 12, a default
+3-store install gets 12 partitions; raise
+`pd.partition.storeMaxShardCount` (likewise seeded once) when more
+partitions are wanted.
+
+An explicit `pd.partition.defaultShardCount` must be odd and at most
+`store.replicas`; the chart rejects other values at render time because PD
+would otherwise clamp or refuse them silently.
+
+### 5 NetworkPolicy
+
+`networkPolicy.enabled` renders one NetworkPolicy per component (PD, Store,
+Server, and Hubble when enabled). Each policy isolates its own Pods in both
+directions and lists the traffic they need, so apply order does not matter.
+It is off in `values.yaml`, because the base values cannot know who your
+clients are, and on in `values-cluster.yaml`.
+
+It only works when the cluster's network plugin enforces NetworkPolicy (kind
+v0.25 or later, k3s, Calico, Cilium); other plugins accept the objects and
+enforce nothing. To check, run a Pod without chart labels in another
+namespace and `curl` the PD client Service on the REST port: it must time
+out.
+
+With the policies on, the release admits only its own traffic:
+
+| To | From, ports |
+|---|---|
+| PD | PD: raft, gRPC. Store, Server, and Hubble in `pd` mode: gRPC, REST |
+| Store | Store: raft. Server: gRPC, REST. Hubble in `pd` mode: REST |
+| Server | Hubble and the `helm test` Pod: `server.port` |
+| Hubble | nothing (port-forward uses loopback and needs no rule) |
+
+Every component may also resolve DNS on port 53. Nothing outside the release
+is admitted unless it is listed in `networkPolicy.<component>.extraIngress`,
+including the Ingress controller and the clients of a NodePort or
+LoadBalancer Service. Exposing PD, Server or Hubble that way, or setting
+`server.advertiseUrl`, with an empty `extraIngress` fails the render instead
+of opening the port. The check sees only exposure the chart creates; a
+Service, Gateway route or proxy you add yourself needs its own entry. An
+in-cluster Hubble in `pd` mode combined with `server.advertiseUrl` also
+fails the render until `networkPolicy.hubble.extraEgress` admits the
+advertised destination: PD hands Hubble that URL for discovery, and
+Hubble's egress policy otherwise reaches only the release's own Pods.
+
+Two egress facts to plan around. PD, Store and Server reach nothing outside
+the release except DNS, so features that call out do not work with the
+policies on; the one default caller is the Store image, which downloads
+`libjemalloc.so` from github.com on every start. With the policies on that
+connection times out after about two minutes and the Store starts without
+jemalloc (measured on kind: Ready after 151 s instead of 11 s); the same
+happens on any cluster without internet access. And the `helm test` Pod is
+selected by no chart policy, so under a namespace-wide default-deny policy
+of your own you must allow it egress to `server.port` and DNS.
+
+**Letting other workloads in.** Every rule must name its peers in `from`;
+to admit any address, write an `ipBlock` such as `0.0.0.0/0` explicitly. A
+`namespaceSelector` and a `podSelector` in the same peer must both match;
+as two separate peers, either one is enough. What a NodePort or
+LoadBalancer client looks like from the Pod depends on the network plugin,
+`externalTrafficPolicy`, and the node the request arrives on. Measured with
+a NodePort Server on two-node kind clusters:
+
+- kindnet, and Cilium with kube-proxy replacement: a call to the Server's
+  own node arrived with the client address; through the other node it
+  arrived with that node's address.
+- Calico: through the other node the call arrived from that node's tunnel
+  address inside the Pod CIDR.
+- Cilium with kube-proxy: no `ipBlock` rule admitted NodePort traffic,
+  because Cilium identifies node addresses by its own node identities
+  rather than by CIDR.
+
+Test with the plugin you run and name the CIDR you see arriving. A worked
+example admitting an Ingress controller, an application namespace, and
+Prometheus:
+
+```yaml
+networkPolicy:
+  server:
+    extraIngress:
+      - from:
+          - namespaceSelector:
+              matchLabels:
+                kubernetes.io/metadata.name: ingress-nginx
+            podSelector:
+              matchLabels:
+                app.kubernetes.io/name: ingress-nginx
+        ports:
+          - port: 8080
+      - from:
+          - namespaceSelector:
+              matchLabels:
+                kubernetes.io/metadata.name: apps
+        ports:
+          - port: 8080
+  pd:
+    extraIngress:
+      - from:
+          - namespaceSelector:
+              matchLabels:
+                kubernetes.io/metadata.name: monitoring
+        ports:
+          - port: 8620
+```
+
+### 6 Rolling Store images safely
+
+Store rolling updates advance on a listener check, not on shard recovery,
+so the controller can replace the next Store while the previous one is
+still rejoining its shard groups. `values-cluster.yaml` therefore sets
+`store.updateStrategy.type=OnDelete` (set it yourself on any other
+production values): an upgrade then updates the StatefulSet without
+replacing any Store Pod, and you delete Store Pods one at a time, checking
+between deletions. `OnDelete` only stops automatic advancement; deleting
+Pods without the checks below carries the same risk.
+
+`Up` in PD is not that check. PD marks a Store `Up` at registration, before
+the Store has restored any partition, and a stopped Store stays `Up` in
+every shard group until its keep-alive entry expires (300 s on current
+images): a Pod deleted and back inside that window never leaves `Up` at
+all. Start with the Pod instead:
+
+```bash
+kubectl -n hugegraph wait --for=condition=Ready \
+  pod/hugegraph-store-<ordinal> --timeout=10m
+```
+
+Then check shard membership and leadership per group, read from the PD
+leader (see Disaster Recovery below for finding the leader):
+
+```bash
+curl -s -u "hg:${PD_SECRET}" http://127.0.0.1:8620/v1/shardGroups | jq '
+  .shardGroups[] | {id: (.id // 0),
+                    shards: [.shards[] | {storeId, role}],
+                    leaders: [.shards[] | select(.role=="Leader")] | length}'
+```
+
+Delete the next Store only when the replaced Pod is `Ready`, its Store id
+shows a fresh `lastHeartBeat` in `/v1/stores`, and every group reports its
+full shard count with exactly one `Leader`.

Review Comment:
   ‼️ critical. Blocking: yes. Summary: This gate counts every shard entry, so 
a Leader+Follower+Learner group satisfies the full-count/one-Leader condition 
before the new replica is promoted; deleting another Store can leave only one 
voter. Please require one Leader and only Followers (no Learner/None) before 
continuing, including the shrink gate, and sync the Chinese instructions. 
Evidence: [PartitionEngine adds peers as Learners and promotes them only after 
waitForReplicate](https://github.com/apache/hugegraph/blob/8fd9841ebc6770df6119fdce07249fa9e558ae9e/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/PartitionEngine.java#L320-L368);
 [PD treats Learner as migration in 
progress](https://github.com/apache/hugegraph/blob/8fd9841ebc6770df6119fdce07249fa9e558ae9e/hugegraph-pd/hg-pd-core/src/main/java/org/apache/hugegraph/pd/TaskScheduleService.java#L313-L320).



##########
content/en/docs/quickstart/hugegraph/hugegraph-helm-operations.md:
##########
@@ -0,0 +1,523 @@
+---
+title: "Operate HugeGraph on Kubernetes"
+linkTitle: "Operations on Kubernetes (Helm)"
+weight: 5
+search_keywords:
+  - helm
+  - kubernetes
+  - operations
+  - disaster recovery
+  - networkpolicy
+---
+
+### 1 Scope
+
+This page is for operating a HugeGraph cluster that the Helm chart already
+deployed. Installing, upgrading, and uninstalling are on the
+[deployment page](/docs/quickstart/hugegraph/hugegraph-helm/); the full
+parameter reference stays in the
+[chart 
README](https://github.com/apache/hugegraph/tree/master/helm/hugegraph#configuration).
+
+Commands below assume the release is named `hugegraph` in namespace
+`hugegraph`; substitute your own names. Two credentials come up repeatedly,
+both read from chart-managed Secrets:
+
+```bash
+PASSWORD="$(kubectl get secret -n hugegraph hugegraph-admin \
+  -o jsonpath='{.data.password}' | base64 --decode)"
+PD_SECRET="$(kubectl get secret -n hugegraph hugegraph-pd-auth \
+  -o jsonpath='{.data.secret-key}' | base64 --decode)"
+```
+
+### 2 Ports and 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; changing `server.port` updates the
+listener, container port, and Service together. A stalled component (process
+alive but frozen) is ended by its liveness probe: the default 20 s period and
+3-failure threshold bound the blast radius of a stalled Store at roughly one
+minute, and raft moves its partition leaders within seconds of the restart.
+
+Reach the API through a port-forward. It runs in the foreground, so keep
+it in one terminal:
+
+```bash
+kubectl port-forward -n hugegraph svc/hugegraph-server 8080:8080
+```
+
+and call the API from a second terminal, with `PASSWORD` set as above:
+
+```bash
+curl --user "admin:${PASSWORD}" http://127.0.0.1:8080/versions
+curl --user "admin:${PASSWORD}" http://127.0.0.1:8080/graphs
+```
+
+### 3 Scheduling
+
+Every component (`pd`, `store`, `server`, `hubble`) exposes `nodeSelector`,
+`tolerations`, `affinity`, `topologySpreadConstraints`, and
+`priorityClassName`. Pinning Store to labeled nodes is just:
+
+```yaml
+store:
+  nodeSelector:
+    hugegraph/role: storage
+```
+
+`antiAffinity` (`required` | `preferred` | `disabled`) renders a hostname
+pod-anti-affinity preset for `pd`, `store`, and `server`; Hubble is
+single-replica by design and has no such key. Setting a raw `affinity`
+replaces the preset entirely. All three default to `preferred`, so the chart
+schedules on clusters with fewer nodes than replicas. The trade: under node
+pressure the scheduler may co-locate replicas, and a single node failure can
+then take more than one PD or Store replica with it. Production clusters with
+enough nodes should pin `pd.antiAffinity` and `store.antiAffinity` to
+`required`, as `values-cluster.yaml` does.
+
+### 4 Partition sharding
+
+A fresh install seeds PD with a partition shard count of 3 when
+`store.replicas` is at least 3, and 1 otherwise; without the seed, the PD
+image pins `partition.default-shard-count` to 1 and chart-deployed clusters
+would run without store-level HA. The chart passes the value as
+`-Dpartition.default-shard-count` in PD's `JAVA_OPTS`, so the image's JVM
+auto-sizing is unaffected.
+
+**The seed applies at first bootstrap only.** PD persists the shard count the
+first time it starts with empty storage; from then on the stored value is
+authoritative, and changing `pd.partition.defaultShardCount` or scaling
+`store.replicas` later has no effect on an initialized cluster. To change
+the shard count of a running cluster, use PD's own config API (odd values
+only, at most the live store count), then trigger
+`GET /v1/task/patrolPartitions` and expect shard-group reallocation.
+
+The shard count also fixes the initial partition count:
+`store.replicas x storeMaxShardCount / shardCount`, computed once at
+bootstrap. With the image default `store-max-shard-count` of 12, a default
+3-store install gets 12 partitions; raise
+`pd.partition.storeMaxShardCount` (likewise seeded once) when more
+partitions are wanted.
+
+An explicit `pd.partition.defaultShardCount` must be odd and at most
+`store.replicas`; the chart rejects other values at render time because PD
+would otherwise clamp or refuse them silently.
+
+### 5 NetworkPolicy
+
+`networkPolicy.enabled` renders one NetworkPolicy per component (PD, Store,
+Server, and Hubble when enabled). Each policy isolates its own Pods in both
+directions and lists the traffic they need, so apply order does not matter.
+It is off in `values.yaml`, because the base values cannot know who your
+clients are, and on in `values-cluster.yaml`.
+
+It only works when the cluster's network plugin enforces NetworkPolicy (kind
+v0.25 or later, k3s, Calico, Cilium); other plugins accept the objects and
+enforce nothing. To check, run a Pod without chart labels in another
+namespace and `curl` the PD client Service on the REST port: it must time
+out.
+
+With the policies on, the release admits only its own traffic:

Review Comment:
   ⚠️ important. Blocking: no. Summary: The statement that the release admits 
only its own traffic holds only when no other applicable NetworkPolicy allows 
traffic to these Pods; Kubernetes unions allowed rules, so a broader policy can 
admit sources omitted from extraIngress. Please qualify this boundary and 
advise operators to audit other policies selecting these Pods in both 
languages. Evidence: [Kubernetes documents additive NetworkPolicy rules and the 
union of allowed 
connections](https://kubernetes.io/docs/concepts/services-networking/network-policies/).



##########
content/en/docs/quickstart/hugegraph/hugegraph-helm-operations.md:
##########
@@ -0,0 +1,523 @@
+---
+title: "Operate HugeGraph on Kubernetes"
+linkTitle: "Operations on Kubernetes (Helm)"
+weight: 5
+search_keywords:
+  - helm
+  - kubernetes
+  - operations
+  - disaster recovery
+  - networkpolicy
+---
+
+### 1 Scope
+
+This page is for operating a HugeGraph cluster that the Helm chart already
+deployed. Installing, upgrading, and uninstalling are on the
+[deployment page](/docs/quickstart/hugegraph/hugegraph-helm/); the full
+parameter reference stays in the
+[chart 
README](https://github.com/apache/hugegraph/tree/master/helm/hugegraph#configuration).
+
+Commands below assume the release is named `hugegraph` in namespace
+`hugegraph`; substitute your own names. Two credentials come up repeatedly,
+both read from chart-managed Secrets:
+
+```bash
+PASSWORD="$(kubectl get secret -n hugegraph hugegraph-admin \
+  -o jsonpath='{.data.password}' | base64 --decode)"
+PD_SECRET="$(kubectl get secret -n hugegraph hugegraph-pd-auth \
+  -o jsonpath='{.data.secret-key}' | base64 --decode)"
+```
+
+### 2 Ports and 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; changing `server.port` updates the
+listener, container port, and Service together. A stalled component (process
+alive but frozen) is ended by its liveness probe: the default 20 s period and
+3-failure threshold bound the blast radius of a stalled Store at roughly one
+minute, and raft moves its partition leaders within seconds of the restart.
+
+Reach the API through a port-forward. It runs in the foreground, so keep
+it in one terminal:
+
+```bash
+kubectl port-forward -n hugegraph svc/hugegraph-server 8080:8080
+```
+
+and call the API from a second terminal, with `PASSWORD` set as above:
+
+```bash
+curl --user "admin:${PASSWORD}" http://127.0.0.1:8080/versions
+curl --user "admin:${PASSWORD}" http://127.0.0.1:8080/graphs
+```
+
+### 3 Scheduling
+
+Every component (`pd`, `store`, `server`, `hubble`) exposes `nodeSelector`,
+`tolerations`, `affinity`, `topologySpreadConstraints`, and
+`priorityClassName`. Pinning Store to labeled nodes is just:
+
+```yaml
+store:
+  nodeSelector:
+    hugegraph/role: storage
+```
+
+`antiAffinity` (`required` | `preferred` | `disabled`) renders a hostname
+pod-anti-affinity preset for `pd`, `store`, and `server`; Hubble is
+single-replica by design and has no such key. Setting a raw `affinity`
+replaces the preset entirely. All three default to `preferred`, so the chart
+schedules on clusters with fewer nodes than replicas. The trade: under node
+pressure the scheduler may co-locate replicas, and a single node failure can
+then take more than one PD or Store replica with it. Production clusters with
+enough nodes should pin `pd.antiAffinity` and `store.antiAffinity` to
+`required`, as `values-cluster.yaml` does.
+
+### 4 Partition sharding
+
+A fresh install seeds PD with a partition shard count of 3 when
+`store.replicas` is at least 3, and 1 otherwise; without the seed, the PD
+image pins `partition.default-shard-count` to 1 and chart-deployed clusters
+would run without store-level HA. The chart passes the value as
+`-Dpartition.default-shard-count` in PD's `JAVA_OPTS`, so the image's JVM
+auto-sizing is unaffected.
+
+**The seed applies at first bootstrap only.** PD persists the shard count the
+first time it starts with empty storage; from then on the stored value is
+authoritative, and changing `pd.partition.defaultShardCount` or scaling
+`store.replicas` later has no effect on an initialized cluster. To change
+the shard count of a running cluster, use PD's own config API (odd values
+only, at most the live store count), then trigger
+`GET /v1/task/patrolPartitions` and expect shard-group reallocation.
+
+The shard count also fixes the initial partition count:
+`store.replicas x storeMaxShardCount / shardCount`, computed once at
+bootstrap. With the image default `store-max-shard-count` of 12, a default
+3-store install gets 12 partitions; raise
+`pd.partition.storeMaxShardCount` (likewise seeded once) when more
+partitions are wanted.
+
+An explicit `pd.partition.defaultShardCount` must be odd and at most
+`store.replicas`; the chart rejects other values at render time because PD
+would otherwise clamp or refuse them silently.
+
+### 5 NetworkPolicy
+
+`networkPolicy.enabled` renders one NetworkPolicy per component (PD, Store,
+Server, and Hubble when enabled). Each policy isolates its own Pods in both
+directions and lists the traffic they need, so apply order does not matter.
+It is off in `values.yaml`, because the base values cannot know who your
+clients are, and on in `values-cluster.yaml`.
+
+It only works when the cluster's network plugin enforces NetworkPolicy (kind
+v0.25 or later, k3s, Calico, Cilium); other plugins accept the objects and
+enforce nothing. To check, run a Pod without chart labels in another
+namespace and `curl` the PD client Service on the REST port: it must time
+out.
+
+With the policies on, the release admits only its own traffic:
+
+| To | From, ports |
+|---|---|
+| PD | PD: raft, gRPC. Store, Server, and Hubble in `pd` mode: gRPC, REST |
+| Store | Store: raft. Server: gRPC, REST. Hubble in `pd` mode: REST |
+| Server | Hubble and the `helm test` Pod: `server.port` |
+| Hubble | nothing (port-forward uses loopback and needs no rule) |
+
+Every component may also resolve DNS on port 53. Nothing outside the release
+is admitted unless it is listed in `networkPolicy.<component>.extraIngress`,
+including the Ingress controller and the clients of a NodePort or
+LoadBalancer Service. Exposing PD, Server or Hubble that way, or setting
+`server.advertiseUrl`, with an empty `extraIngress` fails the render instead
+of opening the port. The check sees only exposure the chart creates; a
+Service, Gateway route or proxy you add yourself needs its own entry. An
+in-cluster Hubble in `pd` mode combined with `server.advertiseUrl` also
+fails the render until `networkPolicy.hubble.extraEgress` admits the
+advertised destination: PD hands Hubble that URL for discovery, and
+Hubble's egress policy otherwise reaches only the release's own Pods.
+
+Two egress facts to plan around. PD, Store and Server reach nothing outside
+the release except DNS, so features that call out do not work with the
+policies on; the one default caller is the Store image, which downloads
+`libjemalloc.so` from github.com on every start. With the policies on that
+connection times out after about two minutes and the Store starts without
+jemalloc (measured on kind: Ready after 151 s instead of 11 s); the same
+happens on any cluster without internet access. And the `helm test` Pod is
+selected by no chart policy, so under a namespace-wide default-deny policy
+of your own you must allow it egress to `server.port` and DNS.
+
+**Letting other workloads in.** Every rule must name its peers in `from`;
+to admit any address, write an `ipBlock` such as `0.0.0.0/0` explicitly. A
+`namespaceSelector` and a `podSelector` in the same peer must both match;
+as two separate peers, either one is enough. What a NodePort or
+LoadBalancer client looks like from the Pod depends on the network plugin,
+`externalTrafficPolicy`, and the node the request arrives on. Measured with
+a NodePort Server on two-node kind clusters:
+
+- kindnet, and Cilium with kube-proxy replacement: a call to the Server's
+  own node arrived with the client address; through the other node it
+  arrived with that node's address.
+- Calico: through the other node the call arrived from that node's tunnel
+  address inside the Pod CIDR.
+- Cilium with kube-proxy: no `ipBlock` rule admitted NodePort traffic,
+  because Cilium identifies node addresses by its own node identities
+  rather than by CIDR.
+
+Test with the plugin you run and name the CIDR you see arriving. A worked
+example admitting an Ingress controller, an application namespace, and
+Prometheus:
+
+```yaml
+networkPolicy:
+  server:
+    extraIngress:
+      - from:
+          - namespaceSelector:
+              matchLabels:
+                kubernetes.io/metadata.name: ingress-nginx
+            podSelector:
+              matchLabels:
+                app.kubernetes.io/name: ingress-nginx
+        ports:
+          - port: 8080
+      - from:
+          - namespaceSelector:
+              matchLabels:
+                kubernetes.io/metadata.name: apps
+        ports:
+          - port: 8080
+  pd:
+    extraIngress:
+      - from:
+          - namespaceSelector:
+              matchLabels:
+                kubernetes.io/metadata.name: monitoring
+        ports:
+          - port: 8620
+```
+
+### 6 Rolling Store images safely
+
+Store rolling updates advance on a listener check, not on shard recovery,
+so the controller can replace the next Store while the previous one is
+still rejoining its shard groups. `values-cluster.yaml` therefore sets
+`store.updateStrategy.type=OnDelete` (set it yourself on any other
+production values): an upgrade then updates the StatefulSet without
+replacing any Store Pod, and you delete Store Pods one at a time, checking
+between deletions. `OnDelete` only stops automatic advancement; deleting
+Pods without the checks below carries the same risk.
+
+`Up` in PD is not that check. PD marks a Store `Up` at registration, before
+the Store has restored any partition, and a stopped Store stays `Up` in
+every shard group until its keep-alive entry expires (300 s on current
+images): a Pod deleted and back inside that window never leaves `Up` at
+all. Start with the Pod instead:
+
+```bash
+kubectl -n hugegraph wait --for=condition=Ready \
+  pod/hugegraph-store-<ordinal> --timeout=10m
+```
+
+Then check shard membership and leadership per group, read from the PD
+leader (see Disaster Recovery below for finding the leader):
+
+```bash
+curl -s -u "hg:${PD_SECRET}" http://127.0.0.1:8620/v1/shardGroups | jq '
+  .shardGroups[] | {id: (.id // 0),
+                    shards: [.shards[] | {storeId, role}],
+                    leaders: [.shards[] | select(.role=="Leader")] | length}'
+```
+
+Delete the next Store only when the replaced Pod is `Ready`, its Store id
+shows a fresh `lastHeartBeat` in `/v1/stores`, and every group reports its
+full shard count with exactly one `Leader`.
+
+Know what this does not prove: the shard list is PD's membership record,
+not a statement that the Store caught up on the raft log. No endpoint on
+current images reports restoration-complete. For a closer look,
+port-forward the replaced Store and read its own view of a group:
+`GET :8520/v1/partition/<groupId>` returns the raft role, leader, and
+committed index (`logIndex`) that Store holds, and fails while the Store
+is down; compare the index with a peer Store rather than reading it
+alone. This route never fills its `term` field, so `term` always reads 0
+there. The plural `GET :8520/v1/partitions` answers 500 on any Store that
+follows a group on images built before
+[apache/hugegraph#3232](https://github.com/apache/hugegraph/pull/3232)
+(merged 2026-09-24); after it, it answers 200 on every Store, with `conf`
+and `peers` null for followed groups. The per-group path works on both.
+
+Leave a margin after the membership check, keep `store.pdb.minAvailable`
+at `replicas - 1` so an accidental second eviction is refused, and treat a
+group that is short a shard or has no leader as a stop. A real
+restoration-complete signal is upstream work, tracked in
+[apache/hugegraph#3229](https://github.com/apache/hugegraph/issues/3229).
+
+### 7 Disaster recovery
+
+What PD automates on current builds is narrow: a 60-second patrol only
+marks Stores that stopped sending heartbeats as `Offline`. There is no
+automatic re-replication; re-placing the replicas of a lost Store,
+reconciling shard groups, and processing tombstoned Stores run only when a
+partition patrol is triggered explicitly.
+
+The task endpoints execute locally on the PD that receives them, and a
+follower answers with an empty success while doing nothing.
+Port-forwarding the client Service selects an arbitrary PD, so identify
+the leader first, then port-forward that Pod (two terminals: the forward
+runs in the foreground):
+
+```bash
+kubectl port-forward -n hugegraph svc/hugegraph-pd-client 8620:8620
+# Read .data.pdLeader.raftUrl; its host names the leader Pod.
+curl -su "hg:${PD_SECRET}" http://127.0.0.1:8620/v1/members
+# Stop the Service forward, then forward the leader Pod instead.
+kubectl port-forward -n hugegraph pod/<leader-pod> 8620:8620
+# Reconcile shard groups and process tombstoned Stores.
+curl -u "hg:${PD_SECRET}" http://127.0.0.1:8620/v1/task/patrolPartitions
+# If the patrol repaired a shard group, wait 180 s first (see below).
+# Spread Raft leaders, then partition data.
+curl -u "hg:${PD_SECRET}" http://127.0.0.1:8620/v1/task/balanceLeaders
+curl -u "hg:${PD_SECRET}" http://127.0.0.1:8620/v1/task/balancePartitions
+```
+
+Read `/v1/members` again after the tasks: if leadership moved mid-sequence,
+the later tasks ran on a follower and did nothing. PD sets a
+balance-shard flag for 180 s in two cases: `patrolPartitions` sets it
+whenever it reallocates a shard group whose shard count is wrong, and
+`balancePartitions` sets it even when it moves nothing. Inside that
+window `balanceLeaders` is refused and `balancePartitions` returns
+without doing anything, so wait at least 180 s after a patrol that
+repaired a group, and after `balancePartitions`, before the next
+balancing call. On images built before
+[apache/hugegraph#3233](https://github.com/apache/hugegraph/pull/3233)
+(merged 2026-09-24) the refusal is a bare HTTP 500 whose reason appears
+only in the PD log; after it, the reason comes back in the body as
+`{"status":1001,"error":"balance shard is processing, please try later!"}`.
+
+Telling a real run from a no-op takes the PD leader's log, because the
+responses do not: `patrolPartitions` answers the same empty success on the
+leader and on a follower, whether or not it repaired anything (look for
+`reallocShards`, `shardOffline` or `storeTurnoff` lines, or diff
+`/v1/shardGroups` before and after), and `balancePartitions` answers `{}`
+on the leader and an empty body on a follower. `balanceLeaders` is the one
+call whose body carries the work. Distinguishable task responses are
+upstream work, tracked in
+[apache/hugegraph#3231](https://github.com/apache/hugegraph/issues/3231).
+
+Run `patrolPartitions` after replacing a Store that is not coming back,
+`balancePartitions` once the cluster is stable again, and `balanceLeaders`

Review Comment:
   ⚠️ important. Blocking: no. Summary: Tombstoning a Store starts a separate 
30-minute cooldown: balancePartitionShard() returns null during that interval, 
even after the documented 180-second patrol window. Please document the 
Tombstone cooldown before advising balancePartitions, and distinguish it from 
the BALANCE_SHARD_KEY timer. Evidence: [The status listener records Tombstone 
time](https://github.com/apache/hugegraph/blob/8fd9841ebc6770df6119fdce07249fa9e558ae9e/hugegraph-pd/hg-pd-core/src/main/java/org/apache/hugegraph/pd/TaskScheduleService.java#L168-L174);
 [the cooldown constant and early 
return](https://github.com/apache/hugegraph/blob/8fd9841ebc6770df6119fdce07249fa9e558ae9e/hugegraph-pd/hg-pd-core/src/main/java/org/apache/hugegraph/pd/TaskScheduleService.java#L59-L60)
 and [the 30-minute 
guard](https://github.com/apache/hugegraph/blob/8fd9841ebc6770df6119fdce07249fa9e558ae9e/hugegraph-pd/hg-pd-core/src/main/java/org/apache/hugegraph/pd/TaskScheduleService.java#L281-L28
 3).



##########
content/en/docs/quickstart/hugegraph/hugegraph-helm.md:
##########
@@ -0,0 +1,367 @@
+---
+title: "Deploy on Kubernetes with Helm"
+linkTitle: "Deploy on Kubernetes (Helm)"
+weight: 4
+search_keywords:
+  - helm
+  - kubernetes
+  - k8s
+---
+
+### 1 Overview
+
+The Helm chart deploys a distributed HugeGraph cluster on Kubernetes: PD, 
Store, and Server, plus the optional
+Hubble UI. It lives in the main repository under
+[`helm/hugegraph`](https://github.com/apache/hugegraph/tree/master/helm/hugegraph).
+
+| Component | Workload | Default replicas | Purpose |
+|-----------|----------|------------------|---------|
+| PD | StatefulSet + PVC | 3 | Placement driver: a Raft group tracking Stores 
and partitions |
+| Store | StatefulSet + PVC | 3 | Graph data storage (HStore) |
+| Server | Deployment | 3 | Gremlin and REST query layer |
+| Hubble | Deployment | 0 (off) | Web UI, enabled with `hubble.enabled=true` |
+
+A distributed HugeGraph cluster has a startup contract (no `init-store` on 
Server, every Server using PD for graph
+metadata, Store waiting for a PD quorum, one PD REST secret shared by three 
readers). The chart encodes that
+contract so operators do not have to; the details are in the
+[chart 
README](https://github.com/apache/hugegraph/tree/master/helm/hugegraph#chart-details).
+Day-2 work (NetworkPolicy details, disaster recovery, scaling, safe Store
+rolls, running Hubble outside the cluster) is on the
+[operations page](/docs/quickstart/hugegraph/hugegraph-helm-operations/).
+
+```mermaid
+flowchart LR
+    subgraph Kubernetes cluster
+        PD[PD StatefulSet<br>Raft group of 3, PVC]
+        Store[Store StatefulSet<br>3 replicas, PVC] -->|register, heartbeat| PD
+        Server[Server Deployment<br>3 replicas] -->|metadata, discovery| PD
+        Server -->|gRPC read/write| Store
+        Hubble[Hubble Deployment<br>optional UI] -->|discovers Servers via PD| 
PD
+    end
+    Client[Client / hugegraph-client] -->|REST / Gremlin| Server
+```
+
+Startup order is enforced by the chart, not by the operator: PD pods elect a 
leader first, each Store pod waits in
+an init container until a majority of PD peers report ready, and Servers keep 
restarting their storage wait until
+Stores have registered. A fresh install converges without manual steps.
+
+### 2 Prerequisites
+
+- Kubernetes 1.23 or later (the chart renders `autoscaling/v2` and `policy/v1`)
+- Helm 3; the `--reset-then-reuse-values` flag mentioned under Upgrade needs 
Helm 3.14 or later
+- Dynamic volume provisioning: a default StorageClass, or an explicit 
`storageClassName` for PD and Store
+- Memory for nine JVMs in the default topology; see the resource note under 
Install
+
+The chart requires component images that carry the PD readiness endpoint and 
PD REST authentication (both merged
+for the release after 1.7.0). The default image tags already point at builds 
that include them; 1.7.0 images are
+not supported.
+
+### 3 Install
+
+#### 3.1 Get the chart
+
+The chart is not published to a chart repository yet, so install it from the 
source tree:
+
+```bash
+git clone https://github.com/apache/hugegraph.git
+cd hugegraph
+```
+
+#### 3.2 Install with default values
+
+First confirm `kubectl` points at the intended cluster and that it can 
provision volumes. PVCs stuck in `Pending`
+for want of a StorageClass are the most common first-run failure:
+
+```bash
+kubectl config current-context
+kubectl get storageclass
+```
+
+Then install:
+
+```bash
+helm install hugegraph ./helm/hugegraph --namespace hugegraph 
--create-namespace --wait --timeout 15m
+```
+
+`--wait` waits for Kubernetes readiness probes, not application workflows. A 
fresh cluster normally converges in
+a few minutes; the 15-minute timeout leaves room for slow image pulls. Verify 
graph queries after installation.
+Without `--wait`, Helm returns as soon as the objects are created.
+
+Two defaults to know before going further:
+
+- **No resources are set.** Every pod is BestEffort, and each JVM sets its 
maximum heap to half of the memory it
+  sees free on the node at start (up to a per-component ceiling), so the JVMs 
on one node can together claim more
+  memory than the node has. That holds for `values-single.yaml` on one node as 
well. Set `resources` per component on every install, the single-node
+  preset included; `values-cluster.yaml` sets them for a multi-node cluster.
+- **Image tags track `latest`** until the next HugeGraph release is published, 
with `pullPolicy: Always`. Pin tags
+  or digests for production.
+
+#### 3.3 Topology presets
+
+The chart ships three values files:
+
+| File | Topology | Intended use |
+|------|----------|--------------|
+| `values.yaml` | 3 PD + 3 Store + 3 Server | Default; preferred 
anti-affinity, auth on, Hubble off |
+| `values-single.yaml` | 1 + 1 + 1 | Single-node development and CI; smaller 
PVCs |
+| `values-cluster.yaml` | 3 + 3 + 3 | Production starting point: JVM heap and 
resource settings, a Server PodDisruptionBudget, `required` anti-affinity, 
NetworkPolicy on, Store `OnDelete` updates |
+
+```bash
+helm install hugegraph ./helm/hugegraph --namespace hugegraph 
--create-namespace \
+    -f helm/hugegraph/values-single.yaml --wait --timeout 15m
+```
+
+`values-cluster.yaml` is a starting point, not a capacity guarantee: 
recalculate resources for your graph size and
+traffic. One of its numbers deserves a note: it requests 5Gi and limits at 8Gi 
of memory per Store, well above the
+1Gi heap, because the Store's RocksDB caches live outside the JVM heap (a 4Gi 
limit OOM-killed Stores after about
+1 GB of data). Scale both numbers with data size. The full parameter reference 
(every component, probe,
+scheduling, and Secret knob) is kept in the
+[chart 
README](https://github.com/apache/hugegraph/tree/master/helm/hugegraph#configuration).
+
+#### 3.4 Verify the install
+
+```bash
+helm test hugegraph --namespace hugegraph --logs --timeout 5m
+```
+
+The test first calls `/versions` and `/graphs` through the Server Service. It 
then resolves the headless Service
+`hugegraph-server-headless` and sends an authenticated Gremlin query bound to 
the `DEFAULT-hugegraph` graph to
+each Server Pod it lists, and it requires at least `server.replicas` Pods 
(`server.hpa.minReplicas` with HPA on).
+The headless Service lists only Ready Pods, the same Pods the Server Service 
routes to, so a Pod that is not Ready
+is not queried. A failing Pod is retried every 5 seconds for up to 150 
seconds, so a short PD election is not
+reported as a broken Pod; after that the test fails and prints each failing 
Pod IP with its HTTP status. This is
+the check that catches a Server that passes readiness and serves REST while 
every Gremlin call on it fails (see
+Limitations). Large HPA fleets may need `helm test --timeout` above the 
5-minute default.
+
+The hook verifies Server Gremlin, not Hubble login. With Hubble enabled, also 
verify a fresh login and graph query
+after installation or recovery; `/actuator/health` alone does not establish 
backend connectivity.
+
+To call the API, start a port-forward in one terminal; it runs in the 
foreground until you stop it:
+
+```bash
+kubectl port-forward -n hugegraph svc/hugegraph-server 8080:8080
+```
+
+Then, in a second terminal, read the generated admin password and call the API:
+
+```bash
+PASSWORD="$(kubectl get secret -n hugegraph hugegraph-admin -o 
jsonpath='{.data.password}' | base64 --decode)"
+curl --user "admin:${PASSWORD}" http://127.0.0.1:8080/versions
+```
+
+The commands above assume the release is named `hugegraph`; with another name, 
substitute the release-prefixed
+resource names (`kubectl get svc,secret -n <namespace>` lists them). The 
post-install notes printed by
+`helm install` repeat these commands with the right names filled in.
+
+### 4 Authentication and Secrets
+
+Authentication is on by default and the chart manages three Secrets. Each 
credential resolves in the same order:
+an `existingSecret` you created wins, then an inline value, then a random 
value generated at install time.
+
+| Secret | Key | Used for | Bring your own with |
+|--------|-----|----------|---------------------|
+| `<release>-admin` | `password` | Server admin account, Hubble login | 
`server.auth.admin.existingSecret` |
+| `<release>-auth-token` | `token_secret` | JWT signing key shared by all 
Server replicas | `server.auth.token.existingSecret` |
+| `<release>-pd-auth` | `secret-key` | PD REST authentication, read by PD, 
Server, and Hubble | `pd.auth.existingSecret` |
+
+To manage a credential yourself, create the Secret before installing and point 
the matching `existingSecret` value
+at it; the chart never modifies a Secret it did not create. Value constraints: 
the admin password must be printable
+ASCII with no spaces, colons, or backslashes (the image stores a space with a 
backslash before it, and the Server splits
+Basic-auth credentials on every colon, so either would leave the account 
unable to log in with the Secret value); the
+JWT key must be at least 32 bytes; the PD secret must be printable ASCII with 
no backslashes and no leading or trailing
+space. Invalid inline values are rejected at render time, and an 
`existingSecret` by the startup wrapper when the Pod
+starts, not silently truncated.
+
+Chart-managed Secrets are kept on uninstall and reused by a later install 
under the same release name.
+
+<details>
+<summary>Rotation and caveats</summary>
+
+- The admin password is applied only when the auth metadata is first created, 
so changing the Secret later does not
+  rotate an existing cluster's password. Rotate it through the Server's auth 
API instead.

Review Comment:
   ⚠️ important. Blocking: no. Summary: Rotating only through the Server auth 
API leaves the chart admin Secret unchanged, so the documented password lookup 
and helm test hook keep using the old credential. Please set the Secret to the 
API value and roll Server replicas to refresh their auth caches, or link to the 
chart README rotation procedure; sync Chinese. Evidence: [The test hook reads 
PASSWORD from the chart 
Secret](https://github.com/apache/hugegraph/blob/90345bb9d8804f6b0726db6c66d18adf33461bf4/helm/hugegraph/templates/tests/test-connection.yaml#L59-L65);
 [the chart README requires Secret synchronization and a Server 
rollout](https://github.com/apache/hugegraph/blob/90345bb9d8804f6b0726db6c66d18adf33461bf4/helm/hugegraph/README.md#L79-L84).



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to