Docker Compose部署多.NET后端API+多Vue前端Web 完整记录(含多数据库扩展+实用场景,亲测无坑)

接上篇文章:Linux Docker Compose 部署.NET+Vue+MySQL+Redis+Nginx 完整记录(亲测无坑)

写在前面:在实际开发和测试中,经常会遇到需要在同一台服务器部署多个后端API(如主服务+附属服务、测试服务+生产服务)和多个前端Web(如管理端+用户端、PC端+移动端适配版)的场景。单纯靠docker run命令逐个启动容器,会面临端口冲突、配置混乱、维护困难等问题。

本文基于Docker Compose,实现「2个及以上.NET8后端API + 2个及以上Vue前端Web」的容器化部署,核心解决端口隔离、配置统一、Nginx分流等问题;同时扩展PostgreSQL、SQL Server两种数据库的替换方案,补充多个实用部署场景(如子域名访问、HTTPS、日志集中),全程亲测可复现,用于后期查阅,也希望能帮到有同样需求的同行。

适用场景:个人测试、小型项目多服务部署、企业内部多环境(测试+开发)部署,可灵活扩展服务数量,无需重新搭建整体环境。

一、部署环境准备(提前确认,避免兼容问题)

1. 服务器环境(通用,测试/小型生产均适用)

  • 系统:CentOS 7.9(最小化安装,已配置静态IP:192.168.1.100,替换为自身服务器真实IP)

  • 内存:8G(建议不低于4G,多服务同时运行需足够内存,避免卡顿)

  • 硬盘:100G(存放多个服务的镜像、打包文件、数据库数据,预留冗余)

  • 网络:能访问外网(前期拉取镜像、安装依赖,后期可断网运行;若内网服务器,提前准备离线镜像)

  • 权限:root用户(或具备Docker、目录操作权限的普通用户,生产环境建议用普通用户)

2. 软件版本(全程统一,避免兼容问题)

  • Docker:Docker CE 24.0.7(CentOS7稳定版,兼容性强)

  • Docker Compose:V2.27.1(支持新版配置,解决多服务依赖、健康检查兼容问题)

  • 后端:.NET 8(本地VS2022发布为publish文件夹,无源码,多后端需单独发布)

  • 前端:Vue3(本地yarn/npm打包为dist文件夹,无源码,多前端需单独打包)

  • 数据库(多版本可选):MySQL 8.0、PostgreSQL 15、SQL Server 2022(Docker镜像,数据持久化)

  • 缓存:Redis 7-alpine(轻量版,占用资源少,适配多服务共享/隔离缓存)

  • 代理:Nginx alpine(轻量版,实现多前端代理、多后端分流)

3. 本地准备文件(提前打包,上传到服务器)

所有文件统一放在本地易找到的目录(如桌面),后续用Xftp/WinSCP上传到服务器,避免混乱,文件清单如下:

  • 后端文件:backend1-publish、backend2-publish(2个后端的.NET8发布文件,可新增backend3-publish等,命名区分)

  • 前端文件:frontend1-dist、frontend2-dist(2个前端的Vue打包文件,可新增,命名区分)

  • 镜像tar包(离线备用):multi-service-images.tar(含所有所需镜像:后端运行时、前端代理、3种数据库、Redis,解决网络拉取超时)

  • 配置文件:

    • nginx.conf(Nginx核心配置,实现多前端/多后端分流)

    • 数据库配置:my.cnf(MySQL)、postgresql.conf(PostgreSQL)、sqlserver.conf(SQL Server,可选)

    • 数据库初始化SQL:init-mysql.sql、init-postgresql.sql、init-sqlserver.sql(对应不同数据库,创建库、表、初始化数据)

    • docker-compose.yml(核心编排文件,管理所有服务,含多数据库可选配置)

二、前期基础准备工作(必做,奠定部署基础)

无论后续用哪种数据库、部署多少服务,这部分工作都是基础,一次性做好,避免后续重复操作。

1. CentOS7系统基础配置(补充依赖,避免报错)

最小化安装的CentOS7缺少很多基础工具,先安装必要依赖,确保Docker、命令执行正常:

bash 复制代码
# 更新系统软件包(可选,建议执行,避免依赖版本过低)
yum update -y

# 安装基础工具(wget、vim、net-tools等,后续编辑配置、查看端口常用)
yum install -y wget vim net-tools epel-release

# 安装Docker依赖所需的额外工具(避免后续安装Docker失败)
yum install -y curl policycoreutils-python-utils

2. 安装Docker CE(稳定版,步骤固定)

