Làm Chủ Memory Overcommit trên Linux: Cấu Hình vm.overcommit_memory cho Database và AI Workloads trong Production

Linux tutorial - IT technology blog
Linux tutorial - IT technology blog

Bối Cảnh & Lý Do — Cú Báo Thức Lúc 2 Giờ Sáng

Lúc 2:14 sáng. Server PostgreSQL của bạn vừa crash với lỗi OOM. Slack đổ chuông liên tục không dứt. Server có 16GB RAM, vậy mà kernel lại quyết định kill đúng process chỉ đang dùng có 4GB. Nếu bạn đã trải qua điều này, bạn biết thủ phạm là gì rồi: memory overcommit bị cấu hình sai.

Linux không cấp phát RAM vật lý ngay lập tức khi một process yêu cầu. Khi ứng dụng của bạn gọi malloc() và nhận về một con trỏ, kernel thực chất chưa hề đặt chỗ bộ nhớ thật — nó chỉ đưa ra một lời hứa. Đây chính là memory overcommit: kernel cam kết cung cấp bộ nhớ mà có thể chưa tồn tại về mặt vật lý, đặt cược rằng không phải mọi process đều sẽ dùng hết những gì chúng đã đặt chỗ.

Canh bạc đó thường ổn trên máy desktop. Nhưng trên server production chạy PostgreSQL, Redis, hay dịch vụ inference PyTorch, nó có thể kết thúc đêm của bạn rất nhanh.

Thiết lập kernel kiểm soát tất cả điều này là vm.overcommit_memory, và nó có ba chế độ:

  • Chế độ 0 (mặc định): Heuristic overcommit. Kernel dùng phỏng đoán — cho phép hầu hết các overcommit nhưng từ chối những yêu cầu rõ ràng là không hợp lý. Vấn đề là “rõ ràng” lại biến đổi rất nhiều tùy theo workload.
  • Chế độ 1: Luôn luôn overcommit. Kernel đồng ý với mọi yêu cầu cấp phát bất kể bộ nhớ khả dụng. Nhanh và lạc quan — nhưng nguy hiểm cho database production.
  • Chế độ 2: Strict overcommit. Kernel tính toán một giới hạn commit cứng và từ chối các yêu cầu vượt quá giới hạn đó. Dự đoán được hơn, bảo thủ hơn. Lựa chọn đúng đắn cho các stateful services.

Thiết lập đi kèm vm.overcommit_ratio (mặc định: 50) kiểm soát mức độ tích cực mà chế độ 2 cho phép commit:

CommitLimit = (TotalRAM × overcommit_ratio / 100) + SwapTotal

Trên máy 16GB không có swap, chế độ 2 với ratio 50 chỉ cho phép tổng cộng 8GB committed memory. Một process database yêu cầu 12GB sẽ bị từ chối ngay từ đầu — điều đó tốt hơn nhiều so với việc bị OOM kill âm thầm giữa chừng một transaction lúc 3 giờ sáng.

Kiểm Tra Baseline Trước Khi Thay Đổi Bất Cứ Điều Gì

Ba năm quản lý hơn 10 VPS Linux đã rèn cho tôi một thói quen: hiểu baseline trước khi thay đổi bất cứ thứ gì. Bỏ qua bước này chính là cách một cập nhật config thông thường biến thành sự cố. Những lệnh này chỉ mất chưa đến một phút và cho bạn biết chính xác hệ thống đang ở đâu:

# Kiểm tra chế độ overcommit hiện tại (0, 1, hoặc 2)
cat /proc/sys/vm/overcommit_memory

# Kiểm tra overcommit ratio (chỉ liên quan trong chế độ 2)
cat /proc/sys/vm/overcommit_ratio

# Xem ngân sách commit so với bộ nhớ thực tế đã commit
grep -E 'CommitLimit|Committed_AS' /proc/meminfo

