Cài đặt hệ thống

Trang này hướng dẫn theo đúng kiểu cài đặt đang có trong thư mục deployment: đưa mã nguồn lên GitHub, để GitHub Actions đóng gói bản build, rồi dùng SSH để đưa phiên bản mới lên server. Cách này phù hợp khi bạn muốn mỗi lần cập nhật chỉ cần push code là hệ thống tự triển khai.

Hiểu nhanh 6 khái niệm trước khi bắt đầu

  • GitHub repository: nơi lưu mã nguồn của dự án
  • GitHub Actions: công cụ tự chạy các bước build và deploy mỗi khi bạn đẩy code lên GitHub
  • server: máy chủ chạy website thật
  • .env: file chứa cấu hình của phần backend như domain, database, mail, queue
  • .env.admin: file chứa cấu hình riêng của phần admin frontend
  • SSH key: cặp khóa giúp GitHub đăng nhập an toàn vào server mà không cần nhập mật khẩu

Cài đặt này đang hoạt động theo luồng nào?

  1. Bạn push code lên nhánh dev hoặc main.
  2. GitHub Actions chạy workflow CI/CD.
  3. Workflow tạo file nén chứa mã nguồn và thư mục .github/deployment.
  4. GitHub dùng SSH để chép file nén lên server.
  5. Server tự tạo thư mục release mới, nối storage, .env, .env.admin, rồi chạy các bước cài đặt.
  6. Khi mọi thứ xong, server đổi liên kết current sang bản mới và giữ lại 5 bản gần nhất để dễ quay lại nếu cần.

Bước 1: Chuẩn bị những gì

Bạn nên có sẵn các thông tin sau trước khi bắt đầu:

  • quyền truy cập vào repository GitHub của dự án
  • quyền tạo SecretsVariables trong GitHub repository
  • quyền SSH vào server
  • domain chạy website và domain chạy admin
  • thông tin database
  • thông tin mail, Redis, lưu trữ file nếu dự án có dùng

Về phần mềm trên server, nên kiểm tra trước:

  • PHP đúng phiên bản đang khai báo trong workflow, hiện file mẫu dùng 8.3
  • Composer
  • Node.jspnpm
  • gói acl để có lệnh setfacl
  • web server như nginx hoặc apache
  • dịch vụ chạy nền như supervisor hoặc giải pháp tương đương

Điểm quan trọng cần nhớ

Trong bộ script hiện tại, các bước build dùng pnpm được chạy trực tiếp trên server. Điều này áp dụng cho phần frontend gốc của dự án và cả admin nếu thư mục admin có tồn tại. Vì vậy server phải có sẵn Node.jspnpm.

Bước 2: Đưa mã nguồn lên GitHub

Nếu dự án chưa có repository:

  1. Tạo repository mới trên GitHub.
  2. Đưa toàn bộ source code của dự án lên repository đó.
  3. Tạo ít nhất 2 nhánh làm việc là devmain nếu bạn muốn tách môi trường thử nghiệm và môi trường chính thức.

Nếu dự án đã có repository, bạn chỉ cần kiểm tra lại:

  • nhánh dev dùng cho môi trường thử nghiệm
  • nhánh main dùng cho môi trường chính thức
  • thư mục .github đã có trong source code hay chưa

Bước 3: Chép workflow và deployment scripts vào dự án

Trong thư mục deployment/build-script đang có sẵn bộ file mẫu. Bạn cần chép chúng vào source code của dự án như sau:

  1. Chép file deployment/build-script/workflows/ci-cd.yml vào .github/workflows/ci-cd.yml.
  2. Chép cả thư mục deployment/build-script/deployment vào .github/deployment/.

Sau khi chép xong, cấu trúc tối thiểu của repo nên có:

.github/
  workflows/
    ci-cd.yml
  deployment/
    prepare.sh
    deploy.sh
    hooks/
      before-activation.sh
      after-activation.sh
      flush-opcache.sh
      set-file-permissions.sh

Bước 4: Kiểm tra file workflow trước khi dùng

Workflow mẫu hiện chạy khi có push vào devmain. Bạn cần rà lại vài chỗ quan trọng:

  • PHP_VERSION đang để 8.3
  • job deploy-dev trong file mẫu vẫn dùng đường dẫn placeholder /var/www/[domain_folder]
  • job deploy-main dùng biến DOMAIN_FOLDER