bash 复制代码
# 1. 卸载旧版本Docker(如果之前装过,避免冲突,没装过可跳过)
yum remove -y docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine

# 2. 配置Docker官方源(CentOS7专属)
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo

# 3. 安装Docker CE 24.0.7(指定版本,避免自动更新到不稳定版本)
yum install -y docker-ce-24.0.7 docker-ce-cli-24.0.7 containerd.io

# 4. 启动Docker服务,并设置开机自启(关键,多服务需随Docker自动启动)
systemctl start docker
systemctl enable docker

# 5. 验证Docker安装成功(输出版本号即成功)
docker --version

✅ 成功标识:Docker version 24.0.7, build afdd53b

3. 配置Docker镜像加速(国内必做,避免镜像拉取超时)

Docker默认拉取国外镜像源,国内访问极慢,甚至超时,配置阿里云专属加速(比公共源更稳定)。登录阿里云官网(https://www.aliyun.com/),搜索 "容器镜像服务",进入 "镜像加速器",复制自己的专属加速地址(示例:https://xxxxxx.mirror.aliyuncs.com,替换成自己的);

bash 复制代码
# 1. 创建Docker配置目录(如果不存在)
mkdir -p /etc/docker

# 2. 写入加速配置(替换成自己的阿里云专属加速地址,获取方式:阿里云→容器镜像服务→镜像加速器)
tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors": ["https://xxxxxx.mirror.aliyuncs.com", "https://mirror.ccs.tencentyun.com"]
}
EOF

# 3. 重新加载配置,重启Docker,让加速生效
systemctl daemon-reload
systemctl restart docker

# 4. 验证加速配置是否生效(输出配置的加速地址即成功)
docker info | grep -A 2 "Registry Mirrors"

✅ 成功标识:输出中包含自己配置的阿里云加速地址,无报错。

4. 升级Docker Compose(V2版本,支持多服务配置)

CentOS7默认安装的Docker Compose是1.x版本,不支持多服务的健康检查、依赖条件等配置,升级到V2版本:

bash 复制代码
# 1. 删除旧版docker-compose(如果之前装过)
rm -f /usr/local/bin/docker-compose

# 2. 安装Docker Compose V2(插件形式,稳定,和Docker联动更好)
yum install -y docker-compose-plugin

# 3. 建立软链接,保持docker-compose命令可用(和旧版用法一致,无需改命令)
ln -s /usr/libexec/docker/cli-plugins/docker-compose /usr/local/bin/docker-compose

# 4. 验证升级成功(输出V2版本号即成功)
docker-compose --version

✅ 成功标识:Docker Compose version v2.27.1(版本号可略有差异,只要是V2即可)

5. 关闭防火墙(测试环境)/ 开放端口(生产环境)

测试环境:直接关闭防火墙,避免端口被拦截,简化配置;生产环境:仅开放所需端口,提升安全性。

bash 复制代码
# 测试环境:关闭防火墙并禁止开机自启
systemctl stop firewalld
systemctl disable firewalld
systemctl status firewalld  # 验证,输出inactive即成功

# 生产环境:开放所需端口(示例,根据自己的端口修改)
# 后端端口:58588、58589;前端端口:6866、6867;数据库端口:3306(MySQL)、5432(PostgreSQL)、1433(SQL Server);Redis端口:6379
firewall-cmd --add-port=58588/tcp --permanent
firewall-cmd --add-port=58589/tcp --permanent
firewall-cmd --add-port=6866/tcp --permanent
firewall-cmd --add-port=6867/tcp --permanent
firewall-cmd --add-port=3306/tcp --permanent
firewall-cmd --add-port=5432/tcp --permanent
firewall-cmd --add-port=1433/tcp --permanent
firewall-cmd --add-port=6379/tcp --permanent
firewall-cmd --reload  # 生效配置
firewall-cmd --list-ports  # 验证,查看开放的端口清单

6. 服务器目录结构整理(规范目录,避免后期混乱)

将本地准备的所有文件,上传到服务器的/root/multi-service-docker目录(自定义目录,方便记忆),最终目录结构如下(支持多后端、多前端、多数据库):

