CI/CD là gì? – Đã một thời gian kể từ bài viết trước đó và bây giờ mới có thời gian ngồi viết tiếp. Dạo gần đây, tôi thường release các dự án outsource nên cũng thường xuyên làm documentation và mở các dự án mới, việc setup CI/CD trở thành điều thường xuyên và đơn giản hơn. Tôi thấy kiến thức này hữu ích nên hôm nay tôi sẽ chia sẻ quy trình CI/CD mà chúng tôi á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ì?
Trong cộng đồng lập trình, có nhiều định nghĩa khác nhau về CI/CD. Tôi sẽ sử dụng cách định nghĩa của mình để mọi người hiểu dễ dàng nhất. CI/CD là một cặp công việc bao gồm CI (Continuous Integration) và CD (Continuous Delivery). Nghĩa là quá trình tích hợp thường xuyên và nhanh chóng hơn khi code cũng như thường xuyên cập nhật phiên bản mới.
Tại sao phải quan tâm đến CI/CD?
Trong thời đại hiện 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 là phải nhanh. Nếu một tính năng mất 2, 3 tháng mới release thì dẫn đến nhiều hệ lụy như không phù hợp với nhu cầu của khách hàng, hoặc đối thủ đã ra mắt trước, mất đ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 tốc độ là ưu tiên hàng đầu 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 do công việc thủ công khá nhiều và mỗi lần release cũng phải huy động một số người không nhỏ để cập nhật một thay đổi nhỏ nhất. Vì vậy, xu hướng CI/CD giúp tiết kiệm thời gian và nguồn lực của quá trình release.
Quy trình CI/CD tham khảo
Có rất nhiều quy trình và công cụ khác nhau để áp dụng CI/CD vào dự án. Nội dung của bài viết này sẽ dựa trên kinh nghiệm triển khai các dự án của chúng tôi cũng như đặc thù là các hệ thống web viết bằng PHP. Đây là các bước thông thường trong quá trình release tính năng trong một dự án thuộc hệ thống Teamcrop:
- Bước 1: [Manual] Khởi tạo repository và có branch mặc định là master và dev trên Gitlab 9.
- Bước 2: [Manual] Các coder sẽ push code tính năng lên branch dev (trừ owner).
- Bước 3: [Auto] Hệ thống tự động thực hiện kiểm tra mã nguồn, nếu thành công thì tự động triển khai (rsync) code lên server beta.
- Bước 4: [Manual] Tester/QA kiểm tra mã nguồn trên server beta và xác nhận mọi thứ ổn định.
- Bước 5: [Manual] Coder hoặc owner tạo Merge Request và merge từ branch dev sang branch master.
- Bước 6: [Manual] Owner chấp nhận Merge Request.
- Bước 7: [Auto] Hệ thống tự động thực hiện kiểm tra mã nguồn, nếu thành công sẽ cho phép triển khai lên production server.
- Bước 8: [Manual] Owner xác nhận là Merge Request đúng, kiểm tra OK. Tiến hành triển khai thay đổi lên môi trường production.
- Bước 9: [Manual] Tester/QA kiểm tra mã nguồn trên môi trường production và xác nhận mọi thứ ổn định. Nếu không ổn định, Owner có thể triển khai phiên bản master trước đó để rollback hệ thống về trạng thái ổn định trước đó.
- Bước 10: [Manual] Nến nhang và hy vọng khách hàng không phàn nàn hoặc gửi email.
Áp dụng CI/CD với Gitlab 9
Trong phạm vi bài viết này, tôi sẽ hướng dẫn cài đặt Gitlab 9 để quản lý mã nguồn và sử dụng công nghệ Git. Điều kiện tiên quyết để cài đặt là sẵn có Docker trên máy chủ. Nếu bạn chưa cài đặt Docker trên máy chủ, bạn có thể tham khảo các bài viết và tìm hiểu thêm về Docker trên bloghoctap và Google.
Quản lý mã nguồn bằng Gitlab
Về phần này, tôi sẽ không đi quá chi tiết vì đây là hệ thống quản lý mã nguồn giống như các nền tảng khác (ví dụ như GitHub). Bạn có thể tìm hiểu thêm về Git và Gitlab để các team có thể cùng làm việc và quản lý mã nguồn trên Gitlab.
CI/CD với Gitlab CI
Thông thường, các hệ thống quản lý mã nguồn không kèm theo tính năng CI/CD. Nếu bạn muốn triển khai CI/CD, bạn phải liên kết mã nguồn với hệ thống CI/CD và cấu hình phân quyền để hệ thống có thể lấy mã nguồn từ repository. Trước khi Gitlab ra mắt tính năng Gitlab CI, tôi đã sử dụng Jenkins cho việc này. Tuy nhiên, sau khi sử dụng tính năng Gitlab CI, tôi đã chia tay Jenkins và chuyển sang Gitlab CI, và kết quả là một cặp đôi hoàn hảo. Mã nguồn được lưu trữ trên Gitlab và có thể cài đặt CI/CD để kiểm tra và triển khai code tự động.
Đối với những người mới tiếp xúc với Gitlab CI, có thể thấy khá khó hiểu và phức tạp vì cần setup nhiều thứ. Sau một số khó khăn, tôi đã hiểu cách Gitlab CI hoạt động và giờ chia sẻ với mọi người để áp dụng cho quy trình công việc của mình.
Để sử dụng Gitlab CI, bạn cần hai thành phần sau: file .gitlab-ci.yml
nằm ở thư mục gốc của dự án và Gitlab Runner.
File .gitlab-ci.yml là gì? Đồng thời, Gitlab không có cơ chế CI cho dự án của bạn nếu không có file .gitlab-ci.yml nằm ở thư mục gốc. File này phải có định dạng và hợp lệ để hoạt động, nếu không, khi bạn push mã nguồn lên, Gitlab sẽ báo lỗi định dạng nội dung file cấu hình không hợp lệ. Bạn có thể tham khảo cú pháp của cấu hình này tại https://docs.gitlab.com/ce/ci/yaml/.
Trong file này, bạn có thể khai báo các phần để chạy test, khi test thì thực hiện lệnh gì (như chạy linter kiểm tra cú pháp, chạy PHPUnit test…), test xong thì triển khai code đi đâu (beta, production) với các lệnh tương ứng.
Gitlab Runner là gì? Gitlab Runner là thành phần cực kỳ quan trọng trong quy trình Gitlab CI. Nếu không có Runner, sẽ không có lệnh test và deploy nào được thực thi. Runner có nhiều loại, phân biệt dựa trên executor, mà bạn chọn khi khởi tạo Runner. Executor sẽ quyết định môi trường thực thi các lệnh trong file cấu hình. Bạn có thể tìm hiểu thêm về các executor và cách cài đặt, cấu hình chúng tại https://docs.gitlab.com/runner/executors/.
Do máy chủ đã có Docker sẵn, nên tôi chỉ sử dụng executor Loại Docker. Dưới đây là lệnh Docker để khởi chạy Gitlab Runner:
docker run -d --name gitlab-runner --restart always
-v /srv/gitlab-runner/config:/etc/gitlab-runner
-v /var/run/docker.sock:/var/run/docker.sock
gitlab/gitlab-runner:latest
Sau khi bạn khởi chạy container, bạn có thể truy cập vào nó bằng địa chỉ IP hoặc tên miền (đã cấu hình DNS). Ví dụ: http://code.teamcrop.com:8080 để truy cập Gitlab 9, tài khoản mặc định là root
.
Việc cài đặt này không phức tạp, bạn có thể tham khảo thêm trên trang chủ của Gitlab.com.
Khai báo biến để sử dụng trong các lệnh
Trong một số trường hợp, bạn cần khai báo biến để sử dụng trong các lệnh của Runner. Có 3 cách để cấu hình biến:
- Cấu hình trực tiếp trong file .gitlab-ci.yml.
- Cấu hình trong dự án. Vào Settings // CI/CD Pipelines, phần Secret variables (xem hình).
- Cấu hình trong file config của Runner.
Bạn có thể nhớ rằng khi tạo Runner, bạn đã chỉ định một thư mục chứa cấu hình chung cho Runner. Trong thư mục này sẽ có file config.toml. Bạn có thể khai báo biến trong cấu hình của từng Runner. Cấu hình ở đây có lợi thế là mỗi Runner 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 cho từng dự án.
Ví dụ về file .gitlab-ci.yml
Dưới đây là một ví dụ về file cấu hình của một dự án trong hệ thống Microservices của Teamcrop:
before_script:
- export "PATH=$PATH:/vendor/bin"
- 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
- eval $(ssh-agent -s)
- mkdir -p ~/.ssh
- '[[ -f /.dockerenv ]] && echo -e "Host * StrictHostKeyChecking non" > ~/.ssh/config'
variables:
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
Trong ví dụ trên, phần test làm ba việc:
- Chạy linter để đảm bảo mã nguồn không có lỗi cú pháp (phplint).
- Kiểm tra mã nguồn theo chuẩn PSR2.
- Chạy PHPUnit.
Về phần triển khai, có cấu hình hai tác vụ là triển khai dev và production. Tác vụ dev triển khai tự động từ branch dev. Còn tác vụ production triển khai từ branch master, nhưng với chế độ triển khai bằng tay, tức là cần nhấn để triển khai.
Trong quá trình triển khai mã nguồn, tôi sử dụng rsync để đồng bộ mã nguồn từ repository đến server. Bạn sẽ thấy cú pháp giống nhau, chỉ khác là cấu hình đồng bộ mã nguồn đến đâu, với người dùng nào và khóa riêng tư nào.
Do cú pháp dòng lệnh, tôi hiểu cần sử dụng khóa riêng tư để đồng bộ mã nguồn thông qua rsync. Vì vậy, trong dự án tôi đã cấu hình khóa riêng tư của người dùng trong bộ cấu hình. Và trên server đích (beta, production), tôi đã thêm khóa công khai vào tệp tin authorized_keys. Bạn có thể tìm hiểu thêm về cách thiết lập và tạo cặp khóa công khai / khóa riêng tư cho người dùng triển khai để hỗ trợ quá trình này tại liên kết https://www.digitalocean.com/community/tutorials/how-to-set-up-ssh-keys-2. Hoặc ngắn gọn, bạn chỉ cần thực hiện lệnh “ssh-keygen -t rsa -C “youremail@gmail.com” -b 4096″, nhập vài thông tin và bạn sẽ có khóa công khai (id_rsa.pub) để upload lên server (beta, production) và khóa riêng tư (id_rsa) để cấu hình biến môi trường.
— Dựa trên kinh nghiệm CI/CD cho hệ thống Teamcrop.com theo mô hình microservice với hơn 40 repository lớn nhỏ, hy vọng bài viết về CI/CD là gì sẽ giúp bạn cài đặt CI/CD cho dự án của mình và tăng tốc quá trình phát triển dự án. Nếu bài viết hữu ích, hãy chia sẻ để mọi người cùng trao đổi và giao lưu.
Đừng bỏ lỡ cơ hội tìm việc developer lương cao tại Topdev.