Skip to content
Closed
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
99 changes: 91 additions & 8 deletions .github/workflows/publish-kafka-docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ env:
# GitHub Container Registry
GHCR_REGISTRY: ghcr.io
# github.repository as <account>/<repo>
GHCR_IMAGE_NAME: ${{ github.repository }}/kafka
# Docker Hub image name
GHCR_KAFKA_IMAGE_NAME: ${{ github.repository }}/kafka
GHCR_JMX_IMAGE_NAME: ${{ github.repository }}/jmx-javaagent
# Docker Hub image name (only for Kafka)
DOCKERHUB_IMAGE: adobe/kafka

jobs:
Expand All @@ -34,7 +35,7 @@ jobs:
run: |
echo "built_at=$(date --rfc-3339=date)" >> $GITHUB_OUTPUT
build-kafka:
build-and-push-kafka-image:
runs-on: ubuntu-latest
needs: prepare
permissions:
Expand All @@ -47,40 +48,122 @@ jobs:
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Extract Kafka version from tag
id: kafka_version
run: |
KAFKA_VERSION=$(echo "${{ needs.prepare.outputs.tag }}" | sed 's/kafka-//')
echo "kafka_version=$KAFKA_VERSION" >> $GITHUB_OUTPUT
- name: Check if Kafka image already exists
id: image_exists
run: |
IMAGE_NAME="${{ env.GHCR_REGISTRY }}/${{ env.GHCR_KAFKA_IMAGE_NAME }}:${{ steps.kafka_version.outputs.kafka_version }}"
echo "Checking if image exists: $IMAGE_NAME"
# Try to inspect the manifest using docker
if docker manifest inspect "$IMAGE_NAME" > /dev/null 2>&1; then
echo "exists=true" >> $GITHUB_OUTPUT
echo "Image $IMAGE_NAME already exists, skipping build"
else
echo "exists=false" >> $GITHUB_OUTPUT
echo "Image $IMAGE_NAME does not exist, will build"
fi
- name: Login to DockerHub Registry
if: startsWith(github.ref, 'refs/tags/')
if: startsWith(github.ref, 'refs/tags/') && github.repository == 'adobe/koperator' && steps.image_exists.outputs.exists == 'false'
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Log into GitHub Container Registry ${{ env.GHCR_REGISTRY }}
if: startsWith(github.ref, 'refs/tags/')
if: startsWith(github.ref, 'refs/tags/') && steps.image_exists.outputs.exists == 'false'
uses: docker/login-action@v3
with:
registry: ${{ env.GHCR_REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract Docker metadata
if: steps.image_exists.outputs.exists == 'false'
id: meta
uses: docker/metadata-action@v5
with:
images: |
${{ env.DOCKERHUB_IMAGE }}
${{ env.GHCR_REGISTRY }}/${{ env.GHCR_IMAGE_NAME }}
${{ github.repository == 'adobe/koperator' && env.DOCKERHUB_IMAGE || '' }}
${{ env.GHCR_REGISTRY }}/${{ env.GHCR_KAFKA_IMAGE_NAME }}
tags: |
type=match,pattern=kafka-(.*),group=1
labels: |
org.opencontainers.image.description=Apache Kafka with OpenJDK 21
- name: Build and push kafka image
if: startsWith(github.ref, 'refs/tags/') && steps.image_exists.outputs.exists == 'false'
uses: docker/build-push-action@v6
with:
context: docker/kafka
platforms: linux/amd64,linux/arm64
push: ${{ startsWith(github.ref, 'refs/tags/') }}
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
annotations: ${{ steps.meta.outputs.annotations }}
build-args: |
VERSION=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.version'] }}
BUILT_AT=${{ needs.prepare.outputs.built_at }}
COMMIT=${{ github.sha }}
build-and-push-jmx-agent-image:
runs-on: ubuntu-latest
needs: prepare
permissions:
contents: read
packages: write
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Extract JMX exporter version from Dockerfile
id: jmx_exporter_version
run: |
JMX_VERSION=$(grep "ARG JMX_EXPORTER_VERSION=" docker/jmx_exporter/Dockerfile | cut -d'=' -f2 | cut -d' ' -f1)
echo "jmx_version=$JMX_VERSION" >> $GITHUB_OUTPUT
- name: Check if JMX exporter image already exists
id: image_exists
run: |
IMAGE_NAME="${{ env.GHCR_REGISTRY }}/${{ env.GHCR_JMX_IMAGE_NAME }}:${{ steps.jmx_exporter_version.outputs.jmx_version }}"
echo "Checking if image exists: $IMAGE_NAME"
# Try to inspect the manifest using docker
if docker manifest inspect "$IMAGE_NAME" > /dev/null 2>&1; then
echo "exists=true" >> $GITHUB_OUTPUT
echo "Image $IMAGE_NAME already exists, skipping build"
else
echo "exists=false" >> $GITHUB_OUTPUT
echo "Image $IMAGE_NAME does not exist, will build"
fi
- name: Log into GitHub Container Registry ${{ env.GHCR_REGISTRY }}
if: startsWith(github.ref, 'refs/tags/') && steps.image_exists.outputs.exists == 'false'
uses: docker/login-action@v3
with:
registry: ${{ env.GHCR_REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract Docker metadata for JMX exporter
if: steps.image_exists.outputs.exists == 'false'
id: meta
uses: docker/metadata-action@v5
with:
images: |
${{ env.GHCR_REGISTRY }}/${{ env.GHCR_JMX_IMAGE_NAME }}
tags: |
type=raw,value=${{ steps.jmx_exporter_version.outputs.jmx_version }}
labels: |
org.opencontainers.image.description=Prometheus JMX Exporter for Kafka monitoring
- name: Build and push JMX exporter image
if: startsWith(github.ref, 'refs/tags/') && steps.image_exists.outputs.exists == 'false'
uses: docker/build-push-action@v6
with:
context: docker/jmx_exporter
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
annotations: ${{ steps.meta.outputs.annotations }}
1 change: 1 addition & 0 deletions .licensei.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ ignored = [
"github.com/rcrowley/go-metrics",
"go.uber.org/atomic",
"go.uber.org/multierr",
"go.uber.org/mock",
"emperror.dev/errors",
"emperror.dev/emperror",
"go.uber.org/zap",
Expand Down
27 changes: 25 additions & 2 deletions api/assets/kafka/jmx-exporter.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,34 @@
httpServer:
threads:
minimum: 5
maximum: 10
keepAliveTime: 120
lowercaseOutputName: true
rules:
# Special cases and very specific rules
# Export kraft current state metrics
# Export Kraft metrics
## Export Kraft current-state count metrics
- pattern: 'kafka.server<type=raft-metrics><>current-state: (.+)'
name: kafka_server_raft_metrics_current_state_$1
type: GAUGE
value: 1
## MetadataLoader count metrics as COUNTERs
- pattern: kafka.server<type=MetadataLoader><>([A-Za-z]+Count)
name: kafka_server_metadataloader_$1_total
type: COUNTER
## Broker metadata error count metrics as COUNTERs
- pattern: kafka.server<type=broker-metadata-metrics><>(metadata-(load|apply)-error-count)
name: kafka_server_broker_metadata_metrics_$1_total
type: COUNTER
## KafkaController ActiveBrokerCount as GAUGE (current state, not cumulative)
- pattern: kafka.controller<type=KafkaController><>ActiveBrokerCount
name: kafka_controller_kafkacontroller_ActiveBrokerCount
type: GAUGE
## KafkaController count metrics as COUNTERs
- pattern: kafka.controller<type=KafkaController><>([A-Za-z]+Count)
name: kafka_controller_kafkacontroller_$1_total
type: COUNTER
# END: Export Kraft metrics

- pattern: 'kafka.server<type=(app-info), id=(\d+)><>(Version): ([-.~+\w\d]+)'
name: kafka_server_$1_$3
type: COUNTER
Expand Down
25 changes: 12 additions & 13 deletions api/v1beta1/kafkacluster_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,16 @@ const (
IsControllerNodeKey = "isControllerNode"

// DefaultCruiseControlImage is the default CC image used when users don't specify it in CruiseControlConfig.Image
DefaultCruiseControlImage = "adobe/cruise-control:3.0.3-adbe-20250804"
DefaultCruiseControlImage = "adobe/cruise-control:3.0.3-adbe-20250804" // renovate: datasource=docker depName=adobe/cruise-control

// DefaultKafkaImage is the default Kafka image used when users don't specify it in KafkaClusterSpec.ClusterImage
DefaultKafkaImage = "ghcr.io/adobe/koperator/kafka:2.13-3.9.1"
DefaultKafkaImage = "ghcr.io/adobe/koperator/kafka:2.13-3.9.1" // renovate: datasource=docker depName=ghcr.io/adobe/koperator/kafka

// DefaultMonitorImage is the default JMX monitor image used when users don't specify it in MonitoringConfig.JmxImage
DefaultMonitorImage = "ghcr.io/adobe/koperator/jmx-javaagent:1.4.0" // renovate: datasource=docker depName=ghcr.io/adobe/koperator/jmx-javaagent

// DefaultEnvoyImage is the default Envoy proxy image used when users don't specify it in EnvoyConfig.Image
DefaultEnvoyImage = "envoyproxy/envoy:v1.22.2" // renovate: datasource=docker depName=envoyproxy/envoy

// ControllerNodeProcessRole represents the node is a controller node
ControllerNodeProcessRole = "controller"
Expand Down Expand Up @@ -90,9 +96,6 @@ const (
defaultEnvoyLimitResourceCpu = "100m"
defaultEnvoyLimitResourceMemory = "100Mi"

// KafkaClusterDeployment.spec.template.spec.container["envoy"].image
defaultEnvoyImage = "envoyproxy/envoy:v1.22.2"

/* Broker Config */

// KafkaBrokerPod.spec.terminationGracePeriodSeconds
Expand All @@ -110,7 +113,7 @@ const (
/* Cruise Control Config */

// CruiseControlDeployment.spec.template.spec.container["%s-cruisecontrol"].image
defaultCruiseControlImage = "adobe/cruise-control:3.0.3-adbe-20250804"
defaultCruiseControlImage = "adobe/cruise-control:3.0.3-adbe-20250804" // renovate: datasource=docker depName=adobe/cruise-control

// CruiseControlDeployment.spec.template.spec.container["%s-cruisecontrol"].resources
defaultCruiseControlRequestResourceCpu = "200m"
Expand All @@ -126,7 +129,7 @@ const (
defaultKafkaClusterK8sClusterDomain = "cluster.local"

// KafkaBroker.spec.container["kafka"].image
defaultKafkaImage = "ghcr.io/adobe/koperator/kafka:2.13-3.9.1"
defaultKafkaImage = "ghcr.io/adobe/koperator/kafka:2.13-3.9.1" // renovate: datasource=docker depName=ghcr.io/adobe/koperator/kafka

/* Istio Ingress Config */

Expand All @@ -143,10 +146,6 @@ const (

/* Monitor Config */

// KafkaBrokerPod.spec.initContainer[jmx-exporter].image
// kafkaClusterDeployment.spec.template.spec.initContainer["jmx-exporter"].image
defaultMonitorImage = "ghcr.io/amuraru/jmx-javaagent:0.19.2"

// KafkaBrokerPod.spec.initContainer["jmx-exporter"].command
// kafkaClusterDeployment.spec.template.spec.initContainer["jmx-exporter"].command
defaultMonitorPathToJar = "/jmx_prometheus_javaagent.jar"
Expand Down Expand Up @@ -1256,7 +1255,7 @@ func (eConfig *EnvoyConfig) GetEnvoyImage() string {
return eConfig.Image
}

return defaultEnvoyImage
return DefaultEnvoyImage
}

// GetEnvoyAdminPort returns the envoy admin port
Expand Down Expand Up @@ -1296,7 +1295,7 @@ func (mConfig *MonitoringConfig) GetImage() string {
if mConfig.JmxImage != "" {
return mConfig.JmxImage
}
return defaultMonitorImage
return DefaultMonitorImage
}

// GetPathToJar returns the path in the used Image for Prometheus JMX exporter
Expand Down
2 changes: 1 addition & 1 deletion config/samples/banzaicloud_v1beta1_kafkacluster.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -708,7 +708,7 @@ spec:
# monitoringConfig describes the monitoring related configs
#monitoringConfig:
# jmxImage describes the used prometheus jmx exporter agent container
# jmxImage: "ghcr.io/amuraru/jmx-javaagent:0.19.2"
# jmxImage: "ghcr.io/adobe/koperator/jmx-javaagent:1.4.0"
# pathToJar describes the path to the jar file in the given image
# pathToJar: "/jmx_prometheus_javaagent.jar"
# kafkaJMXExporterConfig describes jmx exporter config for Kafka
Expand Down
2 changes: 1 addition & 1 deletion config/samples/kraft/simplekafkacluster_kraft.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ metadata:
spec:
kRaft: true
monitoringConfig:
jmxImage: "ghcr.io/amuraru/jmx-javaagent:0.19.2"
jmxImage: "ghcr.io/adobe/koperator/jmx-javaagent:1.4.0"
headlessServiceEnabled: true
propagateLabels: false
oneBrokerPerNode: false
Expand Down
2 changes: 1 addition & 1 deletion config/samples/simplekafkacluster.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ metadata:
spec:
kRaft: false
monitoringConfig:
jmxImage: "ghcr.io/amuraru/jmx-javaagent:0.19.2"
jmxImage: "ghcr.io/adobe/koperator/jmx-javaagent:1.4.0"
headlessServiceEnabled: true
zkAddresses:
- "zookeeper-server-client.zookeeper:2181"
Expand Down
2 changes: 1 addition & 1 deletion config/samples/simplekafkacluster_with_contour.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ metadata:
name: kafka
spec:
monitoringConfig:
jmxImage: "ghcr.io/amuraru/jmx-javaagent:0.19.2"
jmxImage: "ghcr.io/adobe/koperator/jmx-javaagent:1.4.0"
headlessServiceEnabled: true
zkAddresses:
- "zookeeper-server-client.zookeeper:2181"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ func expectCruiseControlDeployment(ctx context.Context, kafkaCluster *v1beta1.Ka
Expect(deployment.Spec.Template.Spec.InitContainers).To(HaveLen(1))
initContainer := deployment.Spec.Template.Spec.InitContainers[0]
Expect(initContainer.Name).To(Equal("jmx-exporter"))
Expect(initContainer.Image).To(Equal("ghcr.io/amuraru/jmx-javaagent:0.19.2"))
Expect(initContainer.Image).To(Equal(v1beta1.DefaultMonitorImage))
Expect(initContainer.Command).To(Equal([]string{"cp", "/jmx_prometheus_javaagent.jar", "/opt/jmx-exporter/jmx_prometheus.jar"}))
Expect(initContainer.VolumeMounts).To(ConsistOf(corev1.VolumeMount{
Name: "jmx-jar-data",
Expand All @@ -292,7 +292,7 @@ func expectCruiseControlDeployment(ctx context.Context, kafkaCluster *v1beta1.Ka
Expect(container.Lifecycle).NotTo(BeNil())
Expect(container.Lifecycle.PreStop).NotTo(BeNil())
Expect(container.Lifecycle.PreStop.Exec).NotTo(BeNil())
Expect(container.Image).To(Equal("adobe/cruise-control:3.0.3-adbe-20250804"))
Expect(container.Image).To(Equal(v1beta1.DefaultCruiseControlImage))
Expect(container.Ports).To(ConsistOf(
corev1.ContainerPort{
ContainerPort: 8090,
Expand Down
2 changes: 1 addition & 1 deletion controllers/tests/kafkacluster_controller_envoy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -698,7 +698,7 @@ func expectEnvoyDeployment(ctx context.Context, kafkaCluster *v1beta1.KafkaClust
Expect(templateSpec.Containers).To(HaveLen(1))
container := templateSpec.Containers[0]
Expect(container.Name).To(Equal("envoy"))
Expect(container.Image).To(Equal("envoyproxy/envoy:v1.22.2"))
Expect(container.Image).To(Equal(v1beta1.DefaultEnvoyImage))

if kafkaCluster.Spec.KRaftMode {
expectEnvoyDeploymentKRaft(container)
Expand Down
25 changes: 25 additions & 0 deletions docker/jmx_exporter/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
ARG JMX_EXPORTER_VERSION=1.5.0 # renovate: datasource=github-releases depName=prometheus/jmx_exporter

FROM maven:3-amazoncorretto-21 AS build
ARG JMX_EXPORTER_VERSION

# Install wget to download the release tarball
RUN yum install -y wget && yum clean all

# Download and extract the JMX exporter release
RUN wget https://github.com/prometheus/jmx_exporter/archive/refs/tags/${JMX_EXPORTER_VERSION}.tar.gz \
-O /tmp/jmx_exporter.tar.gz && \
tar -xzf /tmp/jmx_exporter.tar.gz -C /tmp && \
mv /tmp/jmx_exporter-${JMX_EXPORTER_VERSION} /src && \
rm /tmp/jmx_exporter.tar.gz

WORKDIR /src
RUN mvn -B -Dmaven.javadoc.skip=true -Dskip.javadoc=true \
-Dmaven.compiler.source=21 -Dmaven.compiler.target=21 -Dmaven.compiler.release=21 \
clean package -pl jmx_prometheus_javaagent -am

FROM alpine:latest
ARG JMX_EXPORTER_VERSION
COPY --from=build /src/jmx_prometheus_javaagent/target/jmx_prometheus_javaagent-${JMX_EXPORTER_VERSION}.jar /opt/jmx_exporter/
RUN ln -s /opt/jmx_exporter/jmx_prometheus_javaagent-${JMX_EXPORTER_VERSION}.jar /jmx_prometheus_javaagent.jar
CMD ["/bin/sh"]
Loading