From d12b0c01fcac051f0b49c11e7801a5bb74b2b6a0 Mon Sep 17 00:00:00 2001 From: Dmitriy Petrov Date: Sat, 9 May 2026 10:46:13 +0300 Subject: [PATCH] Use runtime base to break gateway build cycle --- .env.example | 2 ++ .gitea/workflows/gateway-build.yml | 9 ++++++- Dockerfile | 43 +++++++++++++++++++----------- Dockerfile.runtime-base | 20 ++++++++++++++ README.md | 10 +++++++ docker-compose.gateway.yml | 2 ++ scripts/build-on-107-deploy-111.sh | 6 +++-- scripts/build-runtime-base.sh | 16 +++++++++++ 8 files changed, 90 insertions(+), 18 deletions(-) create mode 100644 Dockerfile.runtime-base create mode 100755 scripts/build-runtime-base.sh diff --git a/.env.example b/.env.example index 975bf03..d36b246 100644 --- a/.env.example +++ b/.env.example @@ -1,6 +1,8 @@ PORT=3456 BASE_IMAGE=debian:bookworm-slim SINGBOX_VERSION=1.12.13 +INSTALL_RUNTIME_DEPS=true +INSTALL_SINGBOX=true PROXY_PORT=8080 PROXY_BIND_IP=0.0.0.0 TPROXY_PORT=7895 diff --git a/.gitea/workflows/gateway-build.yml b/.gitea/workflows/gateway-build.yml index 54a8359..98b76cc 100644 --- a/.gitea/workflows/gateway-build.yml +++ b/.gitea/workflows/gateway-build.yml @@ -7,7 +7,7 @@ on: env: DEPLOY_PATH: /opt/vpn-proxy - BASE_IMAGE: mirror.gcr.io/library/debian:bookworm-slim + BASE_IMAGE: vpn-proxy-runtime-base:bookworm-slim SINGBOX_VERSION: 1.12.13 jobs: @@ -39,6 +39,11 @@ jobs: echo "Base image: ${{ env.BASE_IMAGE }}" echo "Docker context: $(docker context show 2>/dev/null || true)" docker info 2>/dev/null | sed -n '/HTTP Proxy:/p;/HTTPS Proxy:/p;/Name:/p' + docker image inspect "${{ env.BASE_IMAGE }}" >/dev/null || { + echo "Runtime base image ${{ env.BASE_IMAGE }} is missing on lxc-107." + echo "Seed it once on 107 with: ./scripts/build-runtime-base.sh" + exit 1 + } echo "${{ secrets.REGISTRY_TOKEN }}" | docker login "$REGISTRY_HOST" -u "${{ gitea.actor }}" --password-stdin DOCKER_BUILDKIT=1 docker build \ @@ -46,6 +51,8 @@ jobs: --pull=false \ --build-arg BASE_IMAGE="${{ env.BASE_IMAGE }}" \ --build-arg SINGBOX_VERSION="${{ env.SINGBOX_VERSION }}" \ + --build-arg INSTALL_RUNTIME_DEPS=false \ + --build-arg INSTALL_SINGBOX=false \ -t "${IMAGE}:latest" \ -t "${IMAGE}:${{ gitea.sha }}" \ . diff --git a/Dockerfile b/Dockerfile index 2e216b1..6ef34f3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,24 +1,37 @@ ARG BASE_IMAGE=debian:bookworm-slim FROM ${BASE_IMAGE} ARG SINGBOX_VERSION=1.12.13 +ARG INSTALL_RUNTIME_DEPS=true +ARG INSTALL_SINGBOX=true COPY dist /app/dist -RUN apt-get update \ - && apt-get install -y --no-install-recommends ca-certificates curl iptables ipset iproute2 nodejs dumb-init \ - && rm -rf /var/lib/apt/lists/* +RUN if [ "${INSTALL_RUNTIME_DEPS}" = "true" ]; then \ + apt-get update \ + && apt-get install -y --no-install-recommends ca-certificates curl iptables ipset iproute2 nodejs dumb-init \ + && rm -rf /var/lib/apt/lists/*; \ + else \ + command -v dumb-init >/dev/null \ + && command -v node >/dev/null \ + && command -v iptables >/dev/null \ + && command -v ipset >/dev/null; \ + fi -RUN set -eux; \ - arch="$(dpkg --print-architecture)"; \ - case "$arch" in \ - amd64) sb_arch="amd64" ;; \ - arm64) sb_arch="arm64" ;; \ - *) echo "Unsupported architecture: $arch" >&2; exit 1 ;; \ - esac; \ - curl -fsSL "https://github.com/SagerNet/sing-box/releases/download/v${SINGBOX_VERSION}/sing-box-${SINGBOX_VERSION}-linux-${sb_arch}.tar.gz" -o /tmp/sing-box.tgz; \ - tar -xzf /tmp/sing-box.tgz -C /tmp; \ - mv "/tmp/sing-box-${SINGBOX_VERSION}-linux-${sb_arch}/sing-box" /usr/local/bin/sing-box; \ - chmod +x /usr/local/bin/sing-box; \ - rm -rf /tmp/sing-box* +RUN if [ "${INSTALL_SINGBOX}" = "true" ]; then \ + set -eux; \ + arch="$(dpkg --print-architecture)"; \ + case "$arch" in \ + amd64) sb_arch="amd64" ;; \ + arm64) sb_arch="arm64" ;; \ + *) echo "Unsupported architecture: $arch" >&2; exit 1 ;; \ + esac; \ + curl -fsSL "https://github.com/SagerNet/sing-box/releases/download/v${SINGBOX_VERSION}/sing-box-${SINGBOX_VERSION}-linux-${sb_arch}.tar.gz" -o /tmp/sing-box.tgz; \ + tar -xzf /tmp/sing-box.tgz -C /tmp; \ + mv "/tmp/sing-box-${SINGBOX_VERSION}-linux-${sb_arch}/sing-box" /usr/local/bin/sing-box; \ + chmod +x /usr/local/bin/sing-box; \ + rm -rf /tmp/sing-box*; \ + else \ + command -v sing-box >/dev/null; \ + fi WORKDIR /app COPY package.json /app/package.json diff --git a/Dockerfile.runtime-base b/Dockerfile.runtime-base new file mode 100644 index 0000000..a32a12a --- /dev/null +++ b/Dockerfile.runtime-base @@ -0,0 +1,20 @@ +ARG BASE_IMAGE=mirror.gcr.io/library/debian:bookworm-slim +FROM ${BASE_IMAGE} +ARG SINGBOX_VERSION=1.12.13 + +RUN apt-get update \ + && apt-get install -y --no-install-recommends ca-certificates curl iptables ipset iproute2 nodejs dumb-init \ + && rm -rf /var/lib/apt/lists/* + +RUN set -eux; \ + arch="$(dpkg --print-architecture)"; \ + case "$arch" in \ + amd64) sb_arch="amd64" ;; \ + arm64) sb_arch="arm64" ;; \ + *) echo "Unsupported architecture: $arch" >&2; exit 1 ;; \ + esac; \ + curl -fsSL "https://github.com/SagerNet/sing-box/releases/download/v${SINGBOX_VERSION}/sing-box-${SINGBOX_VERSION}-linux-${sb_arch}.tar.gz" -o /tmp/sing-box.tgz; \ + tar -xzf /tmp/sing-box.tgz -C /tmp; \ + mv "/tmp/sing-box-${SINGBOX_VERSION}-linux-${sb_arch}/sing-box" /usr/local/bin/sing-box; \ + chmod +x /usr/local/bin/sing-box; \ + rm -rf /tmp/sing-box* diff --git a/README.md b/README.md index 452fcaf..54abd38 100644 --- a/README.md +++ b/README.md @@ -288,6 +288,14 @@ BUILD_HOST=107 DEPLOY_HOST=111 ./scripts/build-on-107-deploy-111.sh Скрипт собирает image на `BUILD_HOST`, переносит его на `DEPLOY_HOST` через `docker save | docker load` и запускает без `docker pull`. Если `107`/`111` не являются SSH-алиасами, укажите реальные адреса, например `BUILD_HOST=root@192.168.1.107 DEPLOY_HOST=root@192.168.1.111`. +Чтобы не получать циклическую зависимость "собрать gateway можно только через уже работающий gateway", подготовьте runtime base на `107` один раз: + +```bash +./scripts/build-runtime-base.sh +``` + +После этого CI и `build-on-107-deploy-111.sh` используют локальный `vpn-proxy-runtime-base:bookworm-slim`: основная сборка gateway больше не делает `apt-get`, не качает sing-box и не обращается к Docker Hub за base image. + UI доступен на `http://:3456`. На роутере указать шлюз по умолчанию (или нужные подсети) на IP контейнера. @@ -301,6 +309,8 @@ UI доступен на `http://:3456`. | `PORT` | `3456` | Порт веб-интерфейса | | `BASE_IMAGE` | `debian:bookworm-slim` | Базовый Docker image для сборки; можно заменить на mirror | | `SINGBOX_VERSION` | `1.12.13` | Версия sing-box для Docker build | +| `INSTALL_RUNTIME_DEPS` | `true` | Устанавливать runtime-пакеты в Docker build; `false` для подготовленного runtime base | +| `INSTALL_SINGBOX` | `true` | Скачивать sing-box в Docker build; `false` для подготовленного runtime base | | `PROXY_PORT` | `8080` | HTTP/SOCKS mixed inbound | | `TPROXY_PORT` | `7895` | TProxy inbound sing-box | | `DATA_DIR` | `/var/lib/vpn-proxy` | Директория данных (volume) | diff --git a/docker-compose.gateway.yml b/docker-compose.gateway.yml index 2251763..2e76e01 100644 --- a/docker-compose.gateway.yml +++ b/docker-compose.gateway.yml @@ -6,6 +6,8 @@ services: args: BASE_IMAGE: ${BASE_IMAGE:-debian:bookworm-slim} SINGBOX_VERSION: ${SINGBOX_VERSION:-1.12.13} + INSTALL_RUNTIME_DEPS: ${INSTALL_RUNTIME_DEPS:-true} + INSTALL_SINGBOX: ${INSTALL_SINGBOX:-true} container_name: vpn-proxy-gateway network_mode: host cap_add: diff --git a/scripts/build-on-107-deploy-111.sh b/scripts/build-on-107-deploy-111.sh index 86eb847..7ecdaf0 100755 --- a/scripts/build-on-107-deploy-111.sh +++ b/scripts/build-on-107-deploy-111.sh @@ -9,9 +9,11 @@ IMAGE_NAME="${IMAGE_NAME:-vpn-proxy-gateway}" GIT_REF="$(git rev-parse --short HEAD 2>/dev/null || echo manual)" IMAGE_TAG="${IMAGE_TAG:-${GIT_REF}-$(date +%Y%m%d%H%M%S)}" GATEWAY_IMAGE="${GATEWAY_IMAGE:-${IMAGE_NAME}:${IMAGE_TAG}}" -BASE_IMAGE="${BASE_IMAGE:-mirror.gcr.io/library/debian:bookworm-slim}" +BASE_IMAGE="${BASE_IMAGE:-vpn-proxy-runtime-base:bookworm-slim}" SINGBOX_VERSION="${SINGBOX_VERSION:-1.12.13}" DOCKER_BUILD_PULL="${DOCKER_BUILD_PULL:-false}" +INSTALL_RUNTIME_DEPS="${INSTALL_RUNTIME_DEPS:-false}" +INSTALL_SINGBOX="${INSTALL_SINGBOX:-false}" echo "Build host: ${BUILD_HOST}" echo "Deploy host: ${DEPLOY_HOST}" @@ -29,7 +31,7 @@ rsync -az --delete \ echo "Building image on ${BUILD_HOST}" ssh "${BUILD_HOST}" \ - "set -e; echo 'Docker context:' \$(docker context show 2>/dev/null || true); docker info 2>/dev/null | sed -n '/HTTP Proxy:/p;/HTTPS Proxy:/p;/Name:/p'; cd '${BUILD_PATH}' && npm ci && npm run build && docker build --pull='${DOCKER_BUILD_PULL}' --build-arg BASE_IMAGE='${BASE_IMAGE}' --build-arg SINGBOX_VERSION='${SINGBOX_VERSION}' -t '${GATEWAY_IMAGE}' ." + "set -e; echo 'Docker context:' \$(docker context show 2>/dev/null || true); docker info 2>/dev/null | sed -n '/HTTP Proxy:/p;/HTTPS Proxy:/p;/Name:/p'; docker image inspect '${BASE_IMAGE}' >/dev/null || { echo 'Runtime base image ${BASE_IMAGE} is missing on ${BUILD_HOST}.'; echo 'Seed it once with: ./scripts/build-runtime-base.sh'; exit 1; }; cd '${BUILD_PATH}' && npm ci && npm run build && docker build --pull='${DOCKER_BUILD_PULL}' --build-arg BASE_IMAGE='${BASE_IMAGE}' --build-arg SINGBOX_VERSION='${SINGBOX_VERSION}' --build-arg INSTALL_RUNTIME_DEPS='${INSTALL_RUNTIME_DEPS}' --build-arg INSTALL_SINGBOX='${INSTALL_SINGBOX}' -t '${GATEWAY_IMAGE}' ." echo "Loading image into ${DEPLOY_HOST}" ssh "${BUILD_HOST}" "docker save '${GATEWAY_IMAGE}'" | ssh "${DEPLOY_HOST}" "docker load" diff --git a/scripts/build-runtime-base.sh b/scripts/build-runtime-base.sh new file mode 100755 index 0000000..bf0ea78 --- /dev/null +++ b/scripts/build-runtime-base.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +set -euo pipefail + +BASE_IMAGE="${BASE_IMAGE:-mirror.gcr.io/library/debian:bookworm-slim}" +RUNTIME_BASE_IMAGE="${RUNTIME_BASE_IMAGE:-vpn-proxy-runtime-base:bookworm-slim}" +SINGBOX_VERSION="${SINGBOX_VERSION:-1.12.13}" + +echo "Building runtime base: ${RUNTIME_BASE_IMAGE}" +echo "Source base image: ${BASE_IMAGE}" + +docker build \ + --build-arg BASE_IMAGE="${BASE_IMAGE}" \ + --build-arg SINGBOX_VERSION="${SINGBOX_VERSION}" \ + -f Dockerfile.runtime-base \ + -t "${RUNTIME_BASE_IMAGE}" \ + .