Sign inSign up

conduktor/conduktor-debug

By conduktor

Updated about 11 hours ago

sidecar image that contains debug tools

Image
0

7.3K

conduktor/conduktor-debug repository overview

conduktor-debug

Public debug sidecar image for Conduktor Console and Gateway. Deploy alongside a running JVM pod (same PID / network namespace) to inspect network, TLS, LDAP, Kafka and JVM state without shelling into the production container or installing tooling on the host.

  • Image: ghcr.io/conduktor/conduktor-debug
  • Architectures: linux/amd64, linux/arm64

What's in the box

CategoryTools
JVM diagnosticsopenjdk-25 (full JDK — jstack, jmap, jcmd, jhsdb, jfr), async-profiler
Network debuggingiproute2 (ip, ss, tc), iputils, bind-tools (dig, nslookup), tcpdump, nmap, netcat-openbsd (nc), socat, mtr, iftop, lsof
TLS / PKIopenssl, libnss-tools (certutil, pk12util)
LDAPopenldap-2.6-clients (ldapsearch, ldapwhoami)
Kafkakcat (formerly kafkacat), the Apache Kafka shell toolskafka-topics.sh, kafka-consumer-groups.sh, kafka-configs.sh, kafka-acls.sh, kafka-reassign-partitions.sh, kafka-console-{consumer,producer}.sh, …
PostgreSQLpostgresql-17-client (psql, pg_dump, pg_isready), pgcli
Conduktorconduktor CLI (conduktor/ctl), cdk-* helpers
Perf / observabilityhtop, sysstat (iostat, pidstat, mpstat), strace
HTTP + JSON/YAMLcurl, wget, jq, yq
Comfortbash, coreutils, sed, gawk, vim

Deploy as a sidecar

The debug image is meant to run next to a live Conduktor JVM pod, sharing its PID namespace so the JDK tools can attach to the target process.

Helm values override
podSpec:
  shareProcessNamespace: true
  containers:
    - name: debug
      image: conduktor/conduktor-debug:latest
      command: ["sleep", "infinity"]
      securityContext:
        runAsUser: 10001    # match the target JVM's UID — see Privileges
        runAsGroup: 10001
Raw kubectl debug (ephemeral, no manifest edits)
kubectl debug -n conduktor pod/console-0 \
  --image=conduktor/conduktor-debug:latest \
  --target=console \
  --profile=general \
  -it -- bash

--target shares the PID namespace with the specified container. Attaching works here either way: the ephemeral container inherits a pod-level runAsUser: 10001 if one is set, and otherwise runs as the image default root — both satisfy HotSpot's check. --profile=general additionally grants SYS_PTRACE, which you only need for strace / jhsdb / the -F variants. Use --profile=netadmin (grants NET_ADMIN + NET_RAW) for tcpdump.

Docker Compose

A Kubernetes pod shares the network namespace by default and the PID namespace only with shareProcessNamespace. Compose shares neither by default, so the sidecar needs both directives to behave like a pod:

services:
  console:
    image: conduktor/conduktor-console:latest
    user: "10001:10001"

  debug:
    image: conduktor/conduktor-debug:latest
    user: "10001:10001"        # must match console's UID/GID
    pid: "service:console"     # == shareProcessNamespace: true
    network_mode: "service:console"
    command: ["sleep", "infinity"]
    # cap_add: ["NET_RAW"]     # tcpdump
    # cap_add: ["SYS_PTRACE"]  # strace / jhsdb / jstack -F
docker compose exec debug bash
pgrep -f java          # the console JVM is visible in this namespace
jcmd <pid> GC.heap_info

network_mode: "service:console" means the debug service may not declare networks or ports of its own — Compose rejects the file if it does. It also makes debug depend on console being up, so a console restart takes the sidecar with it.

Ad-hoc against an already-running container

The closest equivalent of kubectl debug --target, with no compose file edit:

docker run --rm -it \
  --pid "container:conduktor-console" \
  --network "container:conduktor-console" \
  --user 10001:10001 \
  conduktor/conduktor-debug:latest bash

Privileges

jcmd, jstack, jmap, jinfo and jfr use the HotSpot attach mechanism: a unix socket whose peer credentials the target JVM validates as is_root(uid) || (geteuid() == uid && getegid() == gid). Since every Conduktor image runs as conduktor 10001, matching that UID/GID is sufficient — root is not required, and neither is SYS_PTRACE, which this path never uses.

