Sự cố lúc nửa đêm: Cơn ác mộng gia hạn SSL và bài toán cấu hình thủ công
Nửa năm trước, hệ thống thanh toán bên mình gặp một sự cố nhớ đời: SSL của subdomain API đột ngột hết hạn vào đúng 23h đêm thứ Bảy. Giao dịch gián đoạn, khách hàng liên tục báo lỗi. Cả team trực on-call phải thức trắng chỉ vì lệnh certbot renew bị treo do lỗi phân quyền cron job.
Đợt đó, team mình vừa dựng thêm cụm node mới chạy CentOS Stream 9 để tải khoảng 15 microservices Golang và Node.js. Càng mở rộng, stack quen thuộc Nginx + Certbot càng bộc lộ nhiều điểm nghẽn trong khâu vận hành:
- File cấu hình Nginx dài ngoằng. Riêng phần chuyển hướng HTTP sang HTTPS, trỏ cert, key và tinh chỉnh TLS version đã ngốn 30–40 dòng cho mỗi domain.
- Certbot chạy timer systemd độc lập. Mỗi lần cert gia hạn lại phải reload Nginx, rất dễ lỗi ngầm (silent fail) nếu tiến trình reload bị kẹt.
- Bật HTTP/3 (QUIC) trên Nginx của CentOS Stream 9 cực kỳ vất vả. Gói repo mặc định chưa hỗ trợ đầy đủ, buộc phải compile thủ công từ source rồi tự quản lý dependencies.
Nhìn lại bản chất: Vì sao Nginx hay Apache ngốn nhiều công vận hành?
Nginx hay Apache không hề tệ. Chúng vẫn là những tượng đài về hiệu năng. Tuy nhiên, kiến trúc của các web server này ra đời từ thời kỳ HTTPS còn là một tùy chọn thêm thắt, chưa phải tiêu chuẩn mặc định như hiện nay.
Mô hình truyền thống để lộ ba điểm bất tiện:
- Quản lý chứng chỉ bị tách rời: Web server lo định tuyến, còn việc cấp phát và gia hạn cert lại giao cho công cụ bên thứ ba (Certbot, acme.sh). Hai tiến trình này chỉ giao tiếp qua hook script hoặc file tĩnh trên ổ cứng. Chỉ cần phân quyền sai là hệ thống đứt gãy.
- Độ phức tạp khi siết bảo mật: Muốn đạt chuẩn A+ trên Qualys SSL Labs với Nginx, bạn phải tự tìm bộ cipher suite phù hợp, bật HSTS và cấu hình OCSP Stapling hoàn toàn thủ công.
- Cấu hình Reverse Proxy rườm rà: Để đẩy traffic về backend ở port 3000 hay 8080, Nginx bắt buộc khai báo thủ công hàng loạt header:
X-Real-IP,X-Forwarded-For,X-Forwarded-Proto,UpgradevàConnection.
Ba hướng tiếp cận bài toán tự động hóa web server
Đứng trước bài toán trên CentOS Stream 9, mình đưa ra ba giải pháp để cân nhắc:
- Cách 1: Tự viết playbook Ansible để tự động hóa Nginx + acme.sh. Giải pháp này giữ lại Nginx quen thuộc. Đổi lại, script bảo trì ngày càng phình to khi số lượng microservice tăng lên.
- Cách 2: Đặt toàn bộ hạ tầng sau Cloudflare SSL. Thiết lập nhanh, giảm tải cho server. Dù vậy, luồng traffic từ Cloudflare về máy chủ gốc (origin server) vẫn cần mã hóa theo chuẩn Full (Strict). Nghĩa là bạn vẫn không thoát được việc quản lý cert nội bộ.
- Cách 3: Chuyển sang Caddy Web Server (Caddy 2). Caddy viết bằng Go, tích hợp sẵn engine ACME trực tiếp trong lõi tiến trình. Web server tự bật HTTPS, HTTP/2 và HTTP/3 mà không cần cấu hình thêm bất kỳ module nào.
Triển khai Caddy Web Server trên CentOS Stream 9
Sau 6 tháng đưa Caddy lên production chịu tải khoảng 800.000 requests/ngày, hệ thống hoạt động ổn định và chưa gặp sự cố SSL nào. Dưới đây là các bước triển khai chi tiết từ thực tế.
Bước 1: Cài đặt Caddy từ kho COPR
Trên CentOS Stream 9, cộng đồng Caddy duy trì gói RPM chính thức thông qua Fedora COPR. Thao tác cài đặt qua dnf rất nhanh gọn:
# Cài đặt plugin dnf-plugins-core
sudo dnf install -y 'dnf-command(copr)'
# Kích hoạt repo Caddy chính thức
sudo dnf copr enable -y @caddy/caddy
# Cài đặt Caddy
sudo dnf install -y caddy
# Kiểm tra phiên bản sau khi cài đặt
caddy version
Bước 2: Mở port trên Firewall
Caddy yêu cầu mở port 80 (HTTP) để xác thực ACME challenge và port 443 (HTTPS). Do Caddy hỗ trợ sẵn HTTP/3 qua UDP, bạn cần mở cả hai giao thức TCP và UDP cho port 443:
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --permanent --add-port=443/udp
sudo firewall-cmd --reload
Bước 3: Viết Caddyfile tinh gọn
Mở file cấu hình chính tại /etc/caddy/Caddyfile:
sudo nano /etc/caddy/Caddyfile
Thay toàn bộ nội dung mặc định bằng cấu hình reverse proxy sau:
api.itfromzero.vn {
# Bật nén dữ liệu giúp giảm dung lượng tải trang
encode gzip zstd
# Chuyển tiếp request về backend port 3000
# Caddy tự động gán X-Forwarded-For, X-Forwarded-Proto và Host
reverse_proxy 127.0.0.1:3000
# Ghi access log chuẩn JSON để đưa về Grafana Loki hoặc ELK
log {
output file /var/log/caddy/api_access.log {
roll_size 50mb
roll_keep 5
}
format json
}
}
Điểm tiện lợi nhất: bạn chỉ cần trỏ A-record của domain api.itfromzero.vn về IP máy chủ. Khi Caddy khởi chạy, nó tự gửi yêu cầu tới Let’s Encrypt hoặc ZeroSSL qua challenge HTTP-01 hoặc TLS-ALPN-01. Chứng chỉ được cấp tự động và sẽ gia hạn khi còn 30 ngày. Bạn không cần cấu hình đường dẫn cert hay chạy thêm bất kỳ cron job nào.
Bước 4: Cấu hình SELinux cho Reverse Proxy
CentOS Stream 9 kích hoạt SELinux ở chế độ Enforcing theo mặc định. Trừ khi bạn phân quyền, SELinux sẽ chặn web server kết nối tới các port local của ứng dụng, gây ra lỗi 502 Bad Gateway. Khắc phục bằng một lệnh duy nhất:
# Cho phép tiến trình web server kết nối tới mạng nội bộ và các backend port
sudo setsebool -P httpd_can_network_connect 1
Bước 5: Khởi chạy và kiểm tra dịch vụ
Gói cài đặt đã tích hợp sẵn systemd unit. Trước khi chạy, hãy kiểm tra tính hợp lệ của file cấu hình:
# Kiểm tra cú pháp Caddyfile
caddy validate --config /etc/caddy/Caddyfile
# Khởi động Caddy và đặt tự chạy cùng hệ điều hành
sudo systemctl enable --now caddy
# Kiểm tra trạng thái tiến trình
sudo systemctl status caddy
Khi cần sửa domain hoặc thêm route mới, tuyệt đối không dùng lệnh restart gây đứt kết nối hiện tại. Caddy hỗ trợ nạp cấu hình mới mượt mà không gián đoạn kết nối:
sudo systemctl reload caddy
Đánh giá thực tế sau 6 tháng vận hành
Về tài nguyên, Caddy tốn RAM hơn Nginx một chút. Khi ở trạng thái idle, Caddy chiếm khoảng 32MB RAM, trong khi Nginx chỉ tốn tầm 15MB. Ở mức tải đỉnh điểm 2.500 requests/giây, Caddy ngốn xấp xỉ 68MB RAM và 12% một vCPU trên VPS 2 core/4GB RAM. Mức chênh lệch này hoàn toàn nằm trong ngưỡng chấp nhận được.
Đổi lại, team thu về nhiều lợi ích rõ rệt:
- Không còn sự cố SSL: Quá trình cấp mới và gia hạn diễn ra âm thầm, không phát sinh lỗi lệch phân quyền hay nghẽn cron.
- Rút ngắn thời gian deploy: Cấu hình một microservice mới chỉ mất chưa đầy 3 phút thay vì 20 phút căn chỉnh từng dòng chỉ thị như trước.
- Độ trễ người dùng giảm: Nhờ giao thức HTTP/3 kích hoạt sẵn, kết nối mạng trên thiết bị di động giảm 120–180ms thời gian bắt tay (handshake) ở những vùng sóng yếu.
Nếu bạn đang chuẩn bị dựng máy chủ mới trên CentOS Stream 9 hoặc các distro cùng họ RHEL, Caddy là phương án rất đáng để cân nhắc thay cho Nginx truyền thống.

