Linux Docker Compose 部署.NET+Vue+MySQL+Redis+Nginx 完整记录(亲测无坑)

写在前面:为什么用 Docker Compose?比单容器部署好在哪?

做容器化部署时,单靠docker run命令逐个启动 MySQL、Redis、后端、Nginx 容器会非常繁琐 ------ 不仅要记大量命令参数,还得手动控制容器启动顺序、配置网络联动,一旦服务器重启,所有容器要重新逐个启动,维护成本极高。

Docker Compose 是 Docker 官方的多容器编排工具,核心是通过一个docker-compose.yml配置文件,集中管理所有容器的镜像、端口、挂载、环境变量、网络、依赖关系等所有配置,能完美解决单容器部署的痛点。

Docker Compose 核心优点(也是本次部署选择它的原因)

  1. 一键启停 :一条docker-compose up -d启动所有服务,docker-compose down停止并清理,无需逐个执行docker run/docker stop
  2. 配置统一:所有容器配置集中在一个 yaml 文件,易编辑、易备份,后续修改只需改配置文件,无需记复杂命令;
  3. 容器自动联动 :自动创建专属网络,容器间通过服务名 即可通信,无需手动配置网络;可通过depends_on控制启动顺序,解决服务依赖问题;
  4. 环境一致性:配置文件可跨环境复用,本地测试、服务器部署用同一套配置,避免 "本地能跑,服务器不行";
  5. 易维护易迁移:项目目录 + 配置文件 + 离线镜像包,可直接迁移到其他服务器,解压后一键启动,无需重新配置;
  6. 数据卷 / 网络自动管理 :自动创建数据卷、自定义网络,无需手动执行docker volume create/docker network create

相比单容器部署,Docker Compose 让多服务容器化部署的效率提升数倍,尤其适合后端 + 前端 + 数据库 + 缓存 + 代理这类多组件的项目部署,也是目前中小型项目容器化的主流方案。

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

1. 虚拟机环境(个人测试用,非生产)

  • 系统:CentOS 7.9(最小化安装,已配置静态 IP:192.168.1.100,虚拟 IP,替换真实 IP)
  • 内存:4G(建议不低于 2G,否则 Docker 容器启动可能卡顿)
  • 硬盘:50G(足够存放镜像、项目文件和数据库数据)
  • 网络:能访问外网(前期拉取镜像 / 安装依赖用,后期可断网运行)

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

  • Docker:Docker CE 24.0.7(CentOS7 稳定版)
  • Docker Compose:V2.27.1(解决旧版配置兼容问题)
  • 后端:.NET 8(本地 VS2022 发布到 publish 文件夹)
  • 前端:Vue3(本地 yarn 打包到 dist 文件夹)
  • MySQL:8.0(Docker 镜像,数据持久化)
  • Redis:7-alpine(轻量版,适合容器部署)
  • Nginx:alpine(轻量版,代理前端静态文件 + 后端接口)

3. 本地准备文件(提前打包好,上传到虚拟机)

  • 后端:publish 文件夹(VS2022 发布后的.NET8 项目文件,含核心 dll、配置文件)
  • 前端:dist 文件夹(Vue3 打包后的静态文件,含 index.html、css、js)
  • 镜像 tar 包:rdif-all-images.tar(离线镜像包,含 MySQL、Redis、Nginx 等 6 个所需镜像,解决网络拉取超时)
  • 配置文件:my.cnf(MySQL 配置)、nginx.conf(Nginx 配置)、init.sql(MySQL 初始化 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

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

CentOS7 默认源没有 Docker,需要配置 Docker 官方源,同时解决依赖缺失问题(重点解决 container-selinux 依赖):

csharp 复制代码
# 1. 卸载旧版本Docker(如果之前装过,避免冲突,没装过可跳过)
yum remove -y docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine
​
# 2. 安装Docker依赖(必做,否则安装失败)
yum install -y yum-utils device-mapper-persistent-data lvm2 container-selinux
​
# 3. 配置Docker官方源
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
​
# 4. 安装Docker CE(稳定版)
yum install -y docker-ce docker-ce-cli containerd.io
​
# 5. 启动Docker服务,并设置开机自启(提前配置,后续不用再改)
systemctl start docker
systemctl enable docker
​
# 6. 验证Docker安装成功(输出版本号即成功)
docker --version

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

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

Docker 默认拉取官方镜像(国外源),国内访问极慢,甚至超时。这里用阿里云个人专属镜像加速(比公共源更稳定),步骤如下:

  1. 登录阿里云官网(www.aliyun.com/),搜索 "容器镜像服务",进入 "镜像加速器",复制自己的专属加速地址(示例:xxxxxx.mirror.aliyuncs.com,替换成自己的);

  2. 配置镜像加速,修改 Docker 守护进程配置文件:

bash 复制代码
# 创建Docker配置目录(如果不存在)
mkdir -p /etc/docker
​
# 写入加速配置(替换成自己的阿里云专属加速地址)
tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors": ["https://xxxxxx.mirror.aliyuncs.com", "https://mirror.ccs.tencentyun.com"]
}
EOF
​
# 重新加载配置,重启Docker,让加速生效
systemctl daemon-reload
systemctl restart docker
​
# 验证加速配置是否生效(输出配置的加速地址即成功)
docker info | grep -A 2 "Registry Mirrors"

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

