Website AI là sản phẩm website dựng sẵn của Sudo: một bộ core Laravel, mỗi khách hàng nhận một bản cài riêng với database riêng, giao diện và nội dung riêng. Ở giai đoạn đầu, các site Website AI chạy trên Cloud VPS theo mô hình quen thuộc: một balancer phía trước, phía sau là từng cặp máy minion chạy code và máy database đi kèm. Bài mở đầu series Kubernetes mô tả mô hình đó, cách deploy bằng SSH, những điểm đau xuất hiện khi số site tăng lên, và vì sao một sản phẩm kiểu Website AI rất hợp với container và Kubernetes.

1. Website AI chạy thế nào trên Cloud VPS

Hạ tầng Website AI đi theo đúng mô hình ba tầng Balancer – Minion – Database trên Cloud VPS, chỉ khác ở chỗ được tách thành một cụm riêng. Một balancer riêng nhận HTTPS từ Internet. Phía sau là các cặp máy đánh số thứ tự: minion chạy nginx và PHP-FPM, máy database chạy MySQL cho các site nằm trên minion đó.

                  Internet (HTTPS)
                         │
                         ▼
          ┌─────────────────────────────┐
          │ balancer Website AI         │  nginx, SSL Let's Encrypt
          └──────────────┬──────────────┘
         ┌───────────────┼───────────────┐
         ▼               ▼               ▼
  ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
  │ minion 1     │ │ minion 2     │ │ minion N     │   nginx + PHP-FPM
  │ /var/www/... │ │ /var/www/... │ │ /var/www/... │   cron, supervisor
  └──────┬───────┘ └──────┬───────┘ └──────┬───────┘
         ▼                ▼                ▼
  ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
  │ database 1   │ │ database 2   │ │ database N   │   MySQL
  └──────────────┘ └──────────────┘ └──────────────┘

Các tên máy được khai báo trong DNS nội bộ của Cloud VPS. Khi sản phẩm bán tốt, cụm lớn theo đúng một nhịp: hết chỗ thì dựng thêm một cặp minion và database, và khoảng cách giữa hai lần dựng cặp mới ngắn dần.

Trên mỗi minion, một site Website AI là một bản clone đầy đủ của repo core vào /var/www/<domain>, kèm file .env riêng, một dòng cron chạy schedule:run và một file cấu hình supervisor để chạy queue worker. Database của site nằm trên máy database cùng cặp.

2. Deploy Website AI bằng GitLab Runner và SSH

Site mới được tạo bằng pipeline GitLab CI theo cách trong bài Deploy tự động đầu tiên: GitLab Runner, SSH và nginx template. Pipeline gồm các nhóm bước:

  1. Tạo database: SSH vào máy database của cặp, tạo schema và user.
  2. Deploy code: SSH vào minion, git clone core vào thư mục domain, dựng .env bằng một loạt lệnh sed, chạy composer, migrate, key:generate và các lệnh seed riêng của sản phẩm.
  3. Hoàn thiện site: cron, supervisor, robots.txt, phân quyền, tài khoản quản trị, chạy song song sau khi có code.
  4. Cấu hình nginx trên minion, rồi trên balancer tuỳ site dùng Let's Encrypt hay Cloudflare. Nếu bước balancer lỗi, xoá file cấu hình vừa thêm và khởi động lại nginx.

Ba đoạn trích dưới đây (đã lược bớt) là kiểu script hay gặp, và cũng là nơi các vấn đề bắt đầu lộ ra:

# deploy code (chạy trên minion)
git clone $LINK_SSH_REPO $DOMAIN &&
cd $DOMAIN && cp .env.example .env &&
sed -i 's/^DB_HOST=.*/DB_HOST=$PRIVATE_IP_DATABASE/g' .env &&
...
composer update --with-all-dependencies &&
php artisan migrate && php artisan key:generate

# cấu hình supervisor (chạy trên minion)
cp -r supervisord.ini.example supervisord.ini &&
sed -i 's/site-a.example/$DOMAIN/g' supervisord.ini &&
systemctl restart supervisord

# cấp SSL (chạy trên balancer)
systemctl stop nginx &&
certbot certonly --standalone -d $DOMAIN -d www.$DOMAIN \
    --force-renewal --non-interactive --agree-tos &&
systemctl start nginx

Pipeline này đạt mục tiêu ban đầu: nhập vài tham số, bấm chạy là có một site Website AI mới. Nhưng khi số site và số máy tăng lên, các điểm yếu của cách làm "SSH vào từng máy rồi chạy lệnh" bắt đầu lộ rõ.

3. Những điểm đau khi Website AI lớn dần

Mỗi site một bản code, mỗi bản một bộ vendor

Mọi site dùng chung một core, nhưng trên đĩa lại là hàng loạt bản clone độc lập. Lệnh composer update --with-all-dependencies bỏ qua composer.lock có sẵn trong repo và tải phiên bản mới nhất mà ràng buộc trong composer.json cho phép. Hai site tạo cách nhau vài tháng có thể chạy cùng một commit core nhưng khác phiên bản thư viện. Khi có lỗi chỉ xuất hiện ở vài site, câu hỏi đầu tiên luôn là "site này đang chạy bộ vendor nào". Bài học: trong deploy, luôn dùng composer install theo composer.lock.

Cập nhật core cho toàn bộ Website AI cũng không có một thao tác duy nhất. Nếu pipeline chỉ có luồng tạo site mới, muốn đưa bản sửa lỗi tới mọi site phải làm trên từng thư mục của từng minion, và không có khái niệm "phiên bản đang chạy" để so sánh hay quay lui.

Thêm một site có thể làm phiền các site khác

  • Cấp SSL dừng nginx của balancer. certbot --standalone cần chiếm cổng 80, nên phải systemctl stop nginx. Trong lúc certbot chạy, mọi site sau balancer đều không truy cập được. Cách cấp chứng chỉ không cần dừng nginx có trong bài SSL cho hàng trăm domain với Certbot.
  • Khởi động lại supervisor cho cả máy. systemctl restart supervisord làm mọi queue worker của mọi site trên minion dừng rồi chạy lại, chỉ để thêm worker cho một site. Dùng supervisorctl reread && supervisorctl update để chỉ nạp phần thay đổi.
  • Cron dùng chung. Mỗi site thêm một dòng vào /etc/crontab. Gỡ site mà quên xoá dòng cron thì lệnh vẫn chạy vào một thư mục không còn tồn tại.

Mở rộng bằng tay, theo từng cặp máy

Mỗi lần cần thêm chỗ là dựng thêm một cặp: tạo hai Cloud VPS, cài nginx, PHP, MySQL, supervisor, khai báo DNS, rồi thêm IP vào danh sách lựa chọn của pipeline. Mỗi lần như vậy là thêm hai máy phải vá bảo mật, theo dõi đĩa, theo dõi RAM. Tài nguyên giữa các cặp cũng không chia sẻ được: minion rảnh không giúp gì cho minion đang bận, vì site đã gắn cứng vào máy nào thì nằm yên ở đó.

Một minion chết, cả nhóm site chết theo

Đây là nhược điểm đã biết của mô hình ba tầng: mỗi site chạy trên đúng một minion, không có máy dự phòng. Khôi phục một minion hỏng nghĩa là dựng lại máy, clone lại code, khôi phục file người dùng đã upload, và hy vọng không sót bước nào.

4. So sánh công sức: hiện trạng và điều mong muốn

Trước khi so tiền, nên so công việc phải làm cho cùng một kết quả:

ViệcWebsite AI trên Cloud VPSĐiều mong muốn
Thêm năng lựcDựng thêm cặp minion + database, cài tay hoặc bán tự độngThêm máy vào một nhóm chung, phần mềm tự xếp site lên
Cập nhật coreĐi từng thư mục trên từng minionBuild một lần, mọi site dùng cùng một bản đóng gói
Quay lui khi lỗiKhông có, phải sửa tayQuay về bản trước bằng một lệnh
Máy chếtSite trên máy đó ngừng tới khi dựng lạiSite tự được chạy lại trên máy khác
Thêm siteCó thể gây gián đoạn site khácMỗi site tách biệt, thêm site không đụng site cũ

