0

Cấu hình server cho Java Spring Boot: CPU, RAM, JVM và Nginx cho production

Java Spring Boot phù hợp để xây dựng API, backend cho website, hệ thống nội bộ, microservice và các ứng dụng doanh nghiệp. Tuy nhiên, việc đưa một ứng dụng Spring Boot lên Cloud Server không chỉ đơn giản là cài Java rồi chạy file JAR. Cấu hình CPU, RAM, JVM heap, database connection pool, reverse proxy, log, health check và cách khởi động service đều ảnh hưởng trực tiếp đến khả năng chịu tải và độ ổn định của hệ thống.

Một lỗi phổ biến là lựa chọn cấu hình server chỉ dựa trên số người dùng hoặc dung lượng RAM. Trong thực tế, hai ứng dụng Spring Boot có cùng lượng traffic có thể tiêu thụ tài nguyên hoàn toàn khác nhau. API CRUD đọc dữ liệu từ database sẽ khác với dịch vụ xử lý file, mã hóa, tạo báo cáo hoặc chạy nhiều background job.

Bài viết này hướng dẫn cách cấu hình Cloud Server cho Java Spring Boot theo hướng production, từ lựa chọn CPU/RAM, cấu hình JVM, cài Java, chạy ứng dụng bằng systemd, sử dụng Nginx làm reverse proxy đến monitoring và load test trước khi đưa hệ thống vào vận hành.

Lưu ý: Các cấu hình trong bài là điểm bắt đầu để triển khai và kiểm thử. Cấu hình cuối cùng nên dựa trên workload thực tế, dữ liệu monitoring và kết quả load test của chính ứng dụng.

Kiến trúc Spring Boot trên Cloud Server

Một ứng dụng Spring Boot đơn giản có thể chạy trực tiếp trên một Cloud Server:

User
 |
 v
Nginx
 |
 v
Spring Boot
 |
 v
Database

Trong đó:

  • Nginx nhận request từ Internet.
  • Spring Boot xử lý business logic.
  • Database lưu dữ liệu.
  • JVM quản lý bộ nhớ và runtime của Java.

Với hệ thống lớn hơn:

User
 |
 v
Load Balancer / CDN
 |
 v
Nginx
 |
 v
Spring Boot
 |
 |-- Redis
 |-- Database
 `-- Queue / External API

Mỗi thành phần tạo một loại tải khác nhau. Vì vậy cần xác định bottleneck trước khi quyết định tăng CPU hoặc RAM.

Chọn CPU cho Spring Boot

CPU ảnh hưởng trực tiếp đến khả năng xử lý request đồng thời.

Spring Boot thường sử dụng CPU cho:

  • Business logic.
  • Serialize và deserialize JSON.
  • Mã hóa.
  • Compression.
  • Xử lý file.
  • Garbage Collection.
  • Background job.
  • Thread pool.
  • Mapping dữ liệu.
  • Security filter.

Một API CRUD nhẹ có thể chạy tốt trên ít CPU hơn một ứng dụng xử lý ảnh hoặc tạo báo cáo.

Có thể sử dụng bảng tham khảo ban đầu:

Workload Cấu hình thử nghiệm ban đầu
Development / staging 2 vCPU
API nhỏ, traffic thấp 2-4 vCPU
API production trung bình 4-8 vCPU
Workload CPU-heavy 8 vCPU trở lên
Hệ thống traffic lớn Nhiều instance + Load Balancer

Không nên hiểu bảng trên là cấu hình cố định.

Ví dụ một ứng dụng đạt 80% CPU ở 200 RPS trên 4 vCPU có thể cần scale khi traffic tăng. Nhưng nếu CPU chỉ 25% mà response time cao thì bottleneck có thể nằm ở database hoặc API bên ngoài.

Kiểm tra CPU:

top

Hoặc:

htop

Theo dõi thêm:

  • Load Average.
  • Process Java.
  • CPU steal.
  • Context switch.
  • CPU theo từng core.

Chọn RAM cho Spring Boot

RAM là tài nguyên rất quan trọng vì JVM sử dụng nhiều vùng nhớ khác nhau.

Một process Java không chỉ sử dụng heap.

Có thể hình dung:

RAM Server
|
|-- OS
|-- JVM Heap
|-- Metaspace
|-- Thread Stack
|-- Direct Buffer
|-- Native Memory
|-- Agent / Monitoring
`-- File Cache

