DevOps/CI CD

[CI/CD] CircleCI(version 2.1)로 통합, 배포 파이프라인 구성하기(+Android 파이프라인 작성하기)

seunghwaan 2022. 1. 19. 20:53
반응형

CircleCI란?


CircleCI는 VCS(Version Control System)와 연동하여 빌드, 테스트 및 배포를 자동화하는 데 도움이 되는 지속적인 통합(Continuous Integration)을 제공하는 플랫폼입니다. 

또한, 캐싱, 도커 레이어 캐싱, 리소스 클래스 등을 사용하여 매우 복잡한 파이프라인을 효율적으로 실행하도록 구성할 수 있습니다.

 

config.yml 설정키 (version 2.1 기준)


version

version은 CircleCI의 버젼을 나타냅니다. 2.1에서는 orbs, commands, executors를 추가로 사용할 수 있습니다.

version: 2.1

jobs:
	// ...
workflows:
	// ...

orbs

orbs는 반복되는 프로세스를 자동화하고 설정속도를 높이며 다른 툴(ex. slack, jira....)과 쉽게 통합할 수 있도록 도와줍니다.

version: 2.1
orbs:
    hello: circleci/hello-build@0.0.5
workflows:
    "Hello Workflow":
        jobs:
          - hello/hello-build

commands

commands는 job에서 사용할 명령어들을 Map으로 저장해 여러 job들에서 재사용 할 수 있도록 합니다.

version: 2.1

commands:
  sayhello:
    description: "A very simple command for demonstration purposes"
    parameters:
      to:
        type: string
        default: "Hello World"
    steps:
      - run: echo << parameters.to >>

executors

excutors는 job이 실행될 환경을 정의할 수 있습니다. excutors에서 선언한 executor를 통해서 여러 job들에서 재사용할 수 있습니다.

  • docker: executor가 사용할 docker를 정의합니다. 
  • machine: executor가 사용할 machine을 정의합니다. 
  • macos: executor가 사용할 macos을 정의합니다.
  • windows: executor가 사용할 windows을 정의합니다. (docker, machine, macos, windows 중 하나는 정의해야 합니다.)
  • resource_class : job에 할당 될 CPU와 RAM의 크기를 정의합니다.
  • working_directory: job이 실행 될 작업공간을 정의합니다(절대경로)
  • shell: command shell을 정의합니다.
  • environment: 환경변수 name과 value의 Map을 정의합니다.
version: 2.1

executors:
    android-executor:
        docker:
            - image: circleci/android:api-30
        resource_class: medium
        working_directory: ~/code
        
jobs:
  build:
    executor:
      name: android-executor
workflows:
	// ...

jobs

job은 workflow의 기본단위입니다. job은 더 작은단위인 step으로 이루어져 있습니다.

  • steps: job에서 커맨드를 실행하는 독립적인 단위입니다.
  • resource_class: job에 할당 될 CPU와 RAM의 크기를 정의합니다.
  • working_directory: step이 실행 될 작업공간을 정의합니다(절대경로)
  • environment: 환경변수 name과 value의 Map을 정의합니다.

steps

steps는 job에서 커맨드를 실행하는 독립적인 단위입니다.

  • checkout: 저장소를 checkout 합니다.
  • run: command를 통해 실행할 shell 명령어를 실행합니다.
  • steps: 수행할 동작들을 리스트로 나열합니다.
  • save_cache: 종속성을 캐싱합니다. 캐싱함으로써 파이프라인을 효율적으로 구성할 수 있고 빌드시간을 단축시킬 수 있습니다.
  • restore_cache: 캐시를 불러옵니다.
  • store_artifacts: 아티팩트들을 저장합니다.
  • store_test_results: 테스트 결과를 저장합니다.
  • persist_to_workspace: 다음 workflow에서 사용하고 있는 workspace를 저장하고 싶을 때 사용합니다.
  • attach_to_workspace: 저장했던 workspace를 불러옵니다.
jobs:
  build:
    working_directory: ~/canary-python
    environment:
      FOO: bar
    steps:
      - run:
          name: Running tests
          command: make test

Workflows

  • triggers: workflow가 실행될 트리거를 지정합니다. (cron식 혹은 branch에 따른 조건을 지정할 수 있습니다.)
  • jobs: workflow에서 실행될 job을 지정합니다. 
    • requires: job의 수행조건을 지정합니다.(지정하지않으면 job들은 병렬처리)
    • filters: branch 혹은 tag를 통해 job이 실행될 지 필터링합니다.
    • matrix: 여러 다른설정의 반복 테스트를 작업하고 싶을 때 사용합니다. 

 

