Dockerize ứng dụng chat realtime với Laravel, Nginx, VueJS, Laravel Echo, Redis, SocketIO

Tram Ho

Xin chào các bạn đã quay trở lại với series Học Docker và CICD của mình.

Chúc mọi người một mùa giáng sinh an lành ấm áp 😍😍

Mấy ngày vừa rồi tinh thần viết blog đang lên cao thì đôi mắt mình lại đình công sau 1 khoảng thời gian ngồi máy tính quá nhiều, hôm nay “cửa sổ tâm hồn” có vẻ tốt trở lại thì mình lại chầm chậm viết blog tiếp cho mọi người 😉

Dù làm gì cũng phải nhớ giữ sức khoẻ nhé mọi người, Tết nhất đến nơi rồi phải có sức khoẻ để còn đi làm lấy tiền đưa về biếu thầy bu 🤣🤣

Ở bài trước mình đã hướng dẫn các bạn các Dockerize ứng dụng NodeJS với mysql, redis,… cùng với đó là các setup cho môi trường dev và production.

Ở bài này chúng ta sẽ chơi “hardcore” hơn bằng cách Dockerize ứng dụng Chat realtime với Laravel, VueJS, Laravel Echo, SocketIO, Redis cùng với đó là setup Laravel Horizon và Laravel Schedule Task nhé.

Sorry các bạn vì tên bài này mình để hơi dài, mục đích để khi đọc qua tiêu đề các bạn sẽ rõ hơn đồng thời cho những anh em search từ google thì cũng sẽ dễ tìm hơn nhé.

Tiền Setup

Vẫn như thường lệ các bạn cần phải cài đặt Docker và Docker-compose, nếu các bạn chưa làm thì xem ở bài đầu tiên của mình nhé

Setup

Các bạn clone source code ở đây nhé (nhánh master)

Ở bài này chúng ta sẽ chỉ quan tâm tới folder docker-laravel-realtime-chat-app nhé.

Tổng quan ứng dụng

Ứng dụng này đã được mình làm và deploy chạy production ở đây các bạn có thể vào và dùng thử

Tổng quan:

  • Ứng dụng có các phòng chat để user có thể join vào
  • Mỗi phòng chat sẽ có 1 hộp (box) chat chung, tất cả user trong phòng có thể nhắn tin vào đây
  • Cùng với đó là danh sách user có ở trong phòng chat, click chọn vào 1 user bất kì để nhắn tin riêng vơi người đó
  • Cứ mỗi 1 phút sẽ tự động xuất hiện tin nhắn chào mừng của Bot (dùng Laravel Schedule Task)
  • Detect user đang gõ, hay đã xem tin nhắn,…
  • Các bạn có thể chọn biểu cảm tin nhắn như Facebook Messager: Love, Haha, Wow,….. 😉

Lắc não trước khi sử dụng

Trước khi chúng ta đi vào cấu hình Dockerfile thì đầu tiên ta sẽ cùng “lắc não” để phân tích xem project này có những service nào, cần Dockerfile cho những gì nhé các bạn 😉

  • Tương tự như bài Dockerize ứng dụng Laravel, để chạy project cơ bản tối thiểu là ta sẽ dùng 2 service app (có php-fpm) và service webserver ( dùng nginx)
  • Có database để lưu trữ tin nhắn, danh sách user, phòng chat,… -> cần 1 service db
  • Có queue job -> cần 1 service redis
  • Có realtime với Laravel Echo, socketio -> cần 1 service laravel-echo-server
  • Đồng thời ta cũng cần thêm một service adminer để quản trị cơ sở dữ liệu MySQL trông cho trực quan (cái này giống như kiểu phpmyadmin ấy nhé 😃)

Vậy tất cả thảy ta có 6 services tương ứng với 6 container cần khởi tạo để chạy được ứng dụng.

Ở bài này ta sẽ viết Dockerfile cho 2 service là applaravel-echo-server, các service còn lại ta dùng luôn image được cung cấp sẵn nhé

Build Docker image

Ở bài này vì ta có khá nhiều service, nên toàn bộ phần setup + dữ liệu của Mysql, redis ta đều lưu ở một folder tên là .docker nhé