4. 升级 Docker Compose(解决旧版配置兼容问题)

CentOS7 默认安装的 Docker Compose 是 1.x 版本,不支持新版 docker-compose.yml 中的配置(如 condition、start_period),升级到 V2 版本(官方推荐):

bash 复制代码
# 1. 删除旧版docker-compose(如果之前装过)
rm -f /usr/local/bin/docker-compose
​
# 2. 安装Docker Compose V2(插件形式,稳定)
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(版本号可不同)

5. 关闭防火墙(测试环境,避免端口访问失败)

个人测试用,直接关闭 CentOS7 的 FirewallD 防火墙,避免前端、后端、数据库端口被拦截,生产环境可按需开放端口:

bash 复制代码
# 1. 立即停止防火墙服务
systemctl stop firewalld
​
# 2. 禁止防火墙开机自启(避免虚拟机重启后防火墙又开启)
systemctl disable firewalld
​
# 3. 验证防火墙状态(输出inactive即成功关闭)
systemctl status firewalld

三、项目目录结构整理(规范目录,避免后续混乱)

将本地准备好的所有文件,上传到 CentOS7 虚拟机的/root/rdif-docker目录(自定义目录,方便记忆),最终目录结构如下(重点:前后端仅保留打包 / 发布文件,无源码、无多余文件):

csharp 复制代码
rdif-docker/                 # 项目根目录(所有文件都放在这里)
├── docker-compose.yml       # 核心编排文件,管理所有容器
├── backend/                 # 后端目录(仅保留VS发布的publish)
│   └── publish/             # .NET8发布文件(含RDIF.WebHost.dll、appsettings.json)
├── frontend/                # 前端目录(仅保留Vue3打包的dist)
│   └── dist/                # Vue3静态文件(index.html、css、js、assets)
├── nginx/                   # Nginx配置目录
│   └── nginx.conf           # Nginx配置文件(代理前端+后端接口)
├── mysql/                   # MySQL配置目录
│   ├── my.cnf               # MySQL配置(不区分大小写、字符集等)
│   └── init.sql             # MySQL初始化SQL(创建库、表、初始化数据)
└── rdif-all-images.tar      # 离线镜像包(含所有所需镜像,避免拉取超时)

上传文件方法(新手推荐,可视化操作)

用 MobaXterm或Xftp 或 WinSCP 工具,连接虚拟机(IP:192.168.1.100,账号:root,密码:Root@123456,虚拟密码),将本地的 publish、dist、配置文件、镜像 tar 包,拖到对应目录下即可。

四、编写 docker-compose.yml(核心配置,重中之重)

这是整个部署的核心,所有容器(MySQL、Redis、后端、Nginx)的联动、端口映射、目录挂载,都在这里配置。结合本次需求(前后端已打包 / 发布,无需编译构建),编写如下配置(注释详细,可直接复制修改,替换自己的对应信息,Redis 密码已替换为通用虚拟密码):

yaml 复制代码
version: '3.8'
​
# 所有服务的集合
services:
  # 1. MySQL8 服务(数据库,数据持久化)
  mysql:
    image: mysql:8.0                # 使用的镜像(本地已导入,无需拉取)
    container_name: rdif-mysql      # 自定义容器名,方便管理
    restart: always                 # 容器异常退出/ Docker启动时,自动重启
    environment:
      MYSQL_ROOT_PASSWORD: Root@123456   # MySQL root密码(虚拟,替换成自己的)
      MYSQL_USER: guosisoft         # 项目访问MySQL的用户名(自定义)
      MYSQL_PASSWORD: Mysql@123456  # 项目访问MySQL的密码(虚拟)
      MYSQL_DATABASE: rdif_vue3     # 项目所用数据库名(自定义)
      MYSQL_TZINFO_TO_SYS_TABLES: 1 # 初始化MySQL时区表,解决时差问题
      TZ: Asia/Shanghai             # 强制容器时区为东八区(核心,解决时差)
    ports:
      - "3306:3306"                 # 端口映射:宿主机3306 → 容器内3306(本地工具可连接)
    volumes:
      # 挂载MySQL配置文件,实现自定义配置
      - ./mysql/my.cnf:/etc/mysql/conf.d/my.cnf
      # 挂载初始化SQL,容器启动时自动执行,创建库表
      - ./mysql/init.sql:/docker-entrypoint-initdb.d/init.sql
      # 挂载数据卷,持久化MySQL数据(docker-compose down不会删除数据)
      - mysql-data:/var/lib/mysql
      # 挂载宿主机时区文件,双重保障时区同步(只读,避免容器修改)
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro
    command: --lower_case_table_names=1 # MySQL不区分大小写(避免项目表名大小写问题)
    networks:
      - rdif-network                # 加入自定义网络,实现容器间通信
    # 健康检查:检测MySQL是否真正就绪,避免后端启动早于MySQL
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-uguosisoft", "-pMysql@123456"]
      interval: 5s                  # 每5秒检测一次
      timeout: 30s                  # 超时时间30秒
      retries: 10                   # 重试10次,失败则认为容器未就绪
      start_period: 20s             # 容器启动后,延迟20秒开始检测
