Chào tất cả bà con, sau 1 thời gian vắng bóng mình lại quay lại đây, đợt vừa rồi bận quá nên cũng không chia sẻ được gì nhiều, thôi thì cũng đang có series về Deploy, thì mình làm luôn 1 bài về CI/CD bằng cách sử dụng CircleCI và Deployer mọi người nhé.
Như ae Sun thì có SunCI và nếu muốn setup để nghịch ngợm thì phải tầm keymember hoặc là leader trở lên mới có quyền fetch dự án về SunCI để test nên cũng khá bất cập.
Nhưng đến với CircelCI thì mọi người build thoải mái nhé, chức năng và giao diện theo mình đánh giá còn sịn sò hơn SunCI nữa. Ok lan man thế thôi chúng ta bắt đầu thôi.
Để có thể làm được bài này đảm bảo mọi người đã hoặc từng làm qua những bài này:
- [Backend] Deploy ứng dụng Laravel của bạn (P1)
- [Backend] Deploy ứng dụng Laravel của bạn (P2 – Phần cuối)
- [Backend] Deploy dự án Laravel bằng Deployer
- [Frontend] Cài đặt ESLINT cho dự án sử dụng Laravel và Vuejs
- [Backend] PHP static code analysis tools
- [Docker] Biết và sử dụng được docker
- [Docker] Setup Docker Hub
- Hiểu được CI/CD và DevOps là gì ?
1 option thêm nếu bạn muốn viết Test cho dự án của mình thì bạn cần phải có kinh nghiệm về UnitTest và IntegrationTest
Thôi chốt lại để có thể làm tốt và hiểu được về CI/CD các bạn phải có khá là nhiều kinh nghiệm về Build Server, Auto deploy, Check convention JS,PHP, Config được Test trong dự án laravel, Biết sử dụng docker, build dockerhub …
OK chuẩn bị kiến thức thể là đủ rồi, mình triển thôi =))
Setup config.yml file
Để bắt đầu mọi người phải có 1 project (của mình là laravel), 2 là phải đang sử dụng Github hoặc Bitbucket nhé
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:
OK nội dung cơ bản của nó sẽ như sau
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | version<span class="token punctuation">:</span> <span class="token number">2</span> jobs<span class="token punctuation">:</span> build_and_test<span class="token punctuation">:</span> docker<span class="token punctuation">:</span> <span class="token operator">-</span> image<span class="token punctuation">:</span> framgia<span class="token operator">/</span>laravel<span class="token operator">-</span>workspace steps<span class="token punctuation">:</span> <span class="token operator">-</span> checkout <span class="token operator">-</span> run<span class="token punctuation">:</span> cp <span class="token punctuation">.</span>env<span class="token punctuation">.</span>example <span class="token punctuation">.</span>env <span class="token shell-comment comment"># composer cache</span> <span class="token operator">-</span> restore_cache<span class="token punctuation">:</span> keys<span class="token punctuation">:</span> <span class="token operator">-</span> vendor<span class="token operator">-</span>v1<span class="token operator">-</span><span class="token punctuation">{</span><span class="token punctuation">{</span> checksum <span class="token double-quoted-string string">"composer.lock"</span> <span class="token punctuation">}</span><span class="token punctuation">}</span> <span class="token operator">-</span> run<span class="token punctuation">:</span> composer install <span class="token operator">-</span> save_cache<span class="token punctuation">:</span> key<span class="token punctuation">:</span> vendor<span class="token operator">-</span>v1<span class="token operator">-</span><span class="token punctuation">{</span><span class="token punctuation">{</span> checksum <span class="token double-quoted-string string">"composer.lock"</span> <span class="token punctuation">}</span><span class="token punctuation">}</span> paths<span class="token punctuation">:</span> <span class="token operator">-</span> vendor <span class="token shell-comment comment"># run test</span> <span class="token operator">-</span> run<span class="token punctuation">:</span> phpcs <span class="token operator">--</span>standard<span class="token operator">=</span>Framgia app |
Giải thích qua 1 vài syntax mn nhé
- 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ột image có tên là framgia/laravel-workspace. Đố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 cho php 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 file composer.lock
- save_cache – Lưu lại folder vendor nesu file composer.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.
Sau khi đã config xong thì ae push code lên repo nhé
Init project
Mọi người vào trang https://circleci.com/ sau đó login bằng github hoặc Bitbucket của mọi người nhé, mình dùng github cho quen nhé, sau khi login xong nó sẽ chuyển hướng đến trang dashboard là ok
Mọi người chọn vào mục ADD PROJECT nhé
Sau đó chọn project cần Setup, Mọi người chọn project của mn nhé, click vào button Set up Project nhé
Vào màn hình này thì sẽ có chỗ chọn Hệ điều hành, ngôn ngữ, nó đã mặc định cho mình rồi, mình code PHP mà =)) Lúc này chọn Start Building là ok, chọn thì nó sẽ chuyển sang màn hình build, và nó sẽ chạy bản build đầu tiên
Chờ nó build 1 tý mình ok
Success rồi đúng không, phê lòi, lầu đầu mà mượt mà như thế là do mình đã chạy test phpcs trước ở dưới local rồi, còn nếu mn và bị fail thì click vào detail để xem nó fail cái gì để sửa rồi push lại nhé
Tỷ dụ như cái này bị fail eslint thì mình chỉ cần vào xem nó fail chỗ nào rồi sửa và push lại thôi
CI
OK như trên là mọi người đã có thể tự build 1 bản rồi, giờ thì check nhiều hơn nhé, check thêm ESLINT, TEST trong laravel nhé.
Mọi người update lại file config.yml nhé
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | version<span class="token punctuation">:</span> <span class="token number">2</span> jobs<span class="token punctuation">:</span> build_and_test<span class="token punctuation">:</span> docker<span class="token punctuation">:</span> <span class="token operator">-</span> image<span class="token punctuation">:</span> framgia<span class="token operator">/</span>laravel<span class="token operator">-</span>workspace <span class="token operator">-</span> image<span class="token punctuation">:</span> mysql<span class="token punctuation">:</span><span class="token number">5.7</span> environment<span class="token punctuation">:</span> <span class="token constant">MYSQL_HOST</span><span class="token punctuation">:</span> <span class="token number">127.0</span><span class="token number">.0</span><span class="token number">.1</span> <span class="token constant">MYSQL_DATABASE</span><span class="token punctuation">:</span> homestead <span class="token constant">MYSQL_USER</span><span class="token punctuation">:</span> homestead <span class="token constant">MYSQL_PASSWORD</span><span class="token punctuation">:</span> secret <span class="token constant">MYSQL_ROOT_PASSWORD</span><span class="token punctuation">:</span> root steps<span class="token punctuation">:</span> <span class="token operator">-</span> checkout <span class="token operator">-</span> run<span class="token punctuation">:</span> cp <span class="token punctuation">.</span>env<span class="token punctuation">.</span>testing<span class="token punctuation">.</span>example <span class="token punctuation">.</span>env<span class="token punctuation">.</span>testing <span class="token shell-comment comment"># composer cache</span> <span class="token operator">-</span> restore_cache<span class="token punctuation">:</span> keys<span class="token punctuation">:</span> <span class="token operator">-</span> vendor<span class="token operator">-</span>v1<span class="token operator">-</span><span class="token punctuation">{</span><span class="token punctuation">{</span> checksum <span class="token double-quoted-string string">"composer.lock"</span> <span class="token punctuation">}</span><span class="token punctuation">}</span> <span class="token operator">-</span> run<span class="token punctuation">:</span> composer install <span class="token operator">-</span> save_cache<span class="token punctuation">:</span> key<span class="token punctuation">:</span> vendor<span class="token operator">-</span>v1<span class="token operator">-</span><span class="token punctuation">{</span><span class="token punctuation">{</span> checksum <span class="token double-quoted-string string">"composer.lock"</span> <span class="token punctuation">}</span><span class="token punctuation">}</span> paths<span class="token punctuation">:</span> <span class="token operator">-</span> vendor <span class="token shell-comment comment"># Yarn</span> <span class="token operator">-</span> restore_cache<span class="token punctuation">:</span> name<span class="token punctuation">:</span> Restore yarn cache keys<span class="token punctuation">:</span> <span class="token operator">-</span> yarn<span class="token operator">-</span><span class="token punctuation">{</span><span class="token punctuation">{</span> checksum <span class="token double-quoted-string string">"yarn.lock"</span> <span class="token punctuation">}</span><span class="token punctuation">}</span> <span class="token operator">-</span> yarn<span class="token operator">-</span> <span class="token operator">-</span> run<span class="token punctuation">:</span> yarn <span class="token operator">-</span> save_cache<span class="token punctuation">:</span> paths<span class="token punctuation">:</span> <span class="token operator">-</span> node_modules key<span class="token punctuation">:</span> yarn<span class="token operator">-</span><span class="token punctuation">{</span><span class="token punctuation">{</span> checksum <span class="token double-quoted-string string">"composer.json"</span> <span class="token punctuation">}</span><span class="token punctuation">}</span> <span class="token operator">-</span> run<span class="token punctuation">:</span> name<span class="token punctuation">:</span> Check convention javarscript command<span class="token punctuation">:</span> npm run eslint <span class="token shell-comment comment"># run test</span> <span class="token operator">-</span> run<span class="token punctuation">:</span> php artisan key<span class="token punctuation">:</span>generate <span class="token operator">--</span>env<span class="token operator">=</span>testing <span class="token operator">-</span> run<span class="token punctuation">:</span> name<span class="token punctuation">:</span> Check convention <span class="token constant">PHP</span> command<span class="token punctuation">:</span> phpcs <span class="token operator">--</span>standard<span class="token operator">=</span>Framgia app <span class="token operator">-</span> run<span class="token punctuation">:</span> name<span class="token punctuation">:</span> Check connection database command<span class="token punctuation">:</span> framgia<span class="token operator">-</span>ci test<span class="token operator">-</span>connect <span class="token number">127.0</span><span class="token number">.0</span><span class="token number">.1</span> <span class="token number">3306</span> <span class="token number">60</span> <span class="token operator">-</span> run<span class="token punctuation">:</span> php artisan migrate <span class="token operator">--</span>seed <span class="token operator">--</span>env<span class="token operator">=</span>testing <span class="token operator">-</span> run<span class="token punctuation">:</span> name<span class="token punctuation">:</span> Check PhpUnit command<span class="token punctuation">:</span> <span class="token punctuation">.</span><span class="token operator">/</span>vendor<span class="token operator">/</span>bin<span class="token operator">/</span>phpunit |
Để có thể chạy được Test trong laravel cần phải có 1 cái database để chạy test đúng ko , đó chính là lúc mn thêm cái image mysql:5.7 đấy
1 2 3 4 5 6 7 8 | <span class="token operator">-</span> image<span class="token punctuation">:</span> mysql<span class="token punctuation">:</span><span class="token number">5.7</span> environment<span class="token punctuation">:</span> <span class="token constant">MYSQL_HOST</span><span class="token punctuation">:</span> <span class="token number">127.0</span><span class="token number">.0</span><span class="token number">.1</span> <span class="token constant">MYSQL_DATABASE</span><span class="token punctuation">:</span> homestead <span class="token constant">MYSQL_USER</span><span class="token punctuation">:</span> homestead <span class="token constant">MYSQL_PASSWORD</span><span class="token punctuation">:</span> secret <span class="token constant">MYSQL_ROOT_PASSWORD</span><span class="token punctuation">:</span> root |
lúc này mọi người config vài cái biến môi trường để có thể kết nối được nhé,
Bạn cần đảm bảo rằng trong file .env.testing.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:
1 2 3 4 5 6 7 | <span class="token constant">DB_CONNECTION</span><span class="token operator">=</span>mysql <span class="token constant">DB_HOST</span><span class="token operator">=</span><span class="token number">127.0</span><span class="token number">.0</span><span class="token number">.1</span> <span class="token constant">DB_PORT</span><span class="token operator">=</span><span class="token number">3306</span> <span class="token constant">DB_DATABASE</span><span class="token operator">=</span>homestead <span class="token constant">DB_USERNAME</span><span class="token operator">=</span>homestead <span class="token constant">DB_PASSWORD</span><span class="token operator">=</span>secret |
Sau khi đã làm 2 việc trên, ta tiếp tục thêm lệnh sau vào trong file .circleci/config.yml như sau nhé:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | - run: name: Check convention javarscript command: npm run eslint - run: php artisan key:generate --env=testing - run: name: Check convention PHP command: phpcs --standard=Framgia app - run: name: Check connection database command: framgia-ci test-connect 127.0.0.1 3306 60 - run: php artisan migrate --seed --env=testing - run: name: Check PhpUnit command: ./vendor/bin/phpunit |
Mỗi câu lệnh run mình thêm 2 lệnh con đó là name và command để đến lúc build nó hiện thị name lên cho nó sịn thay vì là hiện thị câu lệnh ấy.
Trong đoạn này thì mình có cho thêm check convention của js bằng Eslint nhé.
1 2 3 4 | - run: name: Check convention javarscript command: npm run eslint |
cái này mn phải đảm bảo trong projec mn đã setup eslint rồi nhé. Ai chưa tìm hiểu thì xem lại ở đây
Để chạy được test chắc chắn file env của mn phải có key đúng không, thêm vào mà chạy thôi
1 2 | <span class="token operator">-</span> run<span class="token punctuation">:</span> php artisan key<span class="token punctuation">:</span>generate <span class="token operator">--</span>env<span class="token operator">=</span>testing |
Tiếp theo là check PHPCS theo chuẩn của Framgia. mình thấy chuẩn này khá là ổn, nó chỉ bắt những convention cần thiết nhất. chứ theo full của psr2 .. thì nó bắt nhiều lắm, nào là viết doc cho function cho class … đủ thứ trên đời.
1 2 3 4 | <span class="token operator">-</span> run<span class="token punctuation">:</span> name<span class="token punctuation">:</span> Check convention <span class="token constant">PHP</span> command<span class="token punctuation">:</span> phpcs <span class="token operator">--</span>standard<span class="token operator">=</span>Framgia app |
Tiếp theo là phần test connect với database xem đã ok chưa và chạy migrate
1 2 3 4 5 | <span class="token operator">-</span> run<span class="token punctuation">:</span> name<span class="token punctuation">:</span> Check connection database command<span class="token punctuation">:</span> framgia<span class="token operator">-</span>ci test<span class="token operator">-</span>connect <span class="token number">127.0</span><span class="token number">.0</span><span class="token number">.1</span> <span class="token number">3306</span> <span class="token number">60</span> <span class="token operator">-</span> run<span class="token punctuation">:</span> php artisan migrate <span class="token operator">--</span>seed <span class="token operator">--</span>env<span class="token operator">=</span>testing |
cái framgia-ci là 1 tool của Framgia, nó có sẵn trong cái image framgia/laravel-workspace mn nhé, nó là open source nên cứ lấy mà dùng thôi hihi
Phần cuối là chạy test
1 2 3 4 | <span class="token operator">-</span> run<span class="token punctuation">:</span> name<span class="token punctuation">:</span> Check PhpUnit command<span class="token punctuation">:</span> <span class="token punctuation">.</span><span class="token operator">/</span>vendor<span class="token operator">/</span>bin<span class="token operator">/</span>phpunit |
OK ngon hàng push lên rồi thử xem nó chạy như thế nào , mà nhớ mn thao tác cái này thì tạo 1 branch mới rồi push lên tạo pull compare với develop nhé
Sau khi đẩy lên nó build thì đang báo lỗi JS, ok xem lỗi và check dưới local và push lên thôi
Xanh lè rồi đúng không, nhìn là đã muốn merged thôi =))
CD
Phần trên là setup CI ok rồi đúng không, sau khi check CI tất cả ok thì auto deploy thôi, =)) mình thấy cái phần CD của thằng này khá là ổn, trong file config mình có thể tùy biến được rất nhiều thứ, như CI pass thì phải approved mới được deploy lên môi trường Development, rồi có phần require chạy lần lượt, Deploy xong môi trường này mới được deploy môi trường khác,
Giao diện để mình có thể hình dung được cách tiến trình của nó cũng khá trực quan .
Ví dụ như 1 quy trình như này
Khi mọi người setup xong khi Circle build nó sẽ hiện rất trực quan và mỗi 1 job sẽ là 1 bản build
OK trên lý thuyết là thế , còn lại thì mình deploy cùng lắm là 1 – 2 môi trường thôi, như bài này thì mình chỉ auto deploy lên môi trường development thôi nhé
Để làm được đến đoạn này thì mọi người phải có
- 1 Server đã setup đầy đủ để có thể chạy được project laravel rồi nhé
- Đã config được deployer và đã deploy được lên server này.
Nếu ai chưa làm được thì thử làm qua 3 bài này đã nhé
Ai đã từng setup server thì để biết 1 cách để có thể truy cập vào server thì có thể thông qua ssh-key đúng không, thằng Circle này cũng vậy, muốn auto deploy được thì mình phải cho phép thằng CircleCI này ssh vào được server đúng không,
thế thì mình làm như sau nhé
OK sau khi đã có server, và server đã được gen ssh nhé,
mọi người copy cái private key ở thư mục ~/.ssh/id_rsa và paste cái này vào SSH Permission trong phần setting của project trên CircleCI nhé
Nhém cái hostname và privakey của mn vào đây nhé, nhấn ok là xong
Của mình là private nên ứ show lên cho mn xem đâu hehe, Sau khi đã add ok thì mình chỉnh lại file config.yml tý nhé
1 2 3 4 5 6 7 8 9 | deploy_development<span class="token punctuation">:</span> docker<span class="token punctuation">:</span> <span class="token operator">-</span> image<span class="token punctuation">:</span> framgiaciteam<span class="token operator">/</span>deb<span class="token operator">-</span>deploy<span class="token punctuation">:</span><span class="token number">7.3</span> steps<span class="token punctuation">:</span> <span class="token operator">-</span> checkout <span class="token operator">-</span> run<span class="token punctuation">:</span> name<span class="token punctuation">:</span> Deploy Develop to development command<span class="token punctuation">:</span> dep deploy <span class="token operator">-</span>vv |
Thêm 1 phần deploy development vào phần job nhé, trong này sẽ có 1 cái image framgiaciteam/deb-deploy:7.3 , cái này mình cứ dùng của Sun, mn rảnh có thể tự build cái image make by me cho sịn sò cũng được , Và cuối dùng chạy câu lệnh deployer thôi
1 2 | command: dep deploy -vv |
Ok thế là ổn rồi, còn deployer config như nào thì mn xem ở bài mà mình đã note ở trên nhé.
Thằng Cicle này nó có 1 chức năng là chạy theo workflows, ở cái workflow này mình muốn thằng nào chạy thì nó chạy, hay là đặt điều kiện là thằng này pass thằng kia mới được chạy khá là hay,
1 2 3 4 5 6 7 8 9 10 11 12 | workflows<span class="token punctuation">:</span> version<span class="token punctuation">:</span> <span class="token number">2</span> build<span class="token operator">-</span><span class="token keyword">and</span><span class="token operator">-</span>deploy<span class="token punctuation">:</span> jobs<span class="token punctuation">:</span> <span class="token operator">-</span> build_and_test <span class="token operator">-</span> deploy_development<span class="token punctuation">:</span> requires<span class="token punctuation">:</span> <span class="token operator">-</span> build_and_test filters<span class="token punctuation">:</span> branches<span class="token punctuation">:</span> only<span class="token punctuation">:</span> develop |
Qua đoạn này thì dân chơi nhìn phát đoán ngay là làm gì đúng không, đó là ta sẽ chạy job
chạy thằng build_and_test => deploy_development (Yêu cầu thằng build_and_test phải pass tất cả rồi mới được chạy thằng deploy, và ở thằng deploy này chỉ chạy khi code được update ở nhánh develop)
. OK ngon hàng, đẩy code lên nào , mọi người để lên cái nhánh mn đang code, chờ nó pass hết thì merged vào develop nhé.
Khi merged vào thì nó sẽ có 1 bản build
Bản build này chỉ là để check convention và chạy Test thôi
Khi bản build đấy ngon thì nó sẽ chạy job auto deploy
Mọi người click vào chỗ running kia thì sẽ nhìn thấy 2 cái job của mn đã config sẵn
Thế là xong. chờ bản build kia chạy pass là ngon ăn thôi, làm cốc cafe để tận hưởng nào.
Ở thằng này mình thích nhất là đoạn approved, ví dụ như auto deploy lên production hoặc là một môi trường nào đấy mà cần phải suy nghĩ chẳng hạn thì chức năng này khá hợp lý
Ta chỉ cần thêm đoạn code này vào file config thôi
1 2 3 4 5 6 | <span class="token operator">-</span> build_and_test <span class="token operator">-</span> hold_for_approval<span class="token punctuation">:</span> type<span class="token punctuation">:</span> approval requires<span class="token punctuation">:</span> <span class="token operator">-</span> build_and_test |
Rồi push lên để cảm nhận nhé: Lên xem bản build nó sẽ như thế này
Chờ build test xong, pass thì nhảy sang job, chờ mình appprove, nếu mình chọn approved thì mới được sang job deploy hehe
Lên hình và cảm nhận nhé
Notification
Phần notification thì nó support slack chứ chưa có chatwork nên mình ko config nữa, mọi người thử xem thế nào nhé.
Kết Luận
Ok bài này của mình đến đây thôi, ai mà đọc đến đây thì mn cũng khá là kiên trì đấy ạ, vì để có thể thao tác được bài này , có thể setup được ngon lành thì kiến thức của các bạn cũng phải thuộc dạng hầm hố rồi =))
Đợt này mình đang làm series về CI/CD . bài sau mình làm 1 bài về autodeploy với github action nhé, 1 công cụ của github cũng mới phát triển nhé