workflows의 속성들을 조합하면 정말 다양한 방법으로 처리할 수 있습니다

하지만, 이번 포스트에서는 간단하게 순차처리, 병렬처리를 어떻게 수행할 것이냐만 정리하겠습니다!

Workflows 순차처리 예시

 

Workflow 순차처리

workflows:
  version: 2
  build-test-and-deploy:
    jobs:
      - build
      - test1:
          requires:
            - build
      - test2:
          requires:
            - test1
      - test3:
          requires:
            - test2
      - test4:
          requires:
            - test3
      - deploy:
          requires:
            - test4

 

Workflow 수행 순서

build -> test1 -> test2 -> test3 -> test4 -> deploy

 

Workflows 병렬처리 예시

 

Workflows 병렬처리

 

workflows:
  version: 2
  build_accept_deploy:
    jobs:
      - build
      - test_1:
          requires:
            - build
      - test_2:
          requires:
            - build
      - test_3:
          requires:
            - build
      - test_4:
          requires:
            - build
      - deploy:
          requires:
            - test_1
            - test_2
            - test_3
            - test_4

Workflow 수행 순서

build -> test1, test2, test3, test4 -> deploy

 

Project에 CircleCI 적용해보기(Android 기준)


1) version 설정

version: 2.1
// ...

2) orbs 설정

orbs를 지정해줍니다. 안드로이드 기준으로 최신 orbs는 2.0.0입니다. circleci/android@2.0.0를 지정해주겠습니다.

orbs:
  android: circleci/android@2.0.0
// ...

3) Excutors 설정

excutors를 지정해줍니다. 도커 이미지는 circleci에서 이미 빌드되어있는 이미지를 지정하겠습니다.

executors:
    android-executor:
        docker:
            - image: circleci/android:api-30
        resource_class: medium
// ...

4) build job 작성

checkout을 해준 후, 빌드를 해줍니다. 이 과정에서 캐싱을 통해 성능을 높이고, 빌드속도를 최적화 하도록 해줍니다.

commands:
  restore_gradle:
    steps:
      - run:
          command: |
            find . -name 'build.gradle' | sort | xargs cat |
            shasum | awk '{print $1}' > ./tmp_gradle_cache_seed
          name: Generate cache checksum
      - restore_cache:
          key: gradle-v1a-{{ arch }}-{{ checksum "./tmp_gradle_cache_seed" }}
          name: Restore gradle cache
      - restore_cache:
          name: Restore gradle properties
          key: gradle-properties-${CIRCLE_WORKFLOW_ID}_21
  save_gradle:
    steps:
      - save_cache:
          name: Save gradle cache
          key: gradle-v1a-{{ arch }}-{{ checksum "./tmp_gradle_cache_seed" }}
          paths:
          - ~/.gradle/caches
          - ~/.gradle/wrapper
      - save_cache:
          key: gradle-properties-${CIRCLE_WORKFLOW_ID}_21
          name: Save gradle properties
          paths:
            - ~/.gradle/gradle.properties

jobs:
  build:
    executor:
      name: android-executor
    working_directory: ~/code

    steps:
      - checkout
      - restore_gradle
      - run:
          name: Assemble release build
          command: |
            ./gradlew assembleRelease
      - save_gradle
// ...

빌드 과정을 통해 나온 app bundle을 비롯한 artifacts들을 저장해줍니다.

그 뒤, persist_to_workspace를 통해 workspace를 저장해줍니다.

jobs:
  build:
  	// ...
    steps:
      // ...
      - run:
          name: Make App Bundle
          command: ./gradlew bundleRelease
      - store_artifacts:
          path: app/build/outputs/bundle/release
          destination: bundle
      - store_artifacts:
          path: app/build/outputs/mapping/release
          destination: mapping
      - store_artifacts:
          path: app/build/reports
          destination: reports
      - store_test_results:
          path: app/build/test-results
      - persist_to_workspace:
          root: .
          paths:
          - .

5) test job 작성

unit testui test를 진행해줍니다.

