From 95af07651885ae72ed49bd534f03867a4931721b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 8 Jan 2026 15:25:47 +0000 Subject: [PATCH 1/7] Initial plan From a94d06d705df48020a3a2462b95d5a630f09daae Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 8 Jan 2026 15:32:55 +0000 Subject: [PATCH 2/7] Implement StatefulSet with per-pod services - Changed replicaCount default from 1 to 3 - Converted Deployment to StatefulSet - Made main service headless (clusterIP: None) for StatefulSet - Added per-pod service template with LoadBalancer default - Added service.perPodService configuration to values.yaml - Updated README with StatefulSet architecture documentation Co-authored-by: rrobetti <7221783+rrobetti@users.noreply.github.com> --- charts/ojp-server/README.md | 25 ++++++++++++++ .../ojp-server/templates/per-pod-service.yaml | 34 +++++++++++++++++++ charts/ojp-server/templates/service.yaml | 2 +- .../{deployment.yaml => statefulset.yaml} | 3 +- charts/ojp-server/values.yaml | 6 +++- 5 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 charts/ojp-server/templates/per-pod-service.yaml rename charts/ojp-server/templates/{deployment.yaml => statefulset.yaml} (96%) diff --git a/charts/ojp-server/README.md b/charts/ojp-server/README.md index 93135cc..04d3aeb 100644 --- a/charts/ojp-server/README.md +++ b/charts/ojp-server/README.md @@ -2,6 +2,15 @@ Deploy OJP Server using `ojp/ojp-server` Helm Charts. +## Architecture + +The OJP Server Helm chart uses a **StatefulSet** deployment model with individual per-pod services, allowing each OJP instance to be individually addressable. By default, the chart creates: +- 3 replicas (configurable via `replicaCount`) +- A headless service for StatefulSet pod discovery +- Individual LoadBalancer services for each pod (e.g., `ojp-server-0`, `ojp-server-1`, `ojp-server-2`) + +This architecture ensures stable network identities and allows direct access to specific OJP instances. + ## Usage Install OJP Server ```console @@ -21,6 +30,22 @@ Uninstall OJP Server helm uninstall ojp-server --namespace ojp ``` +## Configuration + +### Deployment Parameters +| Name | Description | Value | +| -------------------------- | ---------------------------------------------- | ---------------------- | +| `replicaCount` | Number of OJP Server replicas | `3` | +| `autoscaling.enabled` | Enable autoscaling (overrides replicaCount) | `false` | + +### Service Parameters +| Name | Description | Value | +| -------------------------- | ---------------------------------------------- | ---------------------- | +| `service.type` | Service type for headless service | `ClusterIP` | +| `service.port` | OJP Server service port | `1059` | +| `service.perPodService.enabled` | Enable individual per-pod services | `true` | +| `service.perPodService.type` | Type for per-pod services (LoadBalancer or NodePort) | `LoadBalancer` | + ### App parameters | Name | Description | Value | | -------------------------- | ---------------------------------------------- | ---------------------- | diff --git a/charts/ojp-server/templates/per-pod-service.yaml b/charts/ojp-server/templates/per-pod-service.yaml new file mode 100644 index 0000000..38fe03b --- /dev/null +++ b/charts/ojp-server/templates/per-pod-service.yaml @@ -0,0 +1,34 @@ +{{- if .Values.service.perPodService.enabled }} +{{- $fullName := include "ojp-server.fullname" . -}} +{{- $serviceType := .Values.service.perPodService.type -}} +{{- $serverPort := .Values.server.port -}} +{{- $prometheusPort := .Values.server.prometheusPort -}} +{{- $labels := include "ojp-server.labels" . -}} +{{- $selectorLabels := include "ojp-server.selectorLabels" . -}} +{{- $namespace := .Release.Namespace -}} +{{- range $i := until (int .Values.replicaCount) }} +--- +apiVersion: v1 +kind: Service +metadata: + namespace: {{ $namespace }} + name: {{ $fullName }}-{{ $i }} + labels: + {{- $labels | nindent 4 }} + statefulset.kubernetes.io/pod-name: {{ $fullName }}-{{ $i }} +spec: + type: {{ $serviceType }} + ports: + - port: {{ $serverPort }} + targetPort: http + protocol: TCP + name: http + - port: {{ $prometheusPort }} + targetPort: prometheus + protocol: TCP + name: prometheus + selector: + {{- $selectorLabels | nindent 4 }} + statefulset.kubernetes.io/pod-name: {{ $fullName }}-{{ $i }} +{{- end }} +{{- end }} diff --git a/charts/ojp-server/templates/service.yaml b/charts/ojp-server/templates/service.yaml index c4584be..252386f 100644 --- a/charts/ojp-server/templates/service.yaml +++ b/charts/ojp-server/templates/service.yaml @@ -6,7 +6,7 @@ metadata: labels: {{- include "ojp-server.labels" . | nindent 4 }} spec: - type: {{ .Values.service.type }} + clusterIP: None ports: - port: {{ .Values.service.port }} targetPort: http diff --git a/charts/ojp-server/templates/deployment.yaml b/charts/ojp-server/templates/statefulset.yaml similarity index 96% rename from charts/ojp-server/templates/deployment.yaml rename to charts/ojp-server/templates/statefulset.yaml index 71a620e..2138177 100644 --- a/charts/ojp-server/templates/deployment.yaml +++ b/charts/ojp-server/templates/statefulset.yaml @@ -1,5 +1,5 @@ apiVersion: apps/v1 -kind: Deployment +kind: StatefulSet metadata: namespace: {{ .Release.Namespace }} name: {{ include "ojp-server.fullname" . }} @@ -9,6 +9,7 @@ spec: {{- if not .Values.autoscaling.enabled }} replicas: {{ .Values.replicaCount }} {{- end }} + serviceName: {{ include "ojp-server.fullname" . }} selector: matchLabels: {{- include "ojp-server.selectorLabels" . | nindent 6 }} diff --git a/charts/ojp-server/values.yaml b/charts/ojp-server/values.yaml index 53bc3ba..b61213a 100644 --- a/charts/ojp-server/values.yaml +++ b/charts/ojp-server/values.yaml @@ -1,4 +1,4 @@ -replicaCount: 1 +replicaCount: 3 server: port: 1059 @@ -47,6 +47,10 @@ securityContext: {} service: type: ClusterIP port: 1059 + # Per-pod services allow individual addressability for each StatefulSet pod + perPodService: + enabled: true + type: LoadBalancer resources: {} # We usually recommend not to specify default resources and to leave this as a conscious From be92913d00d3068e53dbb4d681838c8fe3cdd9d6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 8 Jan 2026 15:35:05 +0000 Subject: [PATCH 3/7] Update NOTES.txt for StatefulSet architecture - Updated installation notes to use StatefulSet pod names - Added guidance for accessing per-pod services - Included examples for LoadBalancer and NodePort service types Co-authored-by: rrobetti <7221783+rrobetti@users.noreply.github.com> --- charts/ojp-server/templates/NOTES.txt | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/charts/ojp-server/templates/NOTES.txt b/charts/ojp-server/templates/NOTES.txt index e1e740e..d4e5768 100644 --- a/charts/ojp-server/templates/NOTES.txt +++ b/charts/ojp-server/templates/NOTES.txt @@ -1,10 +1,27 @@ Test the installation with the steps below: -1. Port-forward Server service: +1. Port-forward to a specific OJP Server pod: {{- if .Values.server.opentelemetry.enabled }} - kubectl -n {{ .Release.Namespace }} port-forward svc/ojp-server {{ .Values.server.prometheusPort }}:{{ .Values.server.prometheusPort }} & + kubectl -n {{ .Release.Namespace }} port-forward {{ include "ojp-server.fullname" . }}-0 {{ .Values.server.prometheusPort }}:{{ .Values.server.prometheusPort }} & {{- end }} - kubectl -n {{ .Release.Namespace }} port-forward svc/ojp-server {{ .Values.service.port }}:{{ .Values.service.port }} & + kubectl -n {{ .Release.Namespace }} port-forward {{ include "ojp-server.fullname" . }}-0 {{ .Values.service.port }}:{{ .Values.service.port }} & -2. Happy Proxying with OJP! \ No newline at end of file +{{- if .Values.service.perPodService.enabled }} + +2. Access individual OJP Server instances via their per-pod services: + + {{- if eq .Values.service.perPodService.type "LoadBalancer" }} + # Get LoadBalancer external IPs for each pod + {{- range $i := until (int .Values.replicaCount) }} + kubectl -n {{ $.Release.Namespace }} get svc {{ include "ojp-server.fullname" $ }}-{{ $i }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}' + {{- end }} + {{- else if eq .Values.service.perPodService.type "NodePort" }} + # Get NodePort for each pod service + {{- range $i := until (int .Values.replicaCount) }} + kubectl -n {{ $.Release.Namespace }} get svc {{ include "ojp-server.fullname" $ }}-{{ $i }} -o jsonpath='{.spec.ports[0].nodePort}' + {{- end }} + {{- end }} +{{- end }} + +3. Happy Proxying with OJP! \ No newline at end of file From 17c35b7189e5691e557e4b91b1026c4a67de8945 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 8 Jan 2026 15:38:16 +0000 Subject: [PATCH 4/7] Fix per-pod services with autoscaling - Disable per-pod services when autoscaling is enabled - Add documentation for service.type field - Update README to clarify autoscaling behavior - Ensures services match actual pod count Co-authored-by: rrobetti <7221783+rrobetti@users.noreply.github.com> --- charts/ojp-server/README.md | 6 +++--- charts/ojp-server/templates/per-pod-service.yaml | 2 +- charts/ojp-server/values.yaml | 2 ++ 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/charts/ojp-server/README.md b/charts/ojp-server/README.md index 04d3aeb..ea3d12a 100644 --- a/charts/ojp-server/README.md +++ b/charts/ojp-server/README.md @@ -36,14 +36,14 @@ helm uninstall ojp-server --namespace ojp | Name | Description | Value | | -------------------------- | ---------------------------------------------- | ---------------------- | | `replicaCount` | Number of OJP Server replicas | `3` | -| `autoscaling.enabled` | Enable autoscaling (overrides replicaCount) | `false` | +| `autoscaling.enabled` | Enable autoscaling (overrides replicaCount, disables per-pod services) | `false` | ### Service Parameters | Name | Description | Value | | -------------------------- | ---------------------------------------------- | ---------------------- | -| `service.type` | Service type for headless service | `ClusterIP` | +| `service.type` | Service type (always ClusterIP for headless service) | `ClusterIP` | | `service.port` | OJP Server service port | `1059` | -| `service.perPodService.enabled` | Enable individual per-pod services | `true` | +| `service.perPodService.enabled` | Enable individual per-pod services (disabled when autoscaling is enabled) | `true` | | `service.perPodService.type` | Type for per-pod services (LoadBalancer or NodePort) | `LoadBalancer` | ### App parameters diff --git a/charts/ojp-server/templates/per-pod-service.yaml b/charts/ojp-server/templates/per-pod-service.yaml index 38fe03b..3bdcacd 100644 --- a/charts/ojp-server/templates/per-pod-service.yaml +++ b/charts/ojp-server/templates/per-pod-service.yaml @@ -1,4 +1,4 @@ -{{- if .Values.service.perPodService.enabled }} +{{- if and .Values.service.perPodService.enabled (not .Values.autoscaling.enabled) }} {{- $fullName := include "ojp-server.fullname" . -}} {{- $serviceType := .Values.service.perPodService.type -}} {{- $serverPort := .Values.server.port -}} diff --git a/charts/ojp-server/values.yaml b/charts/ojp-server/values.yaml index b61213a..64f2ffe 100644 --- a/charts/ojp-server/values.yaml +++ b/charts/ojp-server/values.yaml @@ -45,9 +45,11 @@ securityContext: {} # runAsUser: 1000 service: + # Main headless service for StatefulSet (always clusterIP: None) type: ClusterIP port: 1059 # Per-pod services allow individual addressability for each StatefulSet pod + # Note: Per-pod services are only created when autoscaling is disabled perPodService: enabled: true type: LoadBalancer From 60941dcff52c713e67a649e16c335286bb816b4b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 8 Jan 2026 15:39:34 +0000 Subject: [PATCH 5/7] Add clarifying comments to templates - Document why per-pod services are disabled with autoscaling - Explain StatefulSet headless service requirement - Clarify that service.type is ignored for StatefulSet Co-authored-by: rrobetti <7221783+rrobetti@users.noreply.github.com> --- charts/ojp-server/templates/per-pod-service.yaml | 6 ++++++ charts/ojp-server/templates/service.yaml | 2 ++ 2 files changed, 8 insertions(+) diff --git a/charts/ojp-server/templates/per-pod-service.yaml b/charts/ojp-server/templates/per-pod-service.yaml index 3bdcacd..cf8d3c2 100644 --- a/charts/ojp-server/templates/per-pod-service.yaml +++ b/charts/ojp-server/templates/per-pod-service.yaml @@ -1,3 +1,9 @@ +{{- /* +Per-pod services are only created when autoscaling is disabled because: +- Autoscaling dynamically changes the number of pods +- Per-pod services are created based on a static replicaCount value +- This prevents service/pod mismatches (missing services for new pods or orphaned services) +*/ -}} {{- if and .Values.service.perPodService.enabled (not .Values.autoscaling.enabled) }} {{- $fullName := include "ojp-server.fullname" . -}} {{- $serviceType := .Values.service.perPodService.type -}} diff --git a/charts/ojp-server/templates/service.yaml b/charts/ojp-server/templates/service.yaml index 252386f..a8469f9 100644 --- a/charts/ojp-server/templates/service.yaml +++ b/charts/ojp-server/templates/service.yaml @@ -6,6 +6,8 @@ metadata: labels: {{- include "ojp-server.labels" . | nindent 4 }} spec: + # StatefulSets require a headless service (clusterIP: None) for pod DNS resolution + # The service.type value is ignored for StatefulSet services clusterIP: None ports: - port: {{ .Values.service.port }} From b2231fd77b2300c669ab076d24d3486280a72427 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 8 Jan 2026 21:06:02 +0000 Subject: [PATCH 6/7] Enhance documentation to clarify DNS, ports, and LoadBalancer usage - Added "Why Per-Pod Services?" section explaining benefits - Documented DNS names for internal and external access - Clarified port usage: 1059 for clients, 9090 for metrics - Explained LoadBalancer default for cloud vs NodePort for on-premise - Updated service and server parameter descriptions Co-authored-by: rrobetti <7221783+rrobetti@users.noreply.github.com> --- charts/ojp-server/README.md | 32 ++++++++++++++++++++++++++++---- charts/ojp-server/values.yaml | 5 +++-- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/charts/ojp-server/README.md b/charts/ojp-server/README.md index ea3d12a..8b6684e 100644 --- a/charts/ojp-server/README.md +++ b/charts/ojp-server/README.md @@ -11,6 +11,30 @@ The OJP Server Helm chart uses a **StatefulSet** deployment model with individua This architecture ensures stable network identities and allows direct access to specific OJP instances. +### Why Per-Pod Services? + +Each OJP instance gets its own LoadBalancer service to enable: +- **Direct addressability**: Clients can connect to a specific OJP instance by its unique DNS name or external IP +- **Stable network identity**: Each pod has a predictable, persistent DNS name (e.g., `ojp-server-0.ojp-server.namespace.svc.cluster.local`) +- **Independent external access**: Each instance receives its own external IP address via LoadBalancer +- **Connection affinity**: Clients requiring persistent connections to the same instance can do so reliably + +### DNS Names and Connectivity + +**Internal (within cluster):** +- Pods accessible via StatefulSet DNS: `ojp-server-0.ojp-server.namespace.svc.cluster.local` +- Individual pods: `ojp-server-{0,1,2}.ojp-server.namespace.svc.cluster.local` + +**External (outside cluster):** +- LoadBalancer services: `ojp-server-0`, `ojp-server-1`, `ojp-server-2` (each gets an external IP) +- Access via: `:1059` + +**Ports:** +- **Port 1059**: Main OJP server port - clients connect here for OJP functionality +- **Port 9090**: Prometheus metrics port - for monitoring/observability only + +**Note:** LoadBalancer is the default service type for cloud environments (AWS, GCP, Azure). For on-premise deployments, use `NodePort` instead by setting `service.perPodService.type: NodePort`. + ## Usage Install OJP Server ```console @@ -42,15 +66,15 @@ helm uninstall ojp-server --namespace ojp | Name | Description | Value | | -------------------------- | ---------------------------------------------- | ---------------------- | | `service.type` | Service type (always ClusterIP for headless service) | `ClusterIP` | -| `service.port` | OJP Server service port | `1059` | +| `service.port` | OJP Server service port (main application port for client connections) | `1059` | | `service.perPodService.enabled` | Enable individual per-pod services (disabled when autoscaling is enabled) | `true` | -| `service.perPodService.type` | Type for per-pod services (LoadBalancer or NodePort) | `LoadBalancer` | +| `service.perPodService.type` | Type for per-pod services - LoadBalancer (cloud) or NodePort (on-premise) | `LoadBalancer` | ### App parameters | Name | Description | Value | | -------------------------- | ---------------------------------------------- | ---------------------- | -| `server.port` | OJP Server Port | `1059` | -| `server.prometheusPort` | OJP Server Prometheus Port | `9090` | +| `server.port` | OJP Server Port (main application port for client connections) | `1059` | +| `server.prometheusPort` | OJP Server Prometheus Port (metrics only, not for client connections) | `9090` | | `server.threadPoolSize` | OJP Server Thread Pool Size | `200` | | `server.maxRequestSize` | OJP Server Max Request Size | `4194304` | | `server.connectionIdleTimeout` | OJP Server Connection Idle Timeout | `30000` | diff --git a/charts/ojp-server/values.yaml b/charts/ojp-server/values.yaml index 64f2ffe..bb4f58a 100644 --- a/charts/ojp-server/values.yaml +++ b/charts/ojp-server/values.yaml @@ -47,12 +47,13 @@ securityContext: {} service: # Main headless service for StatefulSet (always clusterIP: None) type: ClusterIP - port: 1059 + port: 1059 # Main OJP server port for client connections # Per-pod services allow individual addressability for each StatefulSet pod + # Each pod gets its own LoadBalancer with external IP for direct access # Note: Per-pod services are only created when autoscaling is disabled perPodService: enabled: true - type: LoadBalancer + type: LoadBalancer # LoadBalancer for cloud (AWS/GCP/Azure), NodePort for on-premise resources: {} # We usually recommend not to specify default resources and to leave this as a conscious From 6c0575787fe890aaa05f21b4fd8aae2779313503 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 8 Jan 2026 21:57:30 +0000 Subject: [PATCH 7/7] Bump chart version to 0.1.5 - Version bumped from 0.1.4 to 0.1.5 to address CI lint failure - Required for chart-testing to pass with changes made Co-authored-by: rrobetti <7221783+rrobetti@users.noreply.github.com> --- charts/ojp-server/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/ojp-server/Chart.yaml b/charts/ojp-server/Chart.yaml index e747bed..a04f61a 100644 --- a/charts/ojp-server/Chart.yaml +++ b/charts/ojp-server/Chart.yaml @@ -10,5 +10,5 @@ maintainers: url: https://github.com/petruki type: application -version: 0.1.4 +version: 0.1.5 appVersion: "0.1.0-beta"