​
  # 2. Redis 服务(缓存,轻量版,密码已替换为虚拟通用密码)
  redis:
    image: redis:7-alpine           # 轻量版Redis,占用资源少
    container_name: rdif-redis      # 自定义容器名
    restart: always                 # 自动重启
    ports:
      - "6379:6379"                 # 端口映射:宿主机6379 → 容器内6379
    volumes:
      - redis-data:/data            # 数据卷持久化Redis数据
    command: redis-server --requirepass "Redis@123456" # Redis虚拟密码,替换成自己的复杂密码
    networks:
      - rdif-network                # 加入自定义网络
    environment:
      TZ: Asia/Shanghai             # 同步东八区时区
​
  # 3. .NET8 后端服务(已发布,直接挂载运行,Redis连接密码同步替换)
  backend:
    image: mcr.microsoft.com/dotnet/aspnet:8.0 # .NET8运行时镜像(无需构建)
    container_name: rdif-backend    # 自定义容器名
    restart: always                 # 自动重启
    ports:
      - "58588:58588"               # 端口映射:宿主机58588 → 容器内58588(后端接口端口)
    depends_on:
      mysql:
        condition: service_healthy  # 仅在MySQL健康检查通过(就绪)后,才启动后端
      redis:
        condition: service_started  # Redis启动后,即可启动后端
    volumes:     
      # 核心:挂载本地publish到容器的/app目录
      - ./backend/publish:/app
      # 宿主机:/wwwroot/Resources → 容器内:/wwwroot/Resources
      - /wwwroot/Resources:/wwwroot/Resources
    environment:
      TZ: Asia/Shanghai             # 同步东八区时区
      ASPNETCORE_URLS: "http://*:58588" # 强制.NET8容器内监听58588端口(解决端口不通)
      ASPNETCORE_ENVIRONMENT: Production # .NET环境(生产环境)
      # MySQL连接字符串(替换成自己的用户名、密码、数据库名,server用容器名mysql)
      ConnectionStrings__MySQL: "server=mysql;port=3306;database=rdif_vue3;user=guosisoft;password=Mysql@123456;charset=utf8mb4;AllowPublicKeyRetrieval=True;SslMode=None"
      # Redis连接字符串(server用容器名redis,密码同步替换为虚拟密码)
      ConnectionStrings__Redis: "redis:6379,password=Redis@123456,defaultDatabase=0,ssl=false,abortConnect=false"
    working_dir: /app               # 容器工作目录,指向挂载的publish目录
    entrypoint: ["dotnet", "RDIF.WebHost.dll"] # 启动后端核心dll(替换成自己的dll名)
    networks:
      - rdif-network                # 加入自定义网络
​
  # 4. Nginx 服务(代理前端静态文件+后端接口)
  nginx:
    image: nginx:alpine             # 轻量版Nginx
    container_name: rdif-nginx      # 自定义容器名
    restart: always                 # 自动重启
    ports:
      - "6866:6866"                 # 端口映射:宿主机6866 → 容器内6866(前端访问端口)
    volumes:
      # 挂载Nginx配置文件,实现前端代理和接口转发
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
      # 挂载前端dist目录,Nginx直接代理静态文件(核心)
      - ./frontend/dist:/usr/share/nginx/html
    depends_on:
      - backend                     # 后端启动后,再启动Nginx
    networks:
      - rdif-network                # 加入自定义网络
    environment:
      TZ: Asia/Shanghai             # 同步东八区时区
​
# 数据卷:持久化MySQL和Redis数据(docker-compose down不会删除)
volumes:
  mysql-data:
  redis-data:
​
# 自定义网络:所有容器加入同一网络,实现容器间通信(用容器名即可访问)
networks:
  rdif-network:
    driver: bridge

