保姆级微服务部署教程
大家好,我是鱼皮。
项目上线是每位学编程同学必须掌握的基本技能。之前我已经给大家分享过很多种上线单体项目的方法了,今天再出一期微服务项目的部署教程,用一种最简单的方法,带大家轻松部署微服务项目。
开始之前,先做个小调研啊,大家更喜欢看 “真实踩坑版” 的教程还是 “压缩纯净版” 的教程呢?
本期教程我还是保持自己一贯的风格,依然是保姆级教程,包含了一些踩坑过程和解决方案,大家跟着做就完事儿~
更推荐看视频版:https://www.bilibili.com/video/BV1Cp4y1F7eA/
项目介绍
这次的教程以我带大家全程直播手把手开发的、基于 Spring Cloud + Docker 代码沙箱的 在线判题系统 为例,进行演示。
这个项目的核心功能是让用户能够在线选题和做题,然后由系统自动判题:

项目用到的核心依赖包括:MySQL 数据库、Redis 缓存、RabbitMQ 消息队列、Nacos 注册中心
涉及的核心服务包括:用户服务、题目服务、判题服务(代码沙箱)、Gateway 网关服务

传统部署
对于这样一个项目,如果我们还用传统单机项目的部署方式,一个个打 jar 包、用 Java 命令来启动,会有哪些问题呢?
- 要一个个安装依赖,比如 MySQL 数据库、Redis、消息队列、Nacos,非常麻烦!
- 要一个个打 jar 包、一个个手动运行 jar 包,非常麻烦!
- 不方便集中观察所有服务的运行状态和资源占用情况
所以,为了解决这些问题,我们会选用一种更高效的微服务部署方式 —— Docker Compose。
Docker Compose 介绍
在介绍 Docker Compose 前,先简单介绍下 Docker。
Docker 是一种容器技术,允许开发者将应用程序和所有依赖项(如代码、库、配置等)制作为 镜像。可以把镜像简单理解为软件安装包,可以在不同的计算机上通过它快速安装和启动应用程序(容器),这些程序独立隔离地运行,不受外部环境的影响。

如果要部署微服务项目,可能要启动多个 Docker 容器,比如 MySQL 容器、用户服务容器等。这时就需要 Docker Compose 了。它是一个容器编排助手,用于集中管理多个 Docker 容器的启动和协同工作。可以在一个配置文件中集中定义所有容器以及它们的关系。然后,可以使用一行命令启动所有容器,而不需要手动运行多个命令。

需要注意的是,Docker Compose 通常适用于把所有微服务部署在同一台服务器的场景,在真实的企业级项目中,往往会使用 K8S 等更专业的容器编排和自动化部署工具,更方便地在多个服务器上部署容器。
部署流程
了解了 Docker 和 Docker Compose 的作用后,我们来快速了解下部署流程,分为 2 大阶段 —— 本地部署和服务器部署。
一、本地部署
- 梳理服务部署表格
- Maven 子父模块打包
- Dockerfile 编写
- 编写环境依赖配置
- 编写服务配置
- 调整程序配置
- 测试访问
二、服务端部署
- 准备服务器
- Docker Compose 安装
- 同步文件
- 获取 jar 包
- 服务启动
- 测试访问
一、本地部署
第一阶段是本地部署,也可以叫做部署准备。
强烈建议大家,比起直接操作线上服务器,最好是先在本地把所有的流程跑通,风险更低、效率更高。
这里我使用的是 Mac 操作系统,已经安装了 Docker Desktop 软件,管理 Docker 容器会更方便一些。

对于本地没有 Docker 环境的同学,这一阶段仔细看一遍有个印象就足够了。可以直接拿我调试好的配置文件在服务器上部署,而不用自己调试。
怎么样,够贴心吧!可以叫我 “保姆鱼皮”,简称 “保姆皮”。

