+29

Deploy ứng dụng chat realtime Laravel, VueJS, Laravel Echo và Laravel Reverb trên Ubuntu

Cập nhật gần nhất: 05/12/2024

Chào mừng các bạn đã quay trở lại với blog của mình 👋👋

Ở 2 bài trước chúng ta đã biết cách sử dụng Public, Private và Presence channel để xây dựng ứng dụng chat realtime với Laravel.

Giờ ở local đã chạy ngon, vấn đề là làm sao để có thể deploy ứng dụng ra server thật cho các anh chị em khác vào sử dụng. Vì việc deploy 1 ứng dụng Laravel ra production sẽ khá là khác so với lúc ta setup ở local và rất nhiều bạn đã comment cho mình là viết về 1 bài về việc này.

Cùng với đó mình thấy ứng dụng Chat realtime này cũng là 1 ví dụ điển hình cho một project Laravel chúng ta thường thấy khi đi làm với đầy đủ các components:

  • Laravel
  • MySQL
  • VueJS
  • Queue/Job
  • Task Scheduling
  • Laravel Reverb (websocket server)
  • Laravel Telescope/Pulse cho monitoring

Ta bắt đầu thôi nhé 🚀🚀

Điều kiện tiên quyết

Nghe như học sinh cấp 2 😂😂

Ở bài này ta sẽ thực hành với project của mình với đầy đủ chức năng và sẵn sàng cho deploy trên server ở đây: https://github.com/maitrungduc1410/realtime-chatapp-laravelecho-socketio

Vì deploy trên server thật nên tất nhiên các bạn sẽ phải có server để thực hành, môi trường Ubuntu là oke nhất. Server các bạn có thể thuê ở bất kì nhà cung cấp nào: AWS, Google, Azure, Digital Ocean,... (Đây là 4 địa chỉ mà mình ưa thích và luôn recommend)

Các bạn chú ý là ta phải có Server (VPS, VM), chứ không phải Hosting thông thường nhé.

Nếu các bạn có domain thì đến cuối bài ta có thể setup được HTTPS xịn xò luôn. Mình khuyến khích các bạn bỏ ra ít tiền (mấy chục tới 1-200K) lên Goddady mua 1 cái domain lởm lởm về để học tập 😘

Ví dụ như ở đây của mình là 1 con VM Ubuntu trên Digital Ocean, phiên bản 24.10

Screenshot 2024-12-05 at 2.14.40 PM.jpg

Ta cũng có thể check bản phân phối Linux, version khi login vào server như sau:

cat /etc/os-release

>>>
PRETTY_NAME="Ubuntu 24.10"
NAME="Ubuntu"
VERSION_ID="24.10"
VERSION="24.10 (Oracular Oriole)"
VERSION_CODENAME=oracular
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=oracular

Tổng quan

Bài này chúng ta sẽ cùng nhau deploy ứng dụng realtime chat giống hệt như của mình ở đây: https://realtime-chat.jamesisme.com/

Đầu tiên ta cùng lắc não trước tổng hợp lại xem để chạy được project này thì ta cần những gì nhé:

  • code Laravel thì server cần phải có PHP + Composer (tất nhiên rồi ☺️)
  • MySQL
  • NodeJS/npm để build code VueJS
  • Crontab (Ubuntu có sẵn) để chạy Scheduled Task. Đợt đầu mới học Laravel mình ngây thơ lắm, đọc docs Laravel xong cứ nghĩ Task Scheduling cứ thế chạy với cái command được ghi trong docs cơ 🤪
  • Process Manager để chạy tất cả những thứ bên trên, ở đây mình chọn Supervisor. Đây là cái gì lát nữa mình sẽ giải thích sau nhé
  • Nginx làm webserver để chạy ứng dụng PHP. Các bạn chú ý là ở production ta không chạy php artisan serve nữa mà ta sẽ dùng một webserver để làm điều đó.
  • Cuối cùng là Certbot để lấy HTTPS

Ồi liệt kê ra cũng nhiều phết đấy chứ nhỉ 😁. Tiến hành thôi nào💪

Cài đặt

Đầu tiên đảm bảo là các bạn SSH vào được VM nha (không SSH vào được thì làm kiểu j đây 😂)

Đảm bảo là user các bạn đang dùng có quyền sudo nhé, như của mình là user root luôn

PHP