关键配置说明(必看,避免踩坑)

  1. 所有容器都配置了restart: always,配合 Docker 开机自启,实现虚拟机重启后所有服务自动启动;
  2. 后端配置ASPNETCORE_URLS: "http://*:58588",强制监听 58588 端口,和端口映射一致,解决.NET8 默认监听 8080 导致的端口不通;
  3. MySQL 连接字符串中,server=mysql(用容器名),而非虚拟机 IP,容器间通信必须这样配置;补充AllowPublicKeyRetrieval=True;SslMode=None,解决容器内连接 MySQL 失败;
  4. 所有容器都配置TZ: Asia/Shanghai,同步东八区时区,解决 MySQL 时差 8 小时问题;
  5. 后端depends_on配置了condition: service_healthy,确保 MySQL 完全就绪后再启动后端,避免后端启动时 MySQL 未初始化完成导致的连接失败;
  6. 所有敏感密码(MySQL/Redis)均为虚拟示例,实际部署请替换为字母 + 数字 + 特殊符号的复杂密码,提升安全性。

五、本地拉取镜像并打包为 tar(离线方案前置步骤,关键!)

本次部署采用离线镜像导入 方案(解决服务器网络拉取超时问题),需在本地能正常联网的电脑上提前拉取所有所需镜像,再打包为 tar 文件,最后上传到 CentOS 服务器。

5.1 本地拉取镜像的前提条件

本地电脑(Windows/macOS)需安装Docker Desktop(Docker 桌面版),内置 Docker 引擎和镜像管理功能,是本地操作 Docker 的必备工具:

  • 下载地址:www.docker.com/products/do...
  • 安装后验证:打开 Docker Desktop,启动后在本地终端(CMD/PowerShell/ 终端)执行docker --version,输出版本号即安装成功;
  • 关键设置:Docker Desktop 中配置镜像加速(和服务器端一致,阿里云 / 网易云均可),避免本地拉取镜像超时。

5.2 本地终端拉取本次部署所有所需镜像

打开本地终端(Windows 用 PowerShell/CMD,macOS/Linux 用终端),执行以下docker pull命令,逐个拉取镜像(按本次部署的版本号拉取,确保版本一致):

bash 复制代码
# 1. 拉取MySQL8.0镜像
docker pull mysql:8.0
# 2. 拉取Redis7轻量版镜像
docker pull redis:7-alpine
# 3. 拉取.NET8运行时镜像(后端运行依赖)
docker pull mcr.microsoft.com/dotnet/aspnet:8.0
# 4. 拉取Nginx轻量版镜像
docker pull nginx:alpine

✅ 拉取成功验证:本地终端执行docker images,能看到以上 4 个镜像,无报错即拉取完成。

5.3 本地将镜像打包为 tar 文件(单镜像 / 批量镜像两种方式)

打包镜像有单镜像打包批量镜像打包 两种方式,推荐批量打包为一个 tar 文件(方便上传和服务器导入),以下两种方式均提供代码示例。

方式 1:批量打包所有镜像为一个 tar 文件(推荐,一次导入所有)

将所有拉取的镜像打包为一个统一的 tar 文件(示例:rdif-all-images.tar),放在本地易找到的目录(如桌面),执行命令前先切换到目标目录(如 Windows 桌面目录):

bash 复制代码
# Windows PowerShell切换到桌面目录(示例,可替换为自己的目录)
cd C:\Users\你的用户名\Desktop

# 批量打包镜像为rdif-all-images.tar(核心命令,包含所有所需镜像)
docker save -o rdif-all-images.tar mysql:8.0 redis:7-alpine mcr.microsoft.com/dotnet/aspnet:8.0 nginx:alpine
  • 关键参数:-o 指定输出的 tar 文件名和路径;后面跟所有需要打包的镜像名:版本,用空格分隔。

方式 2:单个镜像单独打包(按需使用,适合单独更新镜像)

如果后续只需更新某个镜像(如仅更新 MySQL),可单独打包该镜像,命令如下:

bash 复制代码
# 打包MySQL8.0为单独的tar文件
docker save -o mysql_8.0.tar mysql:8.0
# 打包Redis7-alpine为单独的tar文件
docker save -o redis_7_alpine.tar redis:7-alpine
# 打包.NET8运行时为单独的tar文件
docker save -o dotnet_aspnet_8.0.tar mcr.microsoft.com/dotnet/aspnet:8.0
# 打包Nginx-alpine为单独的tar文件
docker save -o nginx_alpine.tar nginx:alpine

5.4 验证本地打包成功

打包完成后,在本地目标目录(如桌面)能看到生成的 tar 文件(如 rdif-all-images.tar),文件大小约 2-3G(正常大小),即打包成功;后续用 MobaXterm/Xftp/WinSCP 将该 tar 文件上传到 CentOS 服务器的/root/rdif-docker目录即可。

