Nodejs项目前后端放入一个docker容器中运行

前言:最近结合bolt AI开发了一个工具管理系统,详情见ToolsManager,用的是nodejs开发的前后端,本来想部署在NAS上,前端部署比较方便,后端部署nas上没有现成的管理界面,导致每次启动和关闭都要用SSH连接并且输入指令,感觉有点麻烦,于是就有了把前后端同时放入一个docker容器内运行的想法,思路如下:

1、环境准备:

docker相关:Windows环境下的docker desktop,docker版本为25.0.3。

使用到的docker镜像:node:18-alpine(轻量化的node环境),nginx:alpine(轻量化的nginx环境)。

docker镜像保存和导入

docker save -o 【文件名】 【镜像id】

导入我用的群晖的Container Manager直接导入的,注意有的镜像导入成功后没显示是因为镜像信息是,只需要在命令行中用一下代码改名即可

docker images

docker tag 【镜像id】 【镜像名字】

文件目录结构:

└─ToolsManager
    ├─backend
    │  ├─【后端文件】  
    ├─frontend  
    │  ├─dist 
    │  │  ├─【前端build文件】
    │  ├─【前端其他文件】
    ├─Dockerfile【docker build文件】  
    └─nginx.conf【nginx 配置文件】   

2、为什么用Nginx代理?

如果直接将前端和后端代码仍进docker里面单独运行,当外部访问前端端口时,如果前端直接用127.0.0.1或者localhost访问就会出现跨域问题,此时的解决办法只能将后端端口也给映射出去,如图所示。

image-20250116122619415

但是实际使用时基本上不会直接操作后端,没必要把后端暴露出来。

既然前端不能通过本地ip直接访问后端,那么加一个中间人转接呢?于是就有了nginx代理的方法,如下图所示:

image-20250116122842921

这样做的好处就是前端通过代理直接访问本地后端,不用将后端端口暴露出去了。

3、相关文件配置

(1)前端

配置url的时候什么都不输入就好了,我就改了下面三处:

/src/config/api.ts

baseUrl: import.meta.env.VITE_API_URL || ''

.env

VITE_API_URL=

.env.example

VITE_API_URL=

(2)后端

只需要设置正确的端口即可。

/src/index.js

const PORT = process.env.PORT || 3000;

.env

PORT=3000

(3)nginx.conf文件


user  nginx;
worker_processes  auto;

error_log  /var/log/nginx/error.log notice;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;
    server {
        listen 3030;
        server_name 【外部访问的ip或者域名】;

        location / {
            root /usr/share/nginx/html;
            index index.html index.htm;
            try_files $uri $uri/ /index.html;
        }
		location /api/ {
            proxy_pass http://localhost:3000/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;
        }
    }
    #gzip  on;

    include /etc/nginx/conf.d/*.conf;
}

(4)Dockerfile文件

docker构建指令

cd 【Dockerfile所在目录】
docker build -t my-app .

Dockerfile

# 使用 node:18-alpine 作为基础镜像
FROM node:18-alpine AS nodejs

# 设置工作目录
WORKDIR /app

# 复制后端文件到容器中
COPY backend/ .

# 安装依赖并构建(如果需要)
RUN npm install
# 如果需要构建,取消下面的注释
# RUN npm run build

# 使用 nginx:alpine 作为基础镜像
FROM nginx:alpine

# 从 nodejs 阶段复制后端文件
COPY --from=nodejs /app /app

# 复制前端文件到 Nginx 的默认服务目录
COPY frontend/dist/ /usr/share/nginx/html/

# 复制自定义的 Nginx 配置文件(如果需要)
COPY nginx.conf /etc/nginx/nginx.conf

# 安装 Node.js 运行时
RUN apk add --no-cache nodejs

# 暴露前端端口
# EXPOSE 3000
EXPOSE 3030

# 启动 Nginx 和 Node.js 应用
CMD nginx && node /app/src/index.js

4、成功示例

image-20250116124624324