Skip to content

Commit 8cd00ee

Browse files
author
Kyle Cupp
committed
Merge branch 'kyle/kafka-4.0.0'
2 parents d73b825 + 39ac2bc commit 8cd00ee

25 files changed

Lines changed: 3075 additions & 4 deletions

kafka/RELEASES.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,33 @@
44

55
- **Kafka Cluster Parallel Scaling Policy**: Changed the default `scalingPolicy` for the Kafka cluster stateful workload from `OrderedReady` to `Parallel`
66

7+
# Release Notes - Version 4.0.0
8+
9+
## What's New
10+
11+
- **kafka-orchestrator sidecar for accurate readiness**: The Kafka cluster workload now runs `ghcr.io/controlplane-com/kafka-orchestrator` as a sidecar container. The sidecar exposes an HTTP `/health/ready` endpoint that validates broker registration, controller election, under-replicated partition count, and log-directory health using franz-go — a much stronger readiness signal than the previous TCP-socket check on port 9093.
12+
- Sidecar readiness probe: `httpGet /health/ready` on port 8080
13+
- Prometheus metrics exposed at `/metrics` (cgroup memory and OOM-risk ratios)
14+
- SASL credentials are wired automatically from the configured listener (default: `client`)
15+
- The kafka container's existing TCP probes on port 9093 are preserved; workload readiness is now gated on both probes passing
16+
- Configurable under the new `kafka_orchestrator:` section in `values.yaml`; set to `null` or comment out to disable the sidecar
17+
18+
- **Graceful broker shutdown**: The kafka container's `terminationGracePeriodSeconds` is now exposed via `kafka.terminationGracePeriodSeconds` in `values.yaml` (default `600` seconds, up from the previous hardcoded `30`). Brokers carrying large amounts of data now have time to complete `controlled.shutdown` (leadership transfer + log flush) before SIGKILL.
19+
20+
- **Init script signal propagation**: The kafka container's bash wrapper now `exec`s into `/tmp/kafka-init.sh`, which already `exec`s into the Kafka run script. PID 1 is now the Kafka JVM itself, so SIGTERM from Control Plane reaches the broker directly and triggers `controlled.shutdown` instead of being absorbed by the bash wrapper.
21+
22+
- **Suppressed Control Plane's default preStop drain delay (all four containers)**: Control Plane's default container lifecycle injects a `preStop sleep $((terminationGracePeriodSeconds / 2))` on **every** container (the actuator's `getLifecycle` runs per-container in the `for...containers` loop in `workloadDeployment.ts:246-258`). For our 600s grace period that means a 300s idle preStop on each of `kafka`, `kafka-orchestrator`, `kafka-exporter`, and `jmx-exporter`. The drain delay is intended for L7 envoy/ingress connections, none of which apply to a kafka stateful workload — clients reconnect via Metadata refresh, inter-broker traffic is handled by `controlled.shutdown`'s leadership transfer, and the prometheus-scrape sidecars have no draining semantics. All four containers now declare an explicit no-op `preStop: exec: ['true']`, suppressing the default on each. Net effect: the entire pod terminates in seconds (bounded by the kafka container's `controlled.shutdown`), not 300s+ of useless sleep on three sidecars holding the pod hostage.
23+
24+
- **`cpln/publishNotReadyAddresses=true` on the Kafka cluster workload**: Required so the headless Service exposes not-yet-Ready broker pods in DNS, which is what lets the KRaft controller quorum form on cold start (or after suspend/unsuspend). Earlier versions of the chart got away with this missing because the Kafka container's TCP probe on 9093 briefly flickered Ready every crash-loop iteration, just long enough to publish endpoints. The new kafka-orchestrator sidecar's `/health/ready` probe (correctly) requires actual cluster health, which closes that race — making the tag mandatory rather than optional. Without it, pods crash-loop with `UnknownHostException: etl-cluster-N.etl-cluster:9093`.
25+
26+
- **Reliability and recovery defaults in `server.properties`**: The chart now emits the following defaults in the broker config (each can be overridden via `kafka.extra_configurations`):
27+
- `default.replication.factor` — auto-derived as `min(3, kafka.replicas)`; clamps correctly when scaling below 3 replicas
28+
- `min.insync.replicas` — auto-derived as `max(1, default.replication.factor - 1)`
29+
- `controlled.shutdown.enable=true`, `controlled.shutdown.max.retries=3`, `controlled.shutdown.retry.backoff.ms=5000` — clean shutdown with retry on leadership-transfer failures
30+
- `unclean.leader.election.enable=false` — never promote out-of-sync replicas; prevents data loss
31+
- `num.recovery.threads.per.data.dir` — auto-derived as `8 * ceil(cores)` from `kafka.cpu` (e.g. `1000m` → 8, `2000m` → 16, `4` → 32). Recovery only runs after a *dirty* shutdown; a clean `controlled.shutdown` (now achievable thanks to the grace-period and signal-propagation fixes above) skips it entirely.
32+
- `num.replica.fetchers=4` — faster follower replication so brokers rejoin the ISR quickly after transient outages
33+
734
# Release Notes - Version 3.4.0
835

936
## What's New

kafka/versions/4.0.0/.helmignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
values.yaml

kafka/versions/4.0.0/Chart.yaml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
apiVersion: v2
2+
name: kafka
3+
description: Kafka cluster app for Control Plane
4+
type: application
5+
version: 4.0.0
6+
appVersion: "3.9"
7+
8+
dependencies:
9+
- name: cpln-common
10+
version: 1.0.0
11+
repository: "oci://ghcr.io/controlplane-com/templates"
12+
13+
annotations:
14+
created: "2026-04-28"
15+
lastModified: "2026-04-28"
16+
category: "event-streaming"
17+
createsGvc: false

kafka/versions/4.0.0/README.md

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
## Kafka App
2+
3+
### How to connect to the cluster
4+
5+
You can connect to Kafka from the same GVC in which it's deployed using the following methods:
6+
7+
- To connect using the cluster's general address, use `{kafka-cluster-workload-name}:9092`.
8+
9+
- To connect to a specific replica, use one of the following addresses based on the replica you wish to connect to:
10+
- `{kafka-cluster-workload-name}-0.{kafka-cluster-workload-name}:9092`
11+
- `{kafka-cluster-workload-name}-1.{kafka-cluster-workload-name}:9092`
12+
- `{kafka-cluster-workload-name}-2.{kafka-cluster-workload-name}:9092`
13+
14+
- If you're configuring your Kafka for external access, you'll need to provide a domain name for the public address of the listener you want to use. Prerequisites:
15+
- Make sure the dedicated load balancer is enabled on the GVC. See [Configure Domain documentation](https://docs.controlplane.com/guides/configure-domain#dedicated-load-balancing).
16+
- Make sure to register your [Apex domain](https://docs.controlplane.com/reference/domain#apex-domain-considerations) name with Control Plane and set up a DNS record for the Kafka public address CNAME with the canonical GVC endpoint in your DNS provider.
17+
18+
### Test Kafka Cluster with Kafka Client
19+
20+
1. To activate the Kafka client, make sure `kafka_client` is uncommented in your values file. If necessary, reinstall the chart with the command:
21+
```bash
22+
cpln helm install kafka-dev -f values-example.yaml
23+
```
24+
25+
2. To connect to the `kafka-client` workload, navigate through the UI to the appropriate GVC and select the `kafka-client` workload. In the workload details, find and use the **Connect** feature to establish a connection, which can be done either via the UI or by utilizing the CLI command provided there.
26+
27+
3. Once connected, you can write and consume messages through the `kafka-client` workload. If it's `PLAINTEXT`, producer and consumer configurations should be omitted below:
28+
29+
```BASH
30+
# Change to bin directory
31+
cd /opt/kafka/bin
32+
33+
# Create client.properties
34+
echo "security.protocol=SASL_PLAINTEXT
35+
sasl.mechanism=PLAIN
36+
sasl.jaas.config=org.apache.kafka.common.security.plain.PlainLoginModule required username=\"admin\" password=\"your-admin-password\";" > ./client.properties
37+
38+
# Produce messages to the 'controlplane' topic
39+
kafka-console-producer.sh --bootstrap-server {kafka-cluster-workload-name}:9092 --topic controlplane --producer.config ./client.properties
40+
41+
# Consume messages from the 'controlplane' topic
42+
kafka-console-consumer.sh --bootstrap-server {kafka-cluster-workload-name}:9092 --topic controlplane --from-beginning --consumer.config ./client.properties
43+
```
44+
45+
### Public Listener Domain Configuration
46+
47+
When configuring Kafka for external access via a public listener, you can choose between two domain routing modes:
48+
49+
#### **Direct Replica Routing Mode (Recommended)**
50+
51+
The recommended approach with automatic replica endpoint generation:
52+
53+
```yaml
54+
kafka:
55+
listeners:
56+
public:
57+
protocol: SASL_PLAINTEXT
58+
name: PUBLIC
59+
directReplicaRouting:
60+
enabled: true
61+
containerPort: 9095 # ports 9091, 9093 and 9094 are reserved
62+
publicAddress: kafka.example.com
63+
sasl:
64+
users: "public-user"
65+
passwords: "your-password"
66+
```
67+
68+
**Behavior:**
69+
- Single domain configuration with the specified container port
70+
- DNS01 certificate challenge for automatic SSL
71+
- Platform automatically generates replica-specific subdomains in format: `{replica-name}-{location}.{publicAddress}`
72+
- Replica-aware routing reduces cross-zone traffic costs in multi-zone deployments
73+
- Connection endpoints (auto-generated examples):
74+
- `kafka-cluster-0-aws-us-east-1.kafka.example.com:9095`
75+
- `kafka-cluster-1-aws-us-east-1.kafka.example.com:9095`
76+
- `kafka-cluster-2-aws-us-east-1.kafka.example.com:9095`
77+
78+
**Prerequisites for Direct Routing:**
79+
- DNS provider must support CNAME records
80+
- Create DNS records for each replica and the ACME challenge record:
81+
1. `CNAME kafka-cluster-0-aws-us-east-1.kafka.example.com → kafka-cluster-<gvcAlias>-0.aws-us-east-1.controlplane.us`
82+
2. `CNAME kafka-cluster-1-aws-us-east-1.kafka.example.com → kafka-cluster-<gvcAlias>-1.aws-us-east-1.controlplane.us`
83+
3. `CNAME kafka-cluster-2-aws-us-east-1.kafka.example.com → kafka-cluster-<gvcAlias>-2.aws-us-east-1.controlplane.us`
84+
4. `CNAME _acme-challenge.kafka → _acme-challenge.cpln.app` (for certificate validation)
85+
86+
#### **Multi-Port Routing**
87+
88+
Each replica gets its own port. Not recommended for multi-zone clusters:
89+
90+
```yaml
91+
kafka:
92+
listeners:
93+
public:
94+
protocol: SASL_PLAINTEXT
95+
name: PUBLIC
96+
publicAddress: kafka.example.com
97+
sasl:
98+
users: "public-user"
99+
passwords: "your-password"
100+
```
101+
102+
**Behavior:**
103+
- Creates ports 3000, 3001, 3002 (one per replica)
104+
- Each port routes to a specific replica
105+
- Custom TLS cipher suites configuration
106+
- Connection format: `kafka.example.com:3000`, `kafka.example.com:3001`, etc.
107+
- **Note**: Not recommended for multi-zone deployments as cross-zone traffic charges may occur
108+
109+
**Which Mode to Use:**
110+
- Use **Direct Replica Routing** for new deployments that require automatic SSL with zone-aware routing and per-replica hostnames
111+
- Avoid using **Multi-Port Routing** unless you have specific use cases or existing clients configured with port numbers (3000-300X)
112+
113+
**Configuration Rules:**
114+
- Cannot use both `publicAddress` and `directReplicaRouting.enabled: true` in the same listener
115+
- When `directReplicaRouting.enabled: true`, both `containerPort` and `publicAddress` must be specified within the `directReplicaRouting` section
116+
- Only one listener can have a public address configured across all listeners
117+
- Direct Replica Routing automatically creates DNS entries in format: `{replica-name}-{location}.{publicAddress}:{containerPort}`
118+
119+
### Enable Custom Encryption using AWS Key Management Service (KMS)
120+
121+
Custom encryption for volumes can be configured by setting the values under `kafka.volumes.customEncryption`.
122+
123+
A key must be created in AWS before proceeding with the template.
124+
125+
In the values file, set `enabled` to `true` and add the proper `region` and `keyId`.
126+
127+
**Important** - To finish configuring in AWS once the template is installed:
128+
129+
1. Navigate in the console to the created volume
130+
2. Click on `spec`
131+
3. Follow the `AWS Custom Encryption Instructions`
132+
4. Repeat for each encrypted volume created
133+
134+
### Kafbat configuration example
135+
136+
Full configuration Docs: https://ui.docs.kafbat.io/configuration/configuration-file
137+
138+
```YAML
139+
kafka:
140+
clusters:
141+
- name: "apache-kafka"
142+
bootstrapServers: "kafka-dev-cluster.kafka-dev.cpln.local:9092"
143+
kafkaConnect:
144+
- name: kafka-dev-connect-connect-cluster
145+
address: http://kafka-dev-connect-connect-cluster.kafka-dev.cpln.local:8083
146+
properties:
147+
security.protocol: "SASL_PLAINTEXT"
148+
sasl.mechanism: "PLAIN"
149+
sasl.jaas.config: "org.apache.kafka.common.security.plain.PlainLoginModule required username=\"admin\" password=\"your-admin-password\";"
150+
151+
management:
152+
health:
153+
ldap:
154+
enabled: false
155+
156+
auth:
157+
type: "LOGIN_FORM"
158+
spring:
159+
security:
160+
user:
161+
name: "admin"
162+
password: "adminPassword"
163+
164+
server:
165+
port: 8080
166+
```
167+
168+
### Release Notes
169+
See [RELEASES.md](https://github.com/controlplane-com/templates/blob/main/kafka/RELEASES.md)
680 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)