六、服务器端核心部署步骤(离线导入镜像 + 启动服务)

6.1 离线导入镜像(解决网络拉取超时,核心步骤)

将本地打包好的镜像 tar 文件上传到服务器后,执行以下命令离线导入,无需再联网拉取:

bash 复制代码
# 1. 进入项目根目录(确保镜像tar包在该目录下)
cd /root/rdif-docker

# 2. 查看镜像tar包是否存在(能看到rdif-all-images.tar即正常)
ls -l

# 3. 离线导入所有镜像(耐心等待,约1-2分钟,镜像包约2-3G)
docker load -i rdif-all-images.tar

# 4. 验证镜像导入成功(能看到4个所需镜像即正常)
docker images

✅ 成功标识:docker images输出中包含mysql:8.0redis:7-alpinenginx:alpinemcr.microsoft.com/dotnet/aspnet:8.0等镜像。

6.2 启动所有服务(核心命令,一键启动)

所有配置和准备工作完成后,执行以下命令,一键启动所有容器,无需手动逐个启动:

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

# 后台启动所有服务(-d:后台运行,无需构建,因为前后端已打包)
docker-compose up -d

# 查看所有服务运行状态(所有服务State列显示Up即正常)
docker-compose ps

✅ 成功标识:docker-compose ps输出中,mysql、redis、backend、nginx 四个服务的 State 列均为Up (healthy)Up

6.3 验证服务启动成功(逐个验证,确保无问题)

启动后,逐个验证前端、后端、数据库、Redis 是否正常,避免后续使用时出现问题:

(1)验证后端服务(Swagger 访问,核心)

.NET8 后端默认集成 Swagger,访问地址:http://192.168.1.100:58588/swagger/index.html(替换成自己的虚拟机 IP 和后端端口)

  • ✅ 成功标识:浏览器能正常打开 Swagger 页面,无报错,能看到所有接口;
  • ❌ 失败排查:如果打不开,执行docker-compose logs -f backend,查看后端实时日志,排查问题(如端口监听错误、MySQL 连接失败)。

(2)验证前端服务(静态页面访问)

前端由 Nginx 代理,访问地址:http://192.168.1.100:6866(替换成自己的虚拟机 IP 和前端端口)

  • ✅ 成功标识:浏览器能正常打开 Vue3 前端页面,样式、图片正常显示;
  • ❌ 失败排查:打不开则查看 Nginx 日志,命令:docker-compose logs -f nginx,检查 Nginx 配置是否正确、dist 目录是否挂载成功。

(3)验证 MySQL 服务(本地工具连接)

用 Navicat 或 DBeaver 等本地数据库工具,连接虚拟机上的 MySQL:

  • 主机:192.168.1.100(虚拟机 IP)
  • 端口:3306
  • 用户名:guosisoft(自定义的 MySQL 用户名)
  • 密码:Mysql@123456(自定义的密码)
  • 数据库:rdif_vue3(自定义的数据库名)
  • ✅ 成功标识:能正常连接,能看到 init.sql 初始化的库、表和数据。

(4)验证 Redis 服务(可选,按需验证)

用 Redis 客户端工具(如 Another Redis Desktop Manager),连接虚拟机上的 Redis:

  • 主机:192.168.1.100(虚拟机 IP)
  • 端口:6379
  • 密码:Redis@123456(自定义的 Redis 密码)
  • ✅ 成功标识:能正常连接,能执行 set、get 等命令。

七、部署过程中遇到的坑及解决方案(重点,避坑指南)

这部分是核心,记录了我部署过程中遇到的所有问题,每个问题都有详细的排查过程和解决方案,亲测有效,帮你少走弯路。

坑 1:Docker 镜像拉取超时,提示 "dial tcp ... i/o timeout"

  • 现象:执行docker pull mysql:8.0docker-compose up -d时,拉取镜像超时,提示访问registry-1.docker.io失败;
  • 排查:虚拟机能 ping 通百度(外网正常),镜像加速配置也生效,但仍拉取超时,推测是虚拟机网络有隐性限制(如内网拦截);
  • 解决方案:采用离线导入镜像方案 (前文 5、6 小节已详细说明),在本地能联网的电脑上拉取镜像、打包成 tar 包,上传到服务器后用docker load -i导入,彻底跳过网络拉取。

坑 2:后端服务启动成功,但访问http://192.168.1.100:58588打不开 Swagger

  • 现象:docker-compose ps显示 backend 服务是 Up 状态,但浏览器访问后端地址打不开,查看后端日志,显示Now listening on: http://[::]:8080
  • 排查:后端容器默认监听 8080 端口,但 docker-compose.yml 中映射的是 58588:58588,端口不匹配,导致访问不通;
  • 解决方案:在 backend 服务的 environment 中,添加ASPNETCORE_URLS: "http://*:58588",强制.NET8 容器内监听 58588 端口,和端口映射一致,重启后端容器即可。

