Skip to content
Merged
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
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ The DOKS team provides this image for use as-is and for transparency as the imag

# Usage

Prefer the **DaemonSet** when you need debug pods on many nodes, or the ephemeral **`debug-node`** helper when you need access to a single node. Avoid leaving a long-lived `doks-debug` Deployment in the cluster.

## DaemonSet

```bash
kubectl apply -f k8s/daemonset.yaml
```
Expand All @@ -18,6 +22,7 @@ This DaemonSet manifest will:
2. Use `hostPID`, `hostIPC`, and `hostNetwork`.
3. Mount the entire host filesystem to `/host` in the containers.
4. Mount the `containerd` socket at `/run/containerd/containerd.sock` from the host into the container.
5. Tolerate all taints (`operator: Exists`) so pods can land on cordoned or specially tainted nodes.

In order to make use of these workloads, you can exec into a pod of choice by name:

Expand All @@ -33,6 +38,28 @@ POD_NAME=$(kubectl -n kube-system get pods --field-selector spec.nodeName=${NODE
kubectl -n kube-system exec -it ${POD_NAME} bash
```

Clean up when finished:

```bash
kubectl delete -f k8s/daemonset.yaml
```

## Ephemeral single-node access (`debug-node`)

For short-lived access to one node, use `script/debug-node`. It creates a `doks-debug` Deployment pinned with a `nodeSelector`, execs into the host via `chroot /host`, and deletes the Deployment when you exit.

```bash
./script/debug-node <node-name>
```

The Deployment manifest does **not** include a default catch-all toleration. A long-lived Deployment with `tolerations: [{operator: Exists}]` can reschedule onto draining nodes and block scale-down or upgrades. If you need to reach a tainted or cordoned node for a brief session, pass `--tolerate-all`:

```bash
./script/debug-node --tolerate-all <node-name>
```

Requires `kubectl`, `curl` (if the local manifest is unavailable), and [`yq`](https://github.com/mikefarah/yq).

Once you're in, you have access to the set of tools listed in the `Dockerfile`. This includes:

- [`vim`](https://github.com/vim/vim) - is a greatly improved version of the good old UNIX editor Vi.
Expand Down
2 changes: 0 additions & 2 deletions k8s/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ spec:
hostPID: true
hostIPC: true
hostNetwork: true
tolerations:
- operator: Exists
containers:
- name: doks-debug
securityContext:
Expand Down
124 changes: 124 additions & 0 deletions script/debug-node
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
#!/usr/bin/env bash
# Ephemeral node access via a doks-debug Deployment.
# Pins to one node, execs in, and deletes the Deployment on exit.
#
# Usage: debug-node [--tolerate-all] <node-name>
#
# --tolerate-all Inject tolerations: operator: Exists so the pod can land on
# tainted/cordoned nodes. Use only for short-lived sessions;
# long-lived Deployments with this toleration can block drains
# and upgrades.

set -euo pipefail

tolerate_all=false
node_name=""

usage() {
echo "Usage: $(basename "$0") [--tolerate-all] <node-name>"
echo " connect and chroot into the node using an ephemeral doks-debug Deployment"
}

while [[ $# -gt 0 ]]; do
case "$1" in
-h|--help)
usage
exit 0
;;
--tolerate-all)
tolerate_all=true
shift
;;
-*)
echo "Error: unknown option: $1" >&2
usage >&2
exit 1
;;
*)
if [[ -n "$node_name" ]]; then
echo "Error: unexpected argument: $1" >&2
usage >&2
exit 1
fi
node_name="$1"
shift
;;
esac
done

if [[ -z "$node_name" ]]; then
usage >&2
exit 1
fi

script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
local_manifest="${script_dir}/../k8s/deployment.yaml"
remote_manifest_url="https://raw.githubusercontent.com/digitalocean/doks-debug/refs/heads/master/k8s/deployment.yaml"

deployment_name="doks-debug"
deployment_namespace="kube-system"

cleanup() {
kubectl delete deployment "${deployment_name}" \
--namespace "${deployment_namespace}" \
--ignore-not-found=true
}
trap cleanup EXIT

if [[ -f "$local_manifest" ]]; then
doks_debug_deployment_yaml="$(cat "$local_manifest")"
else
doks_debug_deployment_yaml="$(curl --silent --fail --location "$remote_manifest_url")"
fi

if [[ -z "$doks_debug_deployment_yaml" ]]; then
echo "Error: failed to load the deployment manifest." >&2
exit 1
fi

if ! command -v yq >/dev/null 2>&1; then
echo "Error: yq is required (https://github.com/mikefarah/yq)." >&2
exit 1
fi

modified_yaml="$(
echo "$doks_debug_deployment_yaml" | \
nodeName="$node_name" yq '.spec.template.spec.nodeSelector["kubernetes.io/hostname"] = env(nodeName)'
)"

if [[ "$tolerate_all" == true ]]; then
modified_yaml="$(
echo "$modified_yaml" | \
yq '.spec.template.spec.tolerations = [{"operator": "Exists"}]'
)"
fi

echo "$modified_yaml" | kubectl apply -f -

kubectl wait deployment "${deployment_name}" \
--namespace "${deployment_namespace}" \
--for=condition=Available \
--timeout=120s \
>/dev/null

pod_name="$(
kubectl get pods \
--namespace "${deployment_namespace}" \
-l name=doks-debug \
-o jsonpath='{.items[0].metadata.name}'
)"

if [[ -z "$pod_name" ]]; then
echo "Error: could not find a running pod for the deployment." >&2
exit 1
fi

kubectl wait pod "${pod_name}" \
--namespace "${deployment_namespace}" \
--for=condition=Ready \
--timeout=60s \
>/dev/null

kubectl exec -it "${pod_name}" \
--namespace "${deployment_namespace}" \
-- chroot /host bash
Loading