2G 内存云服务器部署 Spring Boot + MySQL 实战:从踩坑到上线

2G 内存云服务器部署 Spring Boot + MySQL 实战:从踩坑到上线
在这里插入图片描述

2G 内存云服务器部署 Spring Boot + MySQL 实战:从踩坑到上线

前言

最近把自己的全栈博客项目部署到了腾讯云的入门级服务器(2核2G),过程中踩了不少坑。本文记录完整的部署过程和问题排查思路,希望对同样在小规格服务器上部署 Java 项目的同学有所帮助。

项目技术栈:

  • 后端:Java 17 + Spring Boot 3.2.3 + Spring Security + JPA
  • 数据库:MySQL 8.0
  • 前端:Flutter Web
  • 反向代理:Nginx 1.26
  • 容器:Docker 28.4

服务器配置:

  • 腾讯云轻量应用服务器
  • 2 核 CPU / 2GB 内存 / 50GB 磁盘
  • TencentOS Server 4 (x86_64)

一、方案一:Docker Compose 全容器化(失败)

1.1 docker-compose.yml

最初的方案是标准的全容器化部署:

services:db:image: mysql:8.0environment:MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}MYSQL_DATABASE: vonblog MYSQL_USER: vonblog MYSQL_PASSWORD: ${DB_PASSWORD}ports:-"3306:3306"volumes:- mysql_data:/var/lib/mysql healthcheck:test:["CMD","mysqladmin","ping","-h","localhost"]interval: 10s timeout: 5s retries:5backend:build: ./backend depends_on:db:condition: service_healthy ports:-"8080:8080"environment:SPRING_DATASOURCE_URL: jdbc:mysql://db:3306/vonblog?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/ShanghaiSPRING_DATASOURCE_USERNAME: vonblog SPRING_DATASOURCE_PASSWORD: ${DB_PASSWORD}nginx:image: nginx:alpine depends_on:- backend ports:-"80:80"-"443:443"

1.2 问题现象

后端容器反复报错:

com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure Caused by: java.net.ConnectException: Connection refused 

1.3 排查过程

逐项排查:

# 1. MySQL 容器状态 — 正常docker compose ps# db: Up 18 minutes (healthy)# 2. DNS 解析 — 正常dockerexec vonblog-backend-1 nslookup db # Address: 172.19.0.2# 3. MySQL 用户权限 — 已授权dockerexec vonblog-db-1 mysql -uroot-p-e"GRANT ALL ON vonblog.* TO 'vonblog'@'%';"# 4. 端口监听 — MySQL 确实在监听dockerexec vonblog-db-1 ss -tlnp|grep3306

一切看起来都正常,但后端就是连不上。

1.4 根因分析

2GB 内存是瓶颈。

free -h 一看:

 total used free shared buff/cache available Mem: 1.9Gi 588Mi 643Mi 8.0Mi 916Mi 1.3Gi 

MySQL 8.0 默认配置下内存占用约 400-500MB,Spring Boot JVM 默认堆大小也是几百 MB。两个容器同时启动,加上 Docker 本身的开销,内存直接见底。

在内存不足的情况下,容器间的网络通信会出现各种诡异问题:TCP 连接建立失败、超时、被内核 OOM killer 干掉后重启等。Connection refused 只是表象,本质是资源不足。

二、方案二:混合部署(成功)

2.1 架构调整

┌─────────────────────────────────────┐ │ 腾讯云 2C2G 服务器 │ │ │ │ ┌───────────┐ ┌────────────────┐ │ │ │ Docker │ │ 宿主机 │ │ │ │ MySQL 8.0 │ │ Java 17 (jar) │ │ │ │ (容器) │ │ Nginx (systemd)│ │ │ └───────────┘ └────────────────┘ │ │ ↑ 3306 ↑ 8080 ↑ 80 │ │ └──── 127.0.0.1 ────────┘ │ └─────────────────────────────────────┘ 

核心思路:MySQL 留在 Docker(数据隔离方便),Spring Boot 和 Nginx 直接跑在宿主机上(省内存)。

2.2 步骤一:启动 MySQL 容器

docker run -d\--name vonblog-mysql \--restart always \-eMYSQL_ROOT_PASSWORD=YourRootPassword \-eMYSQL_DATABASE=vonblog \-eMYSQL_USER=vonblog \-eMYSQL_PASSWORD=YourDbPassword \-p3306:3306 \-v mysql_data:/var/lib/mysql \ mysql:8.0 \ --character-set-server=utf8mb4 \ --collation-server=utf8mb4_unicode_ci 

2.3 步骤二:安装 Java 17