坑 3:后端日志提示 "Unable to connect to any of the specified MySQL hosts"

  • 现象:后端启动后,日志持续报错,无法连接 MySQL,但本地 Navicat 能正常连接虚拟机上的 MySQL;

  • 排查:本地能连接,说明 MySQL 端口映射正常;后端容器和 MySQL 容器在同一 Docker 网络,应该用容器名(mysql)访问,而非虚拟机 IP,排查后发现连接字符串正确,但后端启动早于 MySQL 初始化完成;

  • 解决方案:

    1. 给 MySQL 服务添加健康检查(前文 docker-compose.yml 中的 healthcheck 配置),检测 MySQL 是否真正就绪;
    2. 后端服务的 depends_on 中,添加condition: service_healthy,确保 MySQL 健康检查通过后,再启动后端;
    3. 给 MySQL 连接字符串补充AllowPublicKeyRetrieval=True;SslMode=None,解决容器内连接 MySQL 的公钥检索和 SSL 限制。

坑 4:执行docker-compose down提示配置无效,"Unsupported config option for services.backend: 'condition'"

  • 现象:执行 docker-compose 命令时,提示配置无效,不支持conditionstart_period
  • 排查:查看 docker-compose 版本,发现是 1.x 版本,不支持新版配置项;
  • 解决方案:升级 Docker Compose 到 V2 版本(前文二、4 小节已详细说明),升级后即可支持所有配置。

坑 5:MySQL 容器时间比北京时间慢 8 小时

  • 现象:MySQL 中查询当前时间(select now();),比北京时间慢 8 小时,影响项目时间相关功能;

  • 排查:Docker 容器默认使用 UTC 世界标准时间,国内使用东八区(CST),两者相差 8 小时;

  • 解决方案:

    1. 给 MySQL 服务添加环境变量TZ: Asia/Shanghai,强制容器时区为东八区(核心);
    2. 挂载宿主机的时区文件(/etc/localtime:/etc/localtime:ro),双重保障时区同步;
    3. 给后端、Nginx、Redis 容器也添加相同的TZ环境变量,实现全服务时间统一;
    4. 重建 MySQL 容器(环境变量修改后,单纯 restart 不生效,需docker-compose stop mysql && docker-compose rm -f mysql && docker-compose up -d mysql)。

坑 6:虚拟机重启后,所有 Docker 服务都需要手动启动

  • 现象:虚拟机重启后,Docker 服务和所有容器都停止了,需要手动执行systemctl start dockerdocker-compose up -d
  • 排查:Docker 服务未设置开机自启,容器的restart: always配置,需要 Docker 服务启动后才会生效;
  • 解决方案:设置 Docker 服务开机自启,命令:systemctl enable docker,后续虚拟机重启后,Docker 会自动启动,所有容器也会自动后台启动。

坑 7:本地打包镜像后,服务器导入提示 "no such image"

  • 现象:执行docker load -i rdif-all-images.tar时,提示镜像不存在;
  • 排查:本地拉取的镜像版本和服务器 docker-compose.yml 中配置的版本不一致,或打包时镜像名拼写错误;
  • 解决方案:本地执行docker images确认镜像名和版本,确保和 docker-compose.yml 中完全一致,重新打包并上传。

坑 8:虚拟机重启后IP变更问题

首先查看本地网络配置,如下参考:

虚拟机网络适配器设置为桥接模式。

在linux中执行命令:

bash 复制代码
vim /etc/sysconfig/network-scripts/ifcfg-ens33

保存后重启网络服务

复制代码
systemctl restart network

八、常用 Docker/Compose 管理命令(日常运维必备)

部署完成后,日常维护、排错,都会用到以下命令,整理好放在这里,方便后续使用(所有命令均亲测可用)。

(一)Docker 基础命令(全局通用)

bash 复制代码
# 1. Docker服务管理(核心,开机自启/启停)
systemctl start docker        # 启动Docker服务
systemctl stop docker         # 停止Docker服务
systemctl restart docker      # 重启Docker服务
systemctl enable docker       # Docker开机自启(永久生效)
systemctl disable docker      # 关闭Docker开机自启
systemctl status docker       # 查看Docker服务状态
systemctl is-enabled docker   # 检查Docker是否开机自启(输出enabled则是)