Giả sử ta mới tạo VM (VPS), mọi thứ còn mới tinh, thì đầu tiên ta update package list + upgrade các package sẵn có trước phát nhé:

sudo apt update -y && sudo apt upgrade -y

Sau đó ta cài PHP:

sudo apt install php8.3 -y

Hiện tại Laravel 11 cần PHP >= 8.3 rồi nha

Sau đó các bạn check thử xem php đã được cài thành công chưa nhé:

php -v

>>>
PHP 8.3.11 (cli) (built: Sep 30 2024 12:07:44) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.3.11, Copyright (c) Zend Technologies
    with Zend OPcache v8.3.11, Copyright (c), by Zend Technologies

Tiếp theo ta cần cài thêm extenstions của PHP vì đó là những extension mà ứng dụng Laravel của chúng ta sẽ cần dùng tới:

sudo apt install libapache2-mod-php php8.3-common php8.3-cli php8.3-mbstring php8.3-bcmath php8.3-fpm php8.3-mysql php8.3-zip php8.3-gd php8.3-curl php8.3-xml -y

Composer

Tiếp theo ta tiến hành cài Composer nhé. Các bạn chạy command sau:

sudo apt-get install curl php7.3-cli git unzip

Ở trên ta cài curl để download Composer về server, php7.3-cli để cài đặt Composer, Git để Composer có thể download các thành phần liên quan và cũng để sau này chúng ta clone code về luôn, unzip để giải nén.

Tiếp theo ta chuyển tới thư mục ~ (home) và bắt đầu cài đặt nhé:

cd ~

Các bạn vào trang download của Composer: https://getcomposer.org/download, chúng ta sẽ thấy giao diện như sau:

Screenshot 2024-12-05 at 6.18.45 PM.jpg

Ở phần đầu tiên các bạn copy toàn bộ command ở trong ô textbox, ở đây là:

php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php -r "if (hash_file('sha384', 'composer-setup.php') === 'dac665fdc30fdd8ec78b38b9800061b4150413ff2e3b6f88543c636f7cd84f6db9189d43a81e5503cda447da73c7e5b6') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
php composer-setup.php
php -r "unlink('composer-setup.php');"

Ta copy command đó đặt vào terminal VM và chạy nhé:

Screenshot 2024-12-05 at 6.21.00 PM.jpg

Sau đó thì ta đưa composer vào system PATH, thì tí nữa ta đơn giản là gọi trực tiếp composer ... từ terminal:

sudo mv composer.phar /usr/local/bin/composer

Sau khi quá trình cài đặt thành công, các bạn có thể gõ command sau ở terminal để check xem composer cài được hay chưa nha:

composer

Screenshot 2024-12-05 at 6.22.50 PM.jpg