1.1、梳理服务部署表格
在部署微服务项目前,首先要规划好要部署哪些服务、以及各服务的关键信息,比如服务名称、版本号、占用端口号、关键配置等。
对于我的在线判题项目,梳理好的服务表格如下:
| 服务名称 | 英文名 | 端口号 | 版本号 | 服务类别 |
|---|---|---|---|---|
| 数据库 | mysql | 3306 | v8 | 环境依赖 |
| 缓存 | redis | 6379 | v6 | 环境依赖 |
| 消息队列 | rabbitmq | 5672, 15672 | v3.12.6 | 环境依赖 |
| 注册中心 | nacos | 8848 | v2.2.0 | 环境依赖 |
| 网关服务 | gateway | 8101 | java 8 | 业务服务 |
| 用户服务 | yuoj-backend-user-service | 8102 | java 8 | 业务服务 |
| 题目服务 | yuoj-backend-question-service | 8103 | java 8 | 业务服务 |
| 判题服务 | yuoj-backend-judge-service | 8104 | java 8 | 业务服务 |
为什么这里我要划分服务类别为 “环境依赖” 和 “业务服务” 呢?
因为在启动服务时,必须要先启动环境依赖,才能启动业务服务,否则就会报类似 “无法连接数据库” 之类的错误。
1.2、Maven 子父模块打包
对于微服务项目,我们通常是使用 Maven 的子父模块功能进行管理的。需要部署项目时,不用针对每个子服务单独执行 mvn package 命令进行打包,而是可以一键打包所有服务。
想要实现这个功能,需要给子父模块的依赖文件(pom.xml)进行一些配置,主要包括:
1)父模块配置
在父模块的 pom.xml 文件中引入 spring-boot-maven-plugin 即可,注意一定不要配置 configuration 和 repackage!
示例代码如下:
<plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>${spring-boot.version}</version>
</plugin>
2)子模块配置
修改所有需要启动 Spring Boot 的服务(用户服务、题目服务、判题服务、网关服务)的子模块 pom.xml 文件。
主要是增加 executions 配置,使用 spring-boot-maven-plugin 的 repackage 命令来构建子模块,从而自动在构建时将公共模块的依赖打入 jar 包。
示例代码如下:
<plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><executions><execution><id>repackage</id><goals><goal>repackage</goal></goals></execution></executions>
</plugin>
1.3、Dockerfile 编写
Dockerfile 是定义 Docker 容器镜像构建过程的文件,包括容器镜像使用的基础环境、容器内的依赖和文件、容器的配置、启动命令等。
有了 Dockerfile,我们就能很轻松地制作出自己的容器镜像。
虽然 Dockerfile 的写法并不复杂,但我还是建议大家尽量不要自己写,直接去网上找个差不多的项目,复制粘贴别人的 Dockerfile 就足够了!
这里鱼皮给大家提供 2 种常用的 Spring Boot 项目的 Dockerfile。
1)复制 jar 包版
思路:在本地打好 jar 包后,复制 jar 包到容器中运行
示例代码如下:
# 基础镜像
FROM openjdk:8-jdk-alpine# 指定工作目录
WORKDIR /app# 将 jar 包添加到工作目录,比如 target/yuoj-backend-user-service-0.0.1-SNAPSHOT.jar
ADD {本地 jar 包路径} . # 暴露端口
EXPOSE {服务端口号}# 启动命令
ENTRYPOINT ["java","-jar","/app/{jar 包名称}","--spring.profiles.active=prod"]
2)Maven 打包版
思路:复制本地代码到容器中,在容器中使用 Maven 打包再运行
示例代码如下:
# 基础镜像
FROM maven:3.8.1-jdk-8-slim as builder# 指定工作目录
WORKDIR /app# 添加源码文件
COPY pom.xml .
COPY src ./src# 构建 jar 包,跳过测试
RUN mvn package -DskipTests# 启动命令
ENTRYPOINT ["java","-jar","/app/target/{jar 包名称}","--spring.profiles.active=prod"]
此处由于我们的微服务项目可以一键打好所有子服务的 jar 包,就没必要每个服务单独在容器中打包了,所以选择第一种方式的 Dockerfile。
我们需要给每个 Spring Boot 服务(用户服务、题目服务、判题服务、网关服务)都编写一个 Dockerfile,放到每个子服务的根目录下。

以用户服务为例,示例代码如下:
# 基础镜像
FROM openjdk:8-jdk-alpine# 指定工作目录
WORKDIR /app# 将 jar 包添加到工作目录,比如 target/yuoj-backend-user-service-0.0.1-SNAPSHOT.jar
ADD target/yuoj-backend-user-service-0.0.1-SNAPSHOT.jar .# 暴露端口
EXPOSE 8102# 启动命令
ENTRYPOINT ["java","-jar","/app/yuoj-backend-user-service-0.0.1-SNAPSHOT.jar","--spring.profiles.active=prod"]
建议先在本地利用 IDEA 开发工具调通镜像构建流程,确保每个 Dockerfile 都是可以成功制作镜像的:

查看容器的启动日志,发现能够启动服务、看到 Spring 图标即可:

1.4、编写环境依赖配置
接下来,我们就要编写 Docker Compose 的配置文件了,可以根据配置文件快速启动多个服务。
之前我们已经梳理了服务部署表格,将服务划分为了 “环境依赖” 和 “业务服务”。
由于业务服务依赖 MySQL 等环境依赖,所以需要拆分 2 套 Docker Compose 的配置文件,分别为 docker-compose.env.yml 环境配置和 docker-compose.service.yml 业务服务配置,保证先成功启动依赖,再启动服务。
学过 Docker Compose 的同学可能听说过 depends_on 配置,也能决定服务的启动顺序。但是千万注意,depends_on 并不会等待服务完全就绪,只是确保它们在启动时的顺序,并不稳定。
如何编写 Docker Compose 文件呢?
和 Dockerfile 一样,直接去网上找现成的 Docker Compose file,复制粘贴过来略做修改就能使用了~
再配合以下 2 个网站,完全无需记忆 Docker Compose 的写法!
- Docker Compose file 官方文档:https://docs.docker.com/compose/compose-file/
- 搜索现成的 Docker 镜像:https://hub.docker.com/
当然,现在 AI 时代了,还有更简单的方式!
直接把我们整理好的服务部署需要喂给 GPT,让 AI 帮我们生成配置即可~
示例 prompt:
现在我需要用 docker compose 来部署 mysql 8(3306 端口)username=root,password=123456
redis 6(无密码,6379端口)、rabbitmq v.3.12.6( 5672 端口 password: guest,username: guest)、nacos 2.2.0(8848端口);还有 4 个本地的 springboot 服务(名称分别为:yuoj-backend-user-service 8102端口、yuoj-backend-question-service 8103端口、yuoj-backend-judge-service 8104端口、yuoj-backend-gateway 8101 端口),每个服务本地目录都有一个 Dockerfile,请帮我自动生成 docker compose 的 yml 配置文件,要求这些服务网络能够连通
效果还是非常不错的,只要你描述地足够清楚,生成的配置完全可用!

由于这篇文章是教程嘛,我就带大家通过调试的方式一步步完成 Docker Compose 文件,最后会把完整的 Docker Compose 文件给大家分享出来,大家直接用就行了~
我们要分别在 Docker Compose 中定义 4 大基础依赖,包括 MySQL、Redis、RabbitMQ 和 Nacos。
1)MySQL
我们不仅要创建一个 MySQL 服务,还要在创建服务后自动创建我们需要的库表结构。
所以需要先准备数据库 SQL 脚本文件,里面包含了建库、建表语句,我们把它放在微服务项目根目录的 mysql-init 文件夹中:
鱼皮带大家做的每个项目都提供了现成的建表语句,这是一个非常好的开发习惯,便于其他人快速启动你的项目。

由于要在本地启动 MySQL,还需要定义一个文件夹 .mysql-data 来存放 MySQL 的持久化数据,防止容器重启后数据丢失。
做好这两点后,就可以编写 docker-compose.env.yml 文件了,先只写一个 MySQL 服务,示例代码如下:
关键配置的含义我都写到注释里了
version: '3'
services:mysql:image: mysql:8 # 使用的镜像container_name: yuoj-mysql # 启动的实例名称environment:MYSQL_ROOT_PASSWORD: 123456 # root 用户密码ports:- "3306:3306" # 端口映射volumes:- ./.mysql-data:/var/lib/mysql # 将数据目录挂载到本地目录以进行持久化- ./mysql-init:/docker-entrypoint-initdb.d # 自动执行启动脚本restart: always # 崩溃后自动重启networks:- mynetwork # 指定网络
networks:mynetwork: # 自定义网络,实现网络互通和隔离
写好配置文件后,可以直接在 IDEA 里执行 Docker Compose 文件,调试 MySQL 的运行:

运行成功后,我们可以在本地成功连接数据库:

2)Redis
Redis 服务的定义和启动操作和 MySQL 服务几乎一致,Redis 的 Docker Compose 配置示例代码如下:
version: '3'
services:redis:image: redis:6container_name: yuoj-redisports:- "6379:6379"networks:- mynetworkvolumes:- ./.redis-data:/data # 持久化
networks:mynetwork:
然后在本地执行 Docker Compose 文件,启动 Redis 服务,并且尝试进入 Terminal 来调试 Redis:

3)RabbitMQ
同 MySQL 和 Redis,RabbitMQ 的 Docker Compose 配置示例代码如下:
version: '3'
services:rabbitmq:image: rabbitmq:3.12.6-management # 支持管理面板的消息队列container_name: yuoj-rabbitmqenvironment:RABBITMQ_DEFAULT_USER: guestRABBITMQ_DEFAULT_PASS: guestports:- "5672:5672"- "15672:15672" # RabbitMQ Dashboard 端口volumes:- ./.rabbitmq-data:/var/lib/rabbitmq # 持久化networks:- mynetwork
networks:mynetwork:
本地执行 Docker Compose 文件,启动 RabbitMQ 服务,然后可以访问 localhost:15672 查看到管理面板,就表示启动成功了~
账号密码都是 guest

4)Nacos
和其他服务一样,Nacos 也需要编写 Docker Compose 配置。
但是在选择 Nacos 镜像时必须要注意,建议选择支持 linux/arm64 架构的镜像版本,比如 v2.2.0-slim,否则后面可能会无法运行:

Nacos 示例配置文件如下:
version: '3'
services:nacos:image: nacos/nacos-server:v2.2.0-slimcontainer_name: yuoj-nacosports:- "8848:8848"volumes:- ./.nacos-data:/home/nacos/datanetworks:- mynetworkenvironment:- MODE=standalone # 单节点模式启动- PREFER_HOST_MODE=hostname # 支持 hostname- TZ=Asia/Shanghai # 控制时区
networks:mynetwork:
然后在本地执行 Docker Compose 启动 Nacos,访问 localhost:8848/nacos 能够看到管理页面,就表示运行成功了~
管理页面的账号和密码默认都是 nacos

完整 Docker Compose 文件
分别调试完上述服务后,我们把所有的配置拼在一起,就得到了完整的文件,文件名为 docker-compose.env.yml。
完整代码如下:
version: '3'
services:mysql:image: mysql:8 # 使用的镜像container_name: yuoj-mysql # 启动的实例名称environment:MYSQL_ROOT_PASSWORD: 123456 # root 用户密码ports:- "3306:3306" # 端口映射volumes:- ./.mysql-data:/var/lib/mysql # 将数据目录挂载到本地目录以进行持久化- ./mysql-init:/docker-entrypoint-initdb.d # 启动脚本restart: always # 崩溃后自动重启networks:- mynetwork # 指定网络redis:image: redis:6container_name: yuoj-redisports:- "6379:6379"networks:- mynetworkvolumes:- ./.redis-data:/data # 持久化rabbitmq:image: rabbitmq:3.12.6-management # 支持管理面板的消息队列container_name: yuoj-rabbitmqenvironment:RABBITMQ_DEFAULT_USER: guestRABBITMQ_DEFAULT_PASS: guestports:- "5672:5672"- "15672:15672" # RabbitMQ Dashboard 端口volumes:- ./.rabbitmq-data:/var/lib/rabbitmq # 持久化networks:- mynetworknacos:image: nacos/nacos-server:v2.2.0-slimcontainer_name: yuoj-nacosports:- "8848:8848"volumes:- ./.nacos-data:/home/nacos/datanetworks:- mynetworkenvironment:- MODE=standalone # 单节点模式启动- PREFER_HOST_MODE=hostname # 支持 hostname- TZ=Asia/Shanghai # 控制时区
networks:mynetwork:
1.5、编写业务服务配置
用同样的方式,我们可以编写业务服务的 Docker Compose 文件,文件名称 docker-compose.service.yml。
示例代码如下,其中需要格外关注的配置是 build 和 depends_on:
version: '3'
services:yuoj-backend-gateway:container_name: yuoj-backend-gatewaybuild: # 服务的 Docker 构建文件位置context: ./yuoj-backend-gatewaydockerfile: Dockerfileports:- "8101:8101"networks:- mynetworkyuoj-backend-user-service:container_name: yuoj-backend-user-servicebuild:context: ./yuoj-backend-user-servicedockerfile: Dockerfileports:- "8102:8102"networks:- mynetworkdepends_on: # 本服务依赖的服务,控制启动先后顺序- yuoj-backend-gatewayyuoj-backend-question-service:container_name: yuoj-backend-question-servicebuild:context: ./yuoj-backend-question-servicedockerfile: Dockerfileports:- "8103:8103"networks:- mynetworkdepends_on:- yuoj-backend-user-service- yuoj-backend-gatewayyuoj-backend-judge-service:container_name: yuoj-backend-judge-servicebuild:context: ./yuoj-backend-judge-servicedockerfile: Dockerfileports:- "8104:8104"networks:- mynetworkdepends_on:- yuoj-backend-user-service- yuoj-backend-question-service- yuoj-backend-gateway# 网络,不定义的话就是默认网络
networks:mynetwork:
1.6、调整程序配置
编写好上述配置文件后,本地尝试运行 Docker Compose 业务服务,结果发现:报错啦!依赖服务的地址访问不通!
这是由于之前我们的项目访问依赖服务时,全部是使用了固定的 IP 地址(比如 localhost),而容器内部的 localhost(或 127.0.0.1)通常指向容器本身,而不是宿主主机。所以为了在容器内访问其他服务,程序中应该使用服务名称而不是 localhost。
我们给每个 Spring Boot 服务都增加一套 prod 上线配置,在配置中更改服务调用地址。
用户服务、题目服务和判题服务的 application-prod.yml 配置修改如下:
# 生产环境配置文件
spring:# 数据库配置datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://mysql:3306/yuoj # localhost 改为 mysqlusername: rootpassword: 123456# Redis 配置redis:database: 1host: redis # localhost 改为 redisport: 6379timeout: 5000cloud:nacos:discovery:server-addr: nacos:8848 # localhost 改为 nacosrabbitmq:host: rabbitmq # localhost 改为 rabbitmqport: 5672password: guestusername: guest
Gateway 网关服务的配置修改如下:
spring:cloud:nacos:discovery:server-addr: nacos:8848 # localhost 改为 nacosgateway:routes:- id: yuoj-backend-user-serviceuri: lb://yuoj-backend-user-servicepredicates:- Path=/api/user/**- id: yuoj-backend-question-serviceuri: lb://yuoj-backend-question-servicepredicates:- Path=/api/question/**- id: yuoj-backend-judge-serviceuri: lb://yuoj-backend-judge-servicepredicates:- Path=/api/judge/**application:name: yuoj-backend-gatewaymain:web-application-type: reactive
server:port: 8101
knife4j:gateway:enabled: truestrategy: discoverdiscover:enabled: trueversion: swagger2
然后执行 mvn package 命令重新打包、执行 Docker Compose。
结果发现大多数服务都启动了,但是判题服务还有报错。