jobs:
  build:
   	// ...
  test:
    executor:
      name: android/android-machine
      resource-class: medium
      tag: 2021.10.1
    steps:
      - attach_workspace:
          at: .          
      - android/run-tests:
          test-command: ./gradlew lint testDebug --continue

      - android/start-emulator-and-run-tests:
          test-command: ./gradlew connectedDebugAndroidTest
          system-image: system-images;android-30;google_apis;x86
      - android/create-avd:
          avd-name: myavd
          system-image: system-images;android-29;default;x86
          install: true
      - android/start-emulator:
          avd-name: myavd
          no-window: true
          restore-gradle-cache-prefix: v1a
      - android/run-tests
      - android/save-gradle-cache:
          cache-prefix: v1a
          
// ...

5) deploy job 작성

fastlane을 통해 deploy 합니다.

jobs:
  build:
    // ...
  test:
    // ...
  deploy:
    executor:
      name: android-executor
    steps:
      - attach_workspace:
          at: .
      - run:
          name: Install fastlane
          command: bundle install
      - run:
          name: Execute fastlane
          command: bundle exec fastlane playstore

6) workflows 작성

build와 test는 순차처리로 진행을 하고, deploy는 master 브랜치만 동작하도록 해주었습니다.

workflows:
  version: 2
  build_deploy:
    jobs:
    - build
    - test:
       requires:
          - build
    - deploy:
        requires:
          - build
          - test
        filters:
          branches:
            only:
              - master

전체 코드

version: 2.1

orbs:
  android: circleci/android@2.0.0
  
executors:
    android-executor:
        docker:
            - image: circleci/android:api-30
        resource_class: large

commands:
  restore_gradle:
    steps:
      - run:
          command: |
            find . -name 'build.gradle' | sort | xargs cat |
            shasum | awk '{print $1}' > ./tmp_gradle_cache_seed
          name: Generate cache checksum
      - restore_cache:
          key: gradle-v1a-{{ arch }}-{{ checksum "./tmp_gradle_cache_seed" }}
          name: Restore gradle cache
      - restore_cache:
          name: Restore gradle properties
          key: gradle-properties-${CIRCLE_WORKFLOW_ID}_21
  save_gradle:
    steps:
      - save_cache:
          name: Save gradle cache
          key: gradle-v1a-{{ arch }}-{{ checksum "./tmp_gradle_cache_seed" }}
          paths:
          - ~/.gradle/caches
          - ~/.gradle/wrapper
      - save_cache:
          key: gradle-properties-${CIRCLE_WORKFLOW_ID}_21
          name: Save gradle properties
          paths:
            - ~/.gradle/gradle.properties
  
jobs:
  build:
    executor:
      name: android-executor
    working_directory: ~/code

    steps:
      - checkout
      - restore_gradle
      - run:
          name: Assemble release build
          command: |
            ./gradlew assembleRelease
      - save_gradle
      - store_artifacts:
          path: app/build/reports
          destination: reports
      - store_test_results:
          path: app/build/test-results
      - run:
          name: Make App Bundle
          command: ./gradlew bundleRelease
      - store_artifacts:
          path: app/build/outputs/bundle/release
          destination: bundle
      - store_artifacts:
          path: app/build/outputs/mapping/release
          destination: mapping
      - persist_to_workspace:
          root: .
          paths:
          - .

  test:
    executor:
      name: android/android-machine
      resource-class: medium
      tag: 2021.10.1
    steps:
      - attach_workspace:
          at: .
      - android/run-tests:
          test-command: ./gradlew lint testDebug --continue
      - android/start-emulator-and-run-tests:
          test-command: ./gradlew connectedDebugAndroidTest
          system-image: system-images;android-30;google_apis;x86
      - android/create-avd:
          avd-name: myavd
          system-image: system-images;android-29;default;x86
          install: true
      - android/start-emulator:
          avd-name: myavd
          no-window: true
          restore-gradle-cache-prefix: v1a
      - android/run-tests
      - android/save-gradle-cache:
          cache-prefix: v1a
  deploy:
    executor:
      name: android-executor
    steps:
      - attach_workspace:
          at: .
      - restore_gradle
      - run:
          name: Install fastlane
          command: bundle install
      - run:
          name: Execute fastlane
          command: bundle exec fastlane playstore

workflows:
  version: 2
  build_deploy:
    jobs:
    - build
    - test:
       requires:
          - build
    - deploy:
        requires:
          - build
          - test
        filters:
          branches:
            only:
              - master

 

 

반응형