Do đó không nên đặt toàn bộ RAM của server thành -Xmx.

Ví dụ Cloud Server có 8 GB RAM.

Không nên cấu hình:

-Xmx8g

vì hệ điều hành và các thành phần khác vẫn cần bộ nhớ.

Một cấu hình an toàn hơn cần dành phần RAM cho:

  • Linux.
  • Nginx.
  • Monitoring agent.
  • Native memory.
  • Thread.
  • Buffer.
  • Database hoặc Redis nếu chạy cùng server.

Nếu Spring Boot và database cùng chạy trên một máy chủ, cần đặc biệt cẩn thận vì cả JVM và database đều có xu hướng sử dụng nhiều memory.

JVM Heap là gì?

Heap là vùng bộ nhớ chính được JVM sử dụng để lưu object.

Hai tham số thường gặp:

-Xms
-Xmx

Ví dụ:

java -Xms2g -Xmx4g -jar app.jar

Trong đó:

  • -Xms2g: heap khởi tạo 2 GB.
  • -Xmx4g: heap tối đa 4 GB.

Không phải ứng dụng nào cũng cần đặt Xms bằng Xmx.

Với hệ thống cần predictable performance, đội ngũ có thể chọn giá trị gần nhau sau khi đã đo workload.

Quan trọng hơn là theo dõi:

  • Heap usage.
  • GC pause.
  • Allocation rate.
  • Old generation.
  • OutOfMemoryError.

Nếu heap quá nhỏ, Garbage Collection có thể chạy thường xuyên.

Nếu heap quá lớn, server có thể thiếu RAM cho các tiến trình khác.

Cấu hình ban đầu theo RAM server

Một cách tiếp cận đơn giản là không phân bổ quá nhiều RAM cho JVM ngay từ đầu.

Ví dụ Cloud Server 8 GB chạy:

  • Ubuntu.
  • Nginx.
  • Spring Boot.
  • Monitoring.

Có thể bắt đầu kiểm thử:

Xms = 2 GB
Xmx = 4 GB

Sau đó theo dõi.

Với server 16 GB:

Xms = 4 GB
Xmx = 8 GB

có thể là một điểm thử ban đầu cho một application instance.

Đây không phải công thức cố định.

Nếu ứng dụng cần nhiều cache in-memory hoặc object lớn, heap có thể cần cao hơn.

Nếu ứng dụng stateless và chạy nhiều instance, có thể ưu tiên nhiều JVM nhỏ hơn thay vì một JVM rất lớn.

Chọn phiên bản Java

Nên sử dụng phiên bản Java LTS được ứng dụng và phiên bản Spring Boot đang sử dụng hỗ trợ.

Kiểm tra Java:

java -version

Kiểm tra JVM:

java -XshowSettings:vm -version

Không nên nâng Java trên production chỉ vì có phiên bản mới.

Cần kiểm tra:

  • Spring Boot compatibility.
  • Dependency.
  • Build pipeline.
  • JDBC driver.
  • Agent monitoring.
  • Library native.
  • Performance test.

Luôn thử nghiệm trên staging trước.

Cài Java trên Ubuntu

Cập nhật package:

sudo apt update
sudo apt upgrade -y

Tìm các JDK có sẵn:

apt search openjdk

Cài phiên bản JDK phù hợp với ứng dụng.

Ví dụ:

sudo apt install openjdk-21-jdk -y

Kiểm tra:

java -version

Nếu server chỉ dùng để chạy ứng dụng và không build source trực tiếp trên production, có thể cân nhắc runtime phù hợp với quy trình triển khai.

Tạo user riêng để chạy Spring Boot

Không nên chạy application bằng tài khoản root.

Tạo user:

sudo useradd --system --create-home --shell /usr/sbin/nologin springapp

Tạo thư mục application:

sudo mkdir -p /opt/springapp

Phân quyền:

sudo chown -R springapp:springapp /opt/springapp

Copy file JAR vào:

/opt/springapp/app.jar

Cách này giúp giới hạn quyền của process Java nếu application gặp sự cố hoặc bị khai thác.

Chạy Spring Boot bằng systemd

Không nên chạy production bằng:

java -jar app.jar

trong một terminal rồi giữ phiên SSH mở.

Nên quản lý application như một service.

Tạo file:

sudo nano /etc/systemd/system/springapp.service

Ví dụ:

[Unit]
Description=Spring Boot Application
After=network.target