# 2. 容器管理(日常排错、启停常用)
docker ps                     # 查看**运行中**的容器
docker ps -a                  # 查看**所有**容器(运行+停止)
docker start <容器名/ID>       # 启动指定容器(用容器名,更方便)
docker stop <容器名/ID>        # 停止指定容器
docker restart <容器名/ID>     # 重启指定容器(如后端容器:docker restart rdif-backend)
docker rm <容器名/ID>          # 删除指定停止的容器
docker rm -f <容器名/ID>       # 强制删除**运行中**的容器(谨慎使用)
docker exec -it <容器名/ID> /bin/bash  # 进入容器交互终端(Alpine镜像用sh替代/bin/bash)
docker logs <容器名/ID>       	# 查看容器日志(最新)
docker logs -f <容器名/ID>   		# 实时查看容器日志(排错核心,重点)
docker logs --tail 100 <容器名/ID> # 查看容器最后100行日志

# 3. 镜像管理(离线导入/打包常用)
docker images                 # 查看本地所有镜像
docker rmi <镜像名/ID>         # 删除指定镜像(无容器依赖时)
docker rmi -f <镜像名/ID>      # 强制删除镜像(忽略容器依赖,谨慎,如:docker rmi -f mysql:8.0)
docker load -i <镜像tar包>    # 离线导入镜像(如:docker load -i rdif-all-images.tar)
docker save -o <输出tar包> <镜像名> # 打包本地镜像为tar包(如:docker save -o mysql8.tar mysql:8.0)
docker pull <镜像名:版本>      # 拉取远程镜像
docker system prune -f        # 清理无用容器/镜像/缓存(无风险,推荐定期执行)

# 4. 数据卷/网络管理(本次自动创建,按需查看)
docker volume ls              # 查看所有Docker数据卷(保存MySQL/Redis数据)
docker network ls             # 查看所有Docker网络(本次为rdif-network)

(二)Docker Compose 命令(项目管理核心,需在项目根目录执行)

bash 复制代码
# 1. 核心启停命令(最常用)
docker-compose up -d          # 后台启动所有服务(无构建,本次核心用)
docker-compose up -d --build  # 后台启动+构建镜像(有Dockerfile时用)
docker-compose down           # 停止并删除所有容器(保留数据卷/镜像,安全)
docker-compose stop           # 仅停止所有容器(不删除,容器仍存在)
docker-compose start          # 启动已停止的所有容器
docker-compose restart        # 重启所有服务(配置未改时用)
docker-compose restart backend # 重启指定服务(如后端,无需重启所有服务)

# 2. 状态/日志查看(排错常用)
docker-compose ps             # 查看所有服务运行状态(看是否Up)
docker-compose logs -f        # 实时查看所有服务日志
docker-compose logs -f <服务名> # 实时查看指定服务日志(如docker-compose logs -f backend,实时查看后端日志(排错重点)

# 3. 其他常用
docker-compose config         # 验证docker-compose.yml配置语法是否正确(避免配置错误)
docker-compose rm <服务名>     # 删除指定停止的服务容器

九、开机自启配置(无需手动干预,虚拟机重启自动恢复)

前面已经提到,这里再单独整理一遍,确保虚拟机重启后,所有服务自动启动,无需手动操作:

bash 复制代码
# 1. 确保Docker服务开机自启(已配置,再次验证)
systemctl enable docker
systemctl is-enabled docker   # 输出enabled即成功

# 2. 验证容器自动重启策略(docker-compose.yml已配置restart: always)
docker-compose config | grep -i restart # 输出4个restart: always即正常

# 3. 测试验证(可选,手动重启虚拟机)
reboot # 重启虚拟机,重新连接后,直接访问服务,确认能正常打开

✅ 成功标识:虚拟机重启后,无需执行任何命令,docker-compose ps显示所有服务都是 Up 状态,前端、后端、Swagger 都能正常访问。

十、前后端更新方法(日常维护,简单高效)

后续需要更新前端或后端代码时,无需重新部署整个环境,直接替换打包 / 发布文件,重启对应服务即可,步骤如下:

1. 后端更新(VS2022+ 重新发布后)

  1. 本地 VS2022+ 重新发布.NET8 项目,生成新的 publish 文件夹;
  2. 用 Xftp 工具,将新的 publish 文件夹,覆盖服务器/root/rdif-docker/backend/下的旧 publish 文件夹;
  3. 重启后端容器,更新生效:
bash 复制代码
cd /root/rdif-docker
docker-compose restart backend

2. 前端更新(Vue3 重新打包后)

  1. 本地 Vue3 项目重新打包(yarn build/npm run build),生成新的 dist 文件夹;
  2. 用 Xftp 工具,将新的 dist 文件夹,覆盖服务器/root/rdif-docker/frontend/下的旧 dist 文件夹;
  3. 重启 Nginx 容器,更新生效:
bash 复制代码
cd /root/rdif-docker
docker-compose restart nginx