Cột bên phải nghe rất giống mô tả của container và Kubernetes, và đó là lý do Website AI là ứng viên đầu tiên.

5. Vì sao Website AI hợp với container và Kubernetes

Website AI có một đặc điểm khác hẳn các site khách hàng tự viết: mọi site cùng một core. Khác biệt giữa hai site nằm ở biến môi trường (domain, thông tin database, khoá ứng dụng) và dữ liệu (database, file upload, theme đã chỉnh). Phần code thì giống nhau. Đây đúng là trường hợp lý tưởng cho container:

  • Một image cho mọi site. Code, extension PHP và thư viện được đóng gói một lần, có tag phiên bản. composer install chạy lúc build theo composer.lock, không còn chuyện mỗi site một bộ vendor.
  • Cấu hình tách khỏi code. Thay vì sed vào file .env, biến môi trường được truyền vào lúc chạy container.
  • Mỗi site một môi trường riêng. Cron và queue worker nằm trong container của chính site đó, thêm hay xoá site không phải khởi động lại tiến trình dùng chung.

Nếu repo đã có Dockerfile cho môi trường phát triển, đó là điểm xuất phát tốt: bước tiếp theo là biến nó thành image chạy được production.

Container giải quyết chuyện đóng gói, còn bài toán "chạy hàng trăm container trên nhiều máy" cần thêm một tầng điều phối. Theo tài liệu tổng quan của Kubernetes, đây chính là việc Kubernetes làm: bạn khai báo trạng thái mong muốn (site A cần một bản chạy image phiên bản X), hệ thống tự xếp container lên máy còn chỗ, tự khởi động lại khi container chết, tự chuyển sang máy khác khi máy hỏng, và thay phiên bản kiểu cuốn chiếu để site không bị gián đoạn.

Với một đội vận hành nhỏ, Kubernetes dạng managed giúp không phải tự vận hành phần điều khiển của cluster. Phương án được chọn để tìm hiểu là LKE (Linode Kubernetes Engine), các bài sau sẽ giải thích kỹ hơn.

6. Những câu hỏi cần trả lời trước khi chuyển

  • Database đặt ở đâu? Chạy MySQL trong cluster hay giữ trên VPS riêng. Mỗi hướng có cái giá riêng về sao lưu và hiệu năng.
  • File người dùng nằm ở đâu? Ảnh upload, theme đã chỉnh không thể nằm trong container, vì container bị thay mới mỗi lần deploy. Cần một nơi lưu trữ bền, dùng chung được.
  • Team học được bao nhiêu, nhanh tới đâu? Kubernetes có rất nhiều khái niệm mới. Dev quen SSH vào minion để xem log sẽ phải làm quen với cách làm khác.
  • Chi phí thực tế. Cluster cần node chạy liên tục, cộng thêm load balancer và ổ đĩa. Chỉ số đo sau khi chạy thử mới cho biết có rẻ hơn cụm VPS hiện tại hay không.

Thay vì vội dựng cluster cho Website AI, nên đi từng bước: đóng gói core thành image trước, nắm chắc các khái niệm nền của Kubernetes, rồi mới chạy thử.

7. Lộ trình những bài tiếp theo

  1. Docker hoá Laravel: một image all-in-one cho Website AI.
  2. Kubernetes là gì: control plane, node và pod.
  3. Pod, Deployment, Service và Namespace.
  4. ConfigMap và Secret cho biến môi trường Laravel.
  5. Volume, PersistentVolume, PVC và StorageClass.
  6. Ingress và NodeBalancer: đưa traffic vào cluster.

Mỗi bài sẽ đối chiếu với cách Website AI chạy trên Cloud VPS, để thấy rõ cái gì được giải quyết và cái gì chỉ đổi chỗ.

Bài tiếp theo

Docker hoá Laravel: một image all-in-one cho Website AI. Đọc lại Dockerfile đầu tiên của core, hiểu image, layer, build cache và chọn giữa một container hay nhiều container cho một site.