Đừng để việc Deploy là nỗi ác mộng mỗi đêm
Bạn đã bao giờ thức đến 2 giờ sáng chỉ để fix một lỗi “vớ vẩn” do copy thiếu file config trên 1 trong số 30 server chưa? Hồi mới vào nghề, mình thường xuyên rơi vào cảnh tay gõ SSH mỏi nhừ, kéo code từ Git, rồi lại hì hục chmod cho từng node. Khi hệ thống scale từ 5 lên 50 server, phương pháp thủ công này chính là quả bom nổ chậm. Chỉ cần quên một bước nhỏ, toàn bộ cluster sẽ lăn đùng ra chết.
Đóng gói ứng dụng thành file RPM (Red Hat Package Manager) là cách các kỹ sư DevOps chuyên nghiệp giải quyết vấn đề này. Thay vì gửi một mớ file hỗn độn, bạn chỉ cần chuyển duy nhất một file .rpm. Hệ thống sẽ tự động kiểm tra dependency và đặt file vào đúng vị trí. Tuyệt vời nhất là khả năng rollback sạch sẽ qua lệnh dnf remove nếu có sự cố xảy ra.
Sau 6 tháng áp dụng quy trình này cho hệ thống ngân hàng trên CentOS Stream 9, mình đã đúc kết được lộ trình ngắn gọn nhất. Bạn sẽ làm chủ rpmbuild mà không cần đọc hết đống tài liệu khô khan của Red Hat.
Thực hành: “Lên đồ” cho file RPM đầu tiên
Trước khi bắt đầu, hãy chuẩn bị môi trường build sạch. Một lưu ý sống còn: tuyệt đối không dùng user root. Một sai sót nhỏ trong script build có thể quét sạch dữ liệu trên hệ thống của bạn trong tích tắc.
# Cài đặt bộ công cụ tiêu chuẩn
sudo dnf install -y rpm-build rpmdevtools gcc
# Khởi tạo cấu trúc thư mục build
rpmdev-setuptree
Lệnh trên sẽ tạo thư mục ~/rpmbuild. Trong đó, bạn cần quan tâm nhất đến SOURCES (chứa code gốc) và SPECS (chứa file cấu hình build). Hãy thử đóng gói một script đơn giản mang tên hello-itfromzero.sh để hiểu luồng chạy.
# Tạo file source mẫu
echo 'echo "Chào mừng bạn đến với itfromzero.com!"' > ~/rpmbuild/SOURCES/hello-itfromzero.sh
Tiếp theo, chúng ta tạo file SPEC – đây chính là bản thiết kế cho package của bạn:
nano ~/rpmbuild/SPECS/hello.spec
Hãy dán nội dung dưới đây vào:
Name: hello-itfromzero
Version: 1.0
Release: 1%{?dist}
Summary: Script chào mừng từ itfromzero
License: GPL
Source0: hello-itfromzero.sh
BuildArch: noarch
%description
Một script đơn giản để demo cách build RPM trên CentOS Stream 9.
%install
mkdir -p %{buildroot}/usr/bin/
cp %{_sourcedir}/hello-itfromzero.sh %{buildroot}/usr/bin/hello-itfromzero
chmod +x %{buildroot}/usr/bin/hello-itfromzero
%files
/usr/bin/hello-itfromzero
Cuối cùng, kích hoạt lệnh build:
rpmbuild -ba ~/rpmbuild/SPECS/hello.spec
Chỉ mất khoảng 3-5 giây, file RPM của bạn sẽ xuất hiện tại thư mục ~/rpmbuild/RPMS/noarch/. Bây giờ, bạn có thể mang file này cài đặt trên bất kỳ máy CentOS 9 nào khác chỉ với một lệnh dnf install duy nhất.
Mổ xẻ file SPEC: Những thành phần bạn buộc phải nhớ
Trong thực tế, file SPEC sẽ phức tạp hơn ví dụ trên rất nhiều. Dưới đây là 3 điểm mấu chốt mình thường xuyên phải xử lý khi đóng gói các ứng dụng Java hoặc Go.
1. Quản lý Dependency với Requires
Đây là nơi bạn định nghĩa điều kiện cần. Ví dụ: Requires: python3, nginx >= 1.20. Khi người dùng cài package, dnf sẽ tự tính toán và kéo các thư viện còn thiếu về. Điều này giúp tránh lỗi “thư viện không tồn tại” cực kỳ hiệu quả.
2. %install – Chiếc hộp Sandbox
Nhiều bạn mới làm thường nhầm %install là lệnh chạy khi cài RPM vào máy khách. Thực tế, nó là lệnh chạy trong lúc build. Nó copy file từ thư mục code vào một thư mục tạm gọi là BuildRoot. Hãy coi BuildRoot là một hệ điều hành ảo. Bạn xếp file vào đó như thế nào, thì khi cài đặt, RPM sẽ đặt file vào máy thật y hệt như vậy.
3. %post và %pre: Tự động hóa sau cài đặt
Đây là các script chạy trực tiếp trên máy đích. Bạn có thể dùng %pre để tạo user hệ thống trước khi cài, hoặc dùng %post để khởi động service ngay khi cài xong.
Nâng cao: Đóng gói ứng dụng chạy Systemd
Ở môi trường doanh nghiệp, gần như mọi app đều phải chạy dưới dạng service. Trên CentOS Stream 9, bạn nên tận dụng các macro của systemd để đảm bảo tính ổn định. Đừng gõ tay systemctl reload, hãy để RPM lo việc đó.
# Trong phần %install
install -D -m 644 %{SOURCE1} %{buildroot}%{_unitdir}/my-app.service
# Tự động đăng ký service sau khi cài
%post
%systemd_post my-app.service
# Dọn dẹp service khi gỡ bỏ
%preun
%systemd_preun my-app.service
Sử dụng các macro như %systemd_post giúp hệ thống tự reload daemon một cách an toàn. Cách làm này chuyên nghiệp hơn nhiều so với việc viết script bash rời rạc.
Kinh nghiệm thực tế: Những “cú lừa” mình từng gặp
Khi migrate hệ thống từ CentOS 7 sang CentOS Stream 9, mình đã gặp không ít rắc rối. Dưới đây là vài bài học xương máu để anh em tránh vết xe đổ:
- Lỗi “Installed but unpackaged”: Nếu bạn copy file vào
BuildRootnhưng quên liệt kê trong mục%files, quá trình build sẽ thất bại ngay lập tức. Mọi file đều phải được “điểm danh” đầy đủ. - Xung đột file cấu hình: Đừng bao giờ ghi đè trực tiếp vào file hệ thống. Hãy tạo thư mục riêng như
/etc/my-company/app.confđể tránh xung đột với các package khác. - Dùng Mock để build: Để đảm bảo RPM chạy được trên mọi máy (không bị lỗi “chạy tốt trên máy tôi”), mình khuyên dùng
mock. Công cụ này tạo một môi trường chroot sạch 100% để build, giúp phát hiện thiếu dependency ngay từ đầu.
Việc đóng gói RPM ban đầu có vẻ tốn công hơn so với một script bash đơn giản. Tuy nhiên, khi bạn cần quản lý phiên bản cho hàng trăm server, RPM chính là tấm vé bảo hiểm giúp bạn ngủ ngon hơn mỗi đêm. Chúc anh em sớm làm chủ quy trình đóng gói này!