[Service]
User=springapp
Group=springapp
WorkingDirectory=/opt/springapp

ExecStart=/usr/bin/java \
  -Xms2g \
  -Xmx4g \
  -jar /opt/springapp/app.jar

Restart=always
RestartSec=5

SuccessExitStatus=143

[Install]
WantedBy=multi-user.target

Reload systemd:

sudo systemctl daemon-reload

Khởi động:

sudo systemctl start springapp

Bật tự động chạy sau reboot:

sudo systemctl enable springapp

Kiểm tra:

sudo systemctl status springapp

Xem log:

journalctl -u springapp -f

Không nên expose trực tiếp port Spring Boot ra Internet

Spring Boot thường chạy:

8080

Có thể cấu hình:

server.port=8080

Nếu chỉ Nginx truy cập application, nên bind application vào localhost khi phù hợp.

Ví dụ:

server.address=127.0.0.1
server.port=8080

Kiểm tra:

ss -tulpn | grep 8080

Khi đó người dùng Internet không truy cập trực tiếp vào port 8080.

Request đi theo:

Internet
   |
   v
Nginx :443
   |
   v
Spring Boot :8080

Cấu hình Nginx làm reverse proxy

Cài Nginx:

sudo apt install nginx -y

Tạo virtual host:

sudo nano /etc/nginx/sites-available/springapp

Ví dụ:

server {
    listen 80;
    server_name api.example.com;

    location / {
        proxy_pass http://127.0.0.1:8080;

        proxy_http_version 1.1;
        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;

        proxy_connect_timeout 5s;
        proxy_read_timeout 60s;
    }
}

Enable site:

sudo ln -s /etc/nginx/sites-available/springapp /etc/nginx/sites-enabled/springapp

Kiểm tra:

sudo nginx -t

Reload:

sudo systemctl reload nginx

Production nên sử dụng HTTPS và cấu hình SSL phù hợp.

Connection pool database

Spring Boot thường sử dụng HikariCP làm connection pool trong nhiều cấu hình phổ biến.

Một lỗi thường gặp là tăng application worker hoặc tăng số instance nhưng không tính tổng database connection.

Ví dụ:

4 application instance
x
20 DB connection
=
80 connection

Nếu database chỉ xử lý ổn định khoảng 60 connection, scale application có thể làm hệ thống chậm hơn.

Ví dụ cấu hình:

spring.datasource.hikari.maximum-pool-size=20
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.connection-timeout=3000

Không nên đặt:

maximum-pool-size=200

chỉ vì database cho phép 200 connection.

Connection pool cần được tính dựa trên:

  • Số application instance.
  • Database CPU.
  • Query latency.
  • Transaction time.
  • Workload đọc/ghi.

Thread pool và request đồng thời

Một request Spring Boot có thể giữ thread trong thời gian:

  • Chạy business logic.
  • Query database.
  • Gọi API bên ngoài.
  • Chờ storage.
  • Chờ queue.

Nếu external API mất 5 giây, thread có thể bị giữ lâu.

Traffic tăng sẽ tạo ra nhiều request đang chờ.

Có thể hiểu:

RPS tăng
   |
   v
Request chờ lâu
   |
   v
Thread bị chiếm
   |
   v
Request mới phải chờ

Do đó, response time có ảnh hưởng trực tiếp đến concurrency.

Không nên tăng thread pool quá cao để che giấu một dependency chậm.

Cần xử lý nguyên nhân gốc như database query, external API hoặc I/O.

Garbage Collection

Garbage Collection giải phóng object không còn được sử dụng.

Nếu ứng dụng tạo rất nhiều object, GC có thể tiêu tốn CPU hoặc tạo pause.

Cần theo dõi:

  • GC frequency.
  • GC pause.
  • Heap before/after GC.
  • Allocation rate.

Có thể bật GC log phù hợp với phiên bản Java đang sử dụng.

Ví dụ:

-Xlog:gc*:file=/var/log/springapp/gc.log:time,uptime,level,tags

Không nên thay GC algorithm chỉ dựa trên kinh nghiệm từ một ứng dụng khác.

Workload API latency-sensitive, batch processing và application heap lớn có thể có yêu cầu khác nhau.

Log application

Không nên để log tăng không giới hạn.

Nếu Spring Boot ghi log file liên tục:

application.log

cần cấu hình rotation.

Có thể sử dụng:

  • Logback rolling policy.
  • Logrotate.
  • Centralized logging.

