From 597aae1a20e303feac305a683f8406ebc565b50e Mon Sep 17 00:00:00 2001 From: Tommy Verrall Date: Mon, 16 Feb 2026 13:46:23 +0100 Subject: [PATCH] localnet: wire otel --- docker/localnet/Dockerfile.localnet | 7 +- docker/localnet/localnet-logs.sh | 21 +++-- docker/localnet/localnet.sh | 140 ++++++++++++++++------------ 3 files changed, 99 insertions(+), 69 deletions(-) diff --git a/docker/localnet/Dockerfile.localnet b/docker/localnet/Dockerfile.localnet index a6eb260f1a..e3d3460c6e 100644 --- a/docker/localnet/Dockerfile.localnet +++ b/docker/localnet/Dockerfile.localnet @@ -10,10 +10,9 @@ COPY ./ ./ ENV CARGO_BUILD_JOBS=8 # Build all required binaries in release mode -RUN cargo build --release --locked \ - -p nym-node \ - -p nym-network-requester \ - -p nym-socks5-client +# nym-node is built with the otel feature for OpenTelemetry tracing support +RUN cargo build --release --locked -p nym-node --features otel && \ + cargo build --release --locked -p nym-network-requester -p nym-socks5-client # Install runtime dependencies including Go for wireguard-go RUN apt update && apt install -y \ diff --git a/docker/localnet/localnet-logs.sh b/docker/localnet/localnet-logs.sh index 3347943e09..9dfcdb5291 100755 --- a/docker/localnet/localnet-logs.sh +++ b/docker/localnet/localnet-logs.sh @@ -5,6 +5,13 @@ SESSION_NAME="nym-localnet-logs" +# Detect runtime +if command -v container &> /dev/null; then + RUNTIME="container" +else + RUNTIME="docker" +fi + # Container names CONTAINERS=( "nym-mixnode1" @@ -17,9 +24,9 @@ CONTAINERS=( # Check if containers are running running_containers=() -for container in "${CONTAINERS[@]}"; do - if container inspect "$container" &>/dev/null; then - running_containers+=("$container") +for ctr in "${CONTAINERS[@]}"; do + if $RUNTIME inspect "$ctr" &>/dev/null; then + running_containers+=("$ctr") fi done @@ -32,11 +39,11 @@ fi # Check if we're already in tmux if [ -n "$TMUX" ]; then # Inside tmux - create new window - tmux new-window -n "logs" "container logs -f ${running_containers[0]}" + tmux new-window -n "logs" "$RUNTIME logs -f ${running_containers[0]}" # Split for remaining containers for ((i=1; i<${#running_containers[@]}; i++)); do - tmux split-window -t logs "container logs -f ${running_containers[$i]}" + tmux split-window -t logs "$RUNTIME logs -f ${running_containers[$i]}" tmux select-layout -t logs tiled done @@ -48,11 +55,11 @@ else exec tmux attach-session -t "$SESSION_NAME" else # Create new session - tmux new-session -d -s "$SESSION_NAME" -n "logs" "container logs -f ${running_containers[0]}" + tmux new-session -d -s "$SESSION_NAME" -n "logs" "$RUNTIME logs -f ${running_containers[0]}" # Split for remaining containers for ((i=1; i<${#running_containers[@]}; i++)); do - tmux split-window -t "$SESSION_NAME:logs" "container logs -f ${running_containers[$i]}" + tmux split-window -t "$SESSION_NAME:logs" "$RUNTIME logs -f ${running_containers[$i]}" tmux select-layout -t "$SESSION_NAME:logs" tiled done diff --git a/docker/localnet/localnet.sh b/docker/localnet/localnet.sh index e021310bc2..e8b73b8cd1 100755 --- a/docker/localnet/localnet.sh +++ b/docker/localnet/localnet.sh @@ -2,8 +2,8 @@ set -ex -# Nym Localnet Orchestration Script for Apple Container Runtime -# Emulates docker-compose functionality +# Nym Localnet Orchestration Script +# Supports both Docker and Apple Container Runtime SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" @@ -14,6 +14,28 @@ NYM_VOLUME_PATH="/tmp/nym-localnet-home-$$" SUFFIX=${NYM_NODE_SUFFIX:-localnet} +# Detect container runtime: prefer Apple 'container' if available, fall back to docker +if command -v container &> /dev/null; then + RUNTIME="container" + HOST_INTERNAL="host.containers.internal" +else + RUNTIME="docker" + HOST_INTERNAL="host.docker.internal" +fi + +# OpenTelemetry configuration +# Set OTEL_ENABLE=1 to enable OTel tracing on all nym-node instances. +# OTEL_ENDPOINT should point to the OTLP gRPC collector reachable from containers. +OTEL_ENABLE=${OTEL_ENABLE:-1} +OTEL_ENDPOINT=${OTEL_ENDPOINT:-"http://${HOST_INTERNAL}:4317"} + +# Build OTel flags for nym-node run commands +otel_flags() { + if [ "$OTEL_ENABLE" = "1" ]; then + echo "--otel --otel-endpoint $OTEL_ENDPOINT" + fi +} + # Container names INIT_CONTAINER="nym-localnet-init" MIXNODE1_CONTAINER="nym-mixnode1" @@ -64,13 +86,13 @@ cleanup_host_state() { done } -# Check if container command exists +# Check prerequisites check_prerequisites() { - if ! command -v container &> /dev/null; then - log_error "Apple 'container' command not found" - log_error "Install from: https://github.com/apple/container" + if ! command -v docker &> /dev/null; then + log_error "Docker not found" exit 1 fi + log_info "Using runtime: $RUNTIME" } # Build the Docker image @@ -80,7 +102,6 @@ build_image() { cd "$PROJECT_ROOT" - # Build with Docker log_info "Building with Docker..." if ! docker build \ -f "$SCRIPT_DIR/Dockerfile.localnet" \ @@ -90,30 +111,24 @@ build_image() { exit 1 fi - # Transfer image to container runtime - log_info "Transferring image to container runtime..." - - # Save to temporary file (container image load doesn't support stdin) - TEMP_IMAGE="/tmp/nym-localnet-image-$$.tar" - if ! docker save -o "$TEMP_IMAGE" "$IMAGE_NAME"; then - log_error "Failed to save Docker image" - exit 1 - fi - - # Load into container runtime from file - if ! container image load --input "$TEMP_IMAGE"; then + # If using Apple container runtime, transfer image from Docker + if [ "$RUNTIME" = "container" ]; then + log_info "Transferring image to Apple container runtime..." + TEMP_IMAGE="/tmp/nym-localnet-image-$$.tar" + if ! docker save -o "$TEMP_IMAGE" "$IMAGE_NAME"; then + log_error "Failed to save Docker image" + exit 1 + fi + if ! container image load --input "$TEMP_IMAGE"; then + rm -f "$TEMP_IMAGE" + log_error "Failed to load image into container runtime" + exit 1 + fi rm -f "$TEMP_IMAGE" - log_error "Failed to load image into container runtime" - exit 1 - fi - - # Clean up temporary file - rm -f "$TEMP_IMAGE" - - # Verify image is available - if ! container image inspect "$IMAGE_NAME" &>/dev/null; then - log_error "Image not found in container runtime after load" - exit 1 + if ! container image inspect "$IMAGE_NAME" &>/dev/null; then + log_error "Image not found in container runtime after load" + exit 1 + fi fi log_success "Image built and loaded: $IMAGE_NAME" @@ -155,7 +170,7 @@ NETWORK_NAME="nym-localnet-network" # Create container network create_network() { log_info "Creating container network: $NETWORK_NAME" - if container network create "$NETWORK_NAME" 2>/dev/null; then + if $RUNTIME network create "$NETWORK_NAME" 2>/dev/null; then log_success "Network created: $NETWORK_NAME" else log_info "Network $NETWORK_NAME already exists or creation failed" @@ -164,9 +179,9 @@ create_network() { # Remove container network remove_network() { - if container network list | grep -q "$NETWORK_NAME"; then + if $RUNTIME network list | grep -q "$NETWORK_NAME"; then log_info "Removing network: $NETWORK_NAME" - container network rm "$NETWORK_NAME" 2>/dev/null || true + $RUNTIME network rm "$NETWORK_NAME" 2>/dev/null || true log_success "Network removed" fi } @@ -183,7 +198,10 @@ start_mixnode() { local verloc_port="2000${node_id}" local http_port="3000${node_id}" - container run \ + local otel_args + otel_args=$(otel_flags) + + $RUNTIME run \ --name "$container_name" \ -m 2G \ --network "$NETWORK_NAME" \ @@ -215,7 +233,7 @@ start_mixnode() { sleep 2; done; echo "Starting mix'"${node_id}"'..."; - exec nym-node run --id mix'"${node_id}"'-localnet --unsafe-disable-replay-protection --local + exec nym-node '"${otel_args}"' run --id mix'"${node_id}"'-localnet --unsafe-disable-replay-protection --local ' log_success "$container_name started" @@ -224,7 +242,10 @@ start_mixnode() { start_gateway() { log_info "Starting $GATEWAY_CONTAINER..." - container run \ + local otel_args + otel_args=$(otel_flags) + + $RUNTIME run \ --name "$GATEWAY_CONTAINER" \ -m 2G \ --network "$NETWORK_NAME" \ @@ -267,7 +288,7 @@ start_gateway() { sleep 2; done; echo "Starting gateway with LP listener (mock ecash)..."; - exec nym-node run --id gateway-localnet --unsafe-disable-replay-protection --local --wireguard-enabled true --wireguard-userspace true --lp-use-mock-ecash true + exec nym-node '"${otel_args}"' run --id gateway-localnet --unsafe-disable-replay-protection --local --wireguard-enabled true --wireguard-userspace true --lp-use-mock-ecash true ' log_success "$GATEWAY_CONTAINER started" @@ -291,7 +312,10 @@ start_gateway() { start_gateway2() { log_info "Starting $GATEWAY2_CONTAINER..." - container run \ + local otel_args + otel_args=$(otel_flags) + + $RUNTIME run \ --name "$GATEWAY2_CONTAINER" \ -m 2G \ --network "$NETWORK_NAME" \ @@ -334,7 +358,7 @@ start_gateway2() { sleep 2; done; echo "Starting gateway2 with LP listener (mock ecash)..."; - exec nym-node run --id gateway2-localnet --unsafe-disable-replay-protection --local --wireguard-enabled true --wireguard-userspace true --lp-use-mock-ecash true + exec nym-node '"${otel_args}"' run --id gateway2-localnet --unsafe-disable-replay-protection --local --wireguard-enabled true --wireguard-userspace true --lp-use-mock-ecash true ' log_success "$GATEWAY2_CONTAINER started" @@ -360,10 +384,10 @@ start_network_requester() { # Get gateway IP address log_info "Getting gateway IP address..." - GATEWAY_IP=$(container exec "$GATEWAY_CONTAINER" hostname -i) + GATEWAY_IP=$($RUNTIME exec "$GATEWAY_CONTAINER" hostname -i) log_info "Gateway IP: $GATEWAY_IP" - container run \ + $RUNTIME run \ --name "$REQUESTER_CONTAINER" \ --network "$NETWORK_NAME" \ -v "$VOLUME_PATH:/localnet" \ @@ -398,7 +422,7 @@ start_network_requester() { start_socks5_client() { log_info "Starting $SOCKS5_CONTAINER..." - container run \ + $RUNTIME run \ --name "$SOCKS5_CONTAINER" \ --network "$NETWORK_NAME" \ -p 1080:1080 \ @@ -451,15 +475,15 @@ stop_containers() { log_info "Stopping all containers..." for container_name in "${ALL_CONTAINERS[@]}"; do - if container inspect "$container_name" &>/dev/null; then + if $RUNTIME inspect "$container_name" &>/dev/null; then log_info "Stopping $container_name" - container stop "$container_name" 2>/dev/null || true - container rm "$container_name" 2>/dev/null || true + $RUNTIME stop "$container_name" 2>/dev/null || true + $RUNTIME rm "$container_name" 2>/dev/null || true fi done # Also clean up init container if it exists - container rm "$INIT_CONTAINER" 2>/dev/null || true + $RUNTIME rm "$INIT_CONTAINER" 2>/dev/null || true log_success "All containers stopped" @@ -467,7 +491,7 @@ stop_containers() { remove_network } -# Show container logs +# Show $RUNTIME logs show_logs() { local container_name=${1:-} @@ -478,8 +502,8 @@ show_logs() { fi # Show logs for specific container - if container inspect "$container_name" &>/dev/null; then - container logs -f "$container_name" + if $RUNTIME inspect "$container_name" &>/dev/null; then + $RUNTIME logs -f "$container_name" else log_error "Container not found: $container_name" log_info "Available containers:" @@ -496,8 +520,8 @@ show_status() { echo "" for container_name in "${ALL_CONTAINERS[@]}"; do - if container inspect "$container_name" &>/dev/null; then - local status=$(container inspect "$container_name" 2>/dev/null | grep -o '"Status":"[^"]*"' | cut -d'"' -f4 || echo "unknown") + if $RUNTIME inspect "$container_name" &>/dev/null; then + local status=$($RUNTIME inspect "$container_name" 2>/dev/null | grep -o '"Status":"[^"]*"' | cut -d'"' -f4 || echo "unknown") echo -e " ${GREEN}●${NC} $container_name - $status" else echo -e " ${RED}○${NC} $container_name - not running" @@ -554,11 +578,11 @@ build_topology() { # Get container IPs log_info "Getting container IP addresses..." - MIX1_IP=$(container exec "$MIXNODE1_CONTAINER" hostname -i) - MIX2_IP=$(container exec "$MIXNODE2_CONTAINER" hostname -i) - MIX3_IP=$(container exec "$MIXNODE3_CONTAINER" hostname -i) - GATEWAY_IP=$(container exec "$GATEWAY_CONTAINER" hostname -i) - GATEWAY2_IP=$(container exec "$GATEWAY2_CONTAINER" hostname -i) + MIX1_IP=$($RUNTIME exec "$MIXNODE1_CONTAINER" hostname -i) + MIX2_IP=$($RUNTIME exec "$MIXNODE2_CONTAINER" hostname -i) + MIX3_IP=$($RUNTIME exec "$MIXNODE3_CONTAINER" hostname -i) + GATEWAY_IP=$($RUNTIME exec "$GATEWAY_CONTAINER" hostname -i) + GATEWAY2_IP=$($RUNTIME exec "$GATEWAY2_CONTAINER" hostname -i) log_info "Container IPs:" echo " mix1: $MIX1_IP" @@ -568,7 +592,7 @@ build_topology() { echo " gateway2: $GATEWAY2_IP" # Run build_topology.py in a container with access to the volumes - container run \ + $RUNTIME run \ --name "nym-localnet-topology-builder" \ --network "$NETWORK_NAME" \ -v "$VOLUME_PATH:/localnet" \ @@ -613,7 +637,7 @@ start_all() { # Note: Runs after build_topology to ensure gateways have finished WireGuard setup log_info "Configuring gateway networking (IP forwarding, NAT)..." for gw in "$GATEWAY_CONTAINER" "$GATEWAY2_CONTAINER"; do - container exec "$gw" sh -c " + $RUNTIME exec "$gw" sh -c " # Enable IP forwarding echo 1 > /proc/sys/net/ipv4/ip_forward # Add NAT masquerade for outbound traffic