Ở trên composer cảnh báo ta rằng không nên chạy composer với root user, cái này là lí do bảo mật vì khi ta cài PHP package, nhiều cái nó có thể chạy các script mà ta không biết. Nhưng thôi ở bài này cho đơn giản ta bỏ qua vấn đề này nha (chứ nói thêm vào thì lan man quá 😂

MySQL

Tiếp theo chúng ta tiến hành cài MySQL nhé. Các bạn chạy command sau:

sudo apt-get update
sudo apt install mysql-server -y

Cài xong ta kiểm tra:

mysql --version

>>>
mysql  Ver 8.0.40-0ubuntu0.24.10.1 for Linux on x86_64 ((Ubuntu))

Ở trên ta có MySQL 8 😎

Tiếp đó ta kiểm tra trạng thái MySQL server:

service mysql status

Ta cũng có thể dùng command systemctl status mysql

Screenshot 2024-12-05 at 6.27.58 PM.jpg

Ở trên thì MySQL đang activerunning rồi

Tiếp theo ta chạy command sau để cấu hình cho MySQL:

sudo mysql_secure_installation

Khi được hỏi thì các bạn chọn như sau nhé:

VALIDATE PASSWORD PLUGIN: No
Remove anonymous users? y
Disallow root login remotely? y
Remove test database and access to it? y
Reload privilege tables now? y


>>>
All done!

Ta chạy command sau để MySQL có thể khởi động cùng với server nếu sau này server khởi động lại:

sudo systemctl enable mysql

Giờ ta thử login vô MySQL xem nha:

mysql -u root -p

Khi được hỏi Password thì ta để trống và bấm enter luôn, vô trong thì ta thử list các database xem:

show databases;

>>>
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
4 rows in set (0.00 sec)

Ngon rồi đó 😎😎

NodeJS

Tiếp theo ta tiến hành cài đặt NodeJS nhé, các bạn chạy command sau:

curl -fsSL https://deb.nodesource.com/setup_22.x -o nodesource_setup.sh

sudo -E bash nodesource_setup.sh

sudo apt install nodejs

Ở trên mình cài NodeJS bản 12 hiện tại đang là LTS (Long-term support - ý là bản nên dùng bản này 😄), sau này có thể thay đổi các bạn vào nodejs.org để xem bản LTS là bao nhiêu nhé.

Sau đó ta check xem nodejs đã cài thành công hay chưa nhé:

node -v
--->> v22.11.0

npm -v
--->> 10.9.0

Process manager - Supervisor

Trước khi cài đặt ta cùng tìm hiểu qua Process manager là gì nhé.

Khi ở local muốn chạy command nào thì ta chỉ cần mở 1 tab terminal và chạy command đó, ví dụ:

php artisan serve

php artisan queue:work

php artisan reverb:start

Mỗi command chạy trên 1 tab, nhưng nếu ta tắt terminal đi thì các command trên sẽ bị stop, bởi vì các command trên đang chạy ở foreground, tức là khi ta bấm Enter để chạy thì command sẽ treo luôn ở terminal, và khi ta tắt terminal thì các command kia sẽ bị stop. Lên server cũng vậy, ta có thể mở nhiều tab terminal, SSH vào server và chạy các command như vậy, nhưng cuối cùng thì cũng đến lúc ta phải tắt máy đi ngủ lúc đó thì terminal bị tắt và toàn bộ các command sẽ bị stop 😄.

Do đó ta cần có process manager để có thể chạy được các command ở background - chạy ngầm, chúng sẽ liên tục được chạy và ta không phải lo về việc terminal bị tắt nữa 😉.

Và trên Ubuntu thì Supervisor là một process manager rất được ưa chuộng. Ta tiến hành cài nhé:

sudo apt-get install supervisor

Sau đó ta kiểm tra xem supervisor chạy hay chưa nhen:

service supervisor status

Screenshot 2024-12-05 at 7.00.43 PM.jpg

Nginx

Cuối cùng là ta tiến hành cài Nginx (ối dồi ôi cài mệt nghỉ 😝😝)

sudo apt install nginx -y

Cài xong thì ta check version của nginx:

sudo nginx -version

>>>
nginx version: nginx/1.26.0 (Ubuntu)

Sau đó ta check xem Nginx chạy hay chưa nhé:

service nginx status

Screenshot 2024-12-05 at 7.04.20 PM.jpg

Từ từ, có lỗi xảy ra 🥹🥹. Nginx báo là cổng 80 đã bị 1 anh bạn nào đó chiếm mất.

Ta check thử xem các cổng nào đang chạy và ai dùng nó nhé:

sudo lsof -i -P -n | grep LISTEN

>>>
systemd       1            root  162u  IPv4   6731      0t0  TCP *:22 (LISTEN)
systemd       1            root  164u  IPv6   6733      0t0  TCP *:22 (LISTEN)
systemd-r  6567 systemd-resolve   16u  IPv4  45719      0t0  TCP 127.0.0.53:53 (LISTEN)
systemd-r  6567 systemd-resolve   18u  IPv4  45721      0t0  TCP 127.0.0.54:53 (LISTEN)
sshd      11151            root    3u  IPv4   6731      0t0  TCP *:22 (LISTEN)
sshd      11151            root    4u  IPv6   6733      0t0  TCP *:22 (LISTEN)
apache2   24224            root    4u  IPv6  65825      0t0  TCP *:80 (LISTEN)
apache2   24229        www-data    4u  IPv6  65825      0t0  TCP *:80 (LISTEN)
apache2   24230        www-data    4u  IPv6  65825      0t0  TCP *:80 (LISTEN)
apache2   24231        www-data    4u  IPv6  65825      0t0  TCP *:80 (LISTEN)
apache2   24232        www-data    4u  IPv6  65825      0t0  TCP *:80 (LISTEN)
apache2   24233        www-data    4u  IPv6  65825      0t0  TCP *:80 (LISTEN)
mysqld    25170           mysql   21u  IPv4  70993      0t0  TCP 127.0.0.1:33060 (LISTEN)
mysqld    25170           mysql   23u  IPv4  70995      0t0  TCP 127.0.0.1:3306 (LISTEN)
apache2   25487        www-data    4u  IPv6  65825      0t0  TCP *:80 (LISTEN)

Ố ồ, thì ra là Apache đã chiếm mất cổng 80.

Giải thích cho các bạn Apache là gì, thì nó cũng là 1 webserver như Nginx vậy, được cài đặt kèm với Ubuntu, và chạy mặc định khi VM start.

Tại sao mình không không dùng Apache luôn, thì lí do là Nginx hiện tại cực kì phổ biến về tính dễ dùng và hiệu năng của nó. Và mình chỉ khuyến khích các bạn dùng những thứ phổ biến, nếu có gặp lỗi cũng sẽ dễ sửa hơn 😘

Âu cây vậy giờ ta tiến hành shutdown Apache đi để chạy Nginx nhé:

sudo service apache2 stop
sudo service nginx start
sudo service nginx status

Screenshot 2024-12-05 at 7.08.49 PM.jpg

Khi Webserver được chạy sẽ cho phép ta có khả năng truy cập từ bên ngoài vào server, hay cụ thể là ta có thể truy cập qua trình duyệt.

Các bạn thử mở trình duyệt ở địa chỉ là IP server của các bạn xem nhé. Cách xem IP là các bạn xem ở trên trang quản trị của nhà cung cấp nơi bạn mua server, ví dụ của mình là Digital Ocean thì sẽ như sau:

Screenshot 2024-12-05 at 7.09.43 PM.jpg

Chú ý phần IP v4 nhé, chính là nó. Các nơi khác (AWS, Google, Azure,...) chắc chắn có đó, các bạn đừng comment hỏi mình là ở đâu nhé ☺️

Chú ý rằng mình đang dùng Digital Ocean, mặc định họ để VPS "trần" luôn, traffic cho phép vào mọi cổng, nhưng với nhiều cloud provider khác, ví dụ Azure, mặc định họ không cho traffic vào port nào cả mà ta phải tự chỉ định từng port muốn mở, để bảo mật hơn, các bạn chú ý nha. Nếu cần phải làm thì ở bài này các bạn mở cho mình các port sau: 80, 443, 8080, 8081

Âu cây giờ mở trình duyệt ở địa chỉ SERVER_IP coi xem Nginx lên chưa nha:

Screenshot 2024-12-05 at 10.00.29 PM.jpg

Truy cập được rồi mà ủa sao lại show trang của Apache zị nhỉ??? 🤔🤔

À thì ra ở mặc định nginx nó đọc folder /var/www/html và load các file dạng index.html hoặc index.nginx-debian.html ở đó. Ta có thể kiểm chứng bằng cách xem cấu hình mặc định của nginx:

cat /etc/nginx/sites-available/default

>>>
root /var/www/html;

# Add index.php to the list if you are using PHP
index index.html index.htm index.nginx-debian.html;

Như các bạn thấy thứ tự nó đọc là index.html > index.nginx-debian.html, mà file index.html là của Apache.

Ta đơn giản là remove nó đi, nhưng để cho an toàn ta rename nó thôi nha 😁😁 (nhỡ sau này muốn dùng lại)

mv index.html index.html.bk

.bk viết tắt của backup

Ngay sau đó ta quay lại trình duyệt F5 sẽ thấy trang của Nginx:

Screenshot 2024-12-05 at 10.05.48 PM.jpg

Bắt đầu

Ta có thể lưu source code ở bất kì đâu, ở bài này ta chọn luôn /var/www/html nha

cd /var/www/html

Setup Laravel

Đầu tiên ta tiến hành clone source code:

git clone https://github.com/maitrungduc1410/realtime-chatapp-laravelecho-socketio

cd realtime-chatapp-laravelecho-socketio/

Sau đó ta tiến hành tạo file .env và thiết lập giá trị:

cp .env.example .env

Thay thế một số mục trong file .env thành như sau:

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=realtime_chatapp
DB_USERNAME=myuser
DB_PASSWORD=myuserpass

VITE_REVERB_HOST="174.138.21.137"

VITE_REVERB_HOST chính là IP VPS của các bạn. DB_USERNAMEDB_PASSWORD thì tí nữa ta sẽ tạo nha

Tiếp theo ta tiến hành cài dependencies + build Frontend nha:

composer install
npm install
npm run build

Sau đó ta chạy generate key nhé:

php artisan key:generate

Tiếp theo ta cần migrate, nhưng trước hết ta cần tạo user + database trong MySQL đã nhé.

mysql -u root -p

create database realtime_chatapp;

CREATE USER 'myuser'@'localhost' IDENTIFIED BY 'myuserpass';

GRANT ALL PRIVILEGES ON realtime_chatapp.* TO 'myuser'@'localhost';

show databases;

--->> In ra:
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| realtime_chatapp   |
| sys                |
+--------------------+
5 rows in set (0.00 sec)

exit;

Ở trên các bản để ý là sau khi tạo database mình tạo user myuser với password là myuserpass , sau đó gán cho user này có toàn quyền truy cập chỉ trong database realtime_chatapp. Chạy production ta không dùng chỉ 1 user root nữa nhé.

Note: ở trên lúc tạo user cho database nếu các bạn gặp lỗi your password does not satisfy the current policy requirements thì ý MYSQL bảo là "password của chúng ta lởm quá đặt cái nào bảo mật hơn đi", thì các bạn cần đặt mật khẩu nào xịn hơn. Các bạn lên trang này tạo nhanh 1 password rồi làm lại nhé.

Sau đó ta tiến hành migrate nhé:

php artisan migrate --seed

Thấy như sau là oke nà 😘:

Screenshot 2024-12-05 at 10.24.32 PM.jpg

Sau đó ta tạo app key:

php artisan key:generate

Ổn rồi đó, đến bước này là ta đã có thể xem trực tiếp web của chúng ta như thế nào rồi, ta cùng cấu hình Nginx 1 chút nhé.

sudo nano /etc/nginx/sites-available/default

Ở đó ta thấy có sẵn nội dung mặc định, ta xoá hết đi và thay thế toàn bộ bằng nội dung sau nhé:

server {
	listen 80;
	listen [::]:80;

	root /var/www/html/realtime-chatapp-laravelecho-socketio/public;

	index index.php;

	server_name _;

	add_header X-Frame-Options "SAMEORIGIN";
	add_header X-Content-Type-Options "nosniff";
	charset utf-8;
	
	location / {
		try_files $uri $uri/ /index.php?$query_string;
	}

	location = /favicon.ico { access_log off; log_not_found off; }
	location = /robots.txt  { access_log off; log_not_found off; }
	
	error_page 404 /index.php;

	location ~ \.php$ {
		try_files $uri =404;
		fastcgi_split_path_info ^(.+\.php)(/.+)$;
		fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
		fastcgi_index index.php;
		include fastcgi_params;
		fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
		fastcgi_param PATH_INFO $fastcgi_path_info;
		fastcgi_hide_header X-Powered-By;
	}

	location /reverb {
		proxy_http_version 1.1;
		proxy_set_header Host $http_host;
		proxy_set_header Scheme $scheme;
		proxy_set_header SERVER_PORT $server_port;
		proxy_set_header REMOTE_ADDR $remote_addr;
		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
		proxy_set_header Upgrade $http_upgrade;
		proxy_set_header Connection "Upgrade";

        	# trailing slash is important to remove /echo from the url
		proxy_pass http://localhost:8080/;
	}
 
	location ~ /\.(?!well-known).* {
		deny all;
	}
}
  • Ở trên các bạn để ý rằng root ta phải trỏ về folder public vì ở đó ta có file entrypoint index.php là nơi khởi nguồn cho app Laravel
  • chú ý đoạn fastcgi_pass ở đó ta proxy PHP request vào PHP-FPM (xử lý các request tới app Laravel)
  • đoạn location /reverb để proxy websocket request tới Laravel Reverb Websocket server

Sau đó ta kiểm tra xem cú pháp của file ta vừa thay đổi có đúng không, có lỗi gì không nhé:

sudo nginx -t

--->> in ra:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Oke thì ta tiến hành khởi động lại Nginx nhé:

sudo service nginx restart

Ổn rồi đó ta mở trình duyệt ở địa chỉ là IP server của chúng ta và xem nhé:

Screenshot 2024-12-05 at 10.50.56 PM.jpg

Âu cây đã lên rồi đó, nhưng lại bị dính lỗi Permission denied, đây là 1 lỗi cực kì phổ biến khi deploy ứng dụng Laravel 😂

Lí do thì khá là đơn giản: khi ta truy cập từ trình duyệt, request gửi tới Nginx, nginx forward request vào PHP-FPM, sau đó tới project Laravel của chúng ta, sau đó sẽ được ghi ra file Log, bởi vì Nginx được chạy dưới quyền của user www-data. Ta có thể kiểm tra bằng cách xem cấu hình của nginx:

cat /etc/nginx/nginx.conf

>>>
user www-data;
worker_processes auto;
worker_cpu_affinity auto;

....

Ở trên ta thấy rằng worker của Nginx chạy với user www-data, worker chúng là những đứa mà xử lý request của chúng ta 😄

Vậy nên cuối cùng Nginx cần có quyền để ghi vào folder storage, nhưng folder đó đang được đặt dưới quyền user mà ta đang thực hiện SSH (của mình là root)

Do đó ta cần đổi lại quyền cho project Laravel của chúng ta nhé, các bạn quay trở lại folder gốc nơi chứa project và chạy command sau:

cd /var/www/html/realtime-chatapp-laravelecho-socketio

sudo chown -R www-data:www-data .

Sau đó quay trở lại trình duyệt, F5 và pằng pằng chíu chíu:

Screenshot 2024-12-05 at 10.58.51 PM.jpg

Ta thử đăng kí tài khoản mới, sau đó vào thử gửi vài tin nhắn xem nha

Ta để ý rằng gửi tin nhắn thì Oke, F5 lại thấy lưu vô database rồi, nhưng không có realtime, danh sách user Online không lên, browser console lỗi đỏ lòm:

Screenshot 2024-12-05 at 10.59.58 PM.jpg

Cái này là vì ta mới chạy được có Laravel main app à, còn mấy cái là Reverb, workers các thứ chưa lên 😂

Laravel Reverb

Để chạy reverb thì ta sẽ cần dùng tới Supervisor nha, vì nó cần được chạy ở background ấy 😊

Toàn bộ file chạy cho Supervisor sẽ được đặt ở folder /etc/supervisor/conf.d. Ta chuyển tới folder đó để làm việc nhé:

cd /etc/supervisor/conf.d

Sau đó ta tạo 1 file cấu hình để chạy Laravel echo server:

sudo nano laravel-reverb.conf

Và ta thêm vào nội dung như sau:

[program:laravel_reverb]
command=php artisan reverb:start 
user=www-data
directory=/var/www/html/realtime-chatapp-laravelecho-socketio
autostart=true
autorestart=true
stderr_logfile=/var/log/laravel_reverb.err.log
stdout_logfile=/var/log/laravel_reverb.out.log

Sau đó ta "bảo" Supervisor update lại nhé:

sudo supervisorctl reread
sudo supervisorctl update


>>> Thấy in ra
laravel_reverb: available
laravel_reverb: added process group

Mỗi khi ta update lại file cấu hình thì các bạn chạy lại 2 command trên là được nha 😘

Ổn rồi đó, giờ ta quay lại trình duyệt F5 thử xem:

Ta thử mở console, và..... BÙMMM

Screenshot 2024-12-05 at 11.05.23 PM.png

Vẫn lỗi như vậy???? lí do là sao nhỉ?? 🧐🧐

Lí do là khi trình duyệt gửi request tới địa chỉ IP ở cổng 8080, mà ở cấu hình nginx là ta muốn nó đi qua nginx ở path /reverb mà, tức là cổng phải chung cổng với app Laravel, tức là cổng 80 🤪

Giờ ta quay lại app Laravel và update .env:

cd /var/www/html/realtime-chatapp-laravelecho-socketio/

Sửa VITE_REVERB_PORT="80" là oke nha, sau đó ta build lại frontend:

npm run build

Giờ ta quay lại trình duyêt, F5 và kiểm tra nhé:

Screenshot 2024-12-05 at 11.08.30 PM.jpg

Âu cây vậy là ổn rồi đó. Pằng pằng chíu chíu 🎉🎉

Nhưng nếu ta thử mở 2 tab chat thì sẽ không thấy realtime, đơn giản vì ta chưa chạy queue work (không có worker nào hoạt động để xử lý message ở trong queue).

Worker

Quay lại folder /etc/supervisor/conf.d, các bạn tạo file cấu hình Laravel Horizon nhé:

sudo nano laravel-worker.conf

Và nội dung thì ta để như sau nhé:

[program:laravel_worker]
command=php artisan queue:work
user=www-data
directory=/var/www/html/realtime-chatapp-laravelecho-socketio
autostart=true
autorestart=true
stderr_logfile=/var/log/laravel_worker.err.log
stdout_logfile=/var/log/laravel_worker.out.log

Sau đó ta lại "nhắc" Supervisor update lại nhé:

sudo supervisorctl reread
sudo supervisorctl update

>>>
laravel_worker: available
laravel_worker: added process group

Sau đó ta quay trở lại trình duyệt mở 2 tab và thử chat là thấy đã Realtime được rồi nhé

Vậy là ứng dụng chat của ta đã được cài đặt thành công. Pằng pằng chíu chíu 🚀🚀

Task Scheduling (cronjob)

Ở ứng dụng chat này mình có 1 chức năng nữa là cứ đều đặt thì sẽ có 1 tin nhắn thông báo từ Bot gửi tới để hướng dẫn user sử dụng các tính năng chat

Và để làm được việc này thì ta cần chạy thêm 1 process nữa để chạy Scheduled Task (hay còn gọi là CronJob) cho Laravel nhé. Nhưng giờ ta không dùng supervisor nữa mà ta dùng luôn Crontab có sẵn của Ubuntu để chạy nha.

Các bạn chạy command sau:

sudo crontab -u www-data -e

Khi được hỏi dùng trình editor nào thì ta chọn nano cho tiện

Tiếp đó ở màn hình nhập, các bạn thêm vào cuối file giá trị như sau:

* * * * * cd /var/www/html/realtime-chatapp-laravelecho-socketio && php artisan schedule:run

Ở trên ta thiết lập một crontab chạy mỗi phút

Màn hình cấu hình Crontab trông giống như sau nhé các bạn

Screenshot 2024-12-05 at 11.54.55 PM.jpg

Sửa xong thì ta lưu lại, terminal báo như sau là oke nà:

crontab: installing new crontab

Rồi ta quay lại trình duyệt, F5, và ngồi chờ nha 😂. Và khi nào tới đủ 1 phút ta sẽ thấy Bot thông báo:

Screenshot 2024-12-05 at 11.57.13 PM.jpg

Và cứ sau 1 phút thì lại có 1 thông báo như vậy 😎

Đến đây là ta đã có 1 ứng dụng Laravel đầy đủ hoàn chỉnh về mặt chức năng rồi đó 😘😘

Screenshot 2024-12-05 at 11.58.26 PM.jpg

HTTPS

Và đã chạy production thì thường là ta sẽ cần tới HTTPS thì mới đủ xịn xò và tăng tính bảo mật cho website của chúng ta.

Nếu các bạn chỉ search google: How to get SSL certificate thì khả năng cao là sẽ gặp phải nhiều tutorial họ hướng dẫn cách dùng self-signed certificate, tức là chứng chỉ mà ta "tự kí", những loại chứng chỉ này khi chạy ở các tình duyệt hiện đại sẽ bị báo là chứng chỉ fake, tất nhiên đó không phải là thứ ta muốn đúng không nào 😉

Có một cách khác rất đơn giản và mọi người thường làm là lấy chứng chỉ HTTPS Free bằng certbot và Lets Encrypt. Và ta sẽ dùng cách này nhen

Điều kiện cần là các bạn cần phải có 1 domain name nhé, vì Lets Encrypt yêu cầu phải có domain name chứ không dành cho IP.

Các bạn bỏ ra vài chục hoặc 1-2 trăm nghìn VND lên Goddady (Trang trên miền lớn nhất TG), mua 1 cái domain lởm lởm để học tập nhé. Nên mua domain ở các nhà cung cấp lớn thế này vì những công việc như cập nhập DNS, nameserver sẽ rất là nhanh và tiện. Mình đã và đang trải nghiệm cả những nhà cung cấp của VN (dành cho tên miền .vn, và chất lượng thì thật sự là "ba chấm" 😁 )

Sau khi các bạn có tên miền, thì ta vào trang quản trị tên miền và trỏ về IP server của chúng ta là được nhé. Ví dụ ở đây tên miền của mình là jamesisme.com và màn hình quản trị trên Cloudflare sẽ như sau nhé:

Screenshot 2024-12-06 at 12.07.00 AM.jpg

Mình dô tạo 1 bản ghi A, với subdomain là chat, tức là tên đầy đủ là chat.jamesisme.com, trỏ về IP là IP VPS của chúng ta

Bạn nào quen rồi thì có thể dùng CNAME các kiểu, miễn sao các bạn trỏ về cho đúng IP, và ta dùng nó nhất quán với cấu hình nginx 😊

Ngay sau đó ta quay trở lại trình duyệt, truy cập ở địa chỉ tên miền của chúng ta là có thể thấy kết quả ngay nha:

Screenshot 2024-12-06 at 12.14.19 AM.jpg

Chú ý rằng hiện tại thì Chrome nó có cái mặc định redirect HTTP -> HTTPS, nên ta có thể gặp lỗi, vì đoạn này ta vẫn chưa có HTTPS:

Screenshot 2024-12-06 at 12.16.08 AM.jpg

Sau đó kể cả ta có tự tay gõ http://chat.jamesisme.com vào trình duyệt, thì nó vẫn cứ cache và dùng HTTPS, cay lắm 😂

Để khắc phục điều này thì ta mở Chrome ở địa chỉ chrome://net-internals/#hstsDelete domain security policies > cho domain của ta đi:

Screenshot 2024-12-06 at 12.17.43 AM.jpg

Sau đó ta truy cập lại là oke, tự tay gõ http://... vào trình duyệt là được (khuyến khích dùng tab ẩn danh) 💪💪

Tiếp tới là ta cần tạo chứng chỉ HTTPS với certbot nhé. Đầu tiên là ta cần cài certbot, các bạn chạy command sau:

sudo snap install --classic certbot

sudo ln -s /snap/bin/certbot /usr/bin/certbot

Sau đó chúng ta sửa lại file cấu hình Nginx một chút nhé:

sudo nano /etc/nginx/sites-available/default

Các bạn chỉ cần sửa lại duy nhất đoạn server_name với giá trị như sau nhé:

server_name chat.jamesisme.com;

Ở đây ý bảo là ta có thể truy cập ứng dụng Laravel bằng 2 địa chỉ bên trên. Sau đó ta khởi động lại Nginx nhé:

sudo service nginx restart

Sau đó ta tiến hành tạo chứng chỉ HTTPS nhé:

sudo certbot --nginx -d chat.jamesisme.com

Ở trên các bạn thay thế tên domain của các bạn vào nhé.

Chú ý ở cuối quá trình khi được hỏi có muốn tự động redirect HTTP sang HTTPS không thì các bạn chọn có (option 2) nhé. Vì thường ta muốn user truy cập ứng dụng của ta ở HTTPS thôi mà 😘

Ngay sau đó chúng ta quay lại trình duyệt load lại là sẽ thấy app của chúng ta đã có HTTPS nhé. Các bạn thử đăng nhập và test thử nhé.

Và.... BÙM, vẫn gặp lỗi 🥹🥹

Screenshot 2024-12-06 at 10.45.14 PM.png

Thì ra giờ không dùng domain nữa thì phải phải update .env chút và build lại frontend 😁

Ta sửa lại 3 biến sau ở .env:

VITE_REVERB_HOST="chat.jamesisme.com"
VITE_REVERB_PORT="443"
VITE_REVERB_SCHEME="https"

Nhớ thay tên domain của các bạn vào VITE_REVERB_HOST cho đúng nha

Sau đó ta build lại frontend:

npm run build

Sau đó ta quay lại trình duyệt F5 là lên HTTPS + realtime ngon luôn:

Screenshot 2024-12-06 at 10.49.09 PM.jpg

Pòm pòm chíu chíu 🎉🎉🎉🎉🎉

Đóng máy

Như các bạn thấy để chạy được một ứng dụng Laravel đầy đủ ở production ta cần phải setup mệt nghỉ, cộng thêm là debug khi gặp lỗi khá là hack não 😂

Giải pháp hiện đại hơn mình khuyên là dùng Docker cho nhàn, các bạn xem bài của mình ở đây, gọn hơn rấtttttttt nhiều: https://viblo.asia/p/deploy-ung-dung-docker-laravel-realtime-chat-aWj53WJ156m 😘

Một trong những điều mình thấy hạnh phúc khi làm việc với mỗi project đó là: từ quá trình viết design, viết code, tới khi deploy thành công ra production thấy đứa con cưng của mình chạy thành công cảm giác thật là sung sướng. 😘

Bài đến đây quá là dài rồi. Nếu các bạn có gì thắc mắc thì để lại comment cho mình nhé. Hẹn gặp lại các bạn ở những bài sau 👋👋


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí