observability: fix alloy ui component, busybox healthchecks for loki/alloy

- alloy 1.x renamed the server component to ui; the old block failed the
  initial load and the container crash-looped
- grafana/loki is distroless and grafana/alloy lacks wget, so their
  healthchecks could never run; local images add a static busybox from
  alpine for that one command
- deploy.sh builds all three local images if missing
- tests: 3 new seams
This commit is contained in:
Frank
2026-08-22 03:59:14 -07:00
parent 816986139f
commit 1f4766f233
6 changed files with 76 additions and 11 deletions
+15
View File
@@ -0,0 +1,15 @@
# Alloy + a static busybox.
#
# grafana/alloy has a shell but no wget/curl, so the compose healthcheck
# cannot run anything inside the container. This image drops a static
# busybox into it for the healthcheck command only. All Alloy behavior is
# unchanged.
#
# Build on dankclaw:
# podman build -t franksdank/alloy:latest -f dankclaw/alloy/Containerfile .
FROM docker.io/library/alpine:latest AS tools
# busybox provides wget for the healthcheck.
FROM docker.io/grafana/alloy:latest
COPY --from=tools /bin/busybox /usr/local/bin/busybox
+6 -4
View File
@@ -149,7 +149,8 @@ services:
retries: 3
loki:
image: docker.io/grafana/loki:latest
# Local build: grafana/loki + static busybox (healthcheck).
image: franksdank/loki:latest
container_name: loki
restart: unless-stopped
# Rootless: container root maps to the invoking user (dank), which
@@ -163,13 +164,14 @@ services:
- /opt/loki/config:/etc/loki
- /opt/loki/data:/loki
healthcheck:
test: ["CMD", "wget", "--spider", "-q", "http://localhost:3100/ready"]
test: ["CMD", "/usr/local/bin/busybox", "wget", "--spider", "-q", "http://localhost:3100/ready"]
interval: 30s
timeout: 10s
retries: 3
alloy:
image: docker.io/grafana/alloy:latest
# Local build: grafana/alloy + static busybox (healthcheck).
image: franksdank/alloy:latest
container_name: alloy
restart: unless-stopped
volumes:
@@ -180,7 +182,7 @@ services:
depends_on:
- loki
healthcheck:
test: ["CMD", "wget", "--spider", "-q", "http://localhost:12345/-/healthy"]
test: ["CMD", "/usr/local/bin/busybox", "wget", "--spider", "-q", "http://localhost:12345/-/healthy"]
interval: 30s
timeout: 10s
retries: 3
+15
View File
@@ -0,0 +1,15 @@
# Loki + a static busybox.
#
# grafana/loki is distroless: no shell, no wget, so the compose healthcheck
# cannot run anything inside the container. This image drops a static
# busybox into it for the healthcheck command only. All Loki behavior is
# unchanged.
#
# Build on dankclaw:
# podman build -t franksdank/loki:latest -f dankclaw/loki/Containerfile .
FROM docker.io/library/alpine:latest AS tools
# busybox provides wget for the healthcheck.
FROM docker.io/grafana/loki:latest
COPY --from=tools /bin/busybox /usr/local/bin/busybox
+10 -4
View File
@@ -139,10 +139,16 @@ fi
if [ '${COMPOSE_UP[$TARGET]}' = '1' ]; then
echo '[4/4] Restarting stack...'
if ! podman image exists franksdank/caddy-porkbun:latest; then
echo ' franksdank/caddy-porkbun image missing; building it (a few minutes)...'
podman build -t franksdank/caddy-porkbun:latest -f dankclaw/caddy/Containerfile .
fi
# Locally built images: caddy + porkbun module, and loki/alloy + a static
# busybox (their upstream images lack the wget the healthchecks need).
for dir in caddy loki alloy; do
img=franksdank/$dir
[ "$dir" = "caddy" ] && img=franksdank/caddy-porkbun
if ! podman image exists $img:latest; then
echo " $img image missing; building it..."
podman build -t $img:latest -f dankclaw/$dir/Containerfile .
fi
done
podman-compose -f '${COMPOSE[$TARGET]}' up -d
else
echo '[4/4] Skipping compose restart (stack runs as bare containers on this host)'
+4 -3
View File
@@ -6,9 +6,10 @@ logging {
}
// Local listener so the container healthcheck can probe us.
server {
http_listen_addr = "0.0.0.0:12345"
grpc_listen_addr = "0.0.0.0:12346"
// Alloy >= 1.0 renamed the server component to ui.
ui {
host = "0.0.0.0"
port = 12345
}
// Ship logs to Loki (container name on the podman network).
+26
View File
@@ -136,3 +136,29 @@ CADDYFILE="$REPO_ROOT/dankclaw/Caddyfile"
}
done
}
# --- distroless healthchecks (loki/alloy carry a static busybox) ---
@test "loki and alloy run from local images carrying a static busybox" {
local compose="$REPO_ROOT/dankclaw/compose.yaml"
grep -q 'image: franksdank/loki:latest' "$compose" || { echo "loki image not local"; return 1; }
grep -q 'image: franksdank/alloy:latest' "$compose" || { echo "alloy image not local"; return 1; }
local img
for img in loki alloy; do
local cf="$REPO_ROOT/dankclaw/$img/Containerfile"
[ -f "$cf" ] || { echo "missing $cf"; return 1; }
grep -q 'busybox' "$cf" || { echo "$cf does not install busybox"; return 1; }
done
}
@test "loki and alloy healthchecks use the busybox wget" {
local compose="$REPO_ROOT/dankclaw/compose.yaml"
local n
n=$(grep -c '/usr/local/bin/busybox' "$compose")
[ "$n" -ge 2 ] || { echo "expected >=2 busybox healthchecks, found $n"; return 1; }
}
@test "alloy config uses the ui component (the server component was renamed)" {
grep -qE '^ui \{' "$REPO_ROOT/shared/alloy/config.alloy"
! grep -qE '^server \{' "$REPO_ROOT/shared/alloy/config.alloy"
}