Điều này có nghĩa là bạn nên sửa file workflow trước khi dùng thật:

  • thay placeholder của deploy-dev thành đường dẫn thực tế
  • hoặc chuẩn hóa cả devmain để cùng đọc từ GitHub Variables

Ví dụ dễ hiểu:

  • thư mục gốc của dự án trên server là /var/www/ten-du-an
  • vậy base_directory phải là /var/www/ten-du-an
  • không được điền /var/www/ten-du-an/current

prepare.sh có kiểm tra điều này và sẽ báo lỗi nếu bạn trỏ thẳng vào thư mục current.

Bước 5: Tạo tài khoản deploy trên server

Nên dùng một tài khoản riêng để GitHub đăng nhập vào server, ví dụ deploy, githubconnector hoặc tên tương tự. Không nên dùng root nếu không thật sự cần.

Tài khoản này cần:

  • có quyền vào thư mục dự án
  • có quyền chạy php, composer, pnpm
  • thuộc cùng nhóm với user đang phục vụ web, thường là www-data hoặc nginx

Ví dụ thêm user deploy vào nhóm web server:

sudo usermod -aG www-data deploy

Bước 6: Tạo SSH key để GitHub đăng nhập vào server

Bạn cần một cặp khóa:

  • private key: lưu ở GitHub Secrets
  • public key: lưu trong ~/.ssh/authorized_keys của user deploy trên server

Ví dụ tạo khóa:

ssh-keygen -t ed25519 -C "github-actions-deploy"

Sau đó:

  1. Mở file private key và chép toàn bộ nội dung vào GitHub Secret SSH_PRIVATE_KEY.
  2. Mở file public key và thêm vào ~/.ssh/authorized_keys của user deploy trên server.

Lỗi rất hay gặp

Không dán public key vào SSH_PRIVATE_KEY. Script prepare.sh có kiểm tra và sẽ dừng nếu thấy khóa bạn nhập trông giống public key.

Bước 7: Tạo known hosts

known hosts là danh sách máy chủ mà GitHub được phép tin tưởng khi SSH vào. Nếu không có phần này, GitHub có thể từ chối kết nối hoặc kết nối mà không kiểm tra danh tính server.

Lấy giá trị này bằng lệnh:

ssh-keyscan -p 22 -H your-server.com

Nếu server dùng cổng khác, thay 22 bằng cổng thật. Toàn bộ kết quả trả về sẽ được dán vào GitHub Secret SSH_KNOWN_HOSTS.

Bước 8: Khai báo SecretsVariables trên GitHub

Trong repository, vào Settings -> Secrets and variables -> Actions.

Tạo các Secrets sau:

  • REMOTE_USER: user deploy trên server
  • REMOTE_HOST: IP hoặc domain của server
  • REMOTE_PORT: cổng SSH
  • SSH_PRIVATE_KEY: private key vừa tạo
  • SSH_KNOWN_HOSTS: giá trị lấy bằng ssh-keyscan

Tạo các Variables sau nếu bạn dùng workflow giống file mẫu:

  • DOMAIN_FOLDER: tên thư mục gốc của dự án trên server, ví dụ ten-du-an

Sau đó workflow sẽ ghép thành:

/var/www/${DOMAIN_FOLDER}

Bước 9: Chuẩn bị thư mục gốc trên server

Với cách deploy hiện tại, mỗi dự án nên có một thư mục gốc riêng, ví dụ:

/var/www/ten-du-an

Ngay từ đầu, bạn nên tạo các thành phần cơ bản:

sudo mkdir -p /var/www/ten-du-an/config
sudo mkdir -p /var/www/ten-du-an/storage
sudo chown -R deploy:www-data /var/www/ten-du-an
sudo chmod -R 2775 /var/www/ten-du-an

Sau lần deploy đầu tiên, cấu trúc sẽ dần có dạng:

/var/www/ten-du-an
  .env
  .env.admin
  config/
    setting.php
  modules_statuses.json
  storage/
  releases/
    1/
    2/
  current -> /var/www/ten-du-an/releases/2

Nếu server cũ của bạn đang dùng kiểu shared/storageshared/.env giống Deployer, script hiện tại cũng tự nhận ra và dùng lại cấu trúc đó.

Bước 10: Tạo file .env trên server

File .env phải nằm ở thư mục gốc dự án và không được để trống. Nếu file rỗng, deploy.sh sẽ dừng ngay.

