Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ title = 'GitOps'
This is the documentation for using GitOps to manage Strimzi-based event-driven infrastructure.

- [Introduction to GitOps](introduction/_index.md) — Why GitOps over ClickOps, core concepts, and common technologies.
- [Kafka Examples](kafka-examples/_index.md) — Bootstrap, infrastructure, and scenario deployment guide.

177 changes: 177 additions & 0 deletions docs/kafka-examples/_index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
+++
title = 'Deploying Kafka with ArgoCD'
+++

The [introduction](../introduction/_index.md) covered why GitOps matters.
This document shows what it looks like in practice — deploying Apache Kafka infrastructure entirely through ArgoCD, from operators to running clusters.

## How deployment works

Everything in this repository is an ArgoCD Application.
An Application tells ArgoCD: "watch this directory in Git, and keep the cluster in sync with what's there."

When you apply an Application, ArgoCD reads the Kustomize manifests from the specified directory, renders them, and creates the Kubernetes resources.
If someone changes a file in the repository, ArgoCD detects the change and updates the cluster.
If someone changes something directly on the cluster, ArgoCD detects the drift and corrects it.

This means the Git repository is always the source of truth.
There is no need to run `kubectl apply` on individual manifests, no scripts to remember, no runbooks to follow.

## The deployment flow

The only manual step is installing ArgoCD itself.
After that, every operator and every Kafka scenario is deployed by applying a single YAML file — the ArgoCD Application.

1. **Install ArgoCD** (manual, one-time) — `kubectl apply -k`
2. **Deploy operators** — one `kubectl apply -f application.yaml` per operator (Strimzi, Keycloak, ESO)
3. **Deploy scenarios** — one `kubectl apply -f application.yaml` per scenario (basic-kafka, kafka-mirror, kafka-oauth)

Or skip steps 2 and 3 entirely and deploy everything at once with the app-of-apps ApplicationSet.

### Step 1: ArgoCD bootstrap

ArgoCD is the one component that can't deploy itself.
The `examples/argo-cd/` directory contains Kustomize overlays for OpenShift and Kubernetes.

On OpenShift, the install is two steps — the operator and then the instance:

```bash
kubectl apply -k examples/argo-cd/overlays/openshift/operator
kubectl apply -k examples/argo-cd/overlays/openshift/instance
```

The OpenShift overlay includes two important configurations:
- `ARGOCD_CLUSTER_CONFIG_NAMESPACES=argocd` on the operator subscription, which grants the ArgoCD instance cluster-scope permissions.
- A `streamshub` AppProject with `clusterResourceWhitelist`, which allows ArgoCD to manage cluster-scoped resources like Namespaces.

On Kubernetes:

```bash
kubectl apply -k examples/argo-cd/overlays/kubernetes
```

### Step 2: Deploy operators

Each operator has an ArgoCD Application file.
Applying it tells ArgoCD to install and manage the operator.

For example, deploying the Strimzi operator on OpenShift:

```bash
kubectl apply -f examples/operators/strimzi/overlays/streams-for-kafka/argocd/application.yaml
```

That single command creates the namespace, the OLM Subscription, the OperatorGroup, and starts the operator.
ArgoCD keeps it in sync — if someone accidentally deletes the Subscription, ArgoCD recreates it.

The same pattern works for every operator:

```bash
# Keycloak / RHBK (needed for the OAuth scenario)
kubectl apply -f examples/operators/keycloak/overlays/rhbk/argocd/application.yaml

# External Secrets Operator (needed for the mirror scenario)
kubectl apply -f examples/operators/external-secrets/overlays/openshift/argocd/application.yaml
```

### Step 3: Deploy scenarios

Scenarios work exactly the same way — each is an ArgoCD Application:

```bash
kubectl apply -f examples/scenarios/basic-kafka/argocd/application.yaml
```

ArgoCD creates the namespace, deploys the Kafka cluster, node pools, metrics configuration, and all supporting resources.
The Strimzi operator then takes over and creates the actual broker and controller pods.

### Deploying everything at once

Instead of applying Applications one by one, the app-of-apps pattern deploys all operators and scenarios with a single command.
The ApplicationSet uses a combination of a list generator (for operators) and a Git directory generator (for scenarios):

```bash
# OpenShift with Streams for Apache Kafka + RHBK
kubectl apply -f examples/app-of-apps/overlays/openshift/applicationset.yaml

# Kubernetes with community Strimzi + Keycloak
kubectl apply -f examples/app-of-apps/overlays/kubernetes/applicationset.yaml
```

Adding a new scenario directory under `examples/scenarios/` automatically creates a new ArgoCD Application on the next sync.

