Skip to content

Commit d71e37b

Browse files
authored
docker: jemalloc in published relay image + TLS-terminated stats stack (#480)
1 parent 71ac2c1 commit d71e37b

26 files changed

Lines changed: 4513 additions & 116 deletions

.github/workflows/ci-main.yml

Lines changed: 10 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -662,43 +662,16 @@ jobs:
662662
- name: Log in to GHCR
663663
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
664664

665-
- name: Write .env
666-
working-directory: docker
667-
run: |
668-
cat > .env <<EOF
669-
DOMAIN=${DOMAIN}
670-
CERTBOT_EMAIL=gmarzot@openmoq.org
671-
MOQX_PORT=${RELAY_PORT}
672-
MOQX_ADMIN_PORT=${ADMIN_PORT}
673-
MOQX_LOG_LEVEL=0
674-
MOQX_VERBOSE=0
675-
EOF
676-
sed -i 's/^[[:space:]]*//' .env
677-
678-
- name: Pull and deploy
679-
working-directory: docker
680-
run: |
681-
docker compose pull
682-
docker compose down --remove-orphans 2>/dev/null || true
683-
# Remove any stale containers with matching names (e.g. from manual docker run)
684-
docker rm -f moqx logmon 2>/dev/null || true
685-
docker compose up -d
686-
687-
echo "==> Waiting for admin endpoint..."
688-
for i in $(seq 1 30); do
689-
if curl -sf http://127.0.0.1:${ADMIN_PORT}/info >/dev/null 2>&1; then
690-
break
691-
fi
692-
sleep 1
693-
if [ "$i" -eq 30 ]; then
694-
echo "::error::Admin endpoint did not respond within 30s"
695-
docker compose logs moqx
696-
exit 1
697-
fi
698-
done
699-
700-
RESP=$(curl -sf http://127.0.0.1:8000/info)
701-
echo "==> Relay deployed: $RESP"
665+
# Auto-deploy uses the shared core (docker/relay-deploy.sh) with stats ON,
666+
# so main keeps the stats stack + public read-only dashboard live. Pulls
667+
# the compose-pinned :latest (this run's just-published image).
668+
- name: Deploy (relay + stats + public dashboard)
669+
env:
670+
ENABLE_STATS: "true"
671+
STATS_USER: ${{ secrets.STATS_USER }}
672+
STATS_PASSWORD: ${{ secrets.STATS_PASSWORD }}
673+
GRAFANA_ADMIN_PASSWORD: ${{ secrets.GRAFANA_ADMIN_PASSWORD }}
674+
run: bash docker/relay-deploy.sh
702675

703676
# ════════════════════════════════════════════════════════════════════════════
704677
# Notify: aggregate status (runs after everything)

.github/workflows/deploy-relay.yml

Lines changed: 57 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name: deploy relay
22

33
# Ops controls for the CI relay (moqx-main.ci.openmoq.org + moqx-000 alias).
4-
# Restart, redeploy with a specific image tag, or change verbose level.
4+
# Restart, redeploy with a specific image tag, or change the log level.
55

66
on:
77
workflow_dispatch:
@@ -12,25 +12,32 @@ on:
1212
type: boolean
1313
default: false
1414
image_tag:
15-
description: "Docker image tag (default: derived from branch — main→latest, release/X→X)"
15+
description: "Image tag (default derived from branch)"
1616
required: false
1717
default: ""
1818
type: string
1919
domain:
20-
description: "DNS hostname (default: derived from branch — main→moqx-main, release/X→moqx-X)"
20+
description: "Hostname (default derived from branch)"
2121
required: false
2222
default: ""
2323
type: string
24-
verbose:
25-
description: "GLOG verbose level (0=off, 1-3=increasing detail)"
24+
logging:
25+
description: "Log level (default INFO)"
2626
required: false
27-
default: "0"
27+
default: "default"
2828
type: choice
2929
options:
30-
- "0"
31-
- "1"
32-
- "2"
33-
- "3"
30+
- "default"
31+
- "WARN"
32+
- "DBG1"
33+
- "DBG2"
34+
- "DBG3"
35+
- "DBG4"
36+
enable_stats:
37+
description: "Enable stats dashboard (default enabled)"
38+
required: false
39+
type: boolean
40+
default: true
3441

3542
permissions:
3643
contents: read
@@ -65,6 +72,11 @@ jobs:
6572
6673
DOMAIN="${{ inputs.domain }}"
6774
DOMAIN="${DOMAIN:-moqx-${LABEL}.ci.openmoq.org}"
75+
# Qualify a bare label (no dot) into the CI zone, so an explicit
76+
# `domain=moqx-main` works the same as the full FQDN.
77+
if [[ "$DOMAIN" != *.* ]]; then
78+
DOMAIN="${DOMAIN}.ci.openmoq.org"
79+
fi
6880
6981
echo "image_tag=$IMAGE_TAG" >> "$GITHUB_OUTPUT"
7082
echo "domain=$DOMAIN" >> "$GITHUB_OUTPUT"
@@ -126,73 +138,52 @@ jobs:
126138
working-directory: docker
127139
env:
128140
RESTART_ONLY: ${{ inputs.restart_only }}
129-
VERBOSE: ${{ inputs.verbose }}
141+
LOGGING: ${{ inputs.logging }}
130142
DOMAIN: ${{ steps.target.outputs.domain }}
131143
IMAGE_TAG: ${{ steps.target.outputs.image_tag }}
132144
AWS_ACCESS_KEY_ID: ${{ secrets.OMOQ_CERTBOT_ACCESS_KEY_ID }}
133145
AWS_SECRET_ACCESS_KEY: ${{ secrets.OMOQ_CERTBOT_SECRET_ACCESS_KEY }}
146+
ENABLE_STATS: ${{ inputs.enable_stats }}
147+
STATS_USER: ${{ secrets.STATS_USER }}
148+
STATS_PASSWORD: ${{ secrets.STATS_PASSWORD }}
149+
GRAFANA_ADMIN_PASSWORD: ${{ secrets.GRAFANA_ADMIN_PASSWORD }}
134150
run: |
135151
if [ "$RESTART_ONLY" = "true" ]; then
136152
echo "==> Restarting relay..."
137153
docker compose restart moqx
154+
for i in $(seq 1 30); do
155+
curl -sf "http://127.0.0.1:${ADMIN_PORT}/info" >/dev/null 2>&1 && break
156+
sleep 1
157+
[ "$i" -eq 30 ] && { echo "::error::relay did not respond after restart"; docker compose logs moqx; exit 1; }
158+
done
159+
echo "==> Relay restarted: $(curl -sf http://127.0.0.1:${ADMIN_PORT}/info)"
160+
exit 0
161+
fi
162+
163+
# folly XLOG override ("default" from the dialog = no override).
164+
LOGGING_CFG=""
165+
[ "${LOGGING}" != "default" ] && LOGGING_CFG="${LOGGING}"
166+
167+
# Ensure TLS cert
168+
CERT="/etc/letsencrypt/live/${DOMAIN}/fullchain.pem"
169+
if [ ! -f "$CERT" ]; then
170+
echo "::warning::No cert for ${DOMAIN} — provisioning via Route53"
171+
sudo -E certbot certonly --dns-route53 \
172+
-d "$DOMAIN" --non-interactive --agree-tos \
173+
--email gmarzot@openmoq.org
174+
elif ! sudo openssl x509 -in "$CERT" -noout -checkend 2592000 2>/dev/null; then
175+
echo "::warning::Cert for ${DOMAIN} expires within 30 days — renewing"
176+
sudo -E certbot renew --cert-name "$DOMAIN"
138177
else
139-
# Write .env
140-
cat > .env <<EOF
141-
DOMAIN=${DOMAIN}
142-
CERTBOT_EMAIL=gmarzot@openmoq.org
143-
MOQX_PORT=${RELAY_PORT}
144-
MOQX_ADMIN_PORT=${ADMIN_PORT}
145-
MOQX_LOG_LEVEL=0
146-
MOQX_VERBOSE=${VERBOSE}
147-
EOF
148-
sed -i 's/^[[:space:]]*//' .env
149-
150-
# Ensure TLS cert
151-
CERT="/etc/letsencrypt/live/${DOMAIN}/fullchain.pem"
152-
if [ ! -f "$CERT" ]; then
153-
echo "::warning::No cert for ${DOMAIN} — provisioning via Route53"
154-
sudo -E certbot certonly --dns-route53 \
155-
-d "$DOMAIN" --non-interactive --agree-tos \
156-
--email gmarzot@openmoq.org
157-
elif ! sudo openssl x509 -in "$CERT" -noout -checkend 2592000 2>/dev/null; then
158-
echo "::warning::Cert for ${DOMAIN} expires within 30 days — renewing"
159-
sudo -E certbot renew --cert-name "$DOMAIN"
160-
else
161-
echo "Cert for ${DOMAIN} is valid"
162-
fi
163-
164-
# Pull and deploy
165-
docker pull "ghcr.io/${{ github.repository }}:${IMAGE_TAG}"
166-
167-
IMAGE="ghcr.io/${{ github.repository }}:${IMAGE_TAG}"
168-
if [ "$IMAGE_TAG" != "latest" ]; then
169-
docker tag "$IMAGE" "ghcr.io/${{ github.repository }}:latest"
170-
fi
171-
172-
echo "==> Stopping existing container (if any)..."
173-
docker compose down --remove-orphans 2>/dev/null || true
174-
docker rm -f moqx logmon 2>/dev/null || true
175-
176-
echo "==> Starting relay on ${DOMAIN}:${RELAY_PORT}..."
177-
docker compose up -d
178+
echo "Cert for ${DOMAIN} is valid"
178179
fi
179180
180-
# Health check (both paths)
181-
echo "==> Waiting for admin endpoint..."
182-
for i in $(seq 1 30); do
183-
if curl -sf http://127.0.0.1:${ADMIN_PORT}/info >/dev/null 2>&1; then
184-
break
185-
fi
186-
sleep 1
187-
if [ "$i" -eq 30 ]; then
188-
echo "::error::Admin endpoint did not respond within 30s"
189-
docker compose logs moqx
190-
exit 1
191-
fi
192-
done
193-
194-
RESP=$(curl -sf http://127.0.0.1:${ADMIN_PORT}/info)
195-
echo "==> Relay running: $RESP"
181+
# Deploy via the shared core (same script ci-main uses): writes .env,
182+
# pulls IMAGE_TAG (retagged :latest for compose), brings up the relay
183+
# + stats + public dashboard when ENABLE_STATS, health check, publish.
184+
PULL_IMAGE="ghcr.io/${{ github.repository }}:${IMAGE_TAG}" \
185+
MOQX_LOGGING="$LOGGING_CFG" \
186+
bash relay-deploy.sh
196187
197188
- name: Notify Slack
198189
if: always()

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,4 @@ docker/cloudflare.ini
3838

3939
# gh-pages dashboard (lives on gh-pages branch, not main)
4040
/gh-pages/
41+
docker/grafana/provisioning/dashboards/archive/

.gitmodules

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22
path = deps/moxygen
33
url = git@github.com:openmoq/moxygen.git
44
branch = main
5-
ignore = untracked
5+
# "all": pointer drift never shows in status/diff, so bulk git add can't
6+
# sweep an accidental pin bump into a feature commit. Intentional bumps
7+
# (sync PRs) still stage with an explicit `git add deps/moxygen`.
8+
ignore = all
69
[submodule "deps/catapult"]
710
path = deps/catapult
811
url = https://github.com/Quicr/catapult.git

docker/.env.example

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,47 @@ MOQX_PORT=4433
1010
# Admin HTTP port (localhost only)
1111
MOQX_ADMIN_PORT=8000
1212

13-
# Logging: 0=INFO 1=WARNING 2=ERROR 3=FATAL
14-
MOQX_LOG_LEVEL=0
13+
# Log level for the whole stack via folly XLOG (empty = baseline INFO).
14+
# e.g. DBG2, WARN, or a folly config like INFO,quic=WARN. moqx promotes this to
15+
# folly's FOLLY_LOGGING env var internally. See docs/logging.md.
16+
# MOQX_LOGGING=
1517

16-
# Verbose/debug level: 0=off, 1-4=increasing detail
17-
# MOQX_VERBOSE=0
18+
# jemalloc allocator for the relay (~10% speedup). Default: auto (enabled).
19+
# Set to off/false/0 to use the system allocator, or a path to force a lib.
20+
# MOQX_JEMALLOC=auto
1821

1922
# Dozzle log viewer port (localhost only)
2023
# MOQX_LOG_PORT=9999
2124

2225
# Per-module verbose logging (e.g. MoQSession=4,MoQForwarder=4)
2326
# GLOG_vmodule=
2427

28+
# --- Stats stack (compose profile: stats) ---
29+
# nginx terminates TLS (reusing the relay's Let's Encrypt cert for ${DOMAIN})
30+
# and reverse-proxies Grafana, Prometheus, the admin endpoints, and logs behind
31+
# HTTP basic auth. Bring it up with: docker compose --profile stats up -d
32+
#
33+
# Basic-auth credentials for the proxied screens (required for the stats profile)
34+
STATS_USER=admin
35+
STATS_PASSWORD=change-me
36+
# Host bind address for the stats frontend. Default 127.0.0.1 = tunnel-only
37+
# (reach via `ssh -L 8443:localhost:443 <host>`); set 0.0.0.0 to expose publicly.
38+
# STATS_BIND=127.0.0.1
39+
# Published HTTPS / HTTP ports for the stats frontend
40+
# STATS_HTTPS_PORT=443
41+
# STATS_HTTP_PORT=80
42+
# Grafana admin login (Grafana's own console)
43+
GRAFANA_ADMIN_USER=admin
44+
GRAFANA_ADMIN_PASSWORD=change-me
45+
# Prometheus retention: time window and a hard on-disk size cap (whichever hits
46+
# first wins). Relay metrics are fixed-cardinality, so 3s scrape × 365d is a few
47+
# GB/yr — comfortably under the 10G cap.
48+
# PROMETHEUS_RETENTION=365d
49+
# PROMETHEUS_RETENTION_SIZE=10GB
50+
# TSDB backing store. Default = named volume (survives redeploys, lives under
51+
# /var/lib/docker). Set an absolute host path to pin it on a dedicated disk.
52+
# PROMETHEUS_DATA_DIR=prometheus-data
53+
2554
# --- Route53 credentials (certbot-route53 profile only) ---
2655
# AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
2756
# AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY

docker/Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ FROM debian:bookworm-slim
3232
RUN apt-get update && apt-get install -y --no-install-recommends \
3333
libunwind8 libsodium23 libboost-context1.74.0 \
3434
libgoogle-glog0v6 libgflags2.2 libdouble-conversion3 \
35+
libjemalloc2 \
3536
curl gettext-base \
3637
&& rm -rf /var/lib/apt/lists/*
3738

0 commit comments

Comments
 (0)