Ví dụ vị trí:

/var/www/ten-du-an/.env

Những nhóm giá trị bạn cần điền tối thiểu:

  • thông tin ứng dụng: APP_NAME, APP_ENV, APP_KEY, APP_URL, APP_DEBUG
  • domain và session: APP_DOMAIN, ASSET_URL, SESSION_DOMAIN, SANCTUM_STATEFUL_DOMAINS
  • ngôn ngữ và múi giờ: DEFAULT_LOCALE, TIMEZONE
  • database: DB_CONNECTION, DB_HOST, DB_PORT, DB_DATABASE, DB_USERNAME, DB_PASSWORD
  • cache, queue, session: CACHE_DRIVER, QUEUE_CONNECTION, SESSION_DRIVER, REDIS_*
  • xác thực nội bộ: ISPA_API_AUTH_KEY, ISPA_ADMIN_AUTH_KEY
  • email: MAIL_* nếu dự án có gửi thư
  • lưu trữ file: AWS_* nếu dùng object storage

Các biến rất hay ảnh hưởng đến đăng nhập:

  • APP_URL
  • SESSION_DOMAIN
  • SANCTUM_STATEFUL_DOMAINS
  • ISPA_ADMIN_AUTH_KEY

Nếu bạn dùng admin ở subdomain riêng, hãy điền các biến trên theo đúng domain thật. Điền sai ở đây thường gây lỗi đăng nhập, lỗi cookie hoặc lỗi gọi API.

Bước 11: Tạo file .env.admin trên server

File này dành riêng cho admin frontend và cũng không được để trống.

Ví dụ vị trí:

/var/www/ten-du-an/.env.admin

Các biến thường cần điền:

  • VITE_DOMAIN: domain của admin
  • VITE_BASE_URL: địa chỉ API backend mà admin sẽ gọi tới
  • VITE_PROTOCOL: thường là https
  • VITE_NODE_ENV: môi trường chạy
  • VITE_DEFAULT_LOCALE: ngôn ngữ mặc định
  • VITE_APP_ADMIN_AUTH_KEY: phải khớp với khóa admin ở backend
  • VITE_SECURE_COOKIE: bật khi dùng HTTPS

Hai biến cần hiểu đúng:

  • VITE_BUILD_VERSION: script deploy sẽ tự cập nhật theo thời gian build, bạn không cần gán tay cố định
  • VITE_APP_ADMIN_AUTH_KEY: phải tương thích với ISPA_ADMIN_AUTH_KEY ở backend

Bước 12: Chuẩn bị config/setting.php

Nếu dự án có file config/setting.php dùng cho cấu hình riêng, hãy đặt file này tại:

/var/www/ten-du-an/config/setting.php

Nếu lần đầu deploy mà file này chưa có:

  • script sẽ cố lấy từ bản release trước nếu có
  • nếu không có release cũ, script sẽ dùng file đi kèm trong source code

Điều này giúp setting.php được giữ ổn định giữa các lần deploy.

Bước 13: Cấu hình phân quyền file và thư mục

Phần này rất quan trọng nếu bạn muốn upload ảnh, ghi log, tạo cache và chạy queue ổn định.

Theo hook deployment/build-script/deployment/hooks/set-file-permissions.sh, hệ thống mong đợi:

  • user deploy và user web server thuộc cùng một group
  • server có lệnh setfacl
  • thư mục cần ghi có quyền 0775

Bạn nên kiểm tra:

sudo apt install acl
id deploy
id www-data

Nếu user deploy chưa cùng group với web server:

sudo usermod -aG www-data deploy

Ngoài ra, hook cũng kiểm tra config/filesystems.php. Hai disk localpublic cần để quyền thư mục là 0775. Nếu để sai, script có thể dừng để tránh lỗi upload về sau.

Bước 14: Cấu hình web server

Bạn cần trỏ domain về đúng thư mục public của bản đang chạy:

/var/www/ten-du-an/current/public

Trong thư mục deployment hiện có nhiều file .conf mẫu cho nginx để bạn tham khảo cách:

  • trỏ domain chính
  • trỏ domain admin hoặc frontend riêng
  • phục vụ file trong /storage
  • bật HTTPS

Nếu bạn đang dùng nginx, điểm quan trọng nhất là luôn trỏ website PHP/Laravel vào current/public, không trỏ thẳng vào một thư mục release cụ thể.

