Docker images / prepare-release (push) Successful in 5s
Docker images / backend-image (push) Successful in 3m23s
Docker images / frontend-image (push) Successful in 2m1s
Docker images / notify-webhook (push) Skipped
Docker images / updater-image (push) Successful in 38s
Docker images / publish-release (push) Successful in 6s
Единый стек CDN+MM, вариант рядом с CDN Traefik и CLI-скрипт запуска. Co-authored-by: Cursor <cursoragent@cursor.com>
182 lines
6.1 KiB
Bash
182 lines
6.1 KiB
Bash
#!/usr/bin/env bash
|
|
# Run MikrotikManager beside an already-running CDNManager Traefik stack.
|
|
# Does NOT start a second Traefik — attaches frontend to network `cdnmanager`.
|
|
#
|
|
# Usage (on the VPS):
|
|
# curl -fsSL -o /tmp/run-beside-cdn-traefik.sh \
|
|
# https://git.shx.one/denozord/MikrotikManager/raw/branch/main/deploy/run-beside-cdn-traefik.sh
|
|
# chmod +x /tmp/run-beside-cdn-traefik.sh
|
|
# sudo MM_DOMAIN=mm.shnt.top /tmp/run-beside-cdn-traefik.sh
|
|
#
|
|
# Or copy this file to the server and run it.
|
|
#
|
|
# Env overrides:
|
|
# MM_DOMAIN=mm.shnt.top
|
|
# CORS_ORIGIN=https://mm.shnt.top
|
|
# MM_ROOT=/opt/mmapp
|
|
# MM_BACKEND_IMAGE_TAG=latest
|
|
# MM_FRONTEND_IMAGE_TAG=latest
|
|
# MM_UPDATER_IMAGE_TAG=latest
|
|
# REGISTRY_USERNAME=… REGISTRY_PASSWORD=… # optional private pull
|
|
|
|
set -euo pipefail
|
|
|
|
MM_DOMAIN="${MM_DOMAIN:-mm.shnt.top}"
|
|
CORS_ORIGIN="${CORS_ORIGIN:-https://${MM_DOMAIN}}"
|
|
MM_ROOT="${MM_ROOT:-/opt/mmapp}"
|
|
MM_BACKEND_IMAGE_TAG="${MM_BACKEND_IMAGE_TAG:-latest}"
|
|
MM_FRONTEND_IMAGE_TAG="${MM_FRONTEND_IMAGE_TAG:-latest}"
|
|
MM_UPDATER_IMAGE_TAG="${MM_UPDATER_IMAGE_TAG:-latest}"
|
|
REGISTRY="${REGISTRY:-git.shx.one}"
|
|
|
|
BACKEND_IMAGE="${REGISTRY}/denozord/mikrotikmanager-backend:${MM_BACKEND_IMAGE_TAG}"
|
|
FRONTEND_IMAGE="${REGISTRY}/denozord/mikrotikmanager-frontend:${MM_FRONTEND_IMAGE_TAG}"
|
|
UPDATER_IMAGE="${REGISTRY}/denozord/mikrotikmanager-updater:${MM_UPDATER_IMAGE_TAG}"
|
|
|
|
echo "==> Check CDNManager Traefik network"
|
|
if ! docker network inspect cdnmanager >/dev/null 2>&1; then
|
|
echo "ERROR: Docker network 'cdnmanager' not found." >&2
|
|
echo "Start CDNManager Traefik stack first (deploy/docker-compose.traefik.yml)." >&2
|
|
exit 1
|
|
fi
|
|
if ! docker inspect cdnmanager-traefik >/dev/null 2>&1; then
|
|
echo "WARN: container 'cdnmanager-traefik' not found — labels may not be routed." >&2
|
|
fi
|
|
|
|
echo "==> Prepare dirs under ${MM_ROOT}"
|
|
mkdir -p "${MM_ROOT}/data" "${MM_ROOT}/state" "${MM_ROOT}/updater"
|
|
|
|
TARGETS="${MM_ROOT}/updater/targets.json"
|
|
if [[ ! -f "${TARGETS}" ]]; then
|
|
cat >"${TARGETS}" <<'EOF'
|
|
{
|
|
"targets": [
|
|
{
|
|
"id": "backend",
|
|
"container_name": "mmapp-backend",
|
|
"image": "git.shx.one/denozord/mikrotikmanager-backend:latest",
|
|
"health": {
|
|
"type": "http",
|
|
"url": "http://backend:8000/health",
|
|
"expect_status": 200
|
|
}
|
|
},
|
|
{
|
|
"id": "frontend",
|
|
"container_name": "mmapp-frontend",
|
|
"image": "git.shx.one/denozord/mikrotikmanager-frontend:latest",
|
|
"health": {
|
|
"type": "http",
|
|
"url": "http://frontend:3000/dashboard",
|
|
"expect_status": 200
|
|
}
|
|
}
|
|
]
|
|
}
|
|
EOF
|
|
echo " wrote ${TARGETS}"
|
|
fi
|
|
|
|
echo "==> Ensure internal network mmapp"
|
|
docker network inspect mmapp >/dev/null 2>&1 || docker network create mmapp >/dev/null
|
|
|
|
if [[ -n "${REGISTRY_USERNAME:-}" && -n "${REGISTRY_PASSWORD:-}" ]]; then
|
|
echo "==> docker login ${REGISTRY}"
|
|
echo "${REGISTRY_PASSWORD}" | docker login "${REGISTRY}" -u "${REGISTRY_USERNAME}" --password-stdin
|
|
fi
|
|
|
|
echo "==> Pull images"
|
|
docker pull "${BACKEND_IMAGE}"
|
|
docker pull "${FRONTEND_IMAGE}"
|
|
docker pull "${UPDATER_IMAGE}"
|
|
|
|
stop_rm() {
|
|
local name="$1"
|
|
if docker inspect "${name}" >/dev/null 2>&1; then
|
|
docker stop "${name}" >/dev/null || true
|
|
docker rm "${name}" >/dev/null || true
|
|
fi
|
|
}
|
|
|
|
echo "==> Recreate mmapp-backend"
|
|
stop_rm mmapp-backend
|
|
docker run -d \
|
|
--name mmapp-backend \
|
|
--restart unless-stopped \
|
|
--network mmapp \
|
|
--network-alias backend \
|
|
-e NODE_ENV=production \
|
|
-e PORT=8000 \
|
|
-e DATABASE_PATH=/app/data/mikrotik.db \
|
|
-e "CORS_ORIGIN=${CORS_ORIGIN}" \
|
|
-v "${MM_ROOT}/data:/app/data" \
|
|
--label mmapp.updater.managed=true \
|
|
--label mmapp.updater.target=backend \
|
|
--label "mmapp.updater.image=${BACKEND_IMAGE}" \
|
|
--health-cmd="node -e \"fetch('http://127.0.0.1:8000/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))\"" \
|
|
--health-interval=30s \
|
|
--health-timeout=5s \
|
|
--health-retries=3 \
|
|
--health-start-period=15s \
|
|
--log-driver json-file \
|
|
--log-opt max-size=10m \
|
|
--log-opt max-file=3 \
|
|
"${BACKEND_IMAGE}"
|
|
|
|
echo "==> Recreate mmapp-frontend (mmapp + cdnmanager)"
|
|
stop_rm mmapp-frontend
|
|
docker run -d \
|
|
--name mmapp-frontend \
|
|
--restart unless-stopped \
|
|
--network mmapp \
|
|
--network-alias frontend \
|
|
-e BACKEND_INTERNAL_URL=http://backend:8000 \
|
|
--label mmapp.updater.managed=true \
|
|
--label mmapp.updater.target=frontend \
|
|
--label "mmapp.updater.image=${FRONTEND_IMAGE}" \
|
|
--label traefik.enable=true \
|
|
--label traefik.docker.network=cdnmanager \
|
|
--label "traefik.http.routers.mmapp.rule=Host(\`${MM_DOMAIN}\`)" \
|
|
--label traefik.http.routers.mmapp.entrypoints=websecure \
|
|
--label traefik.http.routers.mmapp.tls=true \
|
|
--label traefik.http.routers.mmapp.tls.certresolver=letsencrypt \
|
|
--label traefik.http.services.mmapp.loadbalancer.server.port=3000 \
|
|
--health-cmd="node -e \"fetch('http://127.0.0.1:3000/dashboard').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))\"" \
|
|
--health-interval=30s \
|
|
--health-timeout=5s \
|
|
--health-retries=3 \
|
|
--health-start-period=25s \
|
|
--log-driver json-file \
|
|
--log-opt max-size=10m \
|
|
--log-opt max-file=3 \
|
|
"${FRONTEND_IMAGE}"
|
|
|
|
docker network connect cdnmanager mmapp-frontend
|
|
|
|
echo "==> Recreate mmapp-updater"
|
|
stop_rm mmapp-updater
|
|
docker run -d \
|
|
--name mmapp-updater \
|
|
--restart unless-stopped \
|
|
--network mmapp \
|
|
-e "REGISTRY=${REGISTRY}" \
|
|
-e "REGISTRY_USERNAME=${REGISTRY_USERNAME:-}" \
|
|
-e "REGISTRY_PASSWORD=${REGISTRY_PASSWORD:-}" \
|
|
-e "POLL_INTERVAL_SECONDS=${POLL_INTERVAL_SECONDS:-300}" \
|
|
-e "HEALTH_TIMEOUT_SECONDS=${HEALTH_TIMEOUT_SECONDS:-120}" \
|
|
-e "STOP_TIMEOUT_SECONDS=${STOP_TIMEOUT_SECONDS:-30}" \
|
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
|
-v "${MM_ROOT}/state:/state" \
|
|
-v "${TARGETS}:/etc/updater/targets.json:ro" \
|
|
--log-driver json-file \
|
|
--log-opt max-size=10m \
|
|
--log-opt max-file=3 \
|
|
"${UPDATER_IMAGE}"
|
|
|
|
echo
|
|
echo "OK. UI: https://${MM_DOMAIN}"
|
|
echo "DNS: A/AAAA for ${MM_DOMAIN} → this VPS, Cloudflare proxy OFF (DNS only)."
|
|
echo "Check: curl -fsS https://${MM_DOMAIN}/health"
|
|
echo " docker ps --filter name=mmapp-"
|
|
echo " docker network inspect cdnmanager --format '{{range .Containers}}{{.Name}} {{end}}'"
|