这是因为程序在创建消息队列时存在硬编码的变量,指定了 host 为 “localhost”,示例代码如下:
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
String EXCHANGE_NAME = "code_exchange";
channel.exchangeDeclare(EXCHANGE_NAME, "direct");
举这个例子是为了告诉大家,在程序中尽量不要写固定 IP 或域名,全部改为动态读取配置文件,便于修改。
示例修改后的代码如下:
/*** 用于创建测试程序用到的交换机和队列(只用在程序启动前执行一次)*/
@Slf4j
@Component
public class InitRabbitMqBean {@Value("${spring.rabbitmq.host:localhost}")private String host;@PostConstructpublic void init() {try {ConnectionFactory factory = new ConnectionFactory();factory.setHost(host);Connection connection = factory.newConnection();Channel channel = connection.createChannel();String EXCHANGE_NAME = "code_exchange";channel.exchangeDeclare(EXCHANGE_NAME, "direct");// 创建队列,随机分配一个队列名称String queueName = "code_queue";channel.queueDeclare(queueName, true, false, false, null);channel.queueBind(queueName, EXCHANGE_NAME, "my_routingKey");log.info("消息队列启动成功");} catch (Exception e) {log.error("消息队列启动失败");}}
}
1.7、测试访问
修复上述问题后,所有服务都可以通过 Docker Compose 文件启动了。
然后我们访问 localhost:8101/doc.html 网关地址,能够看到 Swagger 聚合接口文档。

依次调用用户注册 => 登录 => 获取登录用户信息 => 创建题目接口,全部执行成功。
至此,第一阶段就完成啦。
二、服务器部署
在第二阶段,我们的目标就是在真实的 Linux 服务器上部署微服务项目。有了第一阶段的准备,第二阶段简直可以说是易如反掌!
2.1、准备服务器
首先,我们要有一台 Linux 服务器。
选择服务器前,我们必须要评估下微服务项目的内存占用,千万别把服务器的内存买小了!
可以使用 Docker Desktop 直接查看内存占用,虚拟机内存大概占用了 8 个 G、容器实际内存占用了 4 个 G:

那我们搞多少内存的服务器呢?
我猜很多同学会说 8 G,奈何我天生反骨,明知山有虎偏向虎山行(主要是想省 💰),我就搞一台 2 核 4 G 的服务器吧(发行版是 CentOS 7.9 Linux),咱们来猜一猜它够不够部署这套有 4 个业务服务的微服务项目呢?
咱们一起来见证下!
2.2、Docker Compose 安装
有了服务器后,直接参考 Docker Compose 官方文档来安装。这里我们使用 Docker Compose V2 版本,相比 V1 版本统一了命令,使用更方便:
Docker Compose V2 地址:https://docs.docker.com/compose/migrate/
Docker Compose Linux 安装:https://docs.docker.com/compose/install/linux/#install-using-the-repository

安装过程很简单,跟着官方文档来就行了,主要包括以下几个步骤:
1)设定安装来源:
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
2)安装 Docker 和 Docker Compose:
sudo yum install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
3)启动 Docker:
sudo systemctl start docker
4)测试 Docker:
systemctl status docker
sudo docker run hello-world
2.3、同步文件
接下来,我们需要把本地折腾好的微服务项目源码上传到服务器上,可以选择用 FTP 或 SSH 连接文件手动上传文件。
我这里使用 JetBrains 开发工具的远程部署功能,可以配置文件自动上传,步骤如下:
1)进入远程部署配置

2)添加远程部署配置:
这里建议大家不要暴露自己的服务器 IP 啊,当你看到本文的时候,其实我已经把服务器的 IP 更换掉了哈哈哈哈哈哈哈哈!

3)指定连接的服务器配置:

4)配置本地文件和服务器文件路径映射:

5)开启自动上传:

6)首次需要手动上传文件。
上传前记得先删除无用的文件,然后右键项目根目录,点击部署上传代码:

上传成功,在服务器对应路径(/code/yuoj-backend-microservice)下能看到已上传的文件列表:

2.4、获取 jar 包
光把代码上传到服务器还是不够的,因为我们构建 Docker 镜像需要 jar 包。
有 2 种方式得到 jar 包:
- 本地执行
mvn package打好 jar 包,然后再上传 - 服务器上装 Maven,在服务器上打包
但是因为 jar 包比较大,频繁改动的话同步速度会比较慢,所以更建议第二种方式,步骤如下:
1)安装 Maven:
sudo yum install maven
2)安装好后,执行打包:
sudo mvn package
打包成功:

2.5、服务启动
所有一切准备就绪,接下来就是使用 Docker Compose 命令分别启动环境依赖和业务服务啦。
1)启动环境依赖
先使用 docker compose 一行命令启动环境依赖:
docker compose -f docker-compose.env.yml up
注意:
- 老版本使用 “docker-compose” 替代 “docker compose”
- 如果没有权限,命令前加上 “sudo”
然后喝杯咖啡,等待启动即可~
启动成功后,我们可以通过公网 IP 来尝试访问服务。
先进入到云服务商的服务器配置页,修改服务器的防火墙配置,放通以下端口:

然后像访问本地服务一样分别去访问 MySQL、Redis、RabbitMQ、Nacos 即可,应该都是成功的。

由于进程在前台启动会影响我们的操作,所以先按 ctrl + c 退出,加上 -d 参数让容器在后台启动:
sudo docker compose -f docker-compose.env.yml up -d
试着查看下 docker 容器的状态:
sudo docker stats
能够查看到所有容器的资源占用情况:

实话说,我开始紧张了,不知道 4 G 的内存够不够撑。。。
2)启动业务服务
确保环境依赖都启动成功后,接下来启动业务服务:
docker compose -f docker-compose.service.yml up
项目全部启动,看得很爽:

正常来说,应该会启动成功;但如果运气背,可能会有失败,比如我这的网关服务就启动失败了。
如果某个服务启动失败,可以再次单独只启动它,比如网关服务:
sudo docker compose -f docker-compose.service.yml up yuoj-backend-gateway
2.6、测试访问
最后,像验证本地微服务项目部署一样,访问线上网关的接口文档( http://你的服务器 IP:8101/doc.html ),依次调用用户注册 => 登录 => 获取登录用户信息 => 创建题目,全部成功~

最后使用 docker stats 命令查看 Docker 容器的状态,发现总共的内存占用大概 3 G,也就是说 4 G 内存的服务器是完全足够小型微服务项目的部署了~

至此,微服务项目部署教程就结束了。
最后
最后我再问个问题,如果我只有一台 2 G 内存的服务器,能否成功部署这套有 4 个业务服务的项目呢?如果能的话,又应该怎么部署呢?欢迎大家讨论。
下图是个小提示:

终于写完了这篇 6000 多字的教程,如果觉得本期内容有帮助的话,也欢迎点赞、收藏三连支持,谢谢大家!
相关文章:
保姆级微服务部署教程
大家好,我是鱼皮。 项目上线是每位学编程同学必须掌握的基本技能。之前我已经给大家分享过很多种上线单体项目的方法了,今天再出一期微服务项目的部署教程,用一种最简单的方法,带大家轻松部署微服务项目。 开始之前,…...
springboot 定时任务
springboot 定时任务 在Spring Boot中,你可以使用Spring的Scheduled注解来创建定时任务。以下是一个简单的示例: 首先,你需要在Spring Boot的主类上添加EnableScheduling注解以启用定时任务功能。 import org.springframework.boot.Spring…...
【大数据】HBase入门指南
原创不易,注重版权。转载请注明原作者和原文链接 文章目录 HBase特性Hadoop的限制基本概念NameSpaceTableRowKeyColumnTimeStampCell 存储结构HBase 数据访问形式架构体系HBase组件HBase读写流程读流程写流程 MemStore Flush参数说明 StoreFile Compaction参数说明触…...
零售数据分析模板鉴赏-品类销售结构报表
不管是服装零售,还是连锁超市或者其他,只要是零售行业就绕不过商品数据分析,那么商品数据分析该怎么做?奥威BI的零售数据分析方案早早就预设好相关报表模板,点击应用后,一键替换数据源,立得新报…...
竞赛 深度学习 机器视觉 车位识别车道线检测 - python opencv
0 前言 🔥 优质竞赛项目系列,今天要分享的是 🚩 深度学习 机器视觉 车位识别车道线检测 该项目较为新颖,适合作为竞赛课题方向,学长非常推荐! 🥇学长这里给一个题目综合评分(每项满分5分) …...
MySQL中使用函数会使索引失效?
文章目录 1、前置准备2、ChatGPT的答案3、实践证明SQL1SQL2SQL3SQL4SQL5 4、总结 1、前置准备 首先创建我们要测试的库表 CREATE TABLE lianhe_index (id int(11) NOT NULL AUTO_INCREMENT COMMENT id,name varchar(255) DEFAULT NULL,age int(11) DEFAULT NULL,number int(1…...
金和OA C6任意文件读取漏洞 复现[附POC]
文章目录 金和OA C6任意文件读取漏洞 复现[附POC]0x01 前言0x02 漏洞描述0x03 影响版本0x04 漏洞环境0x05 漏洞复现1.访问漏洞环境2.构造POC3.复现 0x06 修复建议 金和OA C6任意文件读取漏洞 复现[附POC] 0x01 前言 免责声明:请勿利用文章内的相关技术从事非法测试…...
IP地址技术原理
IP地址是互联网中的关键元素,它允许数据在全球范围内进行路由和传输。本文将深入探讨IP地址的技术原理,包括IP地址的结构、IP地址分配、子网划分和IPv4与IPv6之间的转换。 第一部分:IP地址基础 1.1IP地址是什么? IP地址…...
【网络安全】密码学基础
前言 密码学是网络安全的核心组成部分,它帮助我们保护信息,防止未经授权的访问。在这篇文章中,我们将从基础开始,深入了解密码学的基本概念和原理,包括加密、解密、密钥、哈希函数等。我们将尽可能使用简单的语言和实…...
AWS AD Connector 的网络配置
配置 AWS 的 AD Connector 通常遇到的都是些网络问题,且 AD Connector 本身屏蔽了一些网络细节,使得查找root cause往往有点困难,本文就把 AD Connector 网络问题梳理一下。 首先,需要搞清楚的是:AD Connector 是 Microsoft Active Directory 的一种代理,IAM可以通过它联…...
kafka详解(三)
2.2 Kafka命令行操作 2.2.1 主题命令行操作 1)查看操作主题命令参数 [aahadoop102 kafka]$ bin/kafka-topics.sh2)查看当前服务器中的所有topic (配置了环境变量不需要写bin/) [aahadoop102 kafka]$ bin/kafka-topics.sh --bootstrap-server hadoop10…...
[Spring] SpringMVC 简介(二)
目录 五、域对象共享数据 1、使用 ServletAPI 向 request 域对象共享数据 2、使用 ModelAndView 向 request 域对象共享数据 3、使用 Model、Map、ModelMap 向 request 域对象共享数据 4、向 session 域和 application 域共享数据 六、SpringMVC 的视图 1、ThymeleafVie…...
idea一些不太常用但是能提升编码效率的快捷键
Ctrl Shift V:从历史选择粘贴 从历史剪粘版中选择要粘贴的内容。Ctrl Shift Z:重做 恢复上一步撤销内容Shift F4:在新窗口中打开 比如要从另一个类中复制代码 可以把这个类在新窗口单独打开 用AltTab 来回切换复制Ctrl PageUpÿ…...
vsto word属性信息 并读取
要通过VSTO (Visual Studio Tools for Office) 读取和操作 Microsoft Word 文档的属性信息,您可以使用 C# 或 VB.NET 等 .NET 编程语言结合 VSTO 来实现。以下是一个示例,演示如何获取 Word 文档的属性信息:首先,确保您已经在 Vis…...
android之TextView自由选择复制
文章目录 前言一、效果图二、实现步骤1.OnSelectListener2.SelectionInfo类3.TextLayoutUtil类4.复制弹框的xml布局5.弹框背景Drawable6.倒三角Drawable7.复制工具类8.调用 总结 前言 根据时代进步,那些干产品的也叼砖起来了,今天就遇到一个需求&#x…...
【mysql】 bash: mysql: command not found
在linux 服务器上安装了mysql 也可以正常运行。 但是执行命令,系统提示:bash: mysql: command not found bash:mysql:找不到命令 执行的命令是: mysql -u root -h 127.0.0.1 -p由于系统默认会查找的 /usr/bin/ 中下的命令,如…...
鲲山科技:引入和鲸 ModelWhale,实现量化策略的高效迭代
量化投资是数据科学在金融行业的应用。 2023 年,量化行业的超额收益开始收敛,量化私募如何形成自身核心竞争力? 和鲸拜访客户鲲山科技(深圳),揭示其“弯道超车”的独家秘诀。 群体作战 年初至今ÿ…...
PFSK152 3BSE018877R1 有源滤波器的定义
PFSK152 3BSE018877R1 有源滤波器的定义 有源滤波器是以晶体管和运算放大器为基本元件设计的滤波电路。除了这些元件,有源滤波器的电路还包含电阻和电容,但不包含电感。 我们知道滤波器具有频率选择性。因此,有源滤波器电路使用晶体管和运算…...
WebDAV之π-Disk派盘 + 恒星播放器
想要拥有一款万能视频播放器,全能解码播放器,无需转码,支持所有格式的视频和音频,直接播放的播放器?那就选恒星播放器。 恒星播放器支持视频投屏,倍速播放,后台播放等功能,还能一键截图和录制gif动图。支持全格式超高清真4K解码,蓝光HDR低占用,支持ISO文件直出的播放…...
亚马逊,速卖通,敦煌产品测评补单攻略:低成本、高安全实操指南
随着电商平台的发展和消费者对产品质量的要求提升,测评补单成为了商家们提升销售和用户口碑的关键环节。然而,如何在保持成本低廉的同时确保操作安全,一直是卖家们面临的挑战。今天林哥分享一些实用的技巧和策略,帮助卖家们产品的…...
变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析
一、变量声明设计:let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性,这种设计体现了语言的核心哲学。以下是深度解析: 1.1 设计理念剖析 安全优先原则:默认不可变强制开发者明确声明意图 let x 5; …...
生成xcframework
打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式,可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...
JavaSec-RCE
简介 RCE(Remote Code Execution),可以分为:命令注入(Command Injection)、代码注入(Code Injection) 代码注入 1.漏洞场景:Groovy代码注入 Groovy是一种基于JVM的动态语言,语法简洁,支持闭包、动态类型和Java互操作性,…...
大数据学习栈记——Neo4j的安装与使用
本文介绍图数据库Neofj的安装与使用,操作系统:Ubuntu24.04,Neofj版本:2025.04.0。 Apt安装 Neofj可以进行官网安装:Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...
Linux链表操作全解析
Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表?1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...
【WiFi帧结构】
文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成:MAC头部frame bodyFCS,其中MAC是固定格式的,frame body是可变长度。 MAC头部有frame control,duration,address1,address2,addre…...
Spring Boot 实现流式响应(兼容 2.7.x)
在实际开发中,我们可能会遇到一些流式数据处理的场景,比如接收来自上游接口的 Server-Sent Events(SSE) 或 流式 JSON 内容,并将其原样中转给前端页面或客户端。这种情况下,传统的 RestTemplate 缓存机制会…...
什么是库存周转?如何用进销存系统提高库存周转率?
你可能听说过这样一句话: “利润不是赚出来的,是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业,很多企业看着销售不错,账上却没钱、利润也不见了,一翻库存才发现: 一堆卖不动的旧货…...
【Web 进阶篇】优雅的接口设计:统一响应、全局异常处理与参数校验
系列回顾: 在上一篇中,我们成功地为应用集成了数据库,并使用 Spring Data JPA 实现了基本的 CRUD API。我们的应用现在能“记忆”数据了!但是,如果你仔细审视那些 API,会发现它们还很“粗糙”:有…...
uniapp微信小程序视频实时流+pc端预览方案
方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度WebSocket图片帧定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐RTMP推流TRTC/即构SDK推流❌ 付费方案 (部分有免费额度&#x…...