bash 复制代码
multi-service-docker/                # 项目根目录(所有文件都放在这里)
├── docker-compose.yml               # 核心编排文件(管理所有服务,含多数据库配置)
# 后端目录(2个,可扩展更多)
├── backend1/                        # 第一个后端API服务
│   └── publish/                     # 后端1的.NET8发布文件(含核心DLL、appsettings.json)
├── backend2/                        # 第二个后端API服务
│   └── publish/                     # 后端2的.NET8发布文件
# 前端目录(2个,可扩展更多)
├── frontend1/                       # 第一个前端Web(如管理端)
│   └── dist/                        # 前端1的Vue打包文件(index.html、css、js)
├── frontend2/                       # 第二个前端Web(如用户端)
│   └── dist/                        # 前端2的Vue打包文件
# Nginx配置目录
├── nginx/
│   └── nginx.conf                   # Nginx核心配置(多前端/多后端分流)
# 数据库配置目录(3种数据库,按需使用)
├── mysql/
│   ├── my.cnf                       # MySQL配置文件
│   └── init-mysql.sql               # MySQL初始化SQL
├── postgresql/
│   ├── postgresql.conf              # PostgreSQL配置文件
│   └── init-postgresql.sql          # PostgreSQL初始化SQL
├── sqlserver/
│   ├── sqlserver.conf               # SQL Server配置文件(可选)
│   └── init-sqlserver.sql           # SQL Server初始化SQL
# 离线镜像包(备用)
└── multi-service-images.tar         # 所有所需镜像的离线包

✅ 上传方法:用 MobaXterm/Xftp/WinSCP连接服务器(IP:服务器真实IP,账号:root,密码:自己的服务器密码),将本地准备的文件,按上述目录结构,拖到对应文件夹中即可。

三、核心方案:2个.NET8后端API + 2个Vue前端Web 基础部署(MySQL版)

先实现最基础的多服务部署(2后端+2前端+MySQL+Redis+Nginx),掌握核心逻辑后,再扩展其他数据库和场景,新手建议先按此方案部署成功,再进行扩展。

1. 编写docker-compose.yml(核心编排文件)

该文件是多服务部署的核心,管理所有容器的启动、依赖、端口、挂载等配置,注释详细,可直接复制使用,关键配置已标注:

yaml 复制代码
version: '3.8'

