Áp dụng CI/CD vào project của bạn (P1)
Bài đăng này đã không được cập nhật trong 5 năm
1. Mở đầu
Trước khi bắt đầu đọc bài viết này, bạn nên đã có những hiểu biết cơ bản về CI/CD là gì và nó phục vụ các mục đích gì trong quá trình phát triển của một project. Nếu bạn chưa biết đến nó là gì thì bạn có thể tìm hiểu qua một chút ở bài viết này. Đồng thời bạn nên có 1 chút kiến thức về Docker thì càng tốt mà không có cùng không sao .
2. Giới thiệu
Ở bài part 1 của viết này, mình sẽ hướng dẫn các bạn tích hợp CI vào project của mình với một dịch vụ cung cấp plan miễn phí là CircleCI. Vì đây là plan miễn phí nên nó cũng có khá nhiều mặt hạn chế tùy thuộc vào project của bạn. Theo cá nhân mình thì nó sẽ phù hợp cho các project thuộc dạng opensource và team dev chỉ khoảng < 5 người. Lý do tại sao thì mình sẽ trình bày ở cuối bài viết. Vì mình chủ yếu làm việc với ngôn ngữ PHP
cụ thể là Laravel
nên trong bài viết này mình sẽ hướng dẫn bạn tích hợp với project Laravel
. Nào chúng ta hãy bắt tay vào việc.
3. Bắt đầu
3.1 Thêm file cấu hình cho CircleCI
Trước khi tích hợp file cấu hình cho CircleCI, bạn cần đảm bảo 2 điều kiện sau:
- Bạn đang có một project (ở đây của mình là
Laravel)
- Project của bạn đang sử dụng Github hoặc Bitbucket vì mặc định CircleCI đang hỗ trợ 2 nền tảng này
Sau khi đã thỏa mã 2 điều kiện trên, trong folder root của project của bạn, bạn tạo 1 folder có tên là .circleci
và bên trong đó tạo thêm 1 file config.yml
. Sau khi tạo, thì cấu trúc folder của mình sẽ có dạng như sau:
Nội dung của file config.yml
đối với project Laravel
sẽ có thể có dạng như sau:
version: 2
jobs:
build:
docker:
- image: 7896/civ3-workspace:2.0
steps:
- checkout
- run: cp .env.example .env
# composer cache
- restore_cache:
keys:
- vendor-v1-{{ checksum "composer.lock" }}
- run: composer install
- save_cache:
key: vendor-v1-{{ checksum "composer.lock" }}
paths:
- vendor
# run test
- run: phpcs app
Giải thích sơ qua một chút về nội dung file:
- Gồm có 2 phần chính là
docker
vàsteps
-
Đầu tiên bạn có thể thấy ở phần
docker
mình đang sử dụng mộtimage
có tên là7896/civ3-workspace:2.0
. Đối với các bạn đã biết về Docker hay chưa biết về Docker thì đơn giản image giống như 1 cái hộp đã được cài một số thứ hỗ trợ chúng ta trong việc chạy test project nhưphp
,composer
,git
và tool dùng để kiểm tra convention chophp
làphpcs
cùng một vài tool khác. Nội dung cụ thể image bạn có thể xem ở đây. -
Tiếp đó phần
steps
là các bước lần lượt mà chúng ta sẽ chạy cho project của chúng ta, cụ thể:checkout
- Clone code từ github - đây là lệnh mặc định của CircleCI.run: [lệnh]
- Các lệnh cần thực hiện.restore_cache
- Khôi phục lại folder chứa cái package (vendor/) dựa trên filecomposer.lock
save_cache
- Lưu lại foldervendor
nesu filecomposer.lock
bị thay đổi hay do bạn xóa hoặc thêm package mới.- Việc cache lại folder vendor giúp tăng tốc cho bản build của bạn cho những lần chạy sau vì thay vì phải install lại các package thì đầu thì ta có thể tái sử dụng lại folder vendor của bản build trước đó nếu file
composer.lock
không bị thay đổi.
Lưu ý phần resore và save cache bạn cần sắp xếp theo đúng thư tự trên để hoạt động đúng
-
3.2 Tiến hành chạy build
Sau khi đã thêm file config cho CircleCI, ta sẽ tiến hành truy cập vào trang web https://circleci.com và tiến hành đăng nhập với Github. Sau khi đã đăng nhập, bạn chọn mục Add project
sau đó chọn project bạn vừa config và bấm Set Up Project
:
Sau khi chọn Set Up Project
sẽ hiện ra một màn hình mới và bạn lần lượt chọn OS
là Linux, Language
là PHP và cuối cùng bấm Start building
để tiến hành build thử lần đầu tiên:
Nếu config đúng thì bạn sẽ thấy bản build đầu tiên của bạn được chạy trên CircleCI như sau:
Bạn cần đợi khoảng vài chục giây cho đến vài chục phút tùy thuộc vào độ lớn của project trước khi thu được kết quả từ bản build. Ở đây mình dùng project mới nên chỉ tốt khoảng vài chục giây để chạy. Đây là kết quả sau khi chạy:
Để xem điều gì dẫn đến bản build của bạn bị fail bạn có thể bấm vào đó để xem chi tiết bản build bằng cách bấm vào bản build đó sau đó chọn chi tiết build
như sau:
Ở đây sẽ xuất hiện lần lượt các lệnh mà chúng ta đã thực hiện cho bản build đó giống như trong file config.yml
mà ta đã cấu hình ở trên bao gồm việc tải image docker
mà chúng ta đã khai báo, clone code về, restore/save cache và chạy lệnh phpcs app
. Ta có thể bấm lần vào các lệnh để xem được nội dung chi tiết với mỗi lệnh đó. Ở đây ta sẽ thử click vào lệnh phpcs app
để xem chúng ta mắc phải lỗi convention gì và có thể tiến hành sửa chữa:
Như vậy là về cơ bản, project của chúng ta đã chạy được trên CircleCI, giờ đây với mỗi push/pull request đến repository này của chúng ta sẽ tạo ra một bạn build tương ứng kiểm tra convention của code mới. Trước khi thử đẩy pull mới, mình sẽ sửa lại lệnh chạy phpcs
thành tiêu chuẩn convention mà mình hay dùng (đã được cài sẵn trong docker image ở trên) và sửa các lỗi phát sinh. Đây là kết quả sau khi chúng ta đã sửa lỗi convention và đẩy lại lên:
Lúc này bản build của chúng ta đã success đồng nghĩa với việc pass các yêu cầu về convention mà ta đặt ra. Tuy nhiên trong project thường chúng ta sẽ có cả phần unit-test
nên chúng ta sẽ tiến hành thêm lệnh chạy unit-test
của Laravel
vào file config.yml
như sau:
version: 2
jobs:
build:
docker:
- image: 7896/civ3-workspace:2.0
steps:
- checkout
- run: cp .env.example .env
# composer cache
- restore_cache:
keys:
- vendor-v1-{{ checksum "composer.lock" }}
- run: composer install
- save_cache:
key: vendor-v1-{{ checksum "composer.lock" }}
paths:
- vendor
# run test
- run: phpcs --standard=Framgia app
- run: ./vendor/bin/phpunit
Sau đó chúng ta lại tiến hành push tiếp lên repository trên github để CircleCI tiếp tục kiểm tra cả phpcs
và phpunit
:
Tuy nhiên lần này phpcs
của chúng ta vẫn pass như bình thường còn unit-test
lại bị fail. Chúng ta có thể mở tab chạy lệnh ./vendor/bint/phpunit
để xem thông tin lỗi và sửa nó. Ở đây có vẻ do mình thiếu lệnh tạo key cho file .env
dẫn tới bản build bị lỗi. Chúng ta có thể sửa nó lại bằng cách thêm lệnh sinh key cho file config.yml
như sau:
version: 2
jobs:
build:
docker:
- image: 7896/civ3-workspace:2.0
steps:
- checkout
- run: cp .env.example .env
# composer cache
- restore_cache:
keys:
- vendor-v1-{{ checksum "composer.lock" }}
- run: composer install
- save_cache:
key: vendor-v1-{{ checksum "composer.lock" }}
paths:
- vendor
# run test
- run: php artisan key:generate
- run: phpcs --standard=Framgia app
- run: ./vendor/bin/phpunit
Sau khi đã sửa thì mọi thứ lại đâu vào đó:
3.3 framgia-cli tool
- Với cách setup như trên sẽ xảy ra một vấn đề đó là trong trường hợp lệnh
phpcs
bị lỗi thì bản build sẽ lập tức báo fail và đồng thời dẫn tới bản build bị fail và không chạy đến tất cả các lệnh phía sau nữa. Trong trường hợp cảphpcs
lẫnphpunit
của bạn bị lỗi thì bạn sẽ phải cần tới 2 lần gửi pull/push request để có thể fix được hết lỗi. Trên thực tế, một project thông thường của bạn có thể test cảphpcs
,phpunit
và thậm chí cảeslint
cho javascript convention. Nếu mỗi trong trường hợp đen đủi bạn sẽ phải tốn tận 3 push/pull request để sửa nó. Để giải quyết vấn đề này. Để giải quyết vấn đề này, ta sẽ sử dụng một cli tool đã có sẵn trongimage docker
mà ta dùng ở trên tên làframgia-cli
. Tool này sẽ hỗ trợ bạn chạy toàn bộ các lệnh bạn khai báo sau khi toàn bộ các lệnh đã chạy xong thì nó mới đưa ra kết quả fail hay success cho toàn bộ bản build đó. Nếu bạn dùngimage docker
cá nhân thì có thể xem hướng dẫn cài đặt tool vào project image của bạn ở đây.
Sau đó trong folder root của project, ta sẽ thêm 1 file có tên là framgia-ci.yml
với nội dung như sau:
project_type: php
test:
phpcs:
command: phpcs --standard=Framgia app
phpunit:
command: ./vendor/bin/phpunit
File này sẽ là nơi các bạn khai báo các lệnh cần chạy khi test với cấu trúc file như trên. Sau khi đó bạn thêm tiếp lệnh sửa lại trong file .circleci/config.yml
như sau:
version: 2
jobs:
build:
docker:
- image: 7896/civ3-workspace:2.0
steps:
- checkout
- run: cp .env.example .env
# composer cache
- restore_cache:
keys:
- vendor-v2-{{ checksum "composer.lock" }}
- run: composer install
- save_cache:
key: vendor-v2-{{ checksum "composer.lock" }}
paths:
- vendor
# run test
- run: php artisan key:generate
- run: framgia-ci run --local
Cuối cùng ta lưu lại và đẩy lại lên repository của bạn rồi kiểm tra bên CircleCI sẽ thấy bản build mới nhất được chạy với tool vừa cài đặt:
Nếu bản build của bạn success thì sẽ trả về kết quả như ảnh trên đồng thời hiển thị các command success như bạn khai báo trong file. Giờ chúng ta sẽ thử lại với trường hơp phpcs
fail xem nó có chạy phpunit
hay không bằng cách bạn thêm vài lỗi convention vào folder app của mình. Đây là kết quả:
Như bạn thấy ta vẫn chạy được phpunit
mặc dù fial ở phpcs
vậy là đã đạt được mục tiêu mà chúng ta mong muốn. Giờ đây kể cả bạn thêm cả eslint
thì chỉ với 1 bản build bạn cũng có thể phát hiện hết các lỗi và sửa nó nhanh chóng hơn.
3.4 Thêm database
Database là một phần không thể thiếu khi bạn chạy unit-test
và đương nhiên việc tích hợp thêm một database hỗ trợ cho unit-test
cũng vô cùng đơn giản. Tất cả những gì bạn cần làm là chọn một loại database: mysql
, pgsql
, ... sau đó lấy image
của nó trên docker-hub sau đó thêm nó vào file .circleci/config.yml
như sau. Ở khối docker mình sẽ thêm database là mysql
cùng với các biến môi trường cần thiết:
version: 2
jobs:
build:
docker:
- image: 7896/civ3-workspace:2.0
- image: mysql:5.7
environment:
MYSQL_HOST: 127.0.0.1
MYSQL_DATABASE: homestead
MYSQL_USER: homestead
MYSQL_PASSWORD: secret
MYSQL_ROOT_PASSWORD: root
steps:
- checkout
- run: cp .env.example .env
# composer cache
- restore_cache:
keys:
- vendor-v1-{{ checksum "composer.lock" }}
- run: composer install
- save_cache:
key: vendor-v1-{{ checksum "composer.lock" }}
paths:
- vendor
# run test
- run: php artisan key:generate
- run: framgia-ci run --local
Bạn cần đảm bảo rằng trong file .env.example
của bạn cũng có phần khai báo cơ sở dữ liệu với các thông tin về host, tên database, username và password trùng với mục ta vừa thêm ở trên như sau:
# .env.example
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=homestead
DB_USERNAME=homestead
DB_PASSWORD=secret
Sau khi đã làm 2 việc trên, ta tiếp tục thử thêm lệnh 2 lệnh sau vào trong file .circleci/config.yml
như sau:
# run test
- run: php artisan key:generate
- run: framgia-ci test-connect 127.0.0.1 3306 60
- run: php artisan migrate
- Ở đây bạn có thể để ý thấy mình thêm 2 lệnh, lệnh thứ nhất dùng để kiểm tra connection đến database mysql mà chúng ta vừa thêm ở phần
docker
vời thới gian timeout là 60 giây. Sở dĩ ta cần thực hiện kiểm tra điều này vì khi bắt đầu khởi tạo container cho database, CircleCI sẽ không dừng lại đợi việc khởi tạo database thành công mà sẽ tiếp tục chạy các lệnh phía dưới. Vì thế trong trường hợp database chưa được khởi tạo thành công mà đã chạy đến lệnh cần dùng đến database sẽ trả về cho chúng ta lỗi không kết nối được database. Hoặc nếu quá 60s mà kết nối database thất bại cũng sẽ báo lại kết quả luôn mà không chạy các lệnh khác. Còn lệnh thứ hai sau đó đơn giản là migrate các bảng của chúng ta vào database.
Sau khi đã thêm các nội dung như trên, ban lưu lại sau đó tiếp tục đẩy lên repository và kiểm tra trên CircleCI sẽ có kết quả như sau:
Bạn có thể thấy có thêm phần Container mysql:5.7
là khối chứa dữ thông tin về việc tạo container cho database của chúng ta cùng với 2 lệnh là framgia-ci test-connect 127.0.0.1 3306 60
và php artisan migrate
mà chúng ta mới thêm vào. Thử mở mục php artisan migrate
ta sẽ thấy việc migrate database đã thành công:
Kết bài
Mong rằng qua bài viết của mình bạn đã biết được cách áp dụng CI vào project của mình với CircleCI. Trong bài viết tiếp theo mình sẽ hướng dẫn các bạn cách sử dụng Notifications
với CircleCI và cách chúng ta có thể thực hiện auto deploy lên Heroku
của bạn, các bạn hãy chú ý đón đọc . Nếu có bất cứ thắc mắc gì bạn có thể comment ở phía dưới để mình giải đáp. Cám ơn các bạn đã đọc !!!
All rights reserved