Cấu hình tự động deploy ứng dụng Laravel với Deployer trên CentOS

ToC

  1. Giới thiệu
  2. Chuẩn bị môi trường
  3. Bắt đầu cài đặt
  4. Kết bài

Giới thiệu

Laravel là một open-source PHP web framework được thiết kế để thực hiện các nhiệm vụ cơ bản của việc phát triển web như authentication, routing, caching một cách dễ dàng.

Deployer là một open-source PHP deployment tool hỗ trợ các framework phổ biến như: Laravel, CodeIgniter, Symfony, và Zend Framework.

Deployer thực hiện việc tự đông deploy bằng cách clone source code từ git repo trên server, cài đặt các dependencies với Composer sau đó cấu hình ứng dụng với kịch bản định sẵn. Điều này giúp bạn tập trung vào việc phát triển, thay vì tốn thời gian cho việc upload và cấu hình server.

Điểm mạnh khác của Deployer là nó hỗ trợ chúng ta Zero downtime deployments bằng cơ chế SymLink trên Linux, đồng thời cho phép rollback về phiên bản trước đó khi cần thiết.

Ở hướng dẫn này, bạn có thể thực hiện một deploy cơ bản từ máy local lên một server CentOS 8 với thời gian Down time = 0.

Chuẩn bị môi trường

Máy local (môi trường phát triển)

  • OS: Windows 10 Home Single Language 64-bit
  • Development Tool:
    • XAMPP Version: 7.3.9
      • PHP 7.3.9
      • Apache 2.4.41
      • MariaDB 10.4.6
      • Perl 5.16.3
      • OpenSSL 1.1.1c (UNIX only)
      • phpMyAdmin 4.9.0.1
    • Git-SCM Git Bash
    • NodeJS Version 12.14.0
  • Editor: Visual Studio Code Version 1.40.2

Máy server (môi trường triển khai)

  • OS: CentOS Linux 8 (Core)
  • Development Tool:
    • LEMP Stack (Linux + Nginx+ MySQL/MongoDB+ PHP)
      • PHP 7.2.11 (cli)
      • Nginx version: nginx/1.14.1
      • MySql version: 8.0.17
    • Git git version 2.18.1 (Linux)
    • NodeJS v10.16.3 (Linux)
  • Editor: Nano

Git server

  • https://github.com/ Tạo sẵn một github repo để push code lên trong mỗi lần deploy.

Bắt đầu cài đặt

Bước 1 — Thiết lập môi trường Local Development

Đầu tiên, chúng ta sẽ thiết lập môi trường phát triển trên máy local để sẵn sàng cho Deployer hoạt động. Deployer sẽ tự động điều khiển toàn bộ quá trình upload code và chạy các lệnh cài đặt theo kịch bản của chúng ta.

Note: Nếu bạn code trên môi trường Linux thì việc cài đặt Deployer sẽ hơi khác hướng dẫn dưới đây :D Cơ bản là dễ hơn nhiều nhé. Vào link này làm theo là xong https://deployer.org/docs/getting-started.html

1.1 Cài đặt Deployer

Trên máy local, mở gitbash, tạo một thư mục mới để lưu file Deployer Excecute. Ở đây mình tạo folder trong ổ C:\ /c/Users/tuanm/deployer
Image

Tải về deployer.phar

$ curl -LO https://deployer.org/deployer.phar

Kiểm tra lại file đã tải xem đúng với mã hash mới nhất trên trang chủ của Deployer chưa?

6.7.3 - a00dd2c9e09e8d809ae14ad695578181a177cea7

$ php -r "if (hash_file('sha1', 'deployer.phar') === 'a00dd2c9e09e8d809ae14ad695578181a177cea7') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('deployer.phar'); } echo PHP_EOL;"

Output như bên dưới là OK.

Installer verified  

Đổi tên deployer.phar thành dep cho dễ nhớ.

$ mv deployer.phar dep

Cấp quyền thực thi cho file dep:

$ chmod +x dep

Test thử cái coi: Ở nguyên folder đó, gõ: ./dep Ra như hình dưới là sắp ngon rồi đấy.
Image

Hiện tại command dep chỉ chạy trong folder chứa nó. Muốn nó chạy ở mọi nơi thì cần thêm nó vào PATH của windows
Image

Thêm mới đường dẫn đến folder chứa file dep đã tải về ở bước trước.
Image

OK, xong các bước trên chúng ta có thể gõ lệnh dep ở mọi nơi rồi.

$ dep
Deployer 6.7.3

Usage:  
  command [options] [arguments]

Options:  
  -h, --help            Display this help message
  -q, --quiet           Do not output any message
  -V, --version         Display this application version
      --ansi            Force ANSI output
      --no-ansi         Disable ANSI output
  -n, --no-interaction  Do not ask any interactive question
  -f, --file[=FILE]     Specify Deployer file
  -v|vv|vvv, --verbose  Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug

Available commands:  
  autocomplete  Install command line autocompletion capabilities
  help          Displays help for a command
  init          Initialize deployer in your project
  list          Lists commands
  run           Run any arbitrary command on hosts
  self-update   Updates deployer.phar to the latest version
  ssh           Connect to host through ssh
 debug
  debug:task    Display the task-tree for a given task

1.2 Khởi tạo 1 project Laravel bằng Composer

$ composer create-project --prefer-dist laravel/laravel laravel-app

Tạo xong rồi chuyển sang bước 2, push code lên git repo của mình.

Bước 2 — Push Code lên Github

Đăng nhập vào Github và tạo mới 1 project để push code. Ví dụ: https://github.com/hapo-tuannd/laravel-base

Trên máy local, Mở gitbash tại folder source code, ví dụ này là: D:\Dev\Laravel\laravel-app. Run các command sau: (có thể yêu cầu đăng nhập)

git init  
git add .  
git commit -m "first commit"  
git remote add origin https://github.com/hapo-tuannd/laravel-base.git  
git push -u origin master  

Done push source lên Github

$ git push -u origin master
Counting objects: 113, done.  
Delta compression using up to 8 threads.  
Compressing objects: 100% (95/95), done.  
Writing objects: 100% (113/113), 58.39 KiB | 3.65 MiB/s, done.  
Total 113 (delta 11), reused 0 (delta 0)  
remote: Resolving deltas: 100% (11/11), done.  
To https://github.com/hapo-tuannd/laravel-base.git  
 * [new branch]      master -> master
Branch 'master' set up to track remote branch 'master' from 'origin'.  

OK, tiếp tục chuyển sang bước 3 trên máy server nhé.

Bước 3 — Cấu hình Deployer User

3.1 Khởi tạo deployer user

Deployer sử dung giao thức SSH để thực hiện các command trên server. Do đó, ở bước đầu tiên, chúng ta sẽ tạo ra một user mới, cái mà Deployer có thể dùng để login vào và thực hiện các command qua SSH.

Server yêu cầu chạy Linux và đã cài LEMP Stack (Linux + Nginx + MySQL/MongoDB + PHP).

Hướng dẫn cài đặt LEMP stack trên CentOS 7

Bắt đầu tạo user mới có tên là deployer và cấu hình nó:

$ sudo adduser deployer 

Laravel cần quyền đọc ghi dữ liệu để lưu cache file và upload file. Do đó, các thư mục tạo bởi deployer phải có quyền đọc ghi qua Nginx web server. Thêm nhóm quyền nginx vào user deployer bằng command sau:

$ sudo usermod -aG nginx deployer

Tiếp theo, cần cấu hình mặc định quyền khi tạo mới file, folder bằng user deployer là 644 (file) và 755 (folder). Điều này giúp cho deployer có thể đọc và ghi file, trong khi các nhóm hoặc user khác có thể đọc được.
Thực hiện cấu hình này bằng lệnh umask =022 như sau:

$ sudo chfn -o umask=022 deployer

Source code upload lên server sẽ lưu ở /var/www/html/laravel-app, nên chúng ta cần đổi ownership của folder cho user deployer và nhóm người dùng nginx:

$ sudo chown deployer:nginx /var/www/html/laravel-app

User deployer cần quyền sửa các file và folder trong /var/www/html/laravel-app ngay cả khi được tạo sau này. Như vậy mọi file, folder, sub-folder được tạo trong folder đều kế thừa permission của folder cha. Thực hiện câu lệnh sau để set group id cho folder:

$ sudo chmod g+s /var/www/html/laravel-app

3.2 Kết nối server với Github

Deployer sẽ clone Git repo về máy server qua SSH, vì vậy ở bước này sẽ hướng dẫn các bạn generate SSH key để cấu hình trên Github. Nếu ở bước 2 các bạn đã push code bằng cách SSH thì bước này cũng tương tự như vậy nhé.

Thao tác trên máy server: chuyển về user deployer vừa tạo ở bước 3.1

$ su - deployer

Tiếp theo, generate SSH key pair với deployer user. Bước này bạn có thể cần accept tên file, password mặc định. (Enter 3 phát là được =))).

$ ssh-keygen -t rsa -b 4096

Hiển thị public key vừa tạo:

$ cat ~/.ssh/id_rsa.pub

Copy public key này và cấu hình SSH trên phần setting của Github. Tham khảo chi tiết ở đây nhé:

Test thử kết nối trên máy server với Github phát xem sao:

$ ssh -T git@github.com
Hi hapo-tuannd! You've successfully authenticated, but GitHub does not provide shell access.  

OK, để cửa sổ đó, mình quay về máy local làm tiếp. (Mở 1 gitbash mới nhé).

3.3 Kết nối server với máy local

Ở máy local, chúng ta cũng sẽ kết nối đến máy server qua SSH. Bước này chúng ta sẽ generate SSH key ở máy local, rồi add nó vào máy server. Cũng khá đơn giản, bắt đầu nhé.

Thao tác trên máy local: bật gitbash, chạy command sau. Thoải mái thay đổi tên file deployerkey nhé.

$ ssh-keygen -t rsa -b 4096 -f  ~/.ssh/deployerkey

Copy đoạn public key hiện ra ở câu lệnh dưới đây:

$ cat ~/.ssh/deployerkey.pub

Thao tác trên máy server: Paste đoạn public key vừa copy vào server bằng các bước dưới đây:

$ nano ~/.ssh/authorized_keys

Dán public key vào editer và bấm CTRL-X, Y, rồi bấm ENTER để lưu và thoát.

Giới hạn quyền đọc ghi của file authorized_keys:

$ chmod 600 ~/.ssh/authorized_keys

NOTE: Nếu không Giới hạn quyền đọc ghi của file authorized_keys sẽ gặp lỗi: Permission denied (publickey,gssapi-keyex,gssapi-with-mic).

Bây giờ máy local của bạn đã có thể kết nối đến máy server rồi. Không tin thử xem:

$ ssh deployer@your_server_ip  -i ~/.ssh/deployerkey

Phùzz, mệt chưa các bạn? Uống cốc trà rồi vào bước cấu hình NginxMySQL trên máy server nhé.

Bước 4 — Cấu hình Nginx

Đoạn cấu hình Nginx này có thể tách riêng thành 1 bài kha khá chữ, nhưng mình vẫn viết vào đây, vì cấu trúc thư mục của Laravel với deployer nó hơi khác một chút.

Các thao tác dưới đây đều thực hiện ở máy server

Đầu tiên, chúng ta cần tạo một server block configuration file cho website mới.

Đăng nhâp vào máy server với user có quyền sudo và tạo một file config mới. Lưu ý example.com thay bằng domain của bạn nhé:

$ sudo nano /etc/nginx/sites-available/example.com.conf 

Thêm đoạn cấu hình sau vào file example.com.conf:

# example.com ’>/etc/nginx/sites-available/example.com.conf
server {  
        listen 80;
        listen [::]:80;

        root /var/www/html/laravel-app/current/public;
        index index.php index.html index.htm index.nginx-debian.html;

        server_name example.com www.example.com;
}
  • listen 2 dòng này báo với Nginx port nào web sẽ nhận
  • root chỉ ra đường dẫn đến source code. Đường dẫn current/public thực ra là một symbolic link trỏ đến latest release của ứng dụng Laravel.
  • index chỉ ra thứ tự ưu tiên đọc file mặc định của Ngnix, ở đây là ưu tiên đọc file index.php trước.
  • server_name chỉ ra domain của ứng dụng

Tiếp theo, cần chỉnh sửa file này thêm để Nginx có thể handler được các requests đến.

  • try_files Chúng ta muốn server thử khớp các $uri, đầu tiên là đúng chính xác, nếu không đúng, thử tiếp với index file của thư mục khớp với request ($uri/), nếu vẫn không khớp, thử tiếp với index.php kèm theo query parameter: /index.php?$query_string

Sửa file example.com.conf:

# example.com ’>/etc/nginx/sites-available/example.com
server {  
        listen 80;
        listen [::]:80;

        root /var/www/html/laravel-app/current/public;
        index index.php index.html index.htm index.nginx-debian.html;

        server_name example.com www.example.com;

        location / {
                try_files $uri $uri/ /index.php?$query_string;
        }
  • location ~ \.php$ block này xử lý việc thực hiện các file PHP. Điều này sẽ áp dụng cho bất kỳ tệp nào kết thúc bằng .php. Nó sẽ tự thử tệp và sau đó thử chuyển nó dưới dạng tham số cho tệp index.php.
    • fastcgi để báo cho Nginx sử dụng đường dẫn thực tế (actual path) thay vì symbolic link. Nếu bạn không thêm các dòng này vào cấu hình, đường dẫn nơi các điểm symbolic link trỏ tới sẽ được lưu vào bộ đệm, nghĩa là một phiên bản cũ của ứng dụng của bạn sẽ được tải sau khi deploy. Nếu không có các cấu hình này, bạn sẽ phải xóa bộ nhớ cache bằng tay sau mỗi lần deploy và các requests đến ứng dụng của bạn có thể bị lỗi.
    • fastcgi_pass sẽ đảm bảo rằng Nginx sử dụng plugin php7-fpm để kết nối và file index.php được sử dụng làm index.

Cuối cùng, chúng ta cần đảm bảo rằng Nginx sẽ không cho phép access file ẩn .htaccess qua block: location ~ /\.ht. File hoàn chỉnh bên dưới:

# example.com ’>/etc/nginx/sites-available/example.com.conf
server {  
        listen 80;
        listen [::]:80;

        root /var/www/html/laravel-app/current/public;
        index index.php index.html index.htm index.nginx-debian.html;

        server_name example.com www.example.com;

        location / {
                try_files $uri $uri/ /index.php?$query_string;
        }


        location ~ \.php$ {
                include snippets/fastcgi-php.conf;

                fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
                fastcgi_param DOCUMENT_ROOT $realpath_root;

                fastcgi_pass unix:/run/php/php7.0-fpm.sock;

        }

        location ~ /\.ht {
                deny all;
        }

}

Lưu lại (CTRL-X, Y, rồi ENTER) và enable server block mới tạo bằng cách tạo symbolic link đến folder sites-enabled

$ sudo ln -s /etc/nginx/sites-available/example.com.conf /etc/nginx/sites-enabled/

Kiểm tra xem cấu hình ok chưa?:

$ sudo nginx -t

Nếu không có lỗi, restart Nginx để xem thay đổi:

$ sudo systemctl restart nginx

Vậy là chúng ta đã cấu hình xong Nginx, tiếp theo là cấu hình MySQL

Bước 5 — Cấu hình MySQL

Sau khi cài đặt, MySQL sẽ tạo một root user mặc định. Đây là user có quyền tối thượng, nên dùng luôn user này mà cấu hình vào ứng dụng thì sẽ là một bad security practice. Thay vào đó chúng ta sẽ tạo ra một database mới và một user mới tương ứng với nó. Bắt đầu nhé.

Thao tác trên máy server: Đăng nhập vào MySQL console với quyền root:

$ mysql -u root -p

Nó sẽ hỏi mật khẩu, nếu mà lúc cài MySQL bạn không cấu hình thì mặc định là rỗng.

Tạo mới database:

mysql> CREATE DATABASE laravel_database DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;  

Sau đó tạo mới database user, tùy chỉnh password ở dưới nhé.

mysql> CREATE USER 'laravel_user'@'localhost' IDENTIFIED BY 'password';  

Gán quyền cho user tương ứng với database vừa tạo:

mysql> GRANT ALL ON laravel_database.* TO 'laravel_user'@'localhost';  

Tiếp theo, reload quyền:

mysql> FLUSH PRIVILEGES;  

Và cuối cùng, thoát SQL console:

mysql> EXIT;  

Bây giờ Server đã cấu hình xong, chúng ta có thể sẵn sàng cho lần phóng tàu đầu tiên. Ahi.

Bước 6 — Deploy ứng dụng

Đến bước này chúng ta đã cài đặt và cấu hình gần đủ các thứ cần thiết để Deploy rồi. Chúng ta sẽ tạo ra một kịch bản deploy để Deployer đọc và thực hiện theo.

Bắt đầu quá trình deploy nhé.

Thực hiện trên máy local Mở gitbash tại folder code đã push lên Github ở Bước 2: Image

Ở folder này, chạy câu lệnh dưới đây để tạo ra file deploy.php. Đây chính là file kịch bản phóng tàuDeployer sẽ đọc để thực hiện.

$ dep init -t Laravel

Tiếp theo, mở file deploy.php bằng text editor mà bạn yêu thích. Ở đây mình dùng Visual Studio Code :

# deploy.php
<?php  
namespace Deployer;

require 'recipe/laravel.php';

. . .

Chúng ta cần chỉnh sửa lại cấu hình ở một số trường dưới đây:

  • // Project Name Tên dự án web của bạn.
  • // Project Repository Link project github của bạn. Lưu ý trong hướng dẫn này là link ssh chứ không phải https nhé.
  • // Hosts section Địa chỉ IP của máy server, hoặc domain cũng được. Kèm theo đó là Deployer user (trong ví dụ này là deployer) . Đồng thời add thêm đường dẫn đến file ssh key đã tạo ở bước 3.3

Ok, cấu hình xong thì file nó sẽ như thế này:

# deploy.php
...
// Project name
set('application', 'laravel-app');

// Project repository
set('repository', 'git@mygitserver.com:username/repository.git');

. . .

// Hosts

host('your_server_ip')  
    ->user('deployer')
    ->identityFile('~/.ssh/deployerkey')
    ->set('deploy_path', '/var/www/html/laravel-app');

Tiếp theo, comment out dòng này lại (dòng cuối của file), before('deploy:symlink', 'artisan:migrate');. Dòng này chỉ thị Deployer chạy migrate database tự động. Chúng ta comment lại để disable tác vụ này. Nếu không comment lại deploy sẽ lỗi đấy, không tin thử mà xem :p:

# deploy.php
...
// Migrate database before symlink new release.

//before('deploy:symlink', 'artisan:migrate');

Cuối cùng, thử phóng tàu bằng command dưới đây nhé:

$ dep deploy

Oh, Something went wrong!

Image

TTY mode is not supported on Windows platform.

Quay lại file deploy.php thấy dòng set('git_tty', true);, sửa lại thành false rồi thử lại nhé.

set('git_tty', false);  

Ơn giời, cậu xong rồi :D:

Ah, vẫn chưa xong, còn cấu hình file .env nữa.

Đăng nhập lại vào máy server để xem các file đã đẩy lên tự động với cấu trúc như thế nào nhé: Mở gitbash từ máy local để login cho đúng user deployer.

Tránh đăng nhập lại bằng root, sẽ ảnh hưởng đến permission các file mà deployer đang quản lý.

$ ssh deployer@your_server_ip  -i ~/.ssh/deployerkey

Kiểm tra các thư mục có trong folder source đã upload lên:

$ ls -la /var/www/html/laravel-app
# Output
total 20  
drwxr-xr-x 5 deployer nginx    4096 Jan  3 10:03 .  
drwxr-xr-x 4 root     root     4096 Jan  2 19:51 ..  
lrwxrwxrwx 1 deployer deployer   10 Jan  3 10:03 current -> releases/1  
drwxrwxr-x 2 deployer deployer 4096 Jan  3 10:03 .dep  
drwxrwxr-x 3 deployer deployer 4096 Jan  3 10:02 releases  
drwxrwxr-x 3 deployer deployer 4096 Jan  3 09:43 shared  

Cấu trúc:

├── .dep
├── current -> releases/1
├── releases
│   └── 1
└── shared
    ├── .env
    └── storage

Hiện tại ứng dụng của chúng ta vẫn chưa chạy, bởi vì file .env đang trống. File này chứa các thông tin nhạy cảm như database, api key, email config, nên không được push lên git (đã có trong .gitignore). Vì vậy chúng ta phải cấu hình bằng tay file này ở lần đầu deploy hoặc ở các deploy có thay đổi cấu hình trong .env. Cũng vì chưa có file này nên chúng ta mới bỏ qua database migrations ở bước trước đó.

Chạy command dưới đây trên server và copy nội dung file .env dưới máy local, paste vào và lưu lại:

$ nano /var/www/html/laravel-app/shared/.env

Trước khi lưu, bạn cần lưu ý các thông tin sau sẽ thay đổi so với local:

  • APP_ENV=production
  • APP_DEBUG=false
  • APPLOGLEVEL=error
  • Cập nhật lại thông tin DB_CONNECTION

Bạn có thể đổi example.com bằng domain của mình:

# /var/www/html/laravel-app/shared/.env
APP_NAME=Laravel  
APP_ENV=production  
APP_KEY=  
APP_DEBUG=false  
APP_LOG_LEVEL=error  
APP_URL=http://example.com

DB_CONNECTION=mysql  
DB_HOST=127.0.0.1  
DB_PORT=3306  
DB_DATABASE=laravel_database  
DB_USERNAME=laravel_user  
DB_PASSWORD=password

BROADCAST_DRIVER=log  
CACHE_DRIVER=file  
SESSION_DRIVER=file  
QUEUE_DRIVER=sync

REDIS_HOST=127.0.0.1  
REDIS_PASSWORD=null  
REDIS_PORT=6379  

Lưu file và đóng editor lại.

Bây giờ bạn có thể uncomment dòng cuối cùng của file deploy.php trên máy local và deploy lại:

# deploy.php
...
// Migrate database before symlink new release.

before('deploy:symlink', 'artisan:migrate');  

Warning: Bỏ comment dòng trên nghĩa là mỗi lần deploy thì database sẽ migrate lại. Vì vậy, những lần deploy sau thì comment lại nhé.

Ok rồi, thử phóng tàu lại lần nữa nhé:

Done rồi nha: (đoạn example.com chạy được là do mình đã sửa file hosts trỏ đến ip của máy server)

Bước 7 — Thử deploy một tính năng mới xem sao

Đến đây là việc deploy tự động đã hoàn tất rồi. Mình thử test sửa cái tiêu đề "Laravel" rồi deploy lại nhé:

# resources/views/welcome.blade.php
...
<title>Haposoft Laravel Development Team</title>  
...
<div class="title m-b-md">  
  Welcome to Haposoft Laravel Development Team
</div>  
...

Lưu lại, rồi push lên github, sau đó chạy lệnh deploy, sẽ thấy kết quả:

$ dep deploy

Kết: Các bạn cứ thử theo các bước trên, gặp lỗi gì thì comment vào bên dưới xem mình đã gặp chưa nhé :)

Bài này hơi dài, các bạn có thể đọc và chia nhỏ từng phần để thực hiện.
Thank you all <3

Related article