From f9524c459a305c5dd0616a28aa529b038c263270 Mon Sep 17 00:00:00 2001 From: d0lwl0b <208416493+d0lwl0b@users.noreply.github.com> Date: Thu, 30 Oct 2025 13:11:29 +0800 Subject: [PATCH] refactor(docker): restructure Docker setup with dedicated backend/frontend services --- Dockerfile | 123 --------------------------------- docker-compose.yml | 30 ++++---- docker/Dockerfile.backend | 68 ++++++++++++++++++ docker/Dockerfile.frontend | 36 ++++++++++ nginx.conf => nginx/nginx.conf | 16 +++-- web/Dockerfile | 72 ------------------- 6 files changed, 129 insertions(+), 216 deletions(-) delete mode 100644 Dockerfile create mode 100644 docker/Dockerfile.backend create mode 100644 docker/Dockerfile.frontend rename nginx.conf => nginx/nginx.conf (67%) delete mode 100644 web/Dockerfile diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 4a8d8fb4..00000000 --- a/Dockerfile +++ /dev/null @@ -1,123 +0,0 @@ -# Multi-stage build for NOFX AI Trading System -FROM golang:1.25-alpine AS backend-builder - -# Install build dependencies including TA-Lib -RUN apk update && \ - apk add --no-cache \ - git \ - make \ - gcc \ - g++ \ - musl-dev \ - wget \ - tar \ - autoconf \ - automake - -# Install TA-Lib -RUN wget http://prdownloads.sourceforge.net/ta-lib/ta-lib-0.4.0-src.tar.gz && \ - tar -xzf ta-lib-0.4.0-src.tar.gz && \ - cd ta-lib && \ - if [ "$(uname -m)" = "aarch64" ]; then \ - CONFIG_GUESS=$(find /usr/share -name config.guess | head -1) && \ - CONFIG_SUB=$(find /usr/share -name config.sub | head -1) && \ - cp "$CONFIG_GUESS" config.guess && \ - cp "$CONFIG_SUB" config.sub && \ - chmod +x config.guess config.sub; \ - fi && \ - ./configure --prefix=/usr && \ - make && \ - make install && \ - cd .. && \ - rm -rf ta-lib ta-lib-0.4.0-src.tar.gz - -# Set working directory -WORKDIR /app - -# Copy go mod files -COPY go.mod go.sum ./ - -# Download dependencies -RUN go mod download - -# Copy backend source code -COPY . . - -# Build the application -RUN CGO_ENABLED=1 GOOS=linux go build -trimpath -ldflags="-s -w" -o nofx . - -# Frontend build stage -FROM node:18-alpine AS frontend-builder - -WORKDIR /app/web - -# Copy package files -COPY web/package*.json ./ - -# Install dependencies -RUN npm ci - -# Copy frontend source -COPY web/ ./ - -# Build frontend -RUN npm run build - -# Final stage -FROM alpine:latest - -# Update package index and install runtime dependencies -RUN apk update && \ - apk add --no-cache \ - ca-certificates \ - tzdata \ - wget \ - tar \ - make \ - gcc \ - g++ \ - musl-dev \ - autoconf \ - automake - -# Install TA-Lib runtime -RUN wget http://prdownloads.sourceforge.net/ta-lib/ta-lib-0.4.0-src.tar.gz && \ - tar -xzf ta-lib-0.4.0-src.tar.gz && \ - cd ta-lib && \ - if [ "$(uname -m)" = "aarch64" ]; then \ - CONFIG_GUESS=$(find /usr/share -name config.guess | head -1) && \ - CONFIG_SUB=$(find /usr/share -name config.sub | head -1) && \ - cp "$CONFIG_GUESS" config.guess && \ - cp "$CONFIG_SUB" config.sub && \ - chmod +x config.guess config.sub; \ - fi && \ - ./configure --prefix=/usr && \ - make && \ - make install && \ - cd .. && \ - rm -rf ta-lib ta-lib-0.4.0-src.tar.gz - -# Set timezone to UTC -ENV TZ=UTC - -WORKDIR /app - -# Copy backend binary from builder -COPY --from=backend-builder /app/nofx . - -# Copy frontend build from builder -COPY --from=frontend-builder /app/web/dist ./web/dist - -# Create directories for logs and data -RUN mkdir -p /app/decision_logs - -# Expose ports -# 8080 for backend API -EXPOSE 8080 - -# Health check -HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \ - CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1 - -# Run the application -CMD ["./nofx"] diff --git a/docker-compose.yml b/docker-compose.yml index e273f511..dad8b7a4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,11 +1,11 @@ version: '3.8' services: - # NOFX Trading Backend + # Backend service (API and core logic) nofx: build: context: . - dockerfile: Dockerfile + dockerfile: ./docker/Dockerfile.backend container_name: nofx-trading restart: unless-stopped ports: @@ -13,10 +13,9 @@ services: volumes: - ./config.json:/app/config.json:ro - ./decision_logs:/app/decision_logs - - /etc/localtime:/etc/localtime:ro # 同步主机时间 - - frontend-dist:/app/web/dist-shared:rw # 共享前端文件 + - /etc/localtime:/etc/localtime:ro # Sync host time environment: - - TZ=${NOFX_TIMEZONE:-Asia/Shanghai} # 使用中国时区 + - TZ=${NOFX_TIMEZONE:-Asia/Shanghai} # Set timezone networks: - nofx-network healthcheck: @@ -25,26 +24,27 @@ services: timeout: 10s retries: 3 start_period: 60s - command: sh -c "cp -r /app/web/dist/* /app/web/dist-shared/ 2>/dev/null || true && exec ./nofx" - # Frontend (Nginx) + # Frontend service (static serving and proxy) nofx-frontend: - image: nginx:alpine + build: + context: . + dockerfile: ./docker/Dockerfile.frontend container_name: nofx-frontend restart: unless-stopped ports: - "${NOFX_FRONTEND_PORT:-3000}:80" - volumes: - - frontend-dist:/usr/share/nginx/html:ro - - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro networks: - nofx-network depends_on: - nofx - -volumes: - frontend-dist: + healthcheck: + test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s networks: nofx-network: - driver: bridge + driver: bridge \ No newline at end of file diff --git a/docker/Dockerfile.backend b/docker/Dockerfile.backend new file mode 100644 index 00000000..18387f67 --- /dev/null +++ b/docker/Dockerfile.backend @@ -0,0 +1,68 @@ +# docker/backend/Dockerfile + +# ═══════════════════════════════════════════════════════════════ +# NOFX Backend Dockerfile (Go + TA-Lib) +# Multi-stage build with shared TA-Lib compilation stage +# Versions extracted as ARGs for maintainability +# ═══════════════════════════════════════════════════════════════ + +ARG GO_VERSION=1.25-alpine +ARG ALPINE_VERSION=latest +ARG TA_LIB_VERSION=0.4.0 + +# ────────────────────────────────────────────────────────────── +# TA-Lib Build Stage (shared across builds) +# ────────────────────────────────────────────────────────────── +FROM alpine:${ALPINE_VERSION} AS ta-lib-builder +ARG TA_LIB_VERSION + +RUN apk update && apk add --no-cache \ + wget tar make gcc g++ musl-dev autoconf automake + +RUN wget http://prdownloads.sourceforge.net/ta-lib/ta-lib-${TA_LIB_VERSION}-src.tar.gz && \ + tar -xzf ta-lib-${TA_LIB_VERSION}-src.tar.gz && \ + cd ta-lib && \ + if [ "$(uname -m)" = "aarch64" ]; then \ + CONFIG_GUESS=$(find /usr/share -name config.guess | head -1) && \ + CONFIG_SUB=$(find /usr/share -name config.sub | head -1) && \ + cp "$CONFIG_GUESS" config.guess && \ + cp "$CONFIG_SUB" config.sub && \ + chmod +x config.guess config.sub; \ + fi && \ + ./configure --prefix=/usr/local && \ + make && make install && \ + cd .. && rm -rf ta-lib ta-lib-${TA_LIB_VERSION}-src.tar.gz + +# ────────────────────────────────────────────────────────────── +# Backend Build Stage (Go Application) +# ────────────────────────────────────────────────────────────── +FROM golang:${GO_VERSION} AS backend-builder + +RUN apk update && apk add --no-cache git make gcc g++ musl-dev + +COPY --from=ta-lib-builder /usr/local /usr/local + +WORKDIR /app +COPY go.mod go.sum ./ +RUN go mod download + +COPY . . +RUN CGO_ENABLED=1 GOOS=linux go build -trimpath -ldflags="-s -w" -o nofx . + +# ────────────────────────────────────────────────────────────── +# Runtime Stage (Minimal Executable Environment) +# ────────────────────────────────────────────────────────────── +FROM alpine:${ALPINE_VERSION} + +RUN apk update && apk add --no-cache ca-certificates tzdata + +COPY --from=ta-lib-builder /usr/local /usr/local +WORKDIR /app +COPY --from=backend-builder /app/nofx . + +EXPOSE 8080 + +HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \ + CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1 + +CMD ["./nofx"] diff --git a/docker/Dockerfile.frontend b/docker/Dockerfile.frontend new file mode 100644 index 00000000..75098bd1 --- /dev/null +++ b/docker/Dockerfile.frontend @@ -0,0 +1,36 @@ +# docker/frontend/Dockerfile + +# ═══════════════════════════════════════════════════════════════ +# NOFX Frontend Dockerfile (Node Build → Nginx Runtime) +# Versions extracted as ARGs for consistency +# ═══════════════════════════════════════════════════════════════ + +ARG NODE_VERSION=20-alpine +ARG NGINX_VERSION=alpine + +# ────────────────────────────────────────────────────────────── +# Build Stage (Node) +# ────────────────────────────────────────────────────────────── +FROM node:${NODE_VERSION} AS builder +WORKDIR /build + +COPY web/package*.json ./ +RUN npm ci + +COPY web/ ./ +RUN npm run build + +# ────────────────────────────────────────────────────────────── +# Runtime Stage (Nginx) +# ────────────────────────────────────────────────────────────── +FROM nginx:${NGINX_VERSION} + +COPY nginx/nginx.conf /etc/nginx/conf.d/default.conf +COPY --from=builder /build/dist /usr/share/nginx/html + +EXPOSE 80 + +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ + CMD wget --no-verbose --tries=1 --spider http://localhost/health || exit 1 + +CMD ["nginx", "-g", "daemon off;"] diff --git a/nginx.conf b/nginx/nginx.conf similarity index 67% rename from nginx.conf rename to nginx/nginx.conf index 4c5abf81..e09eec2c 100644 --- a/nginx.conf +++ b/nginx/nginx.conf @@ -1,3 +1,6 @@ +# nginx.conf - Extracted Nginx configuration for NOFX Frontend +# This configuration merges enhancements from provided variants: improved gzip, static asset caching, adjusted API proxy (preserving /api/ path), extended timeouts, and a static health response for frontend independence. + server { listen 80; server_name localhost; @@ -6,13 +9,13 @@ server { root /usr/share/nginx/html; index index.html; - # Gzip compression + # Gzip compression (enhanced) gzip on; gzip_vary on; gzip_min_length 1024; gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml+rss application/javascript application/json; - # Frontend routes (SPA) + # Frontend routes (SPA) with static asset caching location / { try_files $uri $uri/ /index.html; @@ -23,9 +26,9 @@ server { } } - # Proxy API requests to backend + # Proxy API requests to backend (preserves /api/ path, with timeouts) location /api/ { - proxy_pass http://nofx-trading:8080; + proxy_pass http://nofx:8080/api/; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; @@ -41,9 +44,10 @@ server { proxy_read_timeout 300s; } - # Health check endpoint + # Health check endpoint (static response for frontend health, independent of backend) location /health { return 200 "OK\n"; add_header Content-Type text/plain; + access_log off; } -} +} \ No newline at end of file diff --git a/web/Dockerfile b/web/Dockerfile deleted file mode 100644 index f7614cad..00000000 --- a/web/Dockerfile +++ /dev/null @@ -1,72 +0,0 @@ -# 构建阶段 -FROM node:20-alpine AS builder - -# 设置工作目录 -WORKDIR /app - -# 复制 package 文件 -COPY package*.json ./ - -# 安装依赖 -RUN npm ci --only=production=false - -# 复制源代码 -COPY . . - -# 构建应用 -RUN npm run build - -# 运行阶段 -FROM nginx:alpine - -# 复制自定义 nginx 配置 -COPY <