Bước 15: Cấu hình queue và schedule

CMS muốn chạy ổn định thì gần như luôn cần tiến trình nền. Trong thư mục deployment đang có sẵn các file queue-*.confschedule-*.conf để làm mẫu.

Ý nghĩa của từng loại:

  • queue: xử lý việc chạy nền như gửi mail, đồng bộ dữ liệu, tác vụ nặng
  • schedule: chạy các công việc theo lịch

Ví dụ trong file mẫu:

  • queue chạy artisan queue:work
  • schedule chạy artisan schedule:work

Sau khi chép file .conf vào supervisor, bạn thường cần chạy:

sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl status

Nếu dự án có queue riêng như gpt_caller, bạn cũng cần bật worker riêng cho queue đó giống file mẫu.

Bước 16: Push code để chạy deploy đầu tiên

Sau khi đã hoàn tất các phần trên:

  1. Commit các file .github/workflows/ci-cd.yml.github/deployment/*.
  2. Push code lên nhánh dev hoặc main.
  3. Mở tab Actions trên GitHub để xem workflow chạy.

Nếu mọi thứ đúng, script sẽ tự làm các việc sau:

  • chép artifact lên server
  • tạo thư mục release mới
  • nối storage, .env, .env.admin, setting.php
  • cài composer
  • chạy migration
  • chạy migration cho module
  • đồng bộ permission và translation của CMS
  • build admin bằng pnpm
  • chuyển current sang release mới
  • flush OPCache
  • xóa các release cũ, chỉ giữ lại 5 bản gần nhất

Một chi tiết dễ gây hoang mang

Sau khi build admin xong, script hiện tại xóa thư mục admin khỏi release mới. Đây là hành vi bình thường của script mẫu. Nếu bạn vào thư mục release mà không thấy admin, không có nghĩa là deploy bị lỗi.

Bước 17: Kiểm tra sau khi deploy

Sau lần chạy đầu tiên, bạn nên kiểm tra ngay:

  1. Mở website ngoài xem có vào được không.
  2. Mở admin xem có đăng nhập được không.
  3. Tạo thử một bản ghi hoặc upload một ảnh để kiểm tra phân quyền ghi file.
  4. Kiểm tra trang Dashboard và các module chính.
  5. Kiểm tra log trong storage/logs.
  6. Kiểm tra supervisorctl status để chắc queue và schedule đang chạy.

Các lỗi thường gặp và cách hiểu nhanh

GitHub báo không SSH vào được server

Thường là một trong ba lỗi sau:

  • REMOTE_HOST hoặc REMOTE_PORT sai
  • SSH_PRIVATE_KEY không đúng private key
  • SSH_KNOWN_HOSTS chưa đúng

Workflow báo .env hoặc .env.admin rỗng

Script deploy dừng nếu các file này có tồn tại nhưng không có nội dung. Hãy điền đủ cấu hình rồi chạy lại.

Workflow báo không tìm thấy Composer

Server chưa cài Composer, hoặc user deploy không thấy lệnh composer trong môi trường shell.

Workflow báo lỗi phân quyền

Thường do:

  • chưa cài acl
  • user deploy chưa cùng group với web server
  • thư mục storage hoặc thư mục dự án chưa có quyền ghi phù hợp

Workflow báo không kết nối được database

Hook before-activation.sh có bước chờ database sẵn sàng rồi mới migrate. Nếu nó thất bại, hãy kiểm tra lại DB_HOST, DB_PORT, DB_DATABASE, DB_USERNAME, DB_PASSWORD.

Workflow báo lỗi flush OPCache

Script này gọi theo APP_URL. Nếu APP_URL sai domain, sai protocol hoặc web server chưa trả về đúng site, bước flush OPCache có thể thất bại.

Gợi ý bàn giao cho người không chuyên kỹ thuật

Khi bàn giao hệ thống, bạn nên chuẩn bị sẵn một checklist ngắn:

  • repository GitHub ở đâu
  • ai có quyền xem Actions
  • user nào dùng để SSH deploy
  • .env đang nằm ở đâu trên server
  • .env.admin đang nằm ở đâu trên server
  • cách xem queue và schedule có đang chạy hay không
  • cách kiểm tra bản deploy mới nhất trong thư mục releases

Như vậy, kể cả người tiếp quản không rành code vẫn có thể lần theo đúng chỗ để kiểm tra.