├── README.md └── kubectl-superdebug /README.md: -------------------------------------------------------------------------------- 1 | # kubectl-superdebug 2 | 3 | An extension of `kubectl debug` that not only attaches starts a debugging pod sharing a process namespace with a target container in a running pod, but additionally copies that target container's volume specification to that of the ephemeral container. 4 | 5 | For more information, check out my blog post on this: 6 | 7 | [Debugging Running Pods on Kubernetes](https://medium.com/datamindedbe/debugging-running-pods-on-kubernetes-2ba160c47ef5) 8 | 9 | ## Limitations 10 | 11 | This script was created as a proof-of-concept and for personal use. It is not ready to be industrialised or, say, submitted to the [krew project](https://github.com/kubernetes-sigs/krew) as-is. 12 | 13 | This script does not implement all `kubectl debug` functionality, but only extends the functionality where your debugging container shares a process namespace with a target container specified by `--target`. 14 | -------------------------------------------------------------------------------- /kubectl-superdebug: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | # Mandatory 4 | pod="" 5 | target="" 6 | # Optional 7 | image="ubuntu:latest" 8 | namespace="" 9 | proxy_port="8001" 10 | command=() 11 | verbose=( -s -o /dev/null ) # make curl silent by default 12 | 13 | function help { 14 | cat <<-EOF 15 | kubectl-superdebug <-t|--target target> [-p|--pod} 16 | [--context context] [-n|--namespace namespace] [-I|--image image] 17 | [-P|--proxy-port proxy-port] [command...] 18 | 19 | Attach an ephemeral debug container to a running container in a running pod. 20 | The ephemeral container shares the PID namespace and volume attachments with 21 | the target container. The ability to copy the volume attachments separates the 22 | functionality from that of kubectl debug. 23 | 24 | Options: 25 | --pod/-p: The pod to attach the debug container to 26 | --target/-t: The container to attach the debug container to 27 | --context: The Context name (default: current context name) 28 | --namespace/-n: The namespace of the pod (default: namespace of context) 29 | --image/-I: The image to use for the debug container (default: platform 30 | debug image or ubuntu:latest) 31 | --proxy-port/-P: The port to use for kubectl proxy, used by this script in 32 | the backendd (default: 8001) 33 | 34 | Images: 35 | - You can specify a custom image for the debug container to use with the -I 36 | option. 37 | - If the image is located in a private repository, the image pull secret 38 | required to pull the image must be present in the pod. 39 | - If no image is specified, the script will attempt to use the platform debug 40 | image. 41 | - If the secret required to pull the platform debug image is not available, the 42 | script will use ubuntu:latest. 43 | EOF 44 | } 45 | 46 | while [[ ${#} -gt 0 ]]; do 47 | case "${1}" in 48 | -p | --pod) 49 | pod="${2}" 50 | shift 2 51 | ;; 52 | --context) 53 | context="${2}" 54 | shift 2 55 | ;; 56 | -t | --target) 57 | target="${2}" 58 | shift 2 59 | ;; 60 | -I | --image) 61 | image="${2}" 62 | shift 2 63 | ;; 64 | -n | --namespace) 65 | namespace="${2}" 66 | shift 2 67 | ;; 68 | -P | --proxy-port) 69 | proxy_port="${2}" 70 | shift 2 71 | ;; 72 | -v | --verbose) 73 | verbose=( -v ) 74 | shift 75 | ;; 76 | -h | --help | help) 77 | help 78 | exit 0 79 | ;; 80 | --) 81 | # Everything after is the command to execute. 82 | shift 1 83 | while [[ ${#} -gt 0 ]]; do 84 | command+=( "${1}" ) 85 | shift 1 86 | done 87 | ;; 88 | *) 89 | if [ -z "${pod:-}" ]; then 90 | pod="${1}" 91 | shift 1 92 | else 93 | command+=( "${1}" ) 94 | shift 1 95 | fi 96 | ;; 97 | esac 98 | done 99 | 100 | if [ -z "${pod:-}" ]; then 101 | echo "Specify a pod to target (-p)" 1>&2 102 | exit 1 103 | fi 104 | if [ -z "${target:-}" ]; then 105 | echo "Specify a container to target (-t)" 1>&2 106 | exit 1 107 | fi 108 | if [ -z "${context:-}" ]; then 109 | context="$(kubectl config current-context)" 110 | fi 111 | if [ -z "${namespace:-}" ]; then 112 | namespace="$(kubectl config get-contexts "${context}" | tail -n1 | awk '{ print $5 }')" 113 | [ -z "${namespace:-}" ] && namespace="default" 114 | echo "Using default namespace ${namespace}" 1>&2 115 | fi 116 | 117 | if [[ "${#command[@]}" -eq 0 ]]; then 118 | command=( /bin/sh ) 119 | fi 120 | 121 | kill_kubectl_proxy() { 122 | echo "Stopping kube proxy" 1>&2 123 | # shellcheck disable=SC2046 124 | kill $(jobs -pr) 125 | } 126 | 127 | echo "Retrieving pod spec" 1>&2 128 | pod_spec=$(kubectl --context "${context}" get pod -n "${namespace}" "${pod}" -ojson) 129 | 130 | check_existing() { 131 | echo "Checking for existing ephemeral containers" 1>&2 132 | local running 133 | running=$(jq '.status.ephemeralContainerStatuses[] | select(.state | has("running")).name' -r 2>/dev/null <<<"${pod_spec}") || : 134 | if [ -z "${running:-}" ]; then 135 | return 136 | fi 137 | if [[ "${running}" == "null" ]]; then 138 | return 139 | fi 140 | echo "Ephemeral containers are already running on this pod" 1>&2 141 | echo "You can connect to them using the following commands:" 1>&2 142 | local container 143 | for container in ${running}; do 144 | echo -e "\tkubectl --context \"${context}\" -n \"$namespace\" attach \"${pod}\" -i -t -c \"${container}\"" 1>&2 145 | done 146 | echo 147 | echo -n "Do you want to continue creating a new ephemeral container? [y/N]" 1>&2 148 | read -r answer 149 | if [[ "${answer}" != "y" ]]; then 150 | echo "Aborting" 1>&2 151 | exit 1 152 | fi 153 | } 154 | 155 | check_existing 156 | patches_string="," 157 | mounts="$( 158 | kubectl --context "${context}" get pod "${pod}" -n "${namespace}" -ojson | \ 159 | jq '[.spec.containers[] | select(.name == "'"${target}"'").volumeMounts[] | select(has("subPath") | not)]' -r 160 | )" 161 | if [[ "${mounts}" != "null" ]]; then 162 | patches_string="\"volumeMounts\": ${mounts}," 163 | fi 164 | 165 | #shellcheck disable=SC2018 166 | # LC_ALL is set to C to avoid the error "tr: Illegal byte sequence" when running on Mac 167 | random_suffix="$({ LC_ALL=C tr -dc 'a-z' &2 171 | kubectl --context "${context}" proxy --port "${proxy_port}" 1>&2 & 172 | trap 'kill_kubectl_proxy' SIGINT SIGTERM EXIT 173 | 174 | # Wait for the proxy 175 | echo "Waiting for proxy to come up..." 1>&2 176 | # NOTE: The expansion is for the args at the end. 177 | # shellcheck disable=SC2016 178 | timeout 20 \ 179 | bash -c 'until printf "" 2>/dev/null >/dev/tcp/$0/$1; do sleep 0.2; done' \ 180 | localhost \ 181 | "${proxy_port}" 182 | 183 | command_json="$( 184 | jq --compact-output --null-input '$ARGS.positional' --args -- \ 185 | "${command[@]}" 186 | )" 187 | 188 | # Run the JSON through jq to format it. Helps when debugging. 189 | patch="$(jq . <<-EOF 190 | { 191 | "spec": 192 | { 193 | "ephemeralContainers": [{ 194 | ${patches_string} 195 | "name": "${container_name}", 196 | "image": "${image}", 197 | "command": ${command_json}, 198 | "stdin": true, 199 | "tty": true, 200 | "targetContainerName": "${target}" 201 | }] 202 | } 203 | } 204 | EOF 205 | )" 206 | 207 | # alternatively, construct a JSON patch and apply a patch Content-Type: application/json-patch+json 208 | echo "Patching pod ${pod}, creating ephemeral container ${container_name}" 1>&2 209 | curl "http://localhost:${proxy_port}/api/v1/namespaces/${namespace}/pods/${pod}/ephemeralcontainers" \ 210 | -X PATCH \ 211 | -H "Content-Type: application/strategic-merge-patch+json" \ 212 | --data "${patch}" \ 213 | "${verbose[@]}" \ 214 | -f || 215 | { 216 | echo "Pod patching unsuccessful" 1>&2 217 | echo " when sending data: ${patch}" 1>&2 218 | exit 1 219 | } 220 | 221 | cat <<-EOF 222 | Created ephemeral debug container. Give it some time to start, then connect to 223 | it using the following command: 224 | 225 | kubectl --context "${context}" -n "${namespace}" \\ 226 | attach "${pod}" -i -t \\ 227 | -c "${container_name}" 228 | 229 | You may disconnect from the debug container by pressing Ctrl-P followed by 230 | Ctrl-D. If you exit the shell, the debug container will be terminated. 231 | 232 | If you want to view the logs of the ephemeral container, run the following: 233 | 234 | kubectl logs -n "${namespace}" "${pod}" \\ 235 | -c "${container_name}" 236 | 237 | EOF 238 | --------------------------------------------------------------------------------