## What the scenarios deploy

### Basic Kafka

A single Kafka cluster in KRaft mode with three controllers and three brokers.
Includes Cruise Control for automatic rebalancing, Entity Operator for managing topics and users as custom resources, and JMX Prometheus metrics.

Two listeners: plain-text on port 9092 and TLS with client certificate authentication on port 9093.

### Kafka Mirror

Two independent Kafka clusters with MirrorMaker 2 replicating all topics from source to target.

This scenario demonstrates a practical Kubernetes challenge: MirrorMaker 2 runs in the `kafka-target` namespace but needs TLS credentials from `kafka-source`.
The External Secrets Operator solves this by automatically syncing secrets between namespaces.
A `SecretStore` reads from `kafka-source`, and `ExternalSecret` resources pull the CA certificate and user credentials into `kafka-target`.
If the source cluster rotates its CA certificate, the change propagates automatically.

### Kafka OAuth

A complete OAuth 2.0 setup: Keycloak as the OIDC provider with a Kafka-specific realm, and a Kafka cluster with token-based authentication on the TLS listener.

Strimzi v1 replaced the built-in `type: oauth` listener with `type: custom`.
The OAuth libraries remain bundled in the Kafka images — only the API changed.
The JAAS configuration tells Kafka to validate JWT tokens against Keycloak's JWKS endpoint.

The Keycloak operator is installed into the `keycloak` namespace, and the Keycloak instance is deployed in the same namespace.
This is important because the Keycloak operator watches only its own namespace by default.

