sidecar image that contains debug tools
7.2K
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.
ghcr.io/conduktor/conduktor-debuglinux/amd64, linux/arm64| Category | Tools |
|---|---|
| JVM diagnostics | openjdk-25 (full JDK — jstack, jmap, jcmd, jhsdb, jfr), async-profiler |
| Network debugging | iproute2 (ip, ss, tc), iputils, bind-tools (dig, nslookup), tcpdump, nmap, netcat-openbsd (nc), socat, mtr, iftop, lsof |
| TLS / PKI | openssl, libnss-tools (certutil, pk12util) |
| LDAP | openldap-2.6-clients (ldapsearch, ldapwhoami) |
| Kafka | kcat (formerly kafkacat), the Apache Kafka shell tools — kafka-topics.sh, kafka-consumer-groups.sh, kafka-configs.sh, kafka-acls.sh, kafka-reassign-partitions.sh, kafka-console-{consumer,producer}.sh, … |
| PostgreSQL | postgresql-17-client (psql, pg_dump, pg_isready), pgcli |
| Conduktor | conduktor CLI (conduktor/ctl), cdk-* helpers |
| Perf / observability | htop, sysstat (iostat, pidstat, mpstat), strace |
| HTTP + JSON/YAML | curl, wget, jq, yq |
| Comfort | bash, coreutils, sed, gawk, vim |
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.
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
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.
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.
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
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.
| Tool | Needs |
|---|---|
jcmd, jstack, jmap, jinfo, jfr | matching UID+GID (or root) — no capability |
strace, jstack -F, jmap -F, jhsdb | SYS_PTRACE (these really call ptrace(2)) |
tcpdump | NET_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.
cdk-* support scriptsThe 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.
| Command | What it does |
|---|---|
cdk-doctor | One-shot triage: namespace, UID match, JVM attach, heap, rendered config, HTTP, DNS, Kafka reachability. Exit code = number of failures. |
cdk-target | Lists 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:PORT | Chain, 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. |
# 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
conduktor CLIThe 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
Content type
Image
Digest
sha256:233106c7f…
Size
358.9 MB
Last updated
about 11 hours ago
docker pull conduktor/conduktor-debug