Ở 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:

  1. Checkout code
  2. Download and cache dependencies
  3. Build
  4. 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

  1. Checkout code
  2. Download and cache dependencies
  3. Build
  4. 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ện get_dependencies, sau đó mới thực hiện buildtest
  • Song song: Sau khi xong get_dependencies thì buildtest 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

buildtest 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

  1. 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
  1. 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_LOGINHEROKU_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ùng fingerprints ở 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 khi buildtest 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 testbuild 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

Tham khảo

  1. CricleCI - Orchestrating Workflows
  2. CricleCI - Deployment
  3. CricleCI - Configuration Reference
  4. Getting Started on Heroku with Java