The realm includes test users and OAuth clients.
You can test the setup using [strimzi/test-clients](https://github.com/strimzi/test-clients) Jobs that authenticate via client credentials flow.

## Making changes

The power of this setup is what happens after the initial deployment.

### Scaling brokers

To add a broker, change `replicas: 3` to `replicas: 4` in `brokers.yaml`, commit, and push.
ArgoCD detects the change, updates the KafkaNodePool, and Strimzi creates the new broker pod.
Cruise Control automatically rebalances partitions to include the new broker.

### Updating configuration

To change a Kafka setting like retention time, edit `kafka.yaml`, commit, and push.
ArgoCD syncs the change, and the Strimzi operator performs a rolling restart of the brokers.

### Adding a new scenario

Create a new directory under `examples/scenarios/` with a `kustomization.yaml` and the Kafka resources.
If you're using the app-of-apps ApplicationSet, ArgoCD automatically discovers it and creates a new Application.

## Community and product support

All scenarios use the `kafka.strimzi.io/v1` CRD API, which is identical for both the community project and the commercial product.
The only difference is which operator you install.

| Component | Community | Product |
|-----------|-----------|---------|
| Kafka operator | Strimzi | Streams for Apache Kafka (`amq-streams`) |
| Identity provider | Keycloak | Red Hat Build of Keycloak (`rhbk-operator`) |
| Secret management | ESO (Helm chart) | ESO for Red Hat OpenShift (`openshift-external-secrets-operator`) |

Switching between community and product is a matter of deploying a different operator Application.
The scenarios themselves don't change.

## What's next

These scenarios cover the foundational Kafka patterns.
Future additions could include:

- **KafkaConnect with Debezium** for change data capture
- **StreamsHub Console** for Kafka cluster management UI
- **Kroxylicious** for Kafka proxy with encryption and schema enforcement
- **Monitoring** with Prometheus and Grafana dashboards
58 changes: 58 additions & 0 deletions examples/app-of-apps/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# App-of-Apps Pattern

An ArgoCD ApplicationSet that deploys all infrastructure operators and scenarios from this repository with a single command.

## Platform-Specific Variants

Since infrastructure operators differ between platforms (OLM on OpenShift vs upstream manifests on Kubernetes), there are platform-specific ApplicationSets:

- `overlays/openshift/` — deploys Streams for Apache Kafka, RHBK, ESO (Red Hat), and all scenarios
- `overlays/kubernetes/` — deploys community Strimzi, Keycloak, and all scenarios

## Quick Start

### OpenShift

```bash
kubectl apply -f overlays/openshift/applicationset.yaml
```

### Kubernetes

```bash
kubectl apply -f overlays/kubernetes/applicationset.yaml
```

## What Gets Deployed

### OpenShift

| Application | Path |
|-------------|------|
| `streams-for-kafka-operator` | `operators/strimzi/overlays/streams-for-kafka` |
| `rhbk-operator` | `operators/keycloak/overlays/rhbk` |
| `external-secrets-operator` | `operators/external-secrets/overlays/openshift` |
| `basic-kafka` | `scenarios/basic-kafka` |
| `kafka-mirror` | `scenarios/kafka-mirror` |
| `kafka-oauth` | `scenarios/kafka-oauth` |

### Kubernetes

| Application | Path |
|-------------|------|
| `strimzi-operator` | `operators/strimzi/overlays/kubernetes` |
| `keycloak-operator` | `operators/keycloak/overlays/kubernetes` |
| `basic-kafka` | `scenarios/basic-kafka` |
| `kafka-mirror` | `scenarios/kafka-mirror` |
| `kafka-oauth` | `scenarios/kafka-oauth` |

## Selective Deployment

To deploy only specific components, modify the `generators` section:
- Remove entries from the `list` generator to skip specific operators
- Add `exclude` entries to the `git` generator to skip specific scenarios

## Prerequisites

- ArgoCD installed (see [ArgoCD installation](../argo-cd/))
- The `streamshub` AppProject must exist (created as part of the ArgoCD instance setup)
48 changes: 48 additions & 0 deletions examples/app-of-apps/overlays/kubernetes/applicationset.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: streamshub-gitops
namespace: argocd
spec:
goTemplate: true
goTemplateOptions: ["missingkey=error"]
generators:
- list:
elements:
- name: strimzi-operator
path: examples/operators/strimzi/overlays/kubernetes
namespace: strimzi-operator
- name: keycloak-operator
path: examples/operators/keycloak/overlays/kubernetes
namespace: keycloak
- git:
repoURL: https://github.com/streamshub/streamshub-gitops.git
# TODO: switch back to HEAD before merge
revision: argocd-kafka-examples
directories:
- path: examples/scenarios/*
template:
metadata:
name: '{{ default .path.basename .name }}'
labels:
app.kubernetes.io/part-of: streamshub-gitops
spec:
project: streamshub
source:
repoURL: https://github.com/streamshub/streamshub-gitops.git
# TODO: switch back to HEAD before merge
targetRevision: argocd-kafka-examples
path: '{{ default .path .path.path }}'
destination:
server: https://kubernetes.default.svc
namespace: '{{ default "" .namespace }}'
syncPolicy:
automated:
prune: false
selfHeal: true
managedNamespaceMetadata:
labels:
argocd.argoproj.io/managed-by: argocd
syncOptions:
- CreateNamespace=true
- ServerSideApply=true
57 changes: 57 additions & 0 deletions examples/app-of-apps/overlays/openshift/applicationset.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: streamshub-gitops
namespace: argocd
spec:
goTemplate: true
goTemplateOptions: ["missingkey=error"]
generators:
- list:
elements:
- name: streams-for-kafka-operator
path: examples/operators/strimzi/overlays/streams-for-kafka
namespace: streams-kafka-operator
- name: rhbk-operator
path: examples/operators/keycloak/overlays/rhbk
namespace: keycloak
- name: external-secrets-operator
path: examples/operators/external-secrets/overlays/openshift
namespace: external-secrets-operator
- git:
repoURL: https://github.com/streamshub/streamshub-gitops.git
# TODO: switch back to HEAD before merge
revision: argocd-kafka-examples
directories:
- path: examples/scenarios/*
template:
metadata:
name: '{{ default .path.basename .name }}'
labels:
app.kubernetes.io/part-of: streamshub-gitops
spec:
project: streamshub
source:
repoURL: https://github.com/streamshub/streamshub-gitops.git
# TODO: switch back to HEAD before merge
targetRevision: argocd-kafka-examples
path: '{{ default .path .path.path }}'
destination:
server: https://kubernetes.default.svc
namespace: '{{ default "" .namespace }}'
syncPolicy:
automated:
prune: false
selfHeal: true
managedNamespaceMetadata:
labels:
argocd.argoproj.io/managed-by: argocd
syncOptions:
- CreateNamespace=true
- SkipDryRunOnMissingResource=true
retry:
limit: 5
backoff:
duration: 30s
maxDuration: 5m
factor: 2
File renamed without changes.
14 changes: 14 additions & 0 deletions examples/argo-cd/overlays/openshift/instance/app-project.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: streamshub
namespace: argocd
spec:
clusterResourceWhitelist:
- group: '*'
kind: '*'
destinations:
- namespace: '*'
server: '*'
sourceRepos:
- '*'
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ kind: Kustomization
resources:
- namespace.yaml
- argocd.yaml
- app-project.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,7 @@ spec:
name: openshift-gitops-operator
source: redhat-operators
sourceNamespace: openshift-marketplace
config:
env:
- name: ARGOCD_CLUSTER_CONFIG_NAMESPACES
value: argocd
Loading