From 2adc02cb3f5f320b5354bee6fb6db78787056e40 Mon Sep 17 00:00:00 2001 From: embeddevio Date: Wed, 21 Oct 2026 23:30:57 +0300 Subject: [PATCH] feat(#18): multi-stage Dockerfile (awg-tools + runtime alpine) --- deploy/Dockerfile | 77 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 deploy/Dockerfile diff --git a/deploy/Dockerfile b/deploy/Dockerfile new file mode 100644 index 0000000..e70e00c --- /dev/null +++ b/deploy/Dockerfile @@ -0,0 +1,77 @@ +# syntax=docker/dockerfile:1.7 +# +# awg-dash — единый образ: AmneziaWG gateway + policy routing + REST API + Web UI. +# Задача: #18 (эпик #15). ADR: docs/adr/002-single-image-modular-monolith.md +# +# ВАЖНО: AMNEZIAWG_GO_REF / AMNEZIAWG_TOOLS_REF по умолчанию указывают на master +# и должны быть запинены на проверенный тег/коммит перед релизом (см. #45). + +ARG GO_IMAGE=golang:1.23-alpine + +# --- stage 1: сборка awg-утилит (Go) ----------------------------------------- +FROM ${GO_IMAGE} AS go-build + +ARG AMNEZIAWG_GO_REPO=https://github.com/amnezia-vpn/amneziawg-go.git +ARG AMNEZIAWG_GO_REF=master +ARG AMNEZIAWG_TOOLS_REPO=https://github.com/amnezia-vpn/amneziawg-tools.git +ARG AMNEZIAWG_TOOLS_REF=master + +RUN apk add --no-cache git make gcc musl-dev linux-headers + +WORKDIR /src +RUN git clone --depth 1 --branch "${AMNEZIAWG_GO_REF}" "${AMNEZIAWG_GO_REPO}" amneziawg-go \ + && cd amneziawg-go && make && cp amneziawg-go /usr/local/bin/amneziawg-go + +RUN git clone --depth 1 --branch "${AMNEZIAWG_TOOLS_REF}" "${AMNEZIAWG_TOOLS_REPO}" amneziawg-tools \ + && cd amneziawg-tools/src && make && cp awg /usr/local/bin/awg + +# --- stage 2: runtime ------------------------------------------------------- +FROM alpine:3.21 AS runtime + +LABEL org.opencontainers.image.title="awg-dash" \ + org.opencontainers.image.description="AmneziaWG gateway with intelligent routing: REST API + Web UI + split-tunnel" \ + org.opencontainers.image.source="http://192.168.1.111:3000/embeddevio/awg-dash" + +# Сетевые утилиты: iptables/ip6tables, ipset, iproute2 (policy routing), dnsmasq (ADR-005), +# curl (HEALTHCHECK), tini (PID 1), su-exec (запуск API под непривилегированным пользователем). +RUN apk add --no-cache \ + bash curl iproute2 ipset iptables ip6tables dnsmasq \ + python3 py3-pip tini su-exec \ + && addgroup -S awg \ + && adduser -S -G awg -h /app awg + +ENV PATH=/opt/venv/bin:$PATH \ + PYTHONUNBUFFERED=1 \ + PYTHONDONTWRITEBYTECODE=1 \ + PYTHONPATH=/app/backend \ + AWG_CONFIG_DIR=/config \ + AWG_DATA_DIR=/data \ + AWG_RUN_DIR=/run/awg \ + AWG_API_HOST=0.0.0.0 \ + AWG_API_PORT=8080 + +WORKDIR /app +COPY requirements.txt /app/requirements.txt +RUN apk add --no-cache --virtual .build-deps gcc musl-dev python3-dev libffi-dev \ + && python3 -m venv /opt/venv \ + && /opt/venv/bin/pip install --no-cache-dir --upgrade pip \ + && /opt/venv/bin/pip install --no-cache-dir -r /app/requirements.txt \ + && apk del .build-deps + +COPY backend/ /app/backend/ +COPY --from=go-build /usr/local/bin/awg /usr/local/bin/awg +COPY --from=go-build /usr/local/bin/amneziawg-go /usr/local/bin/amneziawg-go +COPY deploy/entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh \ + && mkdir -p /config /data /run/awg \ + && chown -R awg:awg /app /config /data /run/awg + +VOLUME ["/config", "/data"] +EXPOSE 8080 + +# healthcheck ходит в /api/v1/health (задача #31). До реализации API контейнер +# будет помечаться как unhealthy — это ожидаемое состояние до #31. +HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \ + CMD curl -fsS "http://127.0.0.1:${AWG_API_PORT}/api/v1/health" >/dev/null || exit 1 + +ENTRYPOINT ["/sbin/tini", "--", "/entrypoint.sh"]