Ở gốc project (folder docker-laravel-realtime-chat-app, ở bài này mình nói luôn là gốc project nhé vì tên folder nó dài quá 😄😄), các bạn tạo folder tên là .docker nhé.

Cấu hình Dockerfile cho Laravel Echo Server

Đầu tiên ta sẽ tiến hành cấu hình Dockerfile và build image cho service laravel-echo-server nhé.

Ở folder .docker các bạn tạo 1 folder tên là laravel-echo-server

Service này đúng với tên gọi bên trên là Laravel Echo Server để xử lý phần broadcast từ Laravel đến trình duyệt

Về cơ bản service này khá đơn giản, viết bằng nodejs, service này gần như sẽ không hề thay đổi sau khi build, chúng ta sẽ ít động vào service này

Để chạy service viết bằng NodeJS trong môi trường Docker thì ta sẽ dùng PM2 nhé.

Ở trong folder laravel-echo-server các bạn tạo cho mình 1 file tên là laravel-echo-server.json với nội dung như sau nhé: (file này chính là cấu hình cho Laravel Echo Server)

Tiếp đó vì ta dùng PM2 để chạy service này nên ta sẽ tạo tiếp 1 file cấu hình cho PM2 nhé, vẫn ở folder laravel-echo-server các bạn tạo file echo.json với nội dung như sau:

Cuối cùng là ta tạo file Dockerfile để cấu hình cho image ta cần build nhé (vẫn ở thư mục laravel-echo-server nhé các bạn:

Sau khi cấu hình xong thư mục cấu hình cho laravel echo server của chúng ta trông sẽ như sau:

VScode

Ổn rồi đó, nhưng ở bài này ta sẽ không build image ngay bằng command docker build… như thường lệ, mà lát nữa ta sẽ build với docker-compose nhé (như thế nào thì lát nữa ta sẽ thấy nhé 😄)

Cấu hình cho service app

Tiếp theo là ta sẽ cấu hình cho service app, phần dưới này sẽ có khá nhiều thứ liên quan đến Linux, mình sẽ giải thích vào những thứ quan trọng còn lại những gì các bạn thắc mắc thì search google tiếp để hiểu thêm nhé

Tiếp tục lắc não trước khi sử dụng

Như ở đầu bài mình có giới thiệu ở bài này ta sẽ có Laravel Horizon và Laravel Schedule Task. Do đó ở trong service app ta sẽ chạy các process (tiến trình như sau):

  • PHP-FPM để chạy code PHP (process này là bắt buộc)
  • Crontab cho Laravel Schedule Task (process này là optional)
  • Laravel Horizon (process này cũng là optional)

Ở trên mình có 2 process optional, tức là ta có thể dùng nó hoặc không, ứng dụng vẫn có thể chạy nếu không có 2 process đó, lát nữa cấu hình ta sẽ nói rõ nhé

Và ở Linux để chạy và quản lý nhiều process thì mình sẽ dùng một tool khá phổ biến tên là supervisor nhé.

Ta sẽ tạo các file cấu hình tương ứng với các process bên trên, sau đó supervisor sẽ đọc và khởi động các process đó lên.

Cấu hình Supervisor

Ở folder .docker các bạn tạo cho mình 1 file tên là supervisord.conf, với nội dung như sau:

Tiếp đó ở vẫn ở folder .docker các bạn tạo cho mình 1 folder tên là supervisor.d, folder này sẽ chứa các file các hình cho các process ta nói ở phần trên.

Trong folder supervisor.d các bạn tạo file php-fpm.conf với nội dung như sau:

Vẫn ở folder supervisor.d các bạn tạo file tên là horizon.conf.default với nội dung như sau:

Cấu hình tương tự như process php-fpm chỉ có điều khác là process này sẽ được chạy dưới user có tên là www-data, user này sẽ được tạo sẵn từ php-fpm nhé. Lí do vì ta không cần đến hẳn quyền root để chạy command này, vì lát nữa toàn bộ code khi build image sẽ được chúng ta đặt quyền sở hữu cho user www-data (nhằm hạn chế quyền của user chạy project, chỉ cho user những quyền đủ để chạy)

Tiếp theo đó vẫn ở folder supervisor.d ta tạo tiếp 2 file cron.conf.default, và worker.conf.default với các nội dung như sau (mình đi nhanh không giải thích nữa nhé)

File cron.conf.default

worker.conf.default

Sau khi cấu hình xong xuôi thì folder .docker của chúng ta trông sẽ như sau:

VSCode

Có thể các bạn sẽ thắc mắc:

  • Tại sao chỉ có tên của process php-fpm là không có hậu tố default? Như ở trên mình đã nói, process php-fpm là bắt buộc để code PHP của chúng ta có thể chạy nên ta luôn luôn cần process, còn các process còn lại thì là optional (tuỳ chọn), không có chúng thì ứng dụng của ta vẫn chạy
  • File worker.conf.default ở đâu ra vậy? có cần dùng nó hay không? Vì Laravel Horizon đã tự động tạo các workers cho chúng ta rồi nên nếu các bạn chạy Horizon thì sẽ không cần worker.conf.default nữa, nhưng nếu trong trường hợp các bạn không chạy Horizon thì ta cần tới file này để Queue Job có thể chạy được nhé

Ok cấu hình vậy là ổn rồi đó. Ta chuyển qua Dockerfile nhé 😄

Cấu hình Dockerfile

Ở gốc project (folder docker-laravel-realtime-chat-app) các bạn tạo file Dockerfile với nội dung như sau:

Giải thích một số chỗ có thể có thắc mắc nhé:

  • Ta bắt đầu từ 1 image build sẵn của PHP-FPM mình lấy ở đây
  • Bên dưới ta có tạo 2 biến môi trường ENABLE_CRONTABENABLE_HORIZON để nói rõ là lát nữa ta sẽ dùng process crontab và horizon (như ở phần trước mình có nói là những process này optional có thể dùng hoặc không, ở đây ta dùng cả 2)
  • Tiếp theo ta có ENTRYPOINT, trong đây là những sẽ up để ta có thể tuỳ chọn sử dụng các process như: crontab, horizon hay worker mà ta đã nói. Khi build image thì file docker-entrypoint.sh sẽ được chạy, trong đó có 1 số setup (file này ta sẽ tạo ở phía dưới)
  • Cuối cùng là ta khởi động supervisor với CMD nhé

Những chỗ còn lại ở file Dockerfile các bạn có thể xem ở các bài trước mình đã giải thích nhé

Cấu hình Entrypoint

Như ở trên mình đã nói ta sẽ dùng một file tên là docker-entrypoint.sh để thiết lập tuỳ chọn sử dụng các process như crontab, horizon hay worker, tuỳ vào biến môi trường ta đặt là gì.

Ở folder .docker các bạn tạo cho mình 1 file tên là docker-entrypoint.sh với nội dung như sau:

Nhìn vào file bên trên đã quá rõ rồi phải không nào, Tuỳ vào biến môi trường ta thiết lập mà ta có chọn dùng một process nào đó hay không

Nếu dùng thì đổi tên file của process bỏ hậu tố default đi là được, còn nếu không dùng thì ta xoá cả file cấu hình đi

Cấu hình .dockerignore

Trước khi build image ta sẽ cấu hình .dockerignore để nói với Docker rằng “bỏ qua một số file đừng copy vào bên trong image khi build nhé” 😉

Ở gốc project (folder docker-laravel-realtime-chat-app) các bạn tạo file .dockerignore với nội dung như sau:

Nom khá giống với nội dung của gitignore phải không nào 😉

Note: mặc dùng những folder như node_modules ta chưa có nhưng ta vẫn ignore vì lát nữa khi build VueJS sẽ cần node_modules và tương lai ta có thể build project này nhiều lần nữa, nên ta cứ thêm trước vào nhé 😉

Ok tất cả mọi thứ đã ổn cho service app rồi đó, nhưng ở đây ta vẫn không build image ngay mà lát nữa ta sẽ build với docker-compose nhé

Cấu hình Nginx

Tương tự ở bài Dockerize ứng dung Laravel ta sẽ cần thiết lập cho webserver Nginx nhé.

Ở folder .docker các bạn tạo file nginx.conf với nội dung như sau:

Chú ý ở trên khi với code PHP thì ta sẽ redirect request vào cho service app để service app có PHP-FPM làm việc, còn với socket.io thì ta điều hướng công việc tới service laravel-echo-server nhé

Tạo folder để mount volume

Tương tự như bài Dockerize NodeJS với Mongo và Redis ở bài này ta sẽ tạo các folder để lưu lại data từ MySQL và Redis để khi project của tại khởi động lại thì mọi dữ liệu vẫn còn nguyên nhé.

Ở trong folder .docker các bạn tạo cho mình 1 folder tên là data, bên trong folder data các bạn tạo cho mình 2 folder tên là db (lưu dữ liệu cho MySQL) và redis nhé 😃

Đến bước này thì folder .docker của chúng ta nom sẽ như sau:

VSCODE

Chạy ứng dụng

Cấu hình docker-compose

Để chạy ứng dụng này lên ta sẽ tạo file docker-compose với nội dung như sau nhé:

Nội dung file bên trên thì cũng tương tự các bài trước phải không nào. Có 1 số chỗ mình giải thích để nếu có bạn nào thắc mắc nhé:

  • Ở service db các thông tin ở environment sẽ được lấy từ các biến ở file .env (ta sẽ tạo ngay sau đây nhé)
  • Tương tự ở service laravel-echo-server thì ta sẽ không dùng environment để thiết lập biến môi trường mà Laravel Echo Server sẽ đọc trực tiếp từ file .env (phần này được nói rõ ở github của Laravel echo server nhé)
  • Ở service webserveradminer ở phần mapping port, ta có để giá trị của 2 biến môi trường lát nữa cũng được định nghĩa ở file .env nhé.
  • Như trong bài mình đã nói service applaravel-echo-server sẽ được build bằng docker-compose thì các bạn có thể thấy ta có trường build và nói rõ context (ngữ cảnh là ở đường dẫn nào) cộng với Dockerfile tên là gì. Khi chạy app lên thì docker-compose sẽ tìm tới và build từng service cho ta nhé

Tạo file .env

Tiếp theo ta sẽ tạo file .env nhé. Các bạn tạo file tên .env và copy nội dung từ file .env.example sang nhé.

Sau đó các bạn mở file .env và sửa lại 1 số chỗ như sau:

Giải thích:

  • Ở trên ta có phần cấu hình thông số kết nối tới database, các bạn chú ý DB_HOST phải trùng khớp với tên service ta định nghĩa ở docker-compose.yml nhé. Tương tự cho phần cấu hình Redis

Giây phút của sự thật

Vâng vậy là sau 1 bài cấu hình dài cả 1 năm ánh sáng thì cũng đến lúc chúng ta test thử project này xem vất vả hộc mặt kết quả có ra gì không nhé 😄😄

Các bạn chạy command sau để khởi động project:

Sau khi command trên thành công, các bạn mở trình duyệt ở địa chỉ localhost:4000.

Và….. BÙM 💥 trắng xoá, không thấy gì 😭😭

Vấn đề ở đây là ta chưa chạy composer install hay npm install nên service app (phần chạy Laravel** bị lỗi

Vậy vấn đề ở đây là: làm sao có thể chạy composer hay npm trong khi môi trường của service app không hề có Composer hoặc NodeJS

Cài dependencies (composer install, npm install,…)

Như ở bài Dockerize VueJS mình có nhắc tới dùng container tạm thời để cài dependencies thay vì cài trực tiếp vào image.

Việc này sẽ giúp giảm size của image xuống, vì việc cài dependencies này không xảy ra thường xuyên mà ta thường chỉ làm 1 lần lúc đầu khi setup project.

Các bạn chạy lần lượt các command sau nhé:

Giải thích:

  • Đầu tiên ta tạo 1 container tạm thời từ image compose (có cài sẵn composer) và ta chạy composer install với một số option đằng sau (các bạn search google để biết thêm về các option này nhé)
  • Tiếp theo ta lại tạo 1 container tạm thời vẫn từ image composer và chạy dump-autoload để load các class thư viện PHP
  • Tiếp đó ta tạo 1 container từ image node và chạy npm install ta có option –production ý là “chỉ cài những thứ ở mục dependencies trong file package.json
  • Cuối cùng là ta build phần code VueJS (nếu bạn nào muốn watch thì đổi lại thành run watch là được nhé)

Giây phút của sự thật (phần 2)

Sau khi hoàn thành ta quay lại trình duyệt và F5:

Docker laravel

Đã thấy app khởi động và báo chưa có key.

Trước khi chạy command tạo key mình xin lưu ý: mọi command php artisan … từ giờ ta sẽ luôn chạy dưới dạng docker-compose exec app php artisan…. (để ý phần đầu nhé). Vì sao? Vì khi chạy bên ngoài thì Laravel sẽ dựa vào bối cảnh là môi trường ngoài để chạy, nên những command như migrate sẽ bị lỗi. Nên để đồng bộ và cho các bạn quen thì ta luôn chạy với docker-compose exec… nhé

Chúng ta chạy command sau để tạo key đồng thời tạo DB và seed luôn nhé

Sau đó ta load lại trình duyệt và…… BÙM 💥

Docker Laravel

Docker laravel

Đã thành công 😄

Các bạn thử tạo 2 account và chat với nhau xem thế nào nhé. Phần này mình để cho các bạn tự sướng 😄😄

Để vọc vạch phần quản trị database các bạn truy cập localhost:8080 nhé 😉

Để vọc phần Horizon các bạn truy cập ở địa chỉ localhost:4000/horizon nhé

Vì ta đã mount volume từ môi trường gốc vào trong container nên các bạn có thể sửa trực tiếp code và sẽ thấy thay đổi nhé. Chú ý nếu sửa code VueJS thì nhớ là phải chạy npm nhen 😉

Kết bài

Phùuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu…….. Bài dài thấu trời thấu đất, hàng tỉ kiến thức mới, mắt mờ, đầu óc quay cuồng.

Dockerize được cái ứng dụng chat mà xịt máu mũi 🤣🤣

Bài này cũng khá là dài, nhưng mình không muốn tách ra làm 2 bài vì như thế nó sẽ hơi lưng chừng. Phần code Laravel mình đã làm sẵn hết cho các bạn và ta chỉ tập trung vào phần Docker.

Trong bài có rất nhiều kiến thức liên quan đến Linux, mình không thể giải thích từng tí từng tí một cho các bạn được, nhưng các bạn có thể tự copy paste và search google là sẽ có hết thông tin nhé.

Cũng sẽ có những phần kiến thức Linux các bạn thấy là “dù search xong cũng không hiểu”, thì ta cứ “chấp nhận” dùng và dần dần hấp thụ nhé các bạn. Ngày trước bạn đầu dockerize Laravel mấy phần về supervisor mình gần như mù tịt, ai bảo gì làm nấy, giờ mới đỡ hơn 😄😄. Cá nhân mình thấy qua những project kiểu này trình Linux của chúng ta sẽ lên đáng kể đó 😉

Qua bài này hi vọng các bạn đã hiểu được cách Dockerize một project Laravel full options 😄, đầy đủ các thứ như DB, Redis, Queue Job, Cronjob, … trong sẽ như thế nào nhé. Từ đó vận dụng và áp dụng vào thực tế cho phù hợp nhé. Đồng thời ta cũng để ý là dù cho ta dùng nhiều thứ MySQL, Redis, Nginx, Adminer, PHP, Composer NodeJS… thế nhưng toàn bộ đều được chạy trong container Docker, môi trường gốc của ta vẫn “trinh nguyên” nhé 😂😂

Nếu có vấn đề gì thì các bạn cứ để lại comment cho mình nhé.

Toàn bộ source code đến bước cuối cùng của bài này mình để ở đây (nhánh complete-tutorial nhé)

Cám ơn các bạn đã theo dõi. Hẹn gặp lại các bạn vào những bài sau ^^

Chia sẻ bài viết ngay

Nguồn bài viết : Viblo