# 所有服务的集合(多后端、多前端、数据库、Redis、Nginx)
services:
  # === 公共依赖服务:MySQL(基础版,后续可替换为PostgreSQL/SQL Server) ===
  mysql:
    image: mysql:8.0                  # 使用MySQL 8.0镜像
    container_name: multi-mysql       # 自定义容器名,唯一,方便管理
    restart: always                   # 容器异常退出/服务器重启后,自动重启
    environment:
      MYSQL_ROOT_PASSWORD: Root@123456  # MySQL root密码(自定义,建议复杂密码)
      MYSQL_USER: appuser             # 项目访问MySQL的用户名(自定义)
      MYSQL_PASSWORD: App@123456      # 项目访问MySQL的密码(自定义)
      MYSQL_DATABASE: app_db1         # 后端1所用数据库名
      MYSQL_DATABASE2: app_db2        # 后端2所用数据库名(分库部署,可选)
      TZ: Asia/Shanghai               # 强制容器时区为东八区(解决时差问题)
    ports:
      - "3306:3306"                   # 端口映射:宿主机3306 → 容器内3306(本地工具可连接)
    volumes:
      - ./mysql/my.cnf:/etc/mysql/conf.d/my.cnf  # 挂载MySQL配置文件
      - ./mysql/init-mysql.sql:/docker-entrypoint-initdb.d/init-mysql.sql  # 挂载初始化SQL
      - mysql-data:/var/lib/mysql     # 数据卷:持久化MySQL数据(容器删除不丢失)
      - /etc/localtime:/etc/localtime:ro  # 挂载宿主机时区文件,双重保障时差
    command: --lower_case_table_names=1  # MySQL不区分大小写(避免表名大小写问题)
    networks:
      - multi-service-network         # 加入自定义网络,实现所有容器互通
    # 健康检查:检测MySQL是否真正就绪,避免后端启动早于MySQL,导致连接失败
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-uappuser", "-pApp@123456"]
      interval: 5s                    # 每5秒检测一次
      timeout: 30s                    # 超时时间30秒
      retries: 10                     # 重试10次,失败则认为容器未就绪
      start_period: 20s               # 容器启动后,延迟20秒开始检测

  # === 公共依赖服务:Redis(缓存,多后端可共享/隔离) ===
  redis:
    image: redis:7-alpine             # 轻量版Redis,占用资源少
    container_name: multi-redis       # 唯一容器名
    restart: always                   # 自动重启
    ports:
      - "6379:6379"                   # 端口映射:宿主机6379 → 容器内6379
    volumes:
      - redis-data:/data              # 数据卷:持久化Redis数据
    command: redis-server --requirepass "Redis@123456"  # Redis密码(自定义)
    networks:
      - multi-service-network         # 加入自定义网络
    environment:
      TZ: Asia/Shanghai               # 同步东八区时区
    # Redis健康检查(可选,优化多后端依赖)
    healthcheck:
      test: ["CMD", "redis-cli", "ping", "-a", "Redis@123456"]
      interval: 3s
      timeout: 10s
      retries: 5

  # === 后端API 1(第一个服务,如管理端后端) ===
  backend1:
    image: mcr.microsoft.com/dotnet/aspnet:8.0  # .NET8运行时镜像(无需构建,直接运行发布文件)
    container_name: multi-backend1    # 唯一容器名,区分其他后端
    restart: always                   # 自动重启
    ports:
      - "58588:58588"                 # 唯一端口,避免和其他后端冲突
    depends_on:
      mysql:
        condition: service_healthy    # 仅在MySQL健康检查通过(真正就绪)后,才启动后端1
      redis:
        condition: service_healthy    # Redis就绪后,启动后端1
    volumes:
      - ./backend1/publish:/app       # 挂载后端1的发布目录(核心,容器直接运行发布文件)
      - /wwwroot/Resources1:/wwwroot/Resources  # 挂载后端1的文件目录(如用户头像,按需挂载)
    environment:
      TZ: Asia/Shanghai               # 同步时区
      ASPNETCORE_URLS: "http://*:58588"  # 强制后端1监听58588端口(解决端口不通问题)
      ASPNETCORE_ENVIRONMENT: Production  # .NET环境(生产环境)
      # 后端1的MySQL连接字符串(连接MySQL的app_db1库,server用容器名mysql)
      ConnectionStrings__MySQL: "server=mysql;port=3306;database=app_db1;user=appuser;password=App@123456;charset=utf8mb4;AllowPublicKeyRetrieval=True;SslMode=None"
      # 后端1的Redis连接字符串(server用容器名redis,密码对应上面的Redis密码)
      ConnectionStrings__Redis: "redis:6379,password=Redis@123456,defaultDatabase=0,ssl=false,abortConnect=false"
    working_dir: /app                 # 容器工作目录,指向挂载的发布目录
    entrypoint: ["dotnet", "Backend1.WebHost.dll"]  # 启动后端1的核心DLL(替换为自己的DLL名)
    networks:
      - multi-service-network         # 加入自定义网络,可访问MySQL/Redis/其他后端

  # === 后端API 2(第二个服务,如用户端后端) ===
  backend2:
    image: mcr.microsoft.com/dotnet/aspnet:8.0  # 和后端1共用.NET8运行时镜像
    container_name: multi-backend2    # 唯一容器名,区分后端1
    restart: always                   # 自动重启
    ports:
      - "58589:58589"                 # 唯一端口,避免和后端1冲突(不可重复)
    depends_on:
      mysql:
        condition: service_healthy    # 依赖MySQL就绪
      redis:
        condition: service_healthy    # 依赖Redis就绪
    volumes:
      - ./backend2/publish:/app       # 挂载后端2的发布目录(独立目录,避免代码覆盖)
      - /wwwroot/Resources2:/wwwroot/Resources  # 后端2的文件目录(独立挂载,避免文件混乱)
    environment:
      TZ: Asia/Shanghai
      ASPNETCORE_URLS: "http://*:58589"  # 强制后端2监听58589端口
      ASPNETCORE_ENVIRONMENT: Production
      # 后端2的MySQL连接字符串(可连接不同库app_db2,实现分库部署)
      ConnectionStrings__MySQL: "server=mysql;port=3306;database=app_db2;user=appuser;password=App@123456;charset=utf8mb4;AllowPublicKeyRetrieval=True;SslMode=None"
      # 后端2的Redis连接字符串(可指定不同数据库defaultDatabase=1,实现缓存隔离)
      ConnectionStrings__Redis: "redis:6379,password=Redis@123456,defaultDatabase=1,ssl=false,abortConnect=false"
    working_dir: /app
    entrypoint: ["dotnet", "Backend2.WebHost.dll"]  # 后端2的核心DLL(替换为自己的)
    networks:
      - multi-service-network

  # === Nginx服务(核心:多前端代理+多后端分流) ===
  nginx:
    image: nginx:alpine               # 轻量版Nginx
    container_name: multi-nginx       # 唯一容器名
    restart: always                   # 自动重启
    ports:
      - "6866:6866"                   # 前端1访问端口(唯一)
      - "6867:6867"                   # 前端2访问端口(唯一,避免冲突)
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf  # 挂载Nginx分流配置
      - ./frontend1/dist:/usr/share/nginx/html/web1  # 挂载前端1的静态文件
      - ./frontend2/dist:/usr/share/nginx/html/web2  # 挂载前端2的静态文件
    depends_on:
      - backend1                      # 后端1启动后,再启动Nginx(避免前端访问后端失败)
      - backend2                      # 后端2启动后,再启动Nginx
    networks:
      - multi-service-network
    environment:
      TZ: Asia/Shanghai

