blob: 57ab56d5e987af39ae3fc558620d11786efbb8b9 [file]
#!/usr/bin/env bash
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Simulate network delay/jitter/loss on the worker hosts (supervisor containers)
# using Linux tc/netem. The Storm image has no `tc`, so we inject the qdisc from
# a throwaway helper container that shares each supervisor's network namespace
# (--net container:...) -- no image rebuild, no compose change required.
#
# netem applies to *egress* on each supervisor's eth0, so it shapes ALL traffic
# leaving that container (inter-worker tuples, but also heartbeats to Nimbus/ZK).
# Keep the delay moderate (<= ~150ms) so heartbeats don't time out. With both
# supervisors delayed by D, worker<->worker round-trip latency is ~2*D.
#
# netem's default queue is only 1000 packets; under a high-throughput perf
# topology that buffer overflows at the added delay and drops tuples, which
# collapses TCP and back-pressures the spout to ~0. So we set a large `limit`
# (default 1,000,000 packets) -- big enough to hold rate * delay without drops.
#
# Usage:
# ./netsim.sh add [delay_ms] [jitter_ms] [loss_pct] [limit_pkts] # default 50 10 0 1000000
# ./netsim.sh show
# ./netsim.sh clear
# ./netsim.sh ping # measure RTT between supervisors
#
# Override the shaped containers with TARGETS="..." ./netsim.sh ...
set -euo pipefail
COMPOSE_SERVICES="${COMPOSE_SERVICES:-supervisor1 supervisor2}"
HELPER_IMAGE="${HELPER_IMAGE:-nicolaka/netshoot}"
IFACE="${IFACE:-eth0}"
targets() { # echo the containers to shape (resolved once, memoized in TARGETS)
if [[ -z "${TARGETS:-}" ]]; then
TARGETS="$(docker compose ps -q ${COMPOSE_SERVICES} 2>/dev/null | tr '\n' ' ')"
if [[ -z "${TARGETS// /}" ]]; then
echo "error: no running containers for services: ${COMPOSE_SERVICES}" >&2
echo " run from dev-tools/cluster with the cluster up (docker compose up -d)," >&2
echo " or set TARGETS=\"<name-or-id> ...\" to shape containers explicitly." >&2
exit 1
fi
fi
echo "${TARGETS}"
}
cname() { # human-readable container name for id/name $1 (falls back to $1)
local n; n="$(docker inspect -f '{{.Name}}' "$1" 2>/dev/null)" || true
n="${n#/}"
echo "${n:-$1}"
}
inns() { # run a command inside container $1's network namespace with NET_ADMIN
local c="$1"; shift
docker run --rm --net "container:${c}" --cap-add NET_ADMIN "${HELPER_IMAGE}" "$@"
}
cmd="${1:-show}"
case "${cmd}" in
add)
delay="${2:-50}"; jitter="${3:-10}"; loss="${4:-0}"; limit="${5:-1000000}"
if (( delay > 150 )); then
echo "warning: delay ${delay}ms > 150ms may cause heartbeats to Nimbus/ZK to time out" >&2
fi
for c in $(targets); do
echo "==> $(cname "${c}"): netem delay ${delay}ms ${jitter}ms loss ${loss}% limit ${limit} on ${IFACE}"
inns "${c}" tc qdisc replace dev "${IFACE}" root netem \
delay "${delay}ms" "${jitter}ms" distribution normal loss "${loss}%" limit "${limit}"
done
;;
clear)
for c in $(targets); do
echo "==> $(cname "${c}"): removing netem"
inns "${c}" tc qdisc del dev "${IFACE}" root 2>/dev/null || true
done
;;
show)
for c in $(targets); do
echo "==> $(cname "${c}"):"
inns "${c}" tc qdisc show dev "${IFACE}"
done
;;
ping)
set -- $(targets)
src="$1"; dst_host="$(cname "${PING_DST:-${2:-supervisor2}}")"
echo "==> RTT from $(cname "${src}") to ${dst_host} (5 pings)"
inns "${src}" ping -c 5 "${dst_host}"
;;
*)
echo "usage: $0 {add [delay_ms] [jitter_ms] [loss_pct] | show | clear | ping}" >&2
exit 2
;;
esac