Các mức log trên production cũng cần kiểm soát.

Không nên bật:

DEBUG

cho toàn bộ application trong thời gian dài.

Debug log có thể:

  • Tăng Disk I/O.
  • Tăng dung lượng.
  • Làm lộ dữ liệu.
  • Ảnh hưởng performance.

Health Check với Spring Boot Actuator

Spring Boot Actuator có thể cung cấp endpoint health.

Ví dụ:

/actuator/health

Có thể dùng health check cho:

  • Load Balancer.
  • Monitoring.
  • Kubernetes.
  • Auto-healing.

Không nên public toàn bộ Actuator endpoint ra Internet.

Cần kiểm soát:

  • Endpoint được expose.
  • Authentication.
  • Network access.
  • Dữ liệu nhạy cảm.

Health check production nên phản ánh application có thực sự sẵn sàng phục vụ hay không.

Monitoring cần theo dõi những gì?

Với Spring Boot, nên theo dõi ít nhất:

Server

  • CPU.
  • RAM.
  • Swap.
  • Disk I/O.
  • Disk usage.
  • Network.

JVM

  • Heap.
  • Non-heap.
  • GC.
  • Thread.
  • Class loading.
  • Process CPU.

Application

  • RPS.
  • P95.
  • P99.
  • Error rate.
  • HTTP status.
  • Active request.

Database

  • Connection.
  • Query latency.
  • Slow query.
  • Lock.
  • Disk latency.

Nếu chỉ nhìn CPU và RAM, đội ngũ có thể bỏ sót bottleneck quan trọng.

Giới hạn file descriptor

Ứng dụng nhiều connection có thể cần số file descriptor đủ lớn.

Kiểm tra:

ulimit -n

Kiểm tra process Java:

cat /proc/$(pgrep -f 'app.jar' | head -1)/limits

Không nên tăng limit tùy ý mà không hiểu workload.

Nhưng nếu application xử lý nhiều socket hoặc file đồng thời, giới hạn quá thấp có thể dẫn đến lỗi:

Too many open files

Firewall

Chỉ nên mở những port thực sự cần thiết.

Ví dụ:

22   SSH
80   HTTP
443  HTTPS

Port Spring Boot:

8080

không nhất thiết phải public nếu Nginx chạy cùng server.

Có thể kiểm tra:

sudo ss -tulpn

Kết hợp firewall ở Cloud và firewall trong hệ điều hành khi cần.

Database cũng không nên public nếu application có thể kết nối qua private network.

Chạy database cùng server hay tách riêng?

Với development hoặc ứng dụng nhỏ:

Cloud Server
|
|-- Nginx
|-- Spring Boot
`-- PostgreSQL/MySQL

có thể đơn giản và tiết kiệm.

Nhưng production traffic lớn nên cân nhắc tách:

App Server
    |
    v
Database Server

Lợi ích:

  • CPU database không cạnh tranh với JVM.
  • RAM được quản lý riêng.
  • Scale từng lớp độc lập.
  • Dễ monitoring.
  • Giảm ảnh hưởng khi application restart.

Tách database không tự động làm hệ thống nhanh hơn. Quyết định cần dựa trên tải thực tế.

Redis có cần thiết không?

Không phải mọi Spring Boot application đều cần Redis.

Redis phù hợp khi cần:

  • Cache dữ liệu.
  • Session.
  • Rate limit.
  • Distributed lock.
  • Queue trong một số use case.

Ví dụ:

Spring Boot
    |
    |-- Redis
    `-- Database

Nếu nhiều request đọc cùng dữ liệu, cache có thể giảm số query database.

Tuy nhiên, cache làm hệ thống phức tạp hơn.

Cần xử lý:

  • TTL.
  • Cache invalidation.
  • Memory.
  • Eviction.
  • Cache stampede.

Cấu hình server theo loại workload

API CRUD nhỏ

Có thể bắt đầu thử nghiệm:

2-4 vCPU
4-8 GB RAM
SSD

Nếu database tách riêng, application server có thể dùng ít RAM hơn.

Backend ecommerce

Có thể bắt đầu ở:

4-8 vCPU
8-16 GB RAM
SSD

và theo dõi:

  • Checkout latency.
  • Database connection.
  • Redis.
  • External API.
  • Traffic peak.

Xử lý file hoặc report

Có thể cần nhiều CPU, RAM hoặc Disk throughput hơn.

