使用docker部署nest全栈项目

177 阅读2分钟

背景

新项目使用 nestjs + nginx + vue3 进行全栈开发,使用docker进行部署,记录下遇到的问题及解决方案。 docker 新手,欢迎大佬指正。

部署方案

使用 docker-compose 管理,部署 nestjs 后端服务和 nginx 服务器,使用 github actions 部署前端项目。

问题

ssl 证书申请

ssl 免费证书申请可以通过 Let's Encrypt 申请。 Let's Encrypt 提供了 cebert 工具,可以一行命令搞定。 同时可以使用 docker cebert 镜像部署。

// 生成证书
sudo certbot certonly --standalone -d yourdomain.com -d www.yourdomain.com
// docker 配置
certbot:
    image: certbot/certbot
    container_name: certbot
    restart: always
    volumes:
      - /etc/letsencrypt/:/etc/letsencrypt
    networks:
      - cms-network
    // 容器启动时配置证书自动更新
    entrypoint: /bin/sh -c 'trap exit TERM; while :; do sleep 12h & wait $${!}; certbot renew --webroot --webroot-path=/etc/letsencrypt --quiet --post-hook "nginx -s reload"; done;'
  • certonly 表示只生成证书,而不配置 Web 服务器。

  • --standalone 表示使用内置的临时服务器进行验证。

  • -d 用于指定要为其生成证书的域名。

注意

  • standalone 模式需要占用 80 端口,执行命令前需注意端口占用情况。
  • 证书每90天需要更新

docker 容器时区

由于大部分 Docker 镜像都是基于 Alpine,Ubuntu,Debian,CentOS 等基础镜像制作而成。

基本上都采用 UTC 时间,默认时区为零时区。

所以在使用 docker 时需要检查时区。

ps:我就是在支付时订单总是超时发现的。

    docker run -e TZ=Asia/Shanghai ...
environment:
      - TZ=Asia/Shanghai # 设置为上海时区
volumes:
# 挂载宿主机时区
      - /etc/localtime:/etc/localtime:ro

docker 网络通信

由于使用 docker-compose 进行管理,所以在配置 docker-compose.yml 文件时,需要配置网络,我使用的是桥接网络,用于各 docker 容器间进行通信。

# 定义桥接网络,用于各服务之间的通知
networks:
  cms-network:
    driver: bridge

静态资源服务

对于 nestjs 启动的静态资源服务,需要将数据挂载到服务器,以免重新部署时丢失。

volumes:
      - /root/uploads:/app/uploads

docker-compose.yml

services:
  nginx:
    image: nginx:latest
    container_name: nginx
    restart: always
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
      - /etc/letsencrypt/:/etc/letsencrypt
      - ./nginx/html:/usr/share/nginx/html
    depends_on:
      - certbot
    networks:
      - cms-network
  certbot:
    image: certbot/certbot
    container_name: certbot
    restart: always
    volumes:
      - /etc/letsencrypt/:/etc/letsencrypt
    networks:
      - cms-network
    entrypoint: /bin/sh -c 'trap exit TERM; while :; do sleep 12h & wait $${!}; certbot renew --webroot --webroot-path=/etc/letsencrypt --quiet --post-hook "nginx -s reload"; done;'
  # 构建Nodejs服务
  node:
    # 构建Node.js服务的Docker镜像
    build:
      # 设置构建上下文为当前的目录
      context: ./net-shop-back-end
      # 使用当前目录的Dockerfile进行构建
      dockerfile: Dockerfile
    environment:
      - TZ=Asia/Shanghai # 设置为上海时区
    # 加载环境变量文件 .env
    env_file:
      - ./net-shop-back-end/.env.docker
    volumes:
      - /root/uploads:/app/uploads
    #设置服务的依赖,确保在启动Node服务前先启动依赖的服务
    depends_on:
      - mysql
      - redis
    networks:
      - cms-network
  mysql:
    # 使用官方的MYSQL8.0镜像
    image: mysql:8.0
    #挂载数据卷来持久化数据库
    volumes:
      - mysql-data:/var/lib/mysql
      # 挂载本地的init.sql文件 到容器内,用于数据初始化
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql
    #设置必要的环境变量,包括mysql的root密码和默认的数据库的名称
    environment:
      MYSQL_ROOT_PASSWORD: netshop20241111mysql
      MYSQL_DATABASE: net_shop
    networks:
      - cms-network
  redis:
    image: redis:6.2
    volumes:
      - redis-data:/data
    networks:
      - cms-network

# 在宿主机内创建三个数据卷,用于持久化数据
volumes:
  mysql-data:
  redis-data:
# 定义桥接网络,用于各服务之间的通知
networks:
  cms-network:
    driver: bridge