前言: ubuntu服务器现在已经用docker启动了一个sql server了,我现在想把本地的koa+sql server+react项目部署到ubuntu中,react前端直接打包上传到服务器通过nginx去访问,后端的话通过docker来部署要怎么搞,主要数据库方面不知道要怎么弄?
目前是:
-
✅ Ubuntu 服务器已经用 Docker 跑了一个 SQL Server
-
✅ 本地有:
- Koa(Node 后端)
- React(前端)
- SQL Server(本地开发用)
你想要的最终形态是:
arduino
浏览器
↓
Nginx(9527 或 80)
↓
React 静态文件
↓
/api → Nginx → Koa(Docker)
↓
SQL Server(Docker)
这是完全正确、也是推荐的部署方式。
总体推荐架构
前端不用 Docker,后端 + 数据库用 Docker,Nginx 做统一入口
arduino
Ubuntu
├─ nginx(宿主机)
│
├─ /opt/translationfront/ ← React build
│
├─ docker
│ ├─ sqlserver 容器
│ └─ koa-backend 容器
│ |
│ └── 通过 Docker 网络访问 SQL Server
React 前端
本地打包
arduino
npm run build
注意 package.json:
json
{
"homepage": "translationfront"
}
注意app.js
xml
<Router basename="/translationfront">
<Routes>
<Route path="/" element={<Home />} />
<Route path="*" element={<NotFound />} />
</Routes>
</Router>
👉 前端这部分已经结束。
重点来了:后端 + SQL Server 怎么搞(核心)
你已经在 Ubuntu 上用 Docker 跑了 SQL Server
👉 这是非常好的事情
关键原则(先记住)
❗ 后端不要再起一个新的 SQL Server
👉 Koa 容器直接连"已经存在的 SQL Server 容器"
最关键的一步:Docker 网络(解决数据库问题)
实际部署过程中发现不需要处理数据库这一步,自动就连上了数据库。
❌ 新手最常犯的错
- 用
localhost - 用服务器公网 IP
- 端口乱暴露
正确做法:创建一个 docker 网络(只需一次)
lua
docker network create oa-net
把 SQL Server 加入这个网络
arduino
docker network connect oa-net mssql
新建Dockerfile.backend
bash
FROM node:24-alpine
WORKDIR /app
# 复制 package.json 和 package-lock.json 到镜像中
COPY package*.json ./
# 安装生产所需的依赖包,跳过开发依赖;--omit=dev忽略开发依赖;npm ci就是npm clean install
# RUN npm ci --omit=dev
# 安装项目依赖,不跳过开发依赖
RUN npm ci
# 复制 server 目录到镜像中
COPY server/ ./server/
# 创建用户和组,并设置权限
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001 && \
chown -R nodejs:nodejs /app
USER nodejs
EXPOSE 6051
ENV NODE_ENV=production
ENV PORT=6051
# 指定启动命令
CMD ["node", "server/server.js"]
新建docker-compose.yml
yaml
#version: '3.3'
services:
backend:
build:
context: .
dockerfile: Dockerfile.backend
ports:
- "6051:6051"
restart: unless-stopped
需要上传到服务器的目录文件如下:
csharp
server
build #上传到服务器之后改个名字,如:translationfront
package-lock.json
package.json
Dockerfile.backend
docker-compose.yml
服务器目录结构如下:
lua
Ubuntu服务器
├─ /opt
|---ec-translation
|
translationfront ← React build
|
server
|
Dockerfile.backend
|
docker-compose.yml
|
package.json
|
package-lock.json
nginx配置
ini
server{
listen 9527;
server_name oa.jt-ele.com;
absolute_redirect off;
access_log /var/log/nginx/oa.jt-ele.com-port-9527.access.log;
error_log /var/log/nginx/oa.jt-ele.com-port-9527.error.log;
#ai翻译应用前端
location /translationfront {
root /opt/ec-translation;
index index.html;
try_files $uri $uri/ /translationfront/index.html;
}
#ai翻译应用api服务:代理到 Docker中的 Koa 服务
location /ecTranslationApi/ {
proxy_pass http://127.0.0.1:6050;
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_http_version 1.1;
}
}
路径映射原理:
- root:
bash
location /translationfront {
root /opt/ec-translation;
index index.html;
try_files $uri $uri/ /translationfront/index.html;
}
🔍 root 指令的路径拼接规则
📌 Nginx 的 root 是这样工作的:
root指定的是"根目录",最终文件路径 =root 路径 + 完整的 URI
也就是说:
root /opt/ec-translation;- 请求
/translationfront - Nginx 实际查找的文件是:
/opt/ec-translation/translationfront
- alias
bash
location /translationfront {
alias /opt/ec-translation/translationfront;
index index.html;
try_files $uri $uri/ /translationfront/index.html;
}
🔍 alias 的规则是:
alias会 替换 location 匹配的部分
所以:
location /translationfrontalias /opt/ec-translation/translationfront- 请求
/translationfront→ 实际路径:/opt/ec-translation/translationfront
完美匹配你的文件结构
端口映射原理:为什么没做端口映射,也能访问后端api?
一句话:Nginx在公网端口上"替你转发了请求"
一张图先看全局(非常重要)
less
[ 浏览器 ]
|
| http://oa.jt-ele.com:9527/ecTranslationApi/translateText
|
[ 公网 IP : 9527 ] ←--------- 只有这个端口是公网开放的
|
[ Nginx(宿主机) ]
|
| proxy_pass http://127.0.0.1:6050
|
[ 宿主机 127.0.0.1:6050 ]
|
[ Docker 端口映射 ]
|
[ 容器内 Koa :6050 ]
👉 外网只能看到 9527
👉 6050 从来没暴露给公网
三个端口,各自"身份"是什么?
宿主机端口、容器端口、本地项目端口,这些是怎么映射的?
我们一个一个对。
本地项目端口(Koa listen 的端口)
ini
app.listen(6050);
这是:
✅ Koa 在"容器内部"监听的端口
⚠️ 它的作用范围:
- ❌ 外网访问不到
- ❌ 宿主机也访问不到
- ✅ 只在容器内部有效
容器端口(Docker EXPOSE)
yaml
EXPOSE 6050
这一步:
❗ 只是"声明"
👉 告诉 Docker:这个容器"打算用"6050
⚠️ 它不会:
- ❌ 自动暴露端口
- ❌ 自动对外开放
很多人误以为 EXPOSE 就是"开公网",这是误区。
宿主机端口(docker-compose 的 ports)
makefile
ports:
- "6050:6050"
这一步才是真正的「通道建立」:
yaml
宿主机 6050 ←→ 容器 6050
👉 但注意:
- 这是宿主机端口
- 并不等于公网端口
是否公网可访问,取决于 防火墙 / 云厂商 / Nginx
为什么"没开公网端口,也能公网访问"?
答案只有一句话:
因为 Nginx 帮你做了"中转"
你现在的真实情况(代入你的配置)
1️⃣ 公网只开放了一个端口
yaml
公网 IP : 9527 ✅
云服务器安全组 / 穿透 / 防火墙只放行了:
yaml
9527
2️⃣ Nginx 监听这个公网端口
arduino
server {
listen 9527;
location /ecTranslationApi/ {
proxy_pass http://127.0.0.1:6050/;
}
}
👉 Nginx 是跑在 宿主机上的
3️⃣ 请求"被转发"到了本地 6050
Nginx → 127.0.0.1:6050
这里的 127.0.0.1 是:
宿主机自己
4️⃣ Docker 再把请求送进容器
yaml
宿主机 6050 → 容器 6050 → Koa
所以"公网访问后端"的真相是
公网访问的根本不是 Koa
公网访问的是 Nginx
Koa 永远躲在内网里
这就是标准生产架构
为什么这种架构是"最安全、最推荐的"?
对比一下 👇
❌ 错误/危险做法
makefile
ports:
- "6050:6050"
并且:
- 云防火墙直接放行 6050
后果:
- 外网可以直接打你 Koa
- 没限流
- 没日志
- 没鉴权
- 极易被扫端口
✅ 你现在的做法(正确)
yaml
公网:只开 9527
Nginx:统一入口
后端:内网服务
优势:
- ✅ 所有接口都能统一加鉴权 / 日志 / 限流
- ✅ 后端端口永不暴露
- ✅ 可以随意改后端端口,不影响前端
- ✅ 可以挂多个后端服务