react+nest前后端docker容器化部署实践

1,144 阅读3分钟

记录一下利用docker分别部署前后端服务到阿里云,记录一下坑点

项目整体技术栈

在线demo

整体实现了网站的登录注册/权限校验/基本crud功能,其中

前端: react+axios+zustand

后端: nestjs+typeorm+mysql

login.png

crud.png

在阿里云服务器上用docker部署

前端

主要是dockerfile的配置,以及nginx的配置,这里贴一下

# 第一阶段:构建应用
FROM node:18-alpine as builder

# 创建工作目录
WORKDIR /app

# 复制package.json和yarn.lock文件
COPY package.json yarn.lock ./

# 安装yarn
# RUN npm install -g yarn

# 安装项目依赖
RUN yarn install

# 复制项目文件
COPY . .

# 构建项目
RUN yarn build


# 第二阶段:构建Nginx镜像
FROM nginx:alpine

# 复制构建的文件到nginx html目录
COPY --from=builder /app/dist /usr/share/nginx/html

# 复制nginx配置文件  !!需要替换默认的default.conf文件!!
COPY nginx.conf /etc/nginx/conf.d/default.conf

# 暴露端口 暴露内部容器端口
EXPOSE 80

# 启动nginx
CMD ["nginx", "-g", "daemon off;"]
server {
    listen 80;
    listen [::]:80;
    server_name localhost;

    root /usr/share/nginx/html;

    # Handle root location
    location / {
        try_files $uri $uri/ /index.html; # history 路由需要此配置
        index index.html index.htm;
    }

    # Handle API proxying
    location /api-x/ {
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header REMOTE-HOST $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_pass http://8.140.248.120:3000/api-x/;
        # wsl下可以直接访问host.docker.internal获取到主机Ip
        # proxy_pass http://host.docker.internal:3000/api-x/;
        
    }

    # Error pages
    error_page 404 /404.html;
    location = /404.html {}

    error_page 500 502 503 504 /50x.html;
    location = /50x.html {}
}

有几个坑点:

api转发请求的时候在nginx中不能用loaclhost: 宿主机和容器的网络是独立的,所以Localhost不能直接用,在wsl或者mac下可以用host.docker.internal,但是在ubuntu里无法使用,所以我目前直接写死的api后端服务所在ip

nginx配置需要替换default.conf文件: 在docker中拉取nginx镜像的文件目录/etc/nginx/conf.d/下有一个默认的default.conf配置,所以需要将自定义的配置覆盖掉这个文件,哟啊不然会nginx报错(因为应用到了默认的server配置)

后端

后端采用了nestjs+typeorm,数据库采用的mysql,后端dockerfile部署相对比较顺利,这里也贴一下

# 第一阶段:构建应用
FROM node:18-alpine as builder

# 设置工作目录
WORKDIR /home/nestService

# 复制package.json和yarn.lock文件
COPY package.json yarn.lock ./

# 安装项目依赖
RUN yarn install

# 复制项目文件
COPY . .

# 构建项目
RUN yarn build

# 安装PM2
RUN yarn global add pm2

# 暴露端口
EXPOSE 3000

# 设置启动命令
CMD ["pm2-runtime", "start", "dist/main.js"]

然后就是根据不同的环境连不同的数据库什么的,这里也贴一下

    ConfigModule.forRoot({
      isGlobal: true,
      envFilePath:
        process.env.NODE_ENV === 'production'
          ? path.join(process.cwd(), './env/.prod.env')
          : path.join(process.cwd(), './env/.dev.env'),
    }),

    // 接入mysql数据库
    /**
     * https://juejin.cn/post/7032079740982788132#heading-12
     * 环境配置: https://juejin.cn/post/7316202589603807259
     */
    TypeOrmModule.forRootAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: async (configService: ConfigService) => {
        const config = {
          type: 'mysql' as const, // 数据库类型
          entities: [], // 数据表实体
          host: configService.get('DB_HOST'), // 主机,默认为localhost
          port: configService.get<number>('DB_PORT', 3306), // 端口号
          username: configService.get('DB_USER', 'root'), // 用户名
          password: configService.get('DB_PASSWORD'), // 密码
          database: configService.get('DB_DATABASE'), //数据库名
          timezone: '+08:00', //服务器上配置的时区
          synchronize: true, //根据实体自动创建数据库表, 生产环境建议关闭
          //如果为true,将自动加载实体 forFeature()方法注册的每个实体都将自动添加到配置对象的实体数组中
          autoLoadEntities: true,
        };
        return config;
      },
    }),

部署

docker 构建镜像后部署就比较简单了 直接docker run 命令就行了,在控制台运行docker ps,可以看到前端和后端两个容器,说明前后端服务已经跑起来了。

docker.png

todolist

  • 前端监控数据入库: 目前前端接入了自己写的监控sdk并完成了上报,后端还没有将监控数据写入数据库

最后,有什么问题欢迎各位大佬指教交流~