前一回,在理解了 OpenShift Router 的轉導方式後,
oc get service <服務名稱> -n <命名空間名稱> -o yaml
selector
區塊,例如...
app.kubernetes.io/instance: mgmt
app.kubernetes.io/managed-by: aaaaa-bbbcccc
app.kubernetes.io/name: cvbnm
oc get pods -n apiconnect --selector app.kubernetes.io/instance=mgmt,app.kubernetes.io/managed-by=aaaaa-bbbcccc,app.kubernetes.io/name=cvbnm
oc describe pod mgmt-wwwwwwwwwww -n apiconnect
#!/usr/bin/env bash
set -euo pipefail
# Usage/help
usage() {
cat <<'EOF'
Usage:
ocp-pod-inventory.sh [--namespace <ns1,ns2,...>]
Description:
Inventory all Pods and show:
- Top-level owner (Deployment/StatefulSet/DaemonSet/CronJob/Job/ReplicaSet/Pod)
- Services that select the Pod (selector ⊆ labels)
- "oc describe pod $pod_name -n $ns 2>/dev/null"
Output: pods_inventory.tsv (TSV table)
Requirements:
- oc (logged in), jq
EOF
}
# Parse args (simple long option parser)
NS_FILTER=""
while [ $# -gt 0 ]; do
case "$1" in
--namespace|-n)
[ $# -lt 2 ] && { echo "ERROR: --namespace requires value"; usage; exit 1; }
NS_FILTER="$2"
shift 2
;;
-h|--help)
usage; exit 0
;;
*)
echo "Unknown option: $1"; usage; exit 1
;;
esac
done
OUT_FILE="pods_inventory.tsv"
echo -e "Namespace\tPod\tPhase\tNode\tOwnerKind\tOwnerName\tServices\tMounts" > "$OUT_FILE"
# Resolve top-level owner (chase ownerReferences)
resolve_owner() {
local ns="$1" kind="$2" name="$3"
local safety=0
while [ -n "${kind:-}" ] && [ -n "${name:-}" ] && [ $safety -lt 6 ]; do
safety=$((safety+1))
case "$kind" in
Deployment|StatefulSet|DaemonSet|CronJob)
echo -e "${kind}\t${name}"
return 0
;;
esac
local json
if ! json="$(oc get "$kind" "$name" -n "$ns" -o json 2>/dev/null)"; then
echo -e "${kind}\t${name}"
return 0
fi
local next_kind next_name
next_kind="$(echo "$json" | jq -r '.metadata.ownerReferences[0].kind // empty')"
next_name="$(echo "$json" | jq -r '.metadata.ownerReferences[0].name // empty')"
if [ -z "$next_kind" ] || [ -z "$next_name" ]; then
echo -e "${kind}\t${name}"
return 0
fi
kind="$next_kind"
name="$next_name"
done
echo -e "${kind:-Pod}\t${name:-<unknown>}"
}
# Which services select this pod (selector ⊆ pod.labels)
services_selecting_pod() {
local ns="$1" pod_labels_json="$2"
local svc_cache_file="/tmp/_svc_${ns}.json"
if [ ! -f "$svc_cache_file" ]; then
oc get svc -n "$ns" -o json > "$svc_cache_file" 2>/dev/null || echo '{"items":[]}' > "$svc_cache_file"
fi
# Load services and compare selectors
jq -r --argjson labels "$pod_labels_json" '
.items[]
| {name: .metadata.name, selector: (.spec.selector // {})}
| select((.selector | length) > 0)
| select(
(.selector | to_entries) as $req
| all($req[]; ($labels[.key] // null) == .value)
)
| .name
' "$svc_cache_file"
}
# Build namespace list
NAMESPACES=""
if [ -n "$NS_FILTER" ]; then
# Split comma-separated into lines
NAMESPACES="$(echo "$NS_FILTER" | tr ',' '\n' | sed '/^$/d')"
else
NAMESPACES="$(oc get ns -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}')"
fi
# Iterate namespaces
echo "$NAMESPACES" | while read -r ns; do
[ -z "$ns" ] && continue
pods_json="$(oc get pods -n "$ns" -o json 2>/dev/null || echo '{"items":[]}')"
# Loop through pod names
echo "$pods_json" | jq -r '.items[].metadata.name' | while read -r pod; do
[ -z "$pod" ] && continue
pod_item="$(echo "$pods_json" | jq --arg name "$pod" -c '.items[] | select(.metadata.name == $name)')"
[ -z "$pod_item" ] && continue
phase="$(echo "$pod_item" | jq -r '.status.phase // ""')"
node="$(echo "$pod_item" | jq -r '.spec.nodeName // ""')"
labels_json="$(echo "$pod_item" | jq -c '.metadata.labels // {}')"
owner_kind="$(echo "$pod_item" | jq -r '.metadata.ownerReferences[0].kind // ""')"
owner_name="$(echo "$pod_item" | jq -r '.metadata.ownerReferences[0].name // ""')"
if [ -z "$owner_kind" ] || [ -z "$owner_name" ]; then
top_kind="Pod"
top_name="$pod"
else
read -r top_kind top_name <<EOF
$(resolve_owner "$ns" "$owner_kind" "$owner_name")
EOF
fi
# Services selecting this pod (comma joined)
svc_joined="$(services_selecting_pod "$ns" "$labels_json" | paste -sd, -)"
# Mounts: robust extraction (handles indentation and multiple containers)
mounts_raw="$(
oc describe pod "$pod" -n "$ns" 2>/dev/null \
| awk 'BEGIN{f=0} /^[[:space:]]*Mounts:/{f=1; next} f && NF==0{f=0} f{print}'
)"
# Normalize whitespace and squash to one line
mounts_one_line="$(echo "$mounts_raw" \
| sed 's/^[[:space:]]\+//' \
| tr '\t' ' ' \
| tr '\n' ' ' \
| sed 's/ \+/ /g; s/^ *//; s/ *$//')"
printf "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n" \
"$ns" "$pod" "$phase" "$node" "$top_kind" "$top_name" "${svc_joined}" "${mounts_one_line}" >> "$OUT_FILE"
done
done
echo "Done. Output -> $OUT_FILE"
#!/usr/bin/env bash
set -euo pipefail
# Usage/help
usage() {
cat <<'EOF'
Usage:
ocp-pod-inventory.sh [--namespace <ns1,ns2,...>]
Description:
Inventory all Pods and show:
- Top-level owner (Deployment/StatefulSet/DaemonSet/CronJob/Job/ReplicaSet/Pod)
- Services that select the Pod (selector ⊆ labels)
- "oc describe pod $pod_name -n $ns 2>/dev/null"
Output: pods_inventory.tsv (TSV table)
Requirements:
- oc (logged in), jq
EOF
}
1
行,這邊只是告訴你的作業系統,若不指定的話預設用哪個 shell 來執行。2
行的設定,避免你腳本中,存在了一些回傳值不為零的命令,引發整串腳本結束時噴錯。 這個設定下去後,只需關注最後一個指令正不正常結束即可以他為主。
the return value of a pipeline is the status of the last command to exit with a non-zero status, or zero if no command exited with a non-zero status.
4 ~ 20
行,用來當作提示文字,吐給呼叫者,可能是未來某天你要呼叫這腳本做事,但是忘記當初在裡面埋了什麼,就備註在這吧!# Parse args (simple long option parser)
NS_FILTER=""
while [ $# -gt 0 ]; do
case "$1" in
--namespace|-n)
[ $# -lt 2 ] && { echo "ERROR: --namespace requires value"; usage; exit 1; }
NS_FILTER="$2"
shift 2
;;
-h|--help)
usage; exit 0
;;
*)
echo "Unknown option: $1"; usage; exit 1
;;
esac
done
$#
表示前景一片看好,意思是要去抓取命令列、接在這串後面的參數數量,greater than 0 的話才執行迴圈內的東西。# Resolve top-level owner (chase ownerReferences)
resolve_owner() {
local ns="$1" kind="$2" name="$3"
local safety=0
while [ -n "${kind:-}" ] && [ -n "${name:-}" ] && [ $safety -lt 6 ]; do
safety=$((safety+1))
case "$kind" in
Deployment|StatefulSet|DaemonSet|CronJob)
echo -e "${kind}\t${name}"
return 0
;;
esac
local json
if ! json="$(oc get "$kind" "$name" -n "$ns" -o json 2>/dev/null)"; then
echo -e "${kind}\t${name}"
return 0
fi
local next_kind next_name
next_kind="$(echo "$json" | jq -r '.metadata.ownerReferences[0].kind // empty')"
next_name="$(echo "$json" | jq -r '.metadata.ownerReferences[0].name // empty')"
if [ -z "$next_kind" ] || [ -z "$next_name" ]; then
echo -e "${kind}\t${name}"
return 0
fi
kind="$next_kind"
name="$next_name"
done
echo -e "${kind:-Pod}\t${name:-<unknown>}"
}
kubectl get <resource> <name> -o jsonpath="{.metadata.ownerReferences[*].kind}"
# Which services select this pod (selector ⊆ pod.labels)
services_selecting_pod() {
local ns="$1" pod_labels_json="$2"
local svc_cache_file="/tmp/_svc_${ns}.json"
if [ ! -f "$svc_cache_file" ]; then
oc get svc -n "$ns" -o json > "$svc_cache_file" 2>/dev/null || echo '{"items":[]}' > "$svc_cache_file"
fi
# Load services and compare selectors
jq -r --argjson labels "$pod_labels_json" '
.items[]
| {name: .metadata.name, selector: (.spec.selector // {})}
| select((.selector | length) > 0)
| select(
(.selector | to_entries) as $req
| all($req[]; ($labels[.key] // null) == .value)
)
| .name
' "$svc_cache_file"
}
# Build namespace list
NAMESPACES=""
if [ -n "$NS_FILTER" ]; then
# Split comma-separated into lines
NAMESPACES="$(echo "$NS_FILTER" | tr ',' '\n' | sed '/^$/d')"
else
NAMESPACES="$(oc get ns -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}')"
fi
# Iterate namespaces
echo "$NAMESPACES" | while read -r ns; do
[ -z "$ns" ] && continue
pods_json="$(oc get pods -n "$ns" -o json 2>/dev/null || echo '{"items":[]}')"
# Loop through pod names
echo "$pods_json" | jq -r '.items[].metadata.name' | while read -r pod; do
[ -z "$pod" ] && continue
pod_item="$(echo "$pods_json" | jq --arg name "$pod" -c '.items[] | select(.metadata.name == $name)')"
[ -z "$pod_item" ] && continue
phase="$(echo "$pod_item" | jq -r '.status.phase // ""')"
node="$(echo "$pod_item" | jq -r '.spec.nodeName // ""')"
labels_json="$(echo "$pod_item" | jq -c '.metadata.labels // {}')"
owner_kind="$(echo "$pod_item" | jq -r '.metadata.ownerReferences[0].kind // ""')"
owner_name="$(echo "$pod_item" | jq -r '.metadata.ownerReferences[0].name // ""')"
if [ -z "$owner_kind" ] || [ -z "$owner_name" ]; then
top_kind="Pod"
top_name="$pod"
else
read -r top_kind top_name <<EOF
$(resolve_owner "$ns" "$owner_kind" "$owner_name")
EOF
fi
# Services selecting this pod (comma joined)
svc_joined="$(services_selecting_pod "$ns" "$labels_json" | paste -sd, -)"
# Mounts: robust extraction (handles indentation and multiple containers)
mounts_raw="$(
oc describe pod "$pod" -n "$ns" 2>/dev/null \
| awk 'BEGIN{f=0} /^[[:space:]]*Mounts:/{f=1; next} f && NF==0{f=0} f{print}'
)"
# Normalize whitespace and squash to one line
mounts_one_line="$(echo "$mounts_raw" \
| sed 's/^[[:space:]]\+//' \
| tr '\t' ' ' \
| tr '\n' ' ' \
| sed 's/ \+/ /g; s/^ *//; s/ *$//')"
printf "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n" \
"$ns" "$pod" "$phase" "$node" "$top_kind" "$top_name" "${svc_joined}" "${mounts_one_line}" >> "$OUT_FILE"
done
done
echo "Done. Output -> $OUT_FILE"