Nếu job không cần phản hồi realtime, nên đưa vào queue:

API
 |
 v
Queue
 |
 |-- Worker 1
 |-- Worker 2
 `-- Worker 3

Không nên để một request HTTP giữ connection trong vài phút để xử lý report.

Scale up hay scale out?

Scale up:

4 vCPU -> 8 vCPU
8 GB -> 16 GB

phù hợp khi:

  • Hệ thống còn đơn giản.
  • Application chưa stateless.
  • Cần tăng capacity nhanh.

Scale out:

Load Balancer
 |
 |-- Spring Boot 1
 |-- Spring Boot 2
 `-- Spring Boot 3

phù hợp khi:

  • Traffic lớn.
  • Cần High Availability.
  • Application stateless.
  • Cần scale linh hoạt.

Khi scale out, cần tách:

  • Session.
  • File upload.
  • Cache.
  • Database.

khỏi từng application server.

Load test trước production

Không nên chọn cấu hình server chỉ dựa vào bảng tham khảo.

Cần load test các endpoint quan trọng:

  • Login.
  • Search.
  • CRUD.
  • Checkout.
  • Upload.
  • API có query nặng.
  • External API.

Theo dõi:

RPS
P95
P99
Error Rate
CPU
RAM
GC
DB Connection
Disk I/O

Ví dụ server 4 vCPU xử lý:

300 RPS
P95 = 250 ms
CPU = 65%

có thể còn headroom.

Nhưng nếu ở 350 RPS:

P95 = 2 giây
CPU = 95%
Error = 3%

thì hệ thống đang gần hoặc vượt capacity.

Checklist cấu hình Spring Boot trên Cloud Server

Hệ điều hành

  • Đã cập nhật package.
  • Đã tạo user riêng cho application.
  • Đã cấu hình firewall.
  • Chỉ mở port cần thiết.
  • Đã kiểm tra file descriptor.

Java

  • Dùng phiên bản Java phù hợp.
  • Đã kiểm tra java -version.
  • Đã cấu hình heap.
  • Không dùng toàn bộ RAM cho Xmx.
  • Có theo dõi GC.

Application

  • Chạy bằng systemd hoặc container.
  • Có restart policy.
  • Có health check.
  • Có timeout.
  • Không hardcode secret.
  • Log được rotation.

Nginx

  • Reverse proxy hoạt động.
  • SSL được cấu hình.
  • Forwarded header đúng.
  • Timeout phù hợp.
  • Port application không public khi không cần.

Database

  • Connection pool hợp lý.
  • Slow query được theo dõi.
  • Index được kiểm tra.
  • Không mở database public nếu không cần.
  • Có backup.

Monitoring

  • Theo dõi CPU.
  • Theo dõi RAM.
  • Theo dõi JVM heap.
  • Theo dõi GC.
  • Theo dõi P95/P99.
  • Theo dõi error rate.
  • Theo dõi database.

Trước production

  • Đã load test.
  • Đã kiểm tra traffic peak.
  • Có backup.
  • Có phương án rollback.
  • Có capacity dự phòng.
  • Có cảnh báo khi service down.

Kết luận

Cấu hình Cloud Server cho Java Spring Boot không chỉ là lựa chọn số vCPU và dung lượng RAM. Một hệ thống production cần đồng thời xem xét JVM heap, worker và thread, database connection pool, Nginx, Disk I/O, log, monitoring và khả năng mở rộng.

Một cấu hình server mạnh nhưng JVM được cấu hình sai vẫn có thể gặp OutOfMemoryError. Tăng nhiều application instance nhưng không tính database connection có thể làm database quá tải. Tăng thread pool nhưng external API vẫn chậm có thể khiến lượng request chờ tăng nhanh.

Quy trình phù hợp nên là:

Xác định workload
->
Chọn cấu hình khởi đầu
->
Cấu hình JVM
->
Cấu hình Nginx / Database
->
Monitoring
->
Load test
->
Scale

Với application mới, có thể bắt đầu ở cấu hình vừa phải rồi đo tải thực tế. Khi CPU, RAM hoặc connection bắt đầu đạt giới hạn, hệ thống có thể scale up hoặc scale out tùy kiến trúc.

Quan trọng nhất là không chọn cấu hình chỉ theo số user. Cần đo RPS, response time, P95/P99, CPU, heap, GC và database để xác định chính xác server đang thiếu tài nguyên ở đâu.


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí