Ở bài trước [Test tự động với CircleCI và thông báo kết quả lên Slack] , ta đã tìm hiểu về khả năng tự động test và thông báo kết quả lên Slack.
Trong bài này, ta sẽ tiếp tục tìm hiểu về
- Workflows
- Deploy
với CricleCI
Workflows
Đầu tiên là Workflows
.
Ở ví dụ bài trước, ta đã gộp rất nhiều công việc trong 1 job build như sau:
- Checkout code
- Download and cache dependencies
- Build
- Test
# Java Maven CircleCI 2.0 configuration file
#
# Check https://circleci.com/docs/2.0/language-java/ for more details
#
version: 2
jobs:
build:
docker:
# specify the version you desire here
- image: circleci/openjdk:8-jdk
# Specify service dependencies here if necessary
# CircleCI maintains a library of pre-built images
# documented at https://circleci.com/docs/2.0/circleci-images/
# - image: circleci/postgres:9.4
working_directory: ~/cricle-test
environment:
# Customize the JVM maximum heap limit
MAVEN_OPTS: -Xmx3200m
steps:
- checkout
# Download and cache dependencies
- restore_cache:
keys:
- cricle-test-{{ checksum "pom.xml" }}
# fallback to using the latest cache if no exact match is found
- cricle-test-
- run: mvn dependency:go-offline
- save_cache:
paths:
- ~/.m2
key: cricle-test-{{ checksum "pom.xml" }}
# run tests!
- run: mvn integration-test
- store_test_results:
path: target/junit
Nhìn qua thì ta thấy có vẻ ổn? Tuy nhiên, để tăng tốc độ phát triển phần mềm thông qua việc CI phản hồi nhanh hơn, thời gian chạy ngắn hơn, và sử dụng hiệu quả hơn các nguồn lực, thì Workflows
sẽ giúp ích rất lớn trong việc định nghĩa các job và thứ tự chạy của chúng.
Workflows là gì?
Workflows
là tập hợp các quy tắc định nghĩa tập hợp các công việc và trật tự chạy của chúng, nhằm rút ngắn vòng lặp phản hồi.
Ví dụ, nếu có một công việc trong workflows
của bạn không thành công, bạn sẽ biết ngay chỗ lỗi theo thời gian thực và bạn chỉ phải chạy lại công việc không thành công. Thay vì, lãng phí thời gian và tài nguyên để chờ toàn bộ công việc build thất bại hoặc phải chạy lại toàn bộ công việc.
Workflows Configuration
Với ví dụ ở trên, ta sẽ tạo workflows với 4 jobs, thay vì gộp tất cả vào 1 job như trước
- Checkout code
- Download and cache dependencies
- Build
- Test
# Java Maven CircleCI 2.0 configuration file
#
# Check https://circleci.com/docs/2.0/language-java/ for more details
#
version: 2
jobs:
checkout_code: # job1
docker:
- image: circleci/openjdk:8-jdk
working_directory: ~/circleci-demo-workflows
steps:
- checkout
# Lưu thư file, thư mục để sử dụng ở job khác trong workflows
- persist_to_workspace:
root: /home/circleci
paths: circleci-demo-workflows/*
get_dependencies: # job2
docker:
- image: circleci/openjdk:8-jdk
working_directory: ~/circleci-demo-workflows
steps:
# Lấy lại file đã lưu ở job1
- attach_workspace:
at: ~/
- restore_cache:
keys:
- cricle-test-{{ checksum "pom.xml" }}
# fallback to using the latest cache if no exact match is found
- cricle-test-
- run: mvn dependency:go-offline
- save_cache:
paths:
- ~/.m2
key: cricle-test-{{ checksum "pom.xml" }}
build: # job3
docker:
- image: circleci/openjdk:8-jdk
working_directory: ~/circleci-demo-workflows
environment:
MAVEN_OPTS: -Xmx3200m
steps:
# Lấy lại file đã lưu ở job1
- attach_workspace:
at: ~/
- run: mvn compile
test: #job4
docker:
- image: circleci/openjdk:8-jdk
environment:
MAVEN_OPTS: -Xmx3200m
working_directory: ~/circleci-demo-workflows
steps:
# Lấy lại file đã lưu ở job1
- attach_workspace:
at: ~/
# run tests!
- run: |
mvn integration-test
mkdir target/junit
- store_test_results:
path: target/junit
workflows:
version: 2
build_and_test:
jobs:
- checkout_code
- get_dependencies: # get_dependencies chạy sau checkout_code
requires:
- checkout_code
- build: # xong get_dependencies mới build
requires:
- get_dependencies
- test: # xong get_dependencies mới test
requires:
- get_dependencies
Ở đây ta kết hợp giữa các job tuần tự và song song
- Tuần tự: thực hiện xong
checkout_code
rồi mới thực hiệnget_dependencies
, sau đó mới thực hiệnbuild
vàtest
- Song song: Sau khi xong
get_dependencies
thìbuild
vàtest
có thể chạy đồng thời
Ví dụ, nếu get_dependencies
lỗi thì build và test sẽ không được chạy
build
và test
có thể chạy đồng thời như sau
Kết quả
Với trường hợp chạy lỗi 1 job, ta có thể chọn chỉ chạy lại job bị fail (tuy nhiên trong ví dụ của bài viết, do fail test nên ta phải sửa lại code, và các bước checkout_code cũng như get_dependencies sẽ phải chạy lại)
Ngoài ra, còn có rất nhiều config khác như
- Giữ 1 job pending và chỉ chạy khi được approval job bằng tay. Ví dụ, với job deploy, sau khi test xong, workflow sẽ chờ ta đồng ý có deploy hay không, chứ không tự chạy ngay.
- Ta cũng có thể thiết lập lịch chạy cho workflow bằng cron
- Hay set Branch-Level cho job
- ...
Có thể tìm hiểu chi tiết thêm các thiết lập và 1 số vấn đề hay gặp với workflows tại [CricleCI - Orchestrating Workflows]
Deploy
Tiếp theo ta sẽ tìm hiểu về việc deploy tự động với CricleCI.
CricleCI có thể deploy với các server như
- AWS
- Azure
- Heroku
- Google Cloud
- Firebase
Việc đăng ký tài khoản free với Heroku khá đơn giản, nên ta sẽ thử với Heroku như sau
- Tự động deploy 1 Java App sau khi build và test thành công, chỉ với branch master với các bước như sau
- Checkout code
- Download and cache dependencies
- Build
- Test
- Deploy app lên server Heroku
- Thử chức năng Holding a Workflow for a Manual Approval cho việc deploy
Deploy lên Heroku
Để deploy 1 app (trong ví dụ là Java App) lên server Heroku ta cần phải setting các bước như sau:
Bước 0: Cài Heroku CLI và tạo app trên Heroku
minhnc@minhnc:~/vnlab/cricleci-test (master)$ heroku create
Creating app... done, ⬢ fathomless-inlet-82408
https://fathomless-inlet-82408.herokuapp.com/ | https://git.heroku.com/fathomless-inlet-82408.git
minhnc@minhnc:~/vnlab/cricleci-test (master)$
minhnc@minhnc:~/vnlab/cricleci-test (master)$ git push heroku master
Với https://fathomless-inlet-82408.herokuapp.com/
là page của app. Và fathomless-inlet-82408
là tên app ta sẽ dùng đến ở thiết lập ở phía sau
Bước 1: Tạo script setup-heroku.sh
cài đặt những thứ cần thiết trên CircleCI để deploy với Heroku trong thư mục .circleci
#!/bin/bash
git remote add heroku https://git.heroku.com/fathomless-inlet-82408.git
wget https://cli-assets.heroku.com/branches/stable/heroku-linux-amd64.tar.gz
sudo mkdir -p /usr/local/lib /usr/local/bin
sudo tar -xvzf heroku-linux-amd64.tar.gz -C /usr/local/lib
sudo ln -s /usr/local/lib/heroku/bin/heroku /usr/local/bin/heroku
cat > ~/.netrc << EOF
machine api.heroku.com
login $HEROKU_LOGIN
password $HEROKU_API_KEY
machine git.heroku.com
login $HEROKU_LOGIN
password $HEROKU_API_KEY
EOF
mkdir ~/.ssh
touch ~/.ssh/config
cat >> ~/.ssh/config << EOF
VerifyHostKeyDNS yes
StrictHostKeyChecking no
EOF
Bước 2 : Tạo biến môi trường HEROKU_LOGIN
và HEROKU_API_KEY
sử dụng trong script trên ở trang Project > Settings > Environment Variables
Để lấy Heroku API key ta dùng lệnh sau
heroku auth:token
Bước 3: Tạo SSH key (without a passphrase) để connect tới Heroku từ CricleCI ở mục SSH Permissions với hostname là git.heroku.com
minhnc@minhnc:~/vnlab$ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/minhnc/.ssh/id_rsa): /Users/minhnc/vnlab/id_rsa
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /Users/minhnc/vnlab/id_rsa.
Your public key has been saved in /Users/minhnc/vnlab/id_rsa.pub.
The key fingerprint is:
SHA256:oeVHB5BCF/HZFMqYbZLfIJ0EFY7xoLaDTa5iOrs8wm4 minhnc@minhnc.local
The key's randomart image is:
+---[RSA 2048]----+
| .. OB=.o. |
| .o./ B |
| +.X # o |
| * = B + |
| . * S o . |
| . . . |
|. o . |
|+E . |
|B*. |
+----[SHA256]-----+
Chú ý ghi lại Fingerprint
để phần setting sau dùng.
Bước 4: Thêm public key vào Heroku ở màn hình sau https://dashboard.heroku.com/account.
Bước 5: Chỉnh sửa config.yml
như sau
...
deploy: # Job mới
docker:
- image: circleci/openjdk:8-jdk
working_directory: ~/circleci-demo-workflows
steps:
- attach_workspace:
at: ~/
- run:
name: Run setup script
command: bash .circleci/setup-heroku.sh
- add_ssh_keys:
fingerprints:
- "a1:5d:48:05:92:d2:b4:82:87:41:46:b0:8c:29:3b:46"
- run:
name: Deploy Master to Heroku
command: |
git push --force git@heroku.com:fathomless-inlet-82408.git HEAD:refs/heads/master
heroku run python manage.py deploy
heroku restart
workflows:
version: 2
build_and_test:
jobs:
- checkout_code
- get_dependencies: # get_dependencies chạy sau checkout_code
requires:
- checkout_code
- build: # xong get_dependencies mới build
requires:
- get_dependencies
- test: # xong get_dependencies mới test
requires:
- get_dependencies
- deploy:
requires:
- build
- test
filters:
branches:
only: master
Ta thêm job deploy như trên
- Đầu tiên thực hiện script
.circleci/setup-heroku.sh
để cài đặt những thứ cần thiết để deploy lên Heroku add_ssh_keys
dùngfingerprints
ở bước 3- Rồi deploy bằng các câu lệnh của Heroku
- Ở
workflows
ta thiết lập việc deploy chỉ được thực hiện khibuild
vàtest
thành công với filter là branch master
Ta có kết quả deploy thành công như sau (Do code thử nghiệm không có nội dung nên ta không thể xem app từ page của app ở bước 0)
Với trường hợp test
bị lỗi, deploy
sẽ không được chạy như hình
Manual Approval Deploy
Như đã giới thiệu ở phần Workflows, ta có thể thiết lập để 1 job sẽ chỉ chạy khi ta Approval bằng tay trên màn hình CricleCI.
Ở đây ta sẽ thử nghiệm với job deploy
- deploy:
type: approval
requires:
- build
- test
filters:
branches:
only: master
Sau khi test
và build
chạy xong, job deploy
sẽ không chạy mà ở trạng thái hold như sau
Khi click vào deploy
sẽ xuất hiện popup cho phép ta thực hiện deploy
Kết luận
CricleCI là một công cụ mạnh mẽ, đang được rất nhiều công ty lớn sử dụng. Kết hợp với bài trước [Test tự động với CircleCI và thông báo kết quả lên Slack], cùng với những tiện ích Workflows và deploy trong bài này, mong sẽ giúp mọi người có thể tích hợp CricleCI vào dự án của mình 1 cách nhanh chóng dễ dàng, nhằm nâng cao chất lượng sản phẩm và rút ngắn thời gian phát triển sản phẩm.
Các setting trong bài viết mọi người có thể tham khảo ở trên GitHub: https://github.com/minhnc91/cricleci-test