CI/CD là gì? Triển khai CI/CD với Gitlab

CI/CD là gì? – Cũng hơn một tháng kể từ bài viết gần nhất, nay mới có thời gian ngồi viết lách tiếp. Dạo gần đây thường release các dự án outsource nên cũng hay làm documentation cũng như  mở các dự án mới nên việc setup CI/CD thường xuyên hơn và chân tay hơn. Thấy các kiến thức này hay nên hôm nay mình sẽ chia sẻ mọi người quy trình CI/CD bên mình áp dụng cho “đại dự án” Teamcrop cũng như các dự án outsourcing mà Moout thực hiện.

CI/CD là gì?

Bạn sẽ thấy có nhiều định nghĩa từ hai lúa cho đến hàn lâm cho khái niệm CI/CD. Mình sẽ dùng cách định nghĩa của mình để mọi người dễ hiểu CI/CD là gì theo cách thông thường nhất. CI/CD là một bộ đôi công việc, bao gồm CI (Continuous Integration)CD (Continuous Delivery), ý nói là quá trình tích hợp (integration) thường xuyên, nhanh chóng hơn khi code cũng như thường xuyên cập nhật phiên bản mới (delivery).

Tại sao phải quan tâm đến CI/CD?

Ngày nay, với xu hướng agile/lean dẫn đến việc phát triển tính năng là điều bình thường, quan trọng phải là thần thái, ý lộn, quan trọng là phải nhanh. Nếu một tính năng mà mất 2, 3 tháng mới release thì dẫn đến nhiều hệ lụy như làm không phù hợp nhu cầu khách hàng, hoặc đối thủ đã ra mắt trước đó, mất đi cái lợi thế dẫn đầu. Do đó, việc làm ra một sản phẩm, tính năng đòi hỏi thần tốc là ưu tiên số một hiện nay.

Bên cạnh đó, để nhanh chóng ra mắt một tính năng, phiên bản mới nếu theo cách cổ điển sẽ mất nhiều thời gian bởi công việc chân tay khá nhiều và mỗi lần release cũng huy động một cơ số người không nhỏ để cập nhật một thay đổi dù là nhỏ nhất. Bởi vậy, xu hướng CI/CD giúp cung cấp các framework, workflow giúp tiết kiệm thời gian, nguồn lực của quá trình release (delivery).

Quy trình CI/CD tham khảo

Có rất nhiều quy trình, công cụ khác nhau để ứng dụng CI/CD vào dự án. Nội dung của bài viết này dựa trên kinh nghiệm cho các dự án của mình triển khai cũng như đặc thù là các hệ thống web, và viết bằng PHP (PHP hoặc Python hoặc abc…không quá quan trọng trong ngữ cảnh chia sẻ về CI/CD).

– Bước 10: [Manual] Thắp nhang và hy vọng khách hàng không chửi rủa hoặc email complain.

Áp dụng CI/CD với Gitlab 9

Trong khuôn khổ bài viết này, mình sẽ hướng dẫn mọi người cài đặt Gitlab 9 để quản lý source code, và trên công nghệ Git. Đòi hỏi của tất cả setup này là trên server đã cài Docker, nếu các bạn chưa có docker trên server thì có thể tham khảo các bài viết về docker trên bloghoctap cũng như tìm kiếm thêm trên google.

CI/CD là gì

Đây là câu hỏi hay, bởi vì trước đó bên mình sử dụng Gitlab 8, tuy nhiên, do các hạn chế về UI/UX cũng như không tích hợp ngon lành vụ CI/CD nên khi bản 9 ra mắt, mình đã thử sử dụng và thấy bản 9 phù hợp hơn cho workflow nên quyết định dọn dẹp sang Gitlab 9.

CI/CD là gì

Bạn chạy câu lệnh sau để tạo một container chứa Gitlab 9.

docker run --detach 
--hostname chúng tôi 
--publish 8080:80 --publish 2222:22 
--name gitlab9 
--restart=always 
--volume /gitlab9/config:/etc/gitlab 
--volume /gitlab9/logs:/var/log/gitlab 
--volume /gitlab9/data:/var/opt/gitlab 
gitlab/gitlab-ce:9.0.3-ce.0

Nếu ai từng dùng docker sẽ hiểu ý nghĩa câu lệnh trên. Đơn giản là mình sử dụng image gitlab/gitlab-ce:9.0.3-ce.0. Có mount ra 3 thư mục bên ngoài máy ở thục mục /gitlab9 để lỡ có chuyện gì chỉ cần stop, remove container, khi chạy docker run lại thì không bị mất dữ liệu, source code. Câu lệnh trên có map 2 port là 8080 và 2222 tương ứng tới 2 port 80 và 22 trong container. Mình mapping port vậy bởi vì trên server dev này có rất nhiều service khác và đã chiếm port 80 và 22 (ssh ^^!).

Quản lý sourcecode bằng Gitlab

Về phần này thì mình không cần nói dài dòng, cũng như một hệ thống git thông thường (như github..), bạn có thể tìm hiểu thêm về git và Gitlab để team có thể cùng làm việc và quản lý sourcecode trên Gitlab.

CI/CD với Gitlab CI

Thông thường, các hệ thống quản lý sourcecode không kèm theo cơ chế CI/CD. Nếu bạn muốn triển khai thì buộc phải liên kết đến repository, phân quyền đủ kiểu để hệ thống đó có thể lấy source code từ respository. Trước đây bên mình sử dụng Jenkins cho việc này. Tuy nhiên, từ khi Gitlab ra mắt tính năng Gitlab CI, kèm theo sự chậm chạp, rắc rối và rề rề của Jenkins thì mình quyết định chia tay với Jenkins và đến với Gitlab CI luôn, và quả là một bộ đôi hoàn hảo. Code để ở Gitlab, rồi trong đó có cho cài đặt CI/CD để test và deploy code tự động.

Cũng như một số bạn mới lần đầu tiếp xúc với Gitlab CI, mình đã từng thấy nó khó hiểu và cao siêu vì setup tùm lum. Rồi setup xong lại không biết nó chạy thế nào, cơ chế deploy source code ra sao. Tuy nhiên, sau một vài “va chạm” đầy mồ hôi và nước mắt thì cũng nắm và hiểu được cách Gitlab CI vận hành, và nay chia sẻ cho mọi người để vận dụng cho workflow của mình.

Để dùng được Gitlab CI thì bạn cần có 2 thành phần sau: file `.gitlab-ci.yml` nằm ở thư mục gốc của dự án và Gitlab Runner.

File chúng tôi là gì?

Trong file này có gì? File này có một số section để khai báo như trước khi chạy test thì làm gì, khi test thì thực hiện lệnh gì (vd chạy linter check cú pháp, chạy PHPUnit test…), test xong rồi thì thực hiện deploy đi đâu (beta, production..) với câu lệnh gì (vd: rsync..). Tùy đặc thù ngôn ngữ lập trình, cách đóng gói của dự án mà sẽ có các lệnh tương ứng thực hiện.

Nếu bạn đi đến đây thì bạn đã đoán được là thực ra “cái thứ” thực thi các chỉ dẫn, câu lệnh trong file chúng tôi không phải là Gitlab Server (là cái container đang chạy gitlab 9 mình start ở trên), mà đó chính là Gitlab Runner. Wow! Welcome to matrix!

Gitlab Runner là gì?

Do đặc thù hệ thống đã có docker, nên bên mình chỉ sử dụng executor loại Docker mà thôi. Và bên dưới là câu lệnh docker để start một Gitlab Runner.

docker run -d --name gitlab-runner --restart always 
-v /srv/gitlab-runner/config:/etc/gitlab-runner 
-v chúng tôi 
gitlab/gitlab-runner:latest

Ở đây bạn sẽ thấy container này mount thư mục config ra ngoài, bởi vì mình muốn các cấu hình của runner không bị mất khi stop/remove container. Chỉ cần start lại là giữ được cấu hình. Ngoài ra, nó còn mount chúng tôi vào bên trong container, đây là cách để executor loại docker có thể tận dụng lệnh docker bên ngoài host để thực hiện lệnh tạo container phụ trong quá trình runner chạy (test, deploy).

Sau khi Runner đã được gán vào Gitlab Server, bạn có thể enable runner này cho một hoặc nhiều dự án trong Gitlab. Hình bên dưới minh họa việc gán Runner vào dự án trong phần cài đặt Pipeline của Gitlab 9.

Đến đây hầu như đã cấu hình xong. Dự án đã kích hoạt 1 runner, và dự án đã có file .gitlab-ci.yml. Từ bây giờ, mỗi lần code được đưa lên thì runner sẽ thực thi test cũng như deploy dựa trên các câu lệnh được khai báo trong file cấu hình.

Khai báo biến để dùng trong các câu lệnh

Trong một số trường hợp, bạn có thể khai báo biến để có thể dùng trong các lệnh của runner. Có 3 nơi có thể cấu hình biến:

2. Cấu hình trong dự án. Vào Settings // CI/CD Pipelines, phần Secret variables (xem hình)

3. Cấu hình bên trong file config của runner.

Bạn có nhớ lúc mình khởi tạo runner, có chỉ định một thư mục chứa config không, đây chính là nơi cấu hình chung cho runner này. Trong thư mục này sẽ có file là config.toml. Và bạn có thể gán biến trong cấu hình của từng runner. Cấu hình ở đây có một lợi thế là cứ runner này chạy sẽ nhận được biến đã cấu hình. Bạn không cần phải cấu hình nhiều lần ở từng dự án.

Ví dụ về một file .gitlab-ci.yml

Bên dưới là file cấu hình của một dự án trong hệ thống Microservices thuộc Teamcrop:

before_script:

- export "PATH=$PATH:/vendor/bin"
# Install ssh-agent if not already installed, it is required by Docker.
# (change apt-get to yum if you use a CentOS-based image)

# Run ssh-agent (inside the build environment)
- eval $(ssh-agent -s)

# For Docker builds disable host key checking. Be aware that by adding that
# you are suspectible to man-in-the-middle attacks.
# WARNING: Use this only with the Docker executor, if you use it with shell
# you will overwrite your user's SSH config.
- mkdir -p ~/.ssh

variables:

# Change this base on project name
DEPLOYMENT_FOLDER_NAME: "tc-file"

test:

image: voduytuan/gitlab-php-ci
script:
- bash ./ci/phplint.sh ./src/
- phpcs --config-set ignore_errors_on_exit 1
- phpcs --config-set ignore_warnings_on_exit 1
- phpcs --standard=PSR2 --ignore=./src/index.php --error-severity=1 --warning-severity=8 -w --colors ./src/
- phpunit --configuration ci/phpunit.xml

dev:

image: voduytuan/gitlab-php-ci
stage: deploy
script:
- ssh-add <(echo "$DEPLOYER_BETA_KEY")
- echo "Deploy to $DEPLOYMENT_FOLDER_NAME"
- rsync -avuz -e "ssh -p 22" --exclude-from="ci/deploy_exclude.txt" $CI_PROJECT_DIR/src/ $DEPLOYER_BETA_USER@$DEPLOYER_BETA_IP:/teamcrop/services/$DEPLOYMENT_FOLDER_NAME/src
only:
- dev

production:

image: voduytuan/gitlab-php-ci
stage: deploy
script:
- ssh-add <(echo "$DEPLOYER_PRODUCTION_KEY")
- echo "Deploy to $DEPLOYMENT_FOLDER_NAME"
- rsync -avuz -e "ssh -p 22" --exclude-from="ci/deploy_exclude.txt" $CI_PROJECT_DIR/src/ $DEPLOYER_PRODUCTION_USER@$DEPLOYER_PRODUCTION_IP:/teamcrop/services/$DEPLOYMENT_FOLDER_NAME/src
only:
- master
when: manual

– Chạy PHPUnit

Còn về phần deploy thì có cấu hình 2 task là deploy dev và production. Ở task dev thì auto và lấy code từ branch dev. Còn task production deploy từ branch master, tuy nhiên, có chế độ deploy manual, tức là nhấn thì mới deploy.

Về phần deploy source code thì sử dụng rsync để đẩy code từ repo sang server. Bạn sẽ thấy cú pháp giống nhau, chỉ khác là cấu hình đẩy đi đâu, với user nào và private key nào.

Đừng bỏ lỡ cơ hội tìm việc developer lên đến 3000 USD tại Topdev

TopDev via Võ Duy Tuấn