Khoảng cách giữa CommitLimitCommitted_AS là con số quan trọng nhất. Nếu Committed_AS đang tiệm cận hoặc vượt CommitLimit, bạn đã ở trong vùng nguy hiểm OOM — ngay cả khi free -h vẫn hiển thị còn 4GB “available.”

Hãy kiểm tra xem OOM killer có đang hoạt động âm thầm hay không:

# Các sự kiện OOM gần đây trong kernel ring buffer
dmesg | grep -i "oom\|killed process" | tail -20

# Log OOM lâu dài qua journalctl
journalctl -k | grep -i "oom\|killed" | tail -20

Những entry bạn không biết có nghĩa là OOM killer đã hoạt động ngầm. Hãy sửa baseline trước khi tải thêm lên hệ thống.

Cấu Hình — Điều Chỉnh Theo Loại Workload

Database Servers (PostgreSQL, MySQL, MariaDB)

Database là nạn nhân tệ nhất của overcommit quá mức. shared_buffers của PostgreSQL và innodb_buffer_pool_size của MySQL cấp phát các vùng bộ nhớ lớn mà chúng thực sự sử dụng, không chỉ đặt chỗ đầu cơ. Heuristics của chế độ 0 không bảo vệ chúng một cách đáng tin cậy.

Database servers cần chế độ 2 với ratio được tính toán cẩn thận:

# Ví dụ: 16GB RAM, 4GB swap, muốn cho phép tối đa 14GB committed
# CommitLimit = (16 × ratio/100) + 4
# Giải ra ratio: (14 - 4) / 16 × 100 = 62.5 → dùng 70 để có biên độ an toàn

# Áp dụng ngay (chỉ runtime, không tồn tại sau khi reboot)
sysctl -w vm.overcommit_memory=2
sysctl -w vm.overcommit_ratio=70

# Xác nhận CommitLimit đã được tính lại đúng
grep CommitLimit /proc/meminfo

Để giữ cấu hình qua các lần reboot, hãy ghi vào file sysctl drop-in riêng — tránh chỉnh sửa trực tiếp /etc/sysctl.conf nếu có thể:

# Tạo file cấu hình riêng
cat > /etc/sysctl.d/99-memory-overcommit.conf << 'EOF'
vm.overcommit_memory = 2
vm.overcommit_ratio = 70
EOF

# Áp dụng mà không cần reboot
sysctl --system

# Xác nhận
sysctl vm.overcommit_memory vm.overcommit_ratio

Sau khi áp dụng chế độ 2, hãy restart dịch vụ database. Nếu shared_buffers hay innodb_buffer_pool_size được cấu hình cao hơn CommitLimit mới cho phép, dịch vụ sẽ không khởi động được — đây chính xác là hành vi bạn muốn. Lỗi khởi động thì khắc phục được. OOM kill giữa chừng một query thì không.

Workloads AI và Machine Learning

Các job inference và training AI hoạt động khác hẳn. PyTorch và TensorFlow cấp phát bộ nhớ một cách đầu cơ, theo từng đợt bùng phát, và thường theo những cách làm rối loạn cơ chế tính toán commit của chế độ 2. Ép strict overcommit lên ML workloads thường xuyên gây ra lỗi cấp phát hợp lệ giữa chừng tính toán.

ML workloads cần một cách tiếp cận hoàn toàn khác. Chế độ 1 kết hợp với giới hạn bộ nhớ ở cấp cgroup hiệu quả hơn việc hạn chế overcommit ở cấp kernel:

# Cho phép ML framework cấp phát tự do ở cấp kernel
sysctl -w vm.overcommit_memory=1

# Thay vào đó, hạn chế service cụ thể bằng systemd resource control
systemctl set-property ai-inference.service MemoryMax=12G MemoryHigh=10G
systemctl daemon-reload
systemctl restart ai-inference.service

Chạy AI workloads trong Docker? Hãy đặt giới hạn bộ nhớ cứng ở cấp container:

# Giới hạn bộ nhớ + swap cứng cho container
docker run \
  --memory="10g" \
  --memory-swap="10g" \
  --name inference-server \
  my-model-image python serve.py

Hoặc trong docker-compose.yml:

services:
  inference:
    image: my-model-image
    deploy:
      resources:
        limits:
          memory: 10g

Bằng cách này, AI framework có được sự linh hoạt overcommit mà nó cần trong khi OS vẫn có một giới hạn cứng để thực thi. OOM killer sẽ nhắm vào container trước khi đụng đến bất cứ thứ gì khác trên host.

Xác Nhận & Giám Sát

Đừng bao giờ giả định rằng một thay đổi sysctl đã hoạt động như mong muốn. Hãy xác nhận ngay lập tức, rồi theo dõi dưới tải thực tế. Thói quen đó đã ngăn chặn được nhiều sự cố production hơn bất kỳ thay đổi cấu hình nào tôi từng thực hiện.

Bắt đầu với một script kiểm tra sức khỏe nhanh mà bạn có thể chạy ngay sau bất kỳ thay đổi cấu hình nào:

#!/bin/bash
echo "=== Trạng Thái Memory Overcommit ==="
echo "Chế độ  : $(cat /proc/sys/vm/overcommit_memory)"
echo "Ratio   : $(cat /proc/sys/vm/overcommit_ratio)%"
echo ""
grep -E 'MemTotal|MemAvailable|SwapTotal|CommitLimit|Committed_AS' /proc/meminfo
echo ""

LIMIT=$(grep CommitLimit /proc/meminfo | awk '{print $2}')
COMMITTED=$(grep Committed_AS /proc/meminfo | awk '{print $2}')
PCT=$((COMMITTED * 100 / LIMIT))
echo "Mức sử dụng commit: ${PCT}% của giới hạn"

if [ "$PCT" -gt 85 ]; then
  echo "CẢNH BÁO: Mức cam kết bộ nhớ cực cao — Nguy cơ OOM tăng cao"
fi

Trong production, node_exporter (Prometheus) đã xuất các giá trị này dưới dạng node_memory_CommitLimit_bytesnode_memory_Committed_AS_bytes. Hãy thêm một alert rule để bạn biết trước OOM killer:

# prometheus/rules/memory.yml
groups:
  - name: memory_overcommit
    rules:
      - alert: MemoryCommitHigh
        expr: node_memory_Committed_AS_bytes / node_memory_CommitLimit_bytes > 0.80
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "Mức cam kết bộ nhớ cao trên {{ $labels.instance }}"
          description: "Bộ nhớ đã commit là {{ $value | humanizePercentage }} của giới hạn"

Trong quá trình load testing, theo dõi hai giá trị này song song — chúng kể câu chuyện rõ ràng hơn free hay top đơn thuần:

# Terminal 1 — theo dõi sự kiện OOM kill
watch -n 2 'dmesg | grep -c "Out of memory"'

# Terminal 2 — theo dõi commit ratio theo thời gian thực
watch -n 2 'grep -E "CommitLimit|Committed_AS" /proc/meminfo'

Chạy workload thực tế của bạn — restore database, một batch inference model, hoặc replay traffic spike. Nếu Committed_AS tăng nhanh về phía CommitLimit rồi ổn định ngay dưới đó, ratio của bạn được hiệu chỉnh tốt. Nếu nó không bao giờ vượt 50% giới hạn ngay cả khi tải đỉnh, hãy thắt chặt ratio hơn và giảm thêm nguy cơ OOM.

Một điều cuối cùng: thêm vm.overcommit_memoryvm.overcommit_ratio vào checklist provisioning server của bạn. Mỗi instance mới đều phải có file /etc/sysctl.d/99-memory-overcommit.conf baseline được ghi trước khi triển khai bất kỳ ứng dụng nào. Sự cố lúc 2 giờ sáng mở đầu bài viết này xảy ra trên một server mới được provisioning mà bước đó bị bỏ qua. Sẽ không có lần thứ hai.

Share: