#!/usr/bin/env bash # T51: baseEjectionTime backoff behavior (outlierDetection consecutive5xxErrors) # # NOTE on manifest.yaml: the DestinationRule host is set to # mixed-echo.istio-vt-t51.svc.cluster.local, NOT ...svc.homelab.local. # This cluster's kubeadm clusterDomain is homelab.local, but Istio's internal # Service registry / route-domain generation independently uses cluster.local # regardless of the kubelet's actual --cluster-domain. Using svc.homelab.local # in the DestinationRule host (or as request Host header) leaves # outlier_detection completely unattached to the outbound cluster (verified # via istio-proxy /config_dump) and routes traffic through the ALLOW_ANY # wildcard vhost to PassthroughCluster instead. This script targets the # Service ClusterIP directly so the Host header matches Istio's route-config # domains (which include the raw ClusterIP) and the correctly-named # "outbound|80||mixed-echo...svc.cluster.local" cluster is exercised. set -euo pipefail NS=istio-vt-t51 SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" cleanup() { echo "[cleanup] deleting namespace ${NS}" kubectl delete namespace "${NS}" --wait=false --ignore-not-found } trap cleanup EXIT echo "[setup] creating namespace ${NS}" kubectl create namespace "${NS}" kubectl label namespace "${NS}" istio-injection=enabled echo "[setup] applying manifest.yaml" kubectl apply -f "${SCRIPT_DIR}/manifest.yaml" echo "[wait] client pod Ready" kubectl -n "${NS}" wait --for=condition=Ready pod/client --timeout=90s echo "[wait] good-mixed / bad-mixed deployments available" kubectl -n "${NS}" wait --for=condition=available deploy/good-mixed deploy/bad-mixed --timeout=60s BAD_IP=$(kubectl -n "${NS}" get pod -l health=bad -o jsonpath='{.items[0].status.podIP}') CLUSTER_IP=$(kubectl -n "${NS}" get svc mixed-echo -o jsonpath='{.spec.clusterIP}') echo "[info] BAD_IP=${BAD_IP} CLUSTER_IP=${CLUSTER_IP}" echo "[info] sanity: confirm outlier_detection is attached to the named cluster" kubectl -n "${NS}" exec client -c istio-proxy -- curl -s localhost:15000/config_dump 2>/dev/null | python3 -c " import json,sys d=json.load(sys.stdin) for c in d['configs']: if c.get('@type','').endswith('ClustersConfigDump'): for dc in c.get('dynamic_active_clusters',[]): cluster = dc['cluster'] if 'mixed-echo' in cluster.get('name',''): print('CLUSTER:', cluster['name']) print('outlier_detection:', json.dumps(cluster.get('outlier_detection','NONE'))) " OUT="/tmp/ejection_timeline_$$.txt" echo "[observe] 75 x 2s polling loop -> ${OUT}" ( for i in $(seq 1 75); do TS=$(date +%s) FLAGS=$(kubectl -n "${NS}" exec client -c istio-proxy -- curl -s localhost:15000/clusters 2>/dev/null | grep "${BAD_IP}" | grep -o 'health_flags::[a-zA-Z_,/]*') echo "${TS} ${FLAGS}" kubectl -n "${NS}" exec client -c curl -- curl -s -o /dev/null --max-time 2 "http://${CLUSTER_IP}/" >/dev/null 2>&1 sleep 2 done ) > "${OUT}" echo "[result] timeline:" cat "${OUT}" echo "[analysis] contiguous EJECTED/HEALTHY segments (based on health_flags containing 'failed_outlier_check' vs 'healthy'; note: actual Envoy 1.30 flag string is lowercase '/failed_outlier_check', not the spec's assumed 'FAILED_OUTLIER_CHECK')" python3 -c " lines = open('${OUT}').read().strip().split(chr(10)) rows = [] for l in lines: parts = l.split(' ', 1) ts = int(parts[0]) flag = parts[1].strip() if len(parts) > 1 else '' state = 'EJECTED' if 'failed_outlier_check' in flag else ('HEALTHY' if 'healthy' in flag else 'UNKNOWN') rows.append((ts, state, flag)) segments = [] cur_state = rows[0][1] seg_start = rows[0][0] for i in range(1, len(rows)): ts, state, flag = rows[i] if state != cur_state: segments.append((cur_state, seg_start, rows[i-1][0])) cur_state = state seg_start = ts segments.append((cur_state, seg_start, rows[-1][0])) t0 = rows[0][0] for s in segments: state, start, end = s print(f'{state:8s} rel_start={start-t0:4d}s rel_end={end-t0:4d}s last-seen-span={end-start:4d}s') " echo "[done] see analysis above: baseEjectionTime=10s; first ejection segment should be ~10s, subsequent repeat ejections should be markedly longer (backoff)."