# 数据卷:持久化MySQL、Redis数据(容器删除、docker-compose down不会删除数据)
volumes:
  mysql-data:
  redis-data:

# 自定义网络:所有服务加入同一网络,容器间可通过「容器名」直接通信,无需配置IP
networks:
  multi-service-network:
    driver: bridge

2. 编写Nginx配置(nginx.conf,多服务分流核心)

Nginx的核心作用是:代理多个前端静态文件,将前端的API请求,按路径前缀分流到对应后端,避免端口混乱,配置如下(注释详细):

nginx 复制代码
worker_processes 1;  # 按服务器CPU核心数调整,1核用1,2核用2

events {
    worker_connections 1024;  # 最大连接数,足够多服务使用
}

http {
    include       /etc/nginx/mime.types;  # 引入MIME类型(支持前端静态文件)
    default_type  application/octet-stream;

    sendfile        on;  # 开启高效文件传输
    keepalive_timeout  65;  # 连接超时时间

    # === 前端1配置(访问端口:6866,对应管理端) ===
    server {
        listen       6866;  # 前端1的访问端口(和docker-compose.yml中Nginx的端口映射一致)
        server_name  localhost;  # 测试环境用localhost,生产环境可改为域名

        # 前端1的静态文件代理(加载Vue打包后的index.html、css、js)
        location / {
            root   /usr/share/nginx/html/web1;  # 对应前端1的dist目录(docker-compose.yml中挂载的路径)
            index  index.html index.htm;
            try_files $uri $uri/ /index.html;  # 解决Vue路由刷新404问题(关键)
        }

        # 前端1调用的后端1API(路径前缀:/api1/,分流到backend1)
        location /api1/ {
            proxy_pass http://multi-backend1:58588/;  # 转发到后端1容器(容器名+端口)
            # 转发请求头,确保后端能获取客户端真实IP、请求地址等信息
            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;
        }

        # 前端1如需调用后端2API(可选,按需配置)
        location /api2/ {
            proxy_pass http://multi-backend2:58589/;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }

    # === 前端2配置(访问端口:6867,对应用户端) ===
    server {
        listen       6867;  # 前端2的访问端口(唯一,不重复)
        server_name  localhost;

        # 前端2的静态文件代理
        location / {
            root   /usr/share/nginx/html/web2;  # 对应前端2的dist目录
            index  index.html index.htm;
            try_files $uri $uri/ /index.html;  # Vue路由兼容
        }

        # 前端2调用的后端2API(路径前缀:/api2/,分流到backend2)
        location /api2/ {
            proxy_pass http://multi-backend2:58589/;  # 转发到后端2容器
            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;
        }

        # 前端2如需调用后端1API(可选,按需配置)
        location /api1/ {
            proxy_pass http://multi-backend1:58588/;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }
}

3. 启动所有服务并验证

所有配置完成后,一键启动所有服务,然后逐个验证,确保所有服务正常运行。

bash 复制代码
# 1. 进入项目根目录(必须在docker-compose.yml所在目录执行命令)
cd /root/multi-service-docker

# 2. 后台启动所有服务(-d:后台运行,无需手动值守)
docker-compose up -d

# 3. 查看所有服务运行状态(关键,确认所有服务都启动成功)
docker-compose ps

✅ 成功标识:docker-compose ps输出中,multi-mysql、multi-redis、multi-backend1、multi-backend2、multi-nginx 5个服务的State列,均为Up (healthy)Up,无Exited状态。

3.1 逐个验证服务(确保无问题)

​ ✅ 成功标识:浏览器能正常打开Swagger页面,无报错,能看到后端1的所有接口。

  • 验证后端2 :访问地址 http://服务器IP:58589/swagger

    ✅ 成功标识:正常打开Swagger页面,接口能正常显示。

  • 验证前端1 :访问地址 http://服务器IP:6866

    ✅ 成功标识:能正常打开Vue页面,样式、图片正常,调用/api1/xxx接口能返回正常数据。

  • 验证前端2 :访问地址 http://服务器IP:6867

    ✅ 成功标识:能正常打开Vue页面,调用/api2/xxx接口能返回正常数据。

  • 验证MySQL:用Navicat/DBeaver连接服务器IP:3306,用户名appuser,密码App@123456,能正常连接,能看到app_db1、app_db2两个数据库,以及初始化的表和数据。

  • 验证Redis:用Redis客户端连接服务器IP:6379,密码Redis@123456,能正常执行set、get命令,后端1/2的缓存能正常生效。

❌ 失败排查:若某个服务启动失败或访问不通,执行docker-compose logs -f 服务名(如docker-compose logs -f multi-backend1),查看实时日志,根据日志提示排查问题(如端口冲突、连接串错误、目录挂载失败)。

四、扩展方案1:替换数据库(PostgreSQL / SQL Server)

实际项目中,可能会用到PostgreSQL(开源、适合复杂查询)或SQL Server(微软生态、适合.NET项目),以下是完整的替换方案,只需修改docker-compose.yml和后端连接串,无需修改前端和Nginx配置。

1. 替换为PostgreSQL(开源首选,适配.NET6+)

PostgreSQL是开源的高性能数据库,兼容性强,适合大多数.NET项目,替换步骤如下:

1.1 修改docker-compose.yml(替换MySQL为PostgreSQL)

删除原有的mysql服务,新增postgresql服务,其他服务(后端1/2、前端1/2、Redis、Nginx)无需修改,仅修改后端的连接串:

yaml 复制代码
version: '3.8'

services:
  # === 替换为PostgreSQL服务(替换原有的MySQL) ===
  postgresql:
    image: postgres:15                # PostgreSQL 15镜像(稳定版,适配.NET8)
    container_name: multi-postgresql  # 唯一容器名
    restart: always
    environment:
      POSTGRES_USER: appuser          # 项目访问PostgreSQL的用户名
      POSTGRES_PASSWORD: App@123456   # 密码(和之前MySQL一致,方便记忆)
      POSTGRES_DB: app_db1            # 后端1所用数据库
      POSTGRES_DB2: app_db2           # 后端2所用数据库(分库部署)
      TZ: Asia/Shanghai               # 同步东八区时区
    ports:
      - "5432:5432"                   # PostgreSQL默认端口5432
    volumes:
      - ./postgresql/postgresql.conf:/var/lib/postgresql/data/postgresql.conf  # 挂载配置文件
      - ./postgresql/init-postgresql.sql:/docker-entrypoint-initdb.d/init-postgresql.sql  # 挂载初始化SQL
      - postgresql-data:/var/lib/postgresql/data  # 数据卷:持久化数据
      - /etc/localtime:/etc/localtime:ro
    networks:
      - multi-service-network
    # 健康检查:检测PostgreSQL是否就绪
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U appuser -d app_db1"]
      interval: 5s
      timeout: 30s
      retries: 10
      start_period: 20s

  # === Redis服务(不变) ===
  redis:
    image: redis:7-alpine
    container_name: multi-redis
    restart: always
    ports:
      - "6379:6379"
    volumes:
      - redis-data:/data
    command: redis-server --requirepass "Redis@123456"
    networks:
      - multi-service-network
    environment:
      TZ: Asia/Shanghai
    healthcheck:
      test: ["CMD", "redis-cli", "ping", "-a", "Redis@123456"]
      interval: 3s
      timeout: 10s
      retries: 5

  # === 后端1服务(仅修改MySQL连接串为PostgreSQL连接串) ===
  backend1:
    image: mcr.microsoft.com/dotnet/aspnet:8.0
    container_name: multi-backend1
    restart: always
    ports:
      - "58588:58588"
    depends_on:
      postgresql:  # 依赖改为postgresql,而非mysql
        condition: service_healthy
      redis:
        condition: service_healthy
    volumes:
      - ./backend1/publish:/app
      - /wwwroot/Resources1:/wwwroot/Resources
    environment:
      TZ: Asia/Shanghai
      ASPNETCORE_URLS: "http://*:58588"
      ASPNETCORE_ENVIRONMENT: Production
      # 后端1的PostgreSQL连接串(替换原有的MySQL连接串)
      ConnectionStrings__PostgreSQL: "Host=postgresql;Port=5432;Database=app_db1;Username=appuser;Password=App@123456;Pooling=true;Timeout=30;"
      # Redis连接串(不变)
      ConnectionStrings__Redis: "redis:6379,password=Redis@123456,defaultDatabase=0,ssl=false,abortConnect=false"
    working_dir: /app
    entrypoint: ["dotnet", "Backend1.WebHost.dll"]
    networks:
      - multi-service-network

  # === 后端2服务(仅修改连接串) ===
  backend2:
    image: mcr.microsoft.com/dotnet/aspnet:8.0
    container_name: multi-backend2
    restart: always
    ports:
      - "58589:58589"
    depends_on:
      postgresql:
        condition: service_healthy
      redis:
        condition: service_healthy
    volumes:
      - ./backend2/publish:/app
      - /wwwroot/Resources2:/wwwroot/Resources
    environment:
      TZ: Asia/Shanghai
      ASPNETCORE_URLS: "http://*:58589"
      ASPNETCORE_ENVIRONMENT: Production
      # 后端2的PostgreSQL连接串
      ConnectionStrings__PostgreSQL: "Host=postgresql;Port=5432;Database=app_db2;Username=appuser;Password=App@123456;Pooling=true;Timeout=30;"
      # Redis连接串(不变)
      ConnectionStrings__Redis: "redis:6379,password=Redis@123456,defaultDatabase=1,ssl=false,abortConnect=false"
    working_dir: /app
    entrypoint: ["dotnet", "Backend2.WebHost.dll"]
    networks:
      - multi-service-network

  # === Nginx服务(不变) ===
  nginx:
    image: nginx:alpine
    container_name: multi-nginx
    restart: always
    ports:
      - "6866:6866"
      - "6867:6867"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./frontend1/dist:/usr/share/nginx/html/web1
      - ./frontend2/dist:/usr/share/nginx/html/web2
    depends_on:
      - backend1
      - backend2
    networks:
      - multi-service-network
    environment:
      TZ: Asia/Shanghai

# 数据卷:替换为postgresql-data,删除mysql-data
volumes:
  postgresql-data:
  redis-data:

# 自定义网络(不变)
networks:
  multi-service-network:
    driver: bridge

1.2 后端项目修改(关键)

    1. 后端项目中,安装PostgreSQL依赖包:在VS2022中,右键项目→管理NuGet程序包,搜索并安装Npgsql.EntityFrameworkCore.PostgreSQL(适配.NET8的版本);
    1. 修改appsettings.json:将原来的ConnectionStrings:MySQL,改为ConnectionStrings:PostgreSQL,对应docker-compose.yml中的连接串;
    1. 修改Program.cs:如使用EF Core的数据库提供器,从MySQL改为PostgreSQL,示例:

      // 原来的MySQL配置

      builder.Services.AddDbContext (options => options.UseMySql(builder.Configuration.GetConnectionString("MySQL"), new MySqlServerVersion(new Version(8, 0, 36))));

      // 改为PostgreSQL配置

      builder.Services.AddDbContext (options => options.UseNpgsql(builder.Configuration.GetConnectionString("PostgreSQL")));

    1. 重新发布后端1/2项目,将新的publish文件夹,上传到服务器对应目录,覆盖原来的文件。

1.3 启动并验证

bash 复制代码
cd /root/multi-service-docker
# 停止并删除原有容器(避免冲突)
docker-compose down
# 启动新服务
docker-compose up -d
# 查看状态
docker-compose ps

✅ 验证:和基础方案一致,访问前端、后端、PostgreSQL(用Navicat连接服务器IP:5432),确认所有服务正常。

2. 替换为SQL Server(微软生态,适合.NET项目)

SQL Server是微软推出的关系型数据库,和.NET生态兼容性极佳,适合企业级.NET项目,替换步骤如下(类似PostgreSQL):

2.1 修改docker-compose.yml(替换MySQL为SQL Server)

yaml 复制代码
version: '3.8'

services:
  # === 替换为SQL Server服务 ===
  sqlserver:
    image: mcr.microsoft.com/mssql/server:2022-latest  # SQL Server 2022镜像
    container_name: multi-sqlserver  # 唯一容器名
    restart: always
    environment:
      ACCEPT_EULA: "Y"                # 必须设置为Y,接受SQL Server许可协议
      SA_PASSWORD: "App@12345678"     # SA密码(复杂度要求:至少8位,含字母+数字+特殊符号)
      MSSQL_PID: "Express"            # 版本:Express(免费版,适合测试/小型项目)
      TZ: Asia/Shanghai               # 同步时区
    ports:
      - "1433:1433"                   # SQL Server默认端口1433
    volumes:
      - ./sqlserver/sqlserver.conf:/var/opt/mssql/mssql.conf  # 挂载配置文件
      - ./sqlserver/init-sqlserver.sql:/docker-entrypoint-initdb.d/init-sqlserver.sql  # 初始化SQL
      - sqlserver-data:/var/opt/mssql  # 数据卷:持久化数据
      - /etc/localtime:/etc/localtime:ro
    networks:
      - multi-service-network
    # 健康检查:检测SQL Server是否就绪
    healthcheck:
      test: ["CMD-SHELL", "/opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P App@12345678 -Q 'SELECT 1'"]
      interval: 10s
      timeout: 30s
      retries: 10
      start_period: 30s  # SQL Server启动较慢,延迟30秒开始检测

  # === Redis服务(不变) ===
  redis:
    image: redis:7-alpine
    container_name: multi-redis
    restart: always
    ports:
      - "6379:6379"
    volumes:
      - redis-data:/data
    command: redis-server --requirepass "Redis@123456"
    networks:
      - multi-service-network
    environment:
      TZ: Asia/Shanghai
    healthcheck:
      test: ["CMD", "redis-cli", "ping", "-a", "Redis@123456"]
      interval: 3s
      timeout: 10s
      retries: 5

  # === 后端1服务(仅修改连接串) ===
  backend1:
    image: mcr.microsoft.com/dotnet/aspnet:8.0
    container_name: multi-backend1
    restart: always
    ports:
      - "58588:58588"
    depends_on:
      sqlserver:  # 依赖改为sqlserver
        condition: service_healthy
      redis:
        condition: service_healthy
    volumes:
      - ./backend1/publish:/app
      - /wwwroot/Resources1:/wwwroot/Resources
    environment:
      TZ: Asia/Shanghai
      ASPNETCORE_URLS: "http://*:58588"
      ASPNETCORE_ENVIRONMENT: Production
      # 后端1的SQL Server连接串
      ConnectionStrings__SQLServer: "Server=sqlserver,1433;Database=app_db1;User ID=sa;Password=App@12345678;TrustServerCertificate=True;"
      # Redis连接串(不变)
      ConnectionStrings__Redis: "redis:6379,password=Redis@123456,defaultDatabase=0,ssl=false,abortConnect=false"
    working_dir: /app
    entrypoint: ["dotnet", "Backend1.WebHost.dll"]
    networks:
      - multi-service-network

  # === 后端2服务(仅修改连接串) ===
  backend2:
    image: mcr.microsoft.com/dotnet/aspnet:8.0
    container_name: multi-backend2
    restart: always
    ports:
      - "58589:58589"
    depends_on:
      sqlserver:
        condition: service_healthy
      redis:
        condition: service_healthy
    volumes:
      - ./backend2/publish:/app
      - /wwwroot/Resources2:/wwwroot/Resources
    environment:
      TZ: Asia/Shanghai
      ASPNETCORE_URLS: "http://*:58589"
      ASPNETCORE_ENVIRONMENT: Production
      # 后端2的SQL Server连接串
      ConnectionStrings__SQLServer: "Server=sqlserver,1433;Database=app_db2;User ID=sa;Password=App@12345678;TrustServerCertificate=True;"
      # Redis连接串(不变)
      ConnectionStrings__Redis: "redis:6379,password=Redis@123456,defaultDatabase=1,ssl=false,abortConnect=false"
    working_dir: /app
    entrypoint: ["dotnet", "Backend2.WebHost.dll"]
    networks:
      - multi-service-network

  # === Nginx服务(不变) ===
  nginx:
    image: nginx:alpine
    container_name: multi-nginx
    restart: always
    ports:
      - "6866:6866"
      - "6867:6867"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./frontend1/dist:/usr/share/nginx/html/web1
      - ./frontend2/dist:/usr/share/nginx/html/web2
    depends_on:
      - backend1
      - backend2
    networks:
      - multi-service-network
    environment:
      TZ: Asia/Shanghai

# 数据卷:替换为sqlserver-data
volumes:
  sqlserver-data:
  redis-data:

# 自定义网络(不变)
networks:
  multi-service-network:
    driver: bridge

2.2 后端项目修改

    1. 安装SQL Server依赖包:在VS2022中,安装Microsoft.EntityFrameworkCore.SqlServer(适配.NET8的版本);
    1. 修改appsettings.json:将ConnectionStrings:MySQL改为ConnectionStrings:SQLServer,对应docker-compose.yml中的连接串;

五、相关参考

如果本文对你有一点点帮助,点个赞支持一下吧,你的每一个【赞】都是我创作的最大动力 _

更多技术文章请往:

http://www.guosisoft.com/article

http://www.rdiframework.net/article

大家一起共同交流和进步呀!!