十一、总结与注意事项(最后提醒,避坑收尾)

总结

本次部署基于Docker Compose实现了.NET8 后端(已发布)+Vue 前端(已打包)+MySQL8+Redis+Nginx 的整套多服务容器化部署,全程亲测可复现,核心亮点:

  1. 采用离线镜像导入方案,解决服务器网络拉取镜像超时问题,附本地拉取 + 打包完整步骤,新手也能上手;
  2. 前后端无需容器内编译构建,直接挂载本地打包 / 发布文件,启动更快、更新更方便,适合已完成本地开发的项目;
  3. 解决了端口不通、MySQL 连接失败、时区时差、容器启动顺序、配置兼容、开机自启等 6 大核心问题,附详细避坑方案;
  4. 所有配置集中在docker-compose.yml,一键启停、易维护、易迁移,虚拟机重启后服务自动恢复,无需手动干预;
  5. 全程采用轻量版镜像(alpine),降低服务器资源占用,适合个人测试 / 小型项目部署。

注意事项(重点避坑,生产环境必看)

  1. 版本强一致:本地拉取的镜像版本、docker-compose.yml 中的镜像版本、后端 / 前端的运行依赖版本,必须完全一致,避免兼容问题;

  2. 容器间通信规则 :同一 Docker 自定义网络内,服务间访问必须用容器名 / 服务名 (如后端连 MySQL 用server=mysql),而非服务器 IP;本地工具(Navicat/Redis 客户端)连接才用服务器 IP;

  3. 环境变量修改规则 :修改容器的环境变量(如 TZ、ASPNETCORE_URLS、密码)后,必须重建容器stop+rm+up -d),单纯restart不会加载新配置;

  4. 数据持久化 :MySQL/Redis 数据通过 Docker数据卷 持久化,docker-compose down不会删除数据卷,但手动执行docker volume rm会丢失数据,谨慎操作;

  5. 敏感信息保护 :本文中的 IP、密码、镜像加速地址均为虚拟示例,实际部署请替换为自己的真实信息,并设置复杂密码(字母 + 数字 + 特殊符号);

  6. 生产环境优化

    :本文为个人测试环境

    配置,生产环境需做以下优化:

    • 开启防火墙,仅开放项目所需端口(6866/58588/3306),执行firewall-cmd --add-port=端口/tcp --permanent && firewall-cmd --reload
    • 禁用 root 账号直接登录,创建普通用户并赋予 Docker 操作权限;
    • 配置 SSL 证书,实现 HTTPS 访问;
    • 对 MySQL/Redis 做主从复制 / 集群,提升可用性;
  7. 排错核心思路 :任何服务启动失败、访问不通,优先查看容器日志docker-compose logs -f 服务名),日志是定位问题的最直接依据;

  1. 镜像备份:服务器端的离线镜像 tar 包建议保留,后续服务器重装可直接导入,无需重新本地打包。

整个部署过程虽然踩了不少坑,但核心问题都是忽略了容器化的细节(如端口匹配、时区配置、容器通信规则、启动顺序),只要按步骤做好配置、逐一验证,就能顺利完成部署。Docker Compose 的编排能力让多服务部署变得简单,也是后端开发必备的技能之一。

希望这篇详细的部署记录,能帮到有同样需求的朋友,少走弯路,高效完成.NET8+Vue 项目的 Docker 容器化部署。如果大家在部署过程中遇到其他问题,欢迎在评论区留言,一起交流解决~

十二、结语

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

更多技术文章请往:

www.guosisoft.com/article

www.rdiframework.net/article

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

相关推荐
mCell8 小时前
如何零成本搭建个人站点
前端·程序员·github
mCell9 小时前
为什么 Memo Code 先做 CLI:以及终端输入框到底有多难搞
前端·设计模式·agent
恋猫de小郭9 小时前
AI 在提高你工作效率的同时,也一直在增加你的疲惫和焦虑
前端·人工智能·ai编程
少云清9 小时前
【安全测试】2_客户端脚本安全测试 _XSS和CSRF
前端·xss·csrf
萧曵 丶9 小时前
Vue 中父子组件之间最常用的业务交互场景
javascript·vue.js·交互
银烛木9 小时前
黑马程序员前端h5+css3
前端·css·css3
m0_607076609 小时前
CSS3 转换,快手前端面试经验,隔壁都馋哭了
前端·面试·css3
听海边涛声9 小时前
CSS3 图片模糊处理
前端·css·css3
IT、木易9 小时前
css3 backdrop-filter 在移动端 Safari 上导致渲染性能急剧下降的优化方案有哪些?
前端·css3·safari
0思必得010 小时前
[Web自动化] Selenium无头模式
前端·爬虫·selenium·自动化·web自动化