From 87d6dcc293f4cc3c9e42d3f616b110466aa3a6c1 Mon Sep 17 00:00:00 2001 From: zimy Date: Thu, 14 May 2026 15:06:15 +0800 Subject: [PATCH] refactor: extract nginx to standalone nginx-gateway deployment - Remove coze-web service from docker-compose.yml - Replace nginx second stage in frontend/Dockerfile with alpine dist-only stage - Add nginx-gateway/ with standalone nginx container deployment - docker-compose.yml joining both coze-network and kong-net - Split nginx configs into 6 per-domain files: - 00-upstreams.conf (shared upstreams) - 10-default-server.conf (catch-all 444) - 20-coze.conf (coze studio) - 30-kong-api.conf (kong ai gateway) - 40-admin-portal.conf (admin portal) - 50-grafana.conf (grafana dashboard) Co-Authored-By: Claude Opus 4.7 (1M context) --- docker/docker-compose.yml | 22 ------ frontend/Dockerfile | 20 ++---- nginx-gateway/conf.d/00-upstreams.conf | 30 ++++++++ nginx-gateway/conf.d/10-default-server.conf | 25 +++++++ nginx-gateway/conf.d/20-coze.conf | 76 +++++++++++++++++++++ nginx-gateway/conf.d/30-kong-api.conf | 52 ++++++++++++++ nginx-gateway/conf.d/40-admin-portal.conf | 41 +++++++++++ nginx-gateway/conf.d/50-grafana.conf | 53 ++++++++++++++ nginx-gateway/docker-compose.yml | 27 ++++++++ nginx-gateway/nginx.conf | 14 ++++ 10 files changed, 322 insertions(+), 38 deletions(-) create mode 100644 nginx-gateway/conf.d/00-upstreams.conf create mode 100644 nginx-gateway/conf.d/10-default-server.conf create mode 100644 nginx-gateway/conf.d/20-coze.conf create mode 100644 nginx-gateway/conf.d/30-kong-api.conf create mode 100644 nginx-gateway/conf.d/40-admin-portal.conf create mode 100644 nginx-gateway/conf.d/50-grafana.conf create mode 100644 nginx-gateway/docker-compose.yml create mode 100644 nginx-gateway/nginx.conf diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 2d60bee0..5426ddbb 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -436,28 +436,6 @@ services: condition: service_healthy command: ['/app/opencoze'] - coze-web: - # build: - # context: .. - # dockerfile: frontend/Dockerfile - image: cozedev/coze-studio-web:latest - container_name: coze-web - restart: always - ports: - - "8888:80" - - "443:443" # SSL port (uncomment if using SSL) - volumes: - - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro # Main nginx config - - ./nginx/conf.d/default.conf:/etc/nginx/conf.d/default.conf:ro # Proxy config - - ./nginx/ssl:/etc/nginx/ssl:ro # SSL certificates (uncomment if using SSL) - - /etc/localtime:/etc/localtime:ro - - /etc/timezone:/etc/timezone:ro - depends_on: - - coze-server - - minio - networks: - - coze-network - networks: coze-network: external: true diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 53655e50..4f69a57d 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -36,19 +36,7 @@ RUN chmod +x scripts/hooks/post-rush-install.sh && rm -rf /app/common/temp && ru # Use rush build to build the specific project RUN rush build --to @coze-studio/app -# Production image stage -FROM nginx:1.25-alpine - - -# if you located in China, you can use aliyun mirror to speed up -RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories -# Install necessary tools for SSL configuration -RUN apk add --no-cache bash - -# Copy build artifacts to nginx static directory -COPY --from=builder /app/frontend/apps/coze-studio/dist /usr/share/nginx/html - -EXPOSE 8888 - -# Start nginx -CMD ["nginx", "-g", "daemon off;"] +# Dist-only stage: 镜像仅用于提取构建产物,不运行任何服务 +FROM alpine:latest +WORKDIR /app +COPY --from=builder /app/frontend/apps/coze-studio/dist /app/dist diff --git a/nginx-gateway/conf.d/00-upstreams.conf b/nginx-gateway/conf.d/00-upstreams.conf new file mode 100644 index 00000000..0799b345 --- /dev/null +++ b/nginx-gateway/conf.d/00-upstreams.conf @@ -0,0 +1,30 @@ +# ============================================================ +# 上游定义 - 所有反向代理目标集中在此 +# ============================================================ + +# --- Coze 业务后端 (coze-network) --- +upstream coze_backend { + server coze-server:8888; + keepalive 16; +} + +upstream coze_minio { + server coze-minio:9000; + keepalive 16; +} + +# --- Kong AI Gateway 相关 (kong-ai-gateway-prod_kong-net) --- +upstream kong_proxy { + server kong:8000; + keepalive 32; +} + +upstream admin_portal { + server admin-portal:8080; + keepalive 4; +} + +upstream grafana_ui { + server grafana:3000; + keepalive 4; +} diff --git a/nginx-gateway/conf.d/10-default-server.conf b/nginx-gateway/conf.d/10-default-server.conf new file mode 100644 index 00000000..09d05b2d --- /dev/null +++ b/nginx-gateway/conf.d/10-default-server.conf @@ -0,0 +1,25 @@ +# ============================================================ +# 默认 server - 拒绝未知 host 的请求 +# 必须存在,否则未匹配的请求会落到「按文件顺序的第一个 server 块」, +# 引发「全部变成 XX 服务」的故障 +# ============================================================ + +# HTTP 80 默认:直接拒绝 +server { + listen 80 default_server; + listen [::]:80 default_server; + server_name _; + return 444; +} + +# HTTPS 443 默认:直接拒绝 +server { + listen 443 ssl http2 default_server; + listen [::]:443 ssl http2 default_server; + server_name _; + + ssl_certificate /etc/nginx/ssl/_.kejiankejian.com_chain.pem; + ssl_certificate_key /etc/nginx/ssl/_.kejiankejian.com_key.key; + + return 444; +} diff --git a/nginx-gateway/conf.d/20-coze.conf b/nginx-gateway/conf.d/20-coze.conf new file mode 100644 index 00000000..c09db976 --- /dev/null +++ b/nginx-gateway/conf.d/20-coze.conf @@ -0,0 +1,76 @@ +# ============================================================ +# Coze Studio +# 域名: testcoze.kejiankejian.com / coze-testcoze.kejiankejian.com +# ============================================================ + +# HTTP -> HTTPS +server { + listen 80; + listen [::]:80; + server_name testcoze.kejiankejian.com coze-testcoze.kejiankejian.com; + return 301 https://$host$request_uri; +} + +# HTTPS +server { + listen 443 ssl http2; + listen [::]:443 ssl http2; + server_name testcoze.kejiankejian.com coze-testcoze.kejiankejian.com; + + client_max_body_size 100M; + + ssl_certificate /etc/nginx/ssl/_.kejiankejian.com_chain.pem; + ssl_certificate_key /etc/nginx/ssl/_.kejiankejian.com_key.key; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_prefer_server_ciphers on; + ssl_session_cache shared:SSL:10m; + ssl_session_timeout 10m; + ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA256; + + access_log /dev/stdout; + error_log /dev/stderr; + + # 前端静态资源 + location / { + root /usr/share/nginx/html; + try_files $uri $uri/ /index.html; + index index.html index.htm; + add_header Cache-Control "public, max-age=3600" always; + } + + # 后端 API + location ~ ^/(api|v[1-3]|admin)(/|$) { + proxy_pass http://coze_backend; + proxy_http_version 1.1; + proxy_set_header Host $host; + 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; + proxy_connect_timeout 60s; + proxy_send_timeout 60s; + proxy_read_timeout 600s; + + sub_filter ':8889' ':8888/local_storage'; + sub_filter 'minio:9000' '$http_host/local_storage'; + sub_filter_once off; + sub_filter_types 'application/json' 'text/event-stream'; + } + + # MinIO 文件代理 + location /local_storage/ { + rewrite ^/local_storage/(.*)$ /$1 break; + proxy_pass http://coze_minio; + proxy_set_header Host coze-minio:9000; + proxy_connect_timeout 60s; + proxy_send_timeout 60s; + proxy_read_timeout 600s; + + add_header Access-Control-Allow-Origin "*" always; + add_header Access-Control-Allow-Credentials "true" always; + add_header Access-Control-Allow-Methods "*" always; + add_header Access-Control-Allow-Headers "*" always; + if ($request_method = 'OPTIONS') { + return 204; + } + } +} diff --git a/nginx-gateway/conf.d/30-kong-api.conf b/nginx-gateway/conf.d/30-kong-api.conf new file mode 100644 index 00000000..55e06a8b --- /dev/null +++ b/nginx-gateway/conf.d/30-kong-api.conf @@ -0,0 +1,52 @@ +# ============================================================ +# Kong AI Gateway - 对外 API +# 域名: api.kejiankejian.com +# ============================================================ + +# HTTP -> HTTPS +server { + listen 80; + listen [::]:80; + server_name api.kejiankejian.com; + return 301 https://$host$request_uri; +} + +# HTTPS +server { + listen 443 ssl http2; + listen [::]:443 ssl http2; + server_name api.kejiankejian.com; + + client_max_body_size 50m; + client_body_buffer_size 10m; + + ssl_certificate /etc/nginx/ssl/_.kejiankejian.com_chain.pem; + ssl_certificate_key /etc/nginx/ssl/_.kejiankejian.com_key.key; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256; + ssl_prefer_server_ciphers off; + + add_header Strict-Transport-Security "max-age=63072000" always; + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + + access_log /dev/stdout; + error_log /dev/stderr; + + location / { + proxy_pass http://kong_proxy; + proxy_http_version 1.1; + + proxy_set_header Host $host; + 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; + proxy_set_header Connection ""; + + # SSE / 流式响应 + proxy_buffering off; + proxy_cache off; + proxy_read_timeout 300s; + proxy_send_timeout 300s; + } +} diff --git a/nginx-gateway/conf.d/40-admin-portal.conf b/nginx-gateway/conf.d/40-admin-portal.conf new file mode 100644 index 00000000..27173fa1 --- /dev/null +++ b/nginx-gateway/conf.d/40-admin-portal.conf @@ -0,0 +1,41 @@ +# ============================================================ +# Admin Portal - 管理后台 +# 域名: admin.kejiankejian.com +# ============================================================ + +# HTTP -> HTTPS +server { + listen 80; + listen [::]:80; + server_name admin.kejiankejian.com; + return 301 https://$host$request_uri; +} + +# HTTPS +server { + listen 443 ssl http2; + listen [::]:443 ssl http2; + server_name admin.kejiankejian.com; + + ssl_certificate /etc/nginx/ssl/_.kejiankejian.com_chain.pem; + ssl_certificate_key /etc/nginx/ssl/_.kejiankejian.com_key.key; + ssl_protocols TLSv1.2 TLSv1.3; + + # IP 白名单 (生产建议开启) + # allow 10.0.0.0/8; + # allow 172.16.0.0/12; + # allow 192.168.0.0/16; + # deny all; + + access_log /dev/stdout; + error_log /dev/stderr; + + location / { + proxy_pass http://admin_portal; + proxy_http_version 1.1; + proxy_set_header Host $host; + 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; + } +} diff --git a/nginx-gateway/conf.d/50-grafana.conf b/nginx-gateway/conf.d/50-grafana.conf new file mode 100644 index 00000000..9412f37e --- /dev/null +++ b/nginx-gateway/conf.d/50-grafana.conf @@ -0,0 +1,53 @@ +# ============================================================ +# Grafana - 观测仪表盘 +# 域名: grafana.kejiankejian.com +# ============================================================ + +# HTTP -> HTTPS +server { + listen 80; + listen [::]:80; + server_name grafana.kejiankejian.com; + return 301 https://$host$request_uri; +} + +# HTTPS +server { + listen 443 ssl http2; + listen [::]:443 ssl http2; + server_name grafana.kejiankejian.com; + + ssl_certificate /etc/nginx/ssl/_.kejiankejian.com_chain.pem; + ssl_certificate_key /etc/nginx/ssl/_.kejiankejian.com_key.key; + ssl_protocols TLSv1.2 TLSv1.3; + + # IP 白名单 (生产建议开启) + # allow 10.0.0.0/8; + # allow 172.16.0.0/12; + # allow 192.168.0.0/16; + # deny all; + + # 原配置启用了 auth_basic, 但容器内没挂载 .htpasswd, 会 500 + # 如需开启: + # 1) 在 nginx-gateway/ 下生成 .htpasswd + # 2) 在此 compose volumes 加挂: - ./.htpasswd:/etc/nginx/.htpasswd:ro + # 3) 取消下方注释 + # auth_basic "Grafana"; + # auth_basic_user_file /etc/nginx/.htpasswd; + + access_log /dev/stdout; + error_log /dev/stderr; + + location / { + proxy_pass http://grafana_ui; + proxy_http_version 1.1; + proxy_set_header Host $host; + 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; + + # Grafana Live (WebSocket) + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } +} diff --git a/nginx-gateway/docker-compose.yml b/nginx-gateway/docker-compose.yml new file mode 100644 index 00000000..38718d84 --- /dev/null +++ b/nginx-gateway/docker-compose.yml @@ -0,0 +1,27 @@ +name: nginx-gateway + +services: + nginx-gateway: + image: nginx:1.25-alpine + container_name: nginx-gateway + restart: always + ports: + - "80:80" + - "443:443" + volumes: + - ./nginx.conf:/etc/nginx/nginx.conf:ro + - ./conf.d:/etc/nginx/conf.d:ro + - ./ssl:/etc/nginx/ssl:ro + - ./dist:/usr/share/nginx/html:ro + - /etc/localtime:/etc/localtime:ro + - /etc/timezone:/etc/timezone:ro + networks: + - coze-network + - kong-net + +networks: + coze-network: + external: true + kong-net: + external: + name: kong-ai-gateway-prod_kong-net diff --git a/nginx-gateway/nginx.conf b/nginx-gateway/nginx.conf new file mode 100644 index 00000000..ffe86228 --- /dev/null +++ b/nginx-gateway/nginx.conf @@ -0,0 +1,14 @@ +worker_processes 1; +events { worker_connections 1024; } + +http { + include mime.types; + default_type application/octet-stream; + sendfile on; + keepalive_timeout 65; + + # setting client request body size limit - 0 means no limit + client_max_body_size 0; + + include /etc/nginx/conf.d/*.conf; +}