跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Java大前端java

2G 内存云服务器部署 Spring Boot + MySQL 实践

2G 内存服务器部署 Spring Boot 应用常遇资源不足问题。对比全容器化与混合部署方案,指出 Docker Compose 在低配环境下易因内存耗尽导致连接失败。推荐将 MySQL 保留在容器,Java 应用与 Nginx 部署于宿主机并限制 JVM 堆内存。同时提供文件传输替代方案及 Swap 配置建议,确保小规格服务器稳定运行。

FlinkHero发布于 2026/3/22更新于 2026/8/1738 浏览
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.0
    environment:
      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: 5
  backend:
    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/Shanghai
      SPRING_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 解析 — 正常
docker exec vonblog-backend-1 nslookup db # Address: 172.19.0.2

# 3. MySQL 用户权限 — 已授权
docker exec vonblog-db-1 mysql -uroot -p -e "GRANT ALL ON vonblog.* TO 'vonblog'@'%';"

# 4. 端口监听 — MySQL 确实在监听
docker exec vonblog-db-1 ss -tlnp | grep 3306

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

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 架构调整

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

2.2 步骤一:启动 MySQL 容器
docker run -d\
--name vonblog-mysql \
--restart always \
-e MYSQL_ROOT_PASSWORD=YourRootPassword \
-e MYSQL_DATABASE=vonblog \
-e MYSQL_USER=vonblog \
-e MYSQL_PASSWORD=YourDbPassword \
-p 3306: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
nohup java -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 in 10.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 起一个临时的文件接收服务:

# 服务端(服务器上执行)
import http.server, os

class H(http.server.BaseHTTPRequestHandler):
    def do_PUT(self):
        length = int(self.headers['Content-Length'])
        with open('/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
chmod 600 /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 机器上,混合方案是更务实的选择。

目录

  1. 2G 内存云服务器部署 Spring Boot + MySQL 实践
  2. 前言
  3. 一、方案一:Docker Compose 全容器化(失败)
  4. 1.1 docker-compose.yml
  5. 1.2 问题现象
  6. 1.3 排查过程
  7. 1. MySQL 容器状态 — 正常
  8. db: Up 18 minutes (healthy)
  9. 2. DNS 解析 — 正常
  10. 3. MySQL 用户权限 — 已授权
  11. 4. 端口监听 — MySQL 确实在监听
  12. 1.4 根因分析
  13. 二、方案二:混合部署(成功)
  14. 2.1 架构调整
  15. 2.2 步骤一:启动 MySQL 容器
  16. 2.3 步骤二:安装 Java 17
  17. TencentOS / CentOS / RHEL 系
  18. 验证
  19. openjdk version "17.0.18"
  20. 2.4 步骤三:运行 Spring Boot jar
  21. 2.5 步骤四:配置 Nginx
  22. 2.6 效果
  23. 启动耗时
  24. 内存占用
  25. Mem: 1.9Gi total, ~400Mi free, ~1.1Gi used (含 buff/cache)
  26. MySQL 容器约 300MB + JVM 约 400MB + Nginx 约 20MB + 系统约 300MB
  27. 三、前端文件传输的曲折
  28. 3.1 SCP 被断连
  29. 3.2 解决方案:临时 HTTP 上传服务
  30. 服务端(服务器上执行)
  31. 客户端(本地执行)
  32. 服务端解压
  33. 四、内存优化建议
  34. 4.1 JVM 参数
  35. 严格限制堆内存
  36. 如果更紧张,可以进一步压缩
  37. 使用 G1GC(Java 17 默认),低延迟友好
  38. 4.2 MySQL 内存调优
  39. 4.3 Swap 兜底
  40. 五、待优化
  41. 总结
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • WebStorm 安装与首次配置指南
  • 32 款国产 Claw 系 AI 工具盘点与官网汇总
  • 云原生算力调度:基于 Kubernetes 的智能资源分配
  • 基于 Rust+Tauri 构建带安全沙箱的跨平台清理工具
  • 前端微前端:大型应用的模块化解决方案
  • AVL 树核心详解:从概念到 C++ 手撕实现
  • 并查集数据结构详解与实战应用
  • C++ 红黑树插入与平衡调整详解
  • C++ 转 C#:核心思维转变与实战要点
  • 单链表核心操作实现与详解
  • 初识 AI 大语言模型:核心概念与能力解析
  • 前端加密 encrypt-labs 靶场:环境搭建与全关卡解析
  • Java 中基于属性的访问控制(ABAC):动态上下文感知权限管理
  • SQLAlchemy 核心用法与实战案例解析
  • 图遍历算法详解:DFS 与 BFS 原理及 Java 实现
  • 从视频孪生到镜像孪生:三维空间控制与风险推演技术解析
  • 5 款免费 AI 视频生成工具推荐
  • C++ 推箱子游戏实现
  • 机器人操作 VLA 模型强化学习综述
  • 基于 LLaMA-Factory 的 Docker 环境模型训练与推理指南

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online