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