# TencentOS / CentOS / RHEL 系 yum install-y java-17-openjdk-headless # 验证java-version# openjdk version "17.0.18"

2.4 步骤三:运行 Spring Boot jar

关键:限制 JVM 堆内存,2G 服务器不能让 JVM 随意扩张。

mkdir-p /opt/vonblog/uploads nohupjava-Xms256m-Xmx512m\-jar /opt/vonblog/app.jar \--spring.profiles.active=prod \--spring.datasource.url="jdbc:mysql://127.0.0.1:3306/vonblog?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=false"\--spring.datasource.username=vonblog \--spring.datasource.password=YourDbPassword \> /opt/vonblog/app.log 2>&1&echo"PID: $!"

注意几个要点:

  • -Xms256m -Xmx512m:堆内存限制在 256-512MB,给系统和 MySQL 留空间
  • 127.0.0.1:3306:通过宿主机 loopback 连接 Docker 映射出来的端口
  • allowPublicKeyRetrieval=true:MySQL 8.0 默认使用 caching_sha2_password,需要这个参数

2.5 步骤四:配置 Nginx

yum install-y nginx cat> /etc/nginx/conf.d/vonblog.conf <<'EOF' server { listen 80; server_name _; client_max_body_size 10M; # 前端静态文件 root /opt/vonblog/web; index index.html; # API 反代 location /api/ { proxy_pass http://127.0.0.1:8080/api/; 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; } # 上传文件 location /uploads/ { alias /opt/vonblog/uploads/; expires 30d; } # Flutter Web SPA 路由 location / { try_files $uri $uri/ /index.html; } } EOF systemctl enable nginx systemctl start nginx 

try_files $uri $uri/ /index.html 这行很重要——Flutter Web 是 SPA 单页应用,所有路由都要回落到 index.html,否则刷新页面会 404。

2.6 效果

# 启动耗时 Started VonBlogApplication in10.393 seconds # 内存占用free-h# Mem: 1.9Gi total, ~400Mi free, ~1.1Gi used (含 buff/cache)# MySQL 容器约 300MB + JVM 约 400MB + Nginx 约 20MB + 系统约 300MB

刚好塞下,不富裕但够用。

三、前端文件传输的曲折

3.1 SCP 被断连

本地 flutter build web --release 构建完后用 SCP 上传,结果:

Connection closed by 118.x.x.x port 22 

反复尝试都被断。原因是之前多次 SSH 密码交互触发了服务器的入侵防护策略(fail2ban 或类似机制)。

3.2 解决方案:临时 HTTP 上传服务

在服务器上用 Python 起一个临时的文件接收服务:

# 服务端(服务器上执行) python3 -c " import http.server, os classH(http.server.BaseHTTPRequestHandler):defdo_PUT(self): length =int(self.headers['Content-Length'])withopen('/opt/vonblog/web.tar.gz','wb')as f: f.write(self.rfile.read(length)) self.send_response(200) self.end_headers() self.wfile.write(b'OK') os._exit(0)# 接收完自动退出 http.server.HTTPServer(('0.0.0.0',9999), H).serve_forever() " &
# 客户端(本地执行)tar-czf web.tar.gz web/ curl-X PUT --data-binary @web.tar.gz http://服务器IP:9999/upload 
# 服务端解压cd /opt/vonblog &&tar-xzf web.tar.gz 

7.8MB 的压缩包,3 秒传完。简单粗暴但有效。

⚠️ 注意:这个方案没有任何认证,仅适合临时使用,用完立即关闭端口或进程。

四、内存优化建议

如果你也在 2G 服务器上跑 Java 项目,这些参数值得关注:

4.1 JVM 参数

# 严格限制堆内存-Xms256m-Xmx512m# 如果更紧张,可以进一步压缩-Xms128m-Xmx256m# 使用 G1GC(Java 17 默认),低延迟友好-XX:+UseG1GC

4.2 MySQL 内存调优

如果 MySQL 也要省内存,可以在启动时追加参数:

docker run ... mysql:8.0 \ --innodb-buffer-pool-size=128M \ --max-connections=50\ --table-open-cache=200

4.3 Swap 兜底

2G 内存的服务器强烈建议开 Swap:

fallocate -l 2G /swapfile chmod600 /swapfile mkswap /swapfile swapon /swapfile echo'/swapfile none swap sw 0 0'>> /etc/fstab 

有 Swap 兜底,偶发的内存峰值不会直接触发 OOM killer。

五、待优化

  1. systemd 服务化:目前后端用 nohup 启动,服务器重启后不会自动恢复。应该写一个 systemd service 文件。
  2. HTTPS:用 Let’s Encrypt + certbot 配置免费 SSL 证书。
  3. 日志轮转app.log 会无限增长,需要配置 logrotate。
  4. 监控:没有任何监控,进程挂了无感知。至少应该加个健康检查脚本 + crontab。

总结

方案优点缺点适用场景
Docker Compose 全容器环境隔离、一键部署内存开销大4G+ 内存服务器
混合部署省内存、连接稳定管理稍复杂2G 内存服务器
全宿主机最省资源环境污染1G 极端场景

核心结论:小规格服务器不要迷信全容器化,够用就行。 Docker Compose 在 4G 以上内存的机器上体验很好,但在 2G 机器上,混合方案是更务实的选择。


本文基于实际项目部署经验撰写,项目技术栈:Spring Boot 3.2.3 + Flutter Web + MySQL 8.0 + Nginx + Docker

如有疑问欢迎评论交流。

Read more

基于django+vue的时尚穿搭社区(商城)(前后端分离)

基于django+vue的时尚穿搭社区(商城)(前后端分离)

博主主页:猫头鹰源码 博主简介:Java领域优质创作者、ZEEKLOG博客专家、阿里云专家博主、公司架构师、全网粉丝5万+、专注Java技术领域和毕业设计项目实战,欢迎高校老师\讲师\同行交流合作 主要内容:毕业设计(Javaweb项目|小程序|Python|HTML|数据可视化|SSM|SpringBoot|Vue|Jsp|PHP等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 感兴趣可以先收藏起来,以防走丢,有任何选题、文档编写、代码问题也可以咨询我们 项目介绍:  本系统为最新原创项目,采用前后端分离,项目代码工整,结构清晰,适合选题:时尚穿搭、穿搭社区、穿搭、时尚商城、商城、前后端分离类其他穿搭话题等。系统采用django+vue整合开发,前端主要使用了vue、项目后端主要使用了django。

By Ne0inhk
Spring Boot 4.0 新特性整合 MyBatis-Plus 完整教程

Spring Boot 4.0 新特性整合 MyBatis-Plus 完整教程

Spring Boot 4.0 整合 MyBatis-Plus 完整教程 注:Spring Boot 4.0 ,本教程基于 Spring Boot 4.0 的预览版和新特性预期进行构建。实际发布时可能会有调整。 Spring Cloud全栈实战:手撸企业级项目,从入门到架构师! 一、Spring Boot 4.0 新特性概述 Spring Cloud全栈实战:手撸企业级项目,从入门到架构师! 1.1 主要新特性 * Java 21+ 支持:基于虚拟线程的响应式编程增强 * GraalVM 原生镜像优化:更完善的 AOT 编译支持 * 响应式编程增强:更好的响应式 MyBatis 支持 * 模块化改进:

By Ne0inhk
YOLOv8【第九章:模型部署篇·第14节】一文搞懂,GPU集群分布式推理!

YOLOv8【第九章:模型部署篇·第14节】一文搞懂,GPU集群分布式推理!

🏆 本文收录于 《YOLOv8实战:从入门到深度优化》 专栏。该专栏系统复现并梳理全网各类 YOLOv8 改进与实战案例(当前已覆盖分类 / 检测 / 分割 / 追踪 / 关键点 / OBB 检测等方向),坚持持续更新 + 深度解析,质量分长期稳定在 97 分以上,可视为当前市面上 覆盖较全、更新较快、实战导向极强 的 YOLO 改进系列内容之一。 部分章节也会结合国内外前沿论文与 AIGC 等大模型技术,对主流改进方案进行重构与再设计,内容更偏实战与可落地,适合有工程需求的同学深入学习与对标优化。 ✨ 特惠福利:当前限时活动一折秒杀,一次订阅,终身有效,后续所有更新章节全部免费解锁,👉 点此查看详情 全文目录: * 上期回顾:多模型集成部署 * 本期目标与阅读地图 * 一、分布式推理架构总览 * 1.1 常见落地形态 * 1.2 并行策略选型

By Ne0inhk
【大学生期末Java项目】springboot+vue+mysql实现快递公司物流管理项目【原创】

【大学生期末Java项目】springboot+vue+mysql实现快递公司物流管理项目【原创】

目录 一. 登录界面  二. 用户端  2.1 用户端寄快递界面 2.2 寄快递功能 2.3 取快递功能  【获取源码】  2.4 查快递功能 2.5 快递投诉与拦截 2.6 查询登录者的信息  三. 快递员端   3.1 查询可视化界面 3.2 接单与抢单   3.3 配送订单 3.4 快递员查询个人信息 三. 网点管理员端 3.1 首页可视化界面 3.2 查询投诉,并整改 3.3 签收订单

By Ne0inhk