From 6e6bdf1e57b6101b708f882c3d2f96e1f8c991a1 Mon Sep 17 00:00:00 2001 From: tinkle-community Date: Tue, 6 Jan 2026 18:31:39 +0800 Subject: [PATCH] refactor: simplify Railway deployment using existing GHCR images - Use multi-stage build from existing backend/frontend images - Remove supervisord, use simple shell script - Single process model: backend runs in background, nginx foreground - Auto-generate encryption keys on startup --- Dockerfile.railway | 120 +++++++++--------------------------- railway/nginx.conf.template | 32 +++------- railway/start-backend.sh | 19 ------ railway/start-nginx.sh | 19 ------ railway/start.sh | 41 ++++++++++++ railway/supervisord.conf | 26 -------- 6 files changed, 76 insertions(+), 181 deletions(-) delete mode 100644 railway/start-backend.sh delete mode 100644 railway/start-nginx.sh create mode 100644 railway/start.sh delete mode 100644 railway/supervisord.conf diff --git a/Dockerfile.railway b/Dockerfile.railway index c36fecc9..b5d307aa 100644 --- a/Dockerfile.railway +++ b/Dockerfile.railway @@ -1,107 +1,43 @@ -# NOFX Railway Deployment -# All-in-one Dockerfile for one-click Railway deployment -# Combines backend + frontend in a single container +# Railway All-in-One: 复用现有 GHCR 镜像 +# 从现有镜像提取内容,合并到一个容器 -ARG GO_VERSION=1.25-alpine -ARG NODE_VERSION=20-alpine -ARG ALPINE_VERSION=latest -ARG TA_LIB_VERSION=0.4.0 +# 从后端镜像提取二进制 +FROM ghcr.io/nofxaios/nofx/nofx-backend:latest AS backend -# ────────────────────────────────────────────────────────────── -# Stage 1: TA-Lib Build -# ────────────────────────────────────────────────────────────── -FROM alpine:${ALPINE_VERSION} AS ta-lib-builder -ARG TA_LIB_VERSION +# 从前端镜像提取静态文件 +FROM ghcr.io/nofxaios/nofx/nofx-frontend:latest AS frontend -RUN apk update && apk add --no-cache \ - wget tar make gcc g++ musl-dev autoconf automake +# 最终镜像 +FROM alpine:latest -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 +RUN apk add --no-cache ca-certificates tzdata sqlite nginx openssl gettext -# ────────────────────────────────────────────────────────────── -# Stage 2: Backend Build (Go) -# ────────────────────────────────────────────────────────────── -FROM golang:${GO_VERSION} AS backend-builder +# 复制后端二进制 +COPY --from=backend /app/nofx /app/nofx -RUN apk update && apk add --no-cache git make gcc g++ musl-dev +# 复制 TA-Lib 库 +COPY --from=backend /usr/local/lib/libta_lib* /usr/local/lib/ +RUN ldconfig /usr/local/lib 2>/dev/null || true -COPY --from=ta-lib-builder /usr/local /usr/local +# 复制前端静态文件 +COPY --from=frontend /usr/share/nginx/html /usr/share/nginx/html WORKDIR /app -COPY go.mod go.sum ./ -RUN go mod download - -COPY . . -RUN CGO_ENABLED=1 GOOS=linux \ - CGO_CFLAGS="-D_LARGEFILE64_SOURCE" \ - go build -trimpath -ldflags="-s -w" -o nofx . - -# ────────────────────────────────────────────────────────────── -# Stage 3: Frontend Build (Node) -# ────────────────────────────────────────────────────────────── -FROM node:${NODE_VERSION} AS frontend-builder -WORKDIR /build - -COPY web/package*.json ./ -RUN npm ci - -COPY web/ ./ -RUN npm run build - -# ────────────────────────────────────────────────────────────── -# Stage 4: Runtime (All-in-one) -# ────────────────────────────────────────────────────────────── -FROM alpine:${ALPINE_VERSION} - -RUN apk update && apk add --no-cache \ - ca-certificates tzdata sqlite nginx supervisor openssl gettext - -# Copy TA-Lib -COPY --from=ta-lib-builder /usr/local /usr/local - -# Copy backend binary -WORKDIR /app -COPY --from=backend-builder /app/nofx . - -# Copy frontend build -COPY --from=frontend-builder /build/dist /usr/share/nginx/html - -# Copy Railway-specific nginx config template -COPY railway/nginx.conf.template /etc/nginx/nginx.conf.template - -# Copy nginx startup wrapper -COPY railway/start-nginx.sh /app/start-nginx.sh -RUN chmod +x /app/start-nginx.sh - -# Copy supervisor config -COPY railway/supervisord.conf /etc/supervisord.conf - -# Copy backend startup wrapper (auto-generates encryption keys) -COPY railway/start-backend.sh /app/start-backend.sh -RUN chmod +x /app/start-backend.sh - -# Create data directory RUN mkdir -p /app/data -# Railway uses PORT env var, default to 8080 -ENV PORT=8080 +# nginx 配置模板(使用 $PORT 变量) +COPY railway/nginx.conf.template /etc/nginx/nginx.conf.template -EXPOSE 8080 +# 启动脚本 +COPY railway/start.sh /app/start.sh +RUN chmod +x /app/start.sh + +ENV DB_PATH=/app/data/data.db +ENV PORT=3000 + +EXPOSE 3000 -# 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 + CMD wget --no-verbose --tries=1 --spider http://localhost:${PORT:-3000}/health || exit 1 -CMD ["supervisord", "-c", "/etc/supervisord.conf"] +CMD ["/app/start.sh"] diff --git a/railway/nginx.conf.template b/railway/nginx.conf.template index c30c8062..32cd35a6 100644 --- a/railway/nginx.conf.template +++ b/railway/nginx.conf.template @@ -1,6 +1,3 @@ -# Railway Nginx Configuration -# Frontend static files + API proxy to backend - server { listen ${PORT}; server_name _; @@ -8,45 +5,30 @@ server { root /usr/share/nginx/html; index index.html; - # Gzip compression + # Gzip 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; + gzip_types text/plain text/css application/json application/javascript text/xml; - # Frontend routes (SPA) + # SPA 路由 location / { try_files $uri $uri/ /index.html; - - # Cache static assets - location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { - expires 1y; - add_header Cache-Control "public, immutable"; - } } - # Proxy API requests to backend (runs on 8081 internally) + # API 代理到后端 location /api/ { - proxy_pass http://127.0.0.1:8081/api/; + proxy_pass http://127.0.0.1:8080/api/; proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; - proxy_cache_bypass $http_upgrade; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - - # Timeout for long-running API calls (AI inference) proxy_connect_timeout 300s; proxy_send_timeout 300s; proxy_read_timeout 300s; } - # Health check endpoint + # 健康检查 location /health { - return 200 "OK\n"; + return 200 'OK'; add_header Content-Type text/plain; - access_log off; } } diff --git a/railway/start-backend.sh b/railway/start-backend.sh deleted file mode 100644 index 72084260..00000000 --- a/railway/start-backend.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/sh -# Backend startup wrapper - generates encryption keys if not set - -# Generate RSA private key if not set -if [ -z "$RSA_PRIVATE_KEY" ]; then - echo "🔐 Generating RSA key pair..." - export RSA_PRIVATE_KEY=$(openssl genrsa 2048 2>/dev/null) - echo "✅ RSA key generated" -fi - -# Generate data encryption key if not set -if [ -z "$DATA_ENCRYPTION_KEY" ]; then - echo "🔐 Generating data encryption key..." - export DATA_ENCRYPTION_KEY=$(openssl rand -base64 32) - echo "✅ Data encryption key generated" -fi - -# Start the backend -exec /app/nofx diff --git a/railway/start-nginx.sh b/railway/start-nginx.sh deleted file mode 100644 index 5a6b56d3..00000000 --- a/railway/start-nginx.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/sh -# Nginx startup wrapper - substitutes PORT environment variable - -# Default PORT to 8080 if not set -export PORT=${PORT:-8080} - -echo "🌐 Starting nginx on port $PORT..." -echo "🔍 All environment variables with PORT:" -env | grep -i port || echo "No PORT variables found" - -# Generate nginx config from template -envsubst '${PORT}' < /etc/nginx/nginx.conf.template > /etc/nginx/http.d/default.conf - -# Show generated config for debugging -echo "📄 Generated nginx config:" -cat /etc/nginx/http.d/default.conf | head -10 - -# Start nginx -exec nginx -g "daemon off;" diff --git a/railway/start.sh b/railway/start.sh new file mode 100644 index 00000000..ecd90347 --- /dev/null +++ b/railway/start.sh @@ -0,0 +1,41 @@ +#!/bin/sh +set -e + +# 默认端口 +export PORT=${PORT:-3000} +echo "🚀 Starting NOFX on port $PORT..." + +# 生成加密密钥(如果没有设置) +if [ -z "$RSA_PRIVATE_KEY" ]; then + echo "🔐 Generating RSA key..." + export RSA_PRIVATE_KEY=$(openssl genrsa 2048 2>/dev/null) +fi + +if [ -z "$DATA_ENCRYPTION_KEY" ]; then + echo "🔐 Generating data encryption key..." + export DATA_ENCRYPTION_KEY=$(openssl rand -base64 32) +fi + +# 生成 nginx 配置 +echo "📝 Generating nginx config for port $PORT..." +envsubst '${PORT}' < /etc/nginx/nginx.conf.template > /etc/nginx/http.d/default.conf + +# 启动后端(后台运行) +echo "🔧 Starting backend on port 8080..." +/app/nofx & +BACKEND_PID=$! + +# 等待后端启动 +sleep 3 + +# 检查后端是否启动成功 +if ! kill -0 $BACKEND_PID 2>/dev/null; then + echo "❌ Backend failed to start" + exit 1 +fi + +echo "✅ Backend started (PID: $BACKEND_PID)" + +# 启动 nginx(前台运行) +echo "🌐 Starting nginx on port $PORT..." +exec nginx -g "daemon off;" diff --git a/railway/supervisord.conf b/railway/supervisord.conf deleted file mode 100644 index 8b2f95e5..00000000 --- a/railway/supervisord.conf +++ /dev/null @@ -1,26 +0,0 @@ -[supervisord] -nodaemon=true -logfile=/dev/stdout -logfile_maxbytes=0 -pidfile=/tmp/supervisord.pid - -[program:backend] -command=/app/start-backend.sh -directory=/app -autostart=true -autorestart=true -stdout_logfile=/dev/stdout -stdout_logfile_maxbytes=0 -stderr_logfile=/dev/stderr -stderr_logfile_maxbytes=0 -environment=API_SERVER_PORT="8081",DB_PATH="/app/data/data.db" - -[program:nginx] -command=/app/start-nginx.sh -autostart=true -autorestart=true -stdout_logfile=/dev/stdout -stdout_logfile_maxbytes=0 -stderr_logfile=/dev/stderr -stderr_logfile_maxbytes=0 -environment=PORT="%(ENV_PORT)s"