ToolNeeds
jcmd, jstack, jmap, jinfo, jfrmatching UID+GID (or root) — no capability
strace, jstack -F, jmap -F, jhsdbSYS_PTRACE (these really call ptrace(2))
tcpdumpNET_RAW

The UID rule is enforced by the kernel one level below the attach socket: reaching the target's /tmp goes through /proc/<pid>/root, which only the process owner (or root) may traverse. A mismatched UID fails with Permission denied there, before HotSpot's own check is reached.

The cdk-* support scripts

The image ships Conduktor support helpers on PATH — type cdk- and tab to list them. Every one takes -h. They are redacted by default, so their output can be pasted into a ticket as-is.

CommandWhat it does
cdk-doctorOne-shot triage: namespace, UID match, JVM attach, heap, rendered config, HTTP, DNS, Kafka reachability. Exit code = number of failures.
cdk-targetLists visible JVMs with their UID and the config paths derived from each one's environment. Run this first.
cdk-env [-a] [PATTERN]The target's environment, sorted and masked.
cdk-config [-l|-i] [NAME]The rendered per-app configs (-i for the input platform config).
cdk-jvm [threads|heap|gc|flags|props|dump F]JVM state over the attach mechanism.
cdk-tls HOST:PORTChain, expiry, SANs, and validation against the target JVM's own truststore.
cdk-pg [check|url|psql|pgcli|sizes|activity]Console's Postgres: discovers the connection from the rendered config, checks it, or opens a session.

Typical debugging recipes

# JVM
pgrep -f java                          # find the target PID
jcmd <pid> GC.heap_info                # heap summary without a heap dump
jstack <pid>                           # thread dump
jmap -dump:live,format=b,file=/tmp/heap.hprof <pid>
jfr start <pid> duration=60s filename=/tmp/rec.jfr

# Network
tcpdump -i any -n port 9092            # sniff Kafka traffic
ss -tunp                               # sockets + owning process
mtr --report --report-cycles=20 kafka  # path + loss to a hop

# TLS
openssl s_client -connect kafka:9093 -servername kafka -showcerts
openssl x509 -in cert.pem -noout -text

# LDAP
ldapsearch -H ldaps://ldap:636 -x -b "dc=conduktor,dc=io" -D "cn=admin,..." -W

# Kafka — quick metadata / consume with kcat
kafkacat -b kafka:9092 -L                          # metadata
kafkacat -b kafka:9092 -t my-topic -C -o beginning # consume from earliest

# Kafka — admin operations with the Apache shell tools (already on PATH)
kafka-topics.sh --bootstrap-server kafka:9092 --list
kafka-topics.sh --bootstrap-server kafka:9092 --describe --topic my-topic
kafka-consumer-groups.sh --bootstrap-server kafka:9092 --describe --group my-group
kafka-configs.sh --bootstrap-server kafka:9092 --describe --entity-type brokers --entity-name 1
kafka-broker-api-versions.sh --bootstrap-server kafka:9092   # protocol compatibility

# Against a secured cluster, pass a properties file:
kafka-topics.sh --bootstrap-server kafka:9093 \
  --command-config /tmp/client.properties --list

# Console's PostgreSQL
cdk-pg check                           # reachable? version? connection headroom?
cdk-pg sizes                           # what is actually consuming disk
cdk-pg activity                        # non-idle backends, longest query first
cdk-pg psql                            # interactive session on that database

# Console API via the Conduktor CLI
conduktor version
conduktor get application              # needs CDK_BASE_URL + CDK_API_KEY
The conduktor CLI

The image bundles conduktor/ctl so you can drive Console's API from inside the cluster — useful when Console is reachable on the pod network but not from your laptop. It reads CDK_BASE_URL and CDK_API_KEY from the environment, so with a shared network namespace:

export CDK_BASE_URL="http://127.0.0.1:$(cdk-target | awk '/http port/{print $3}')"
export CDK_API_KEY="<admin or application token>"
conduktor get application

Tag summary

Content type

Image

Digest

sha256:cfb0263e0

Size

358.9 MB

Last updated

about 11 hours ago

docker pull conduktor/conduktor-debug