레넌의 개발 일기
Github Actions - Gradle 캐싱을 통한 CI 속도 향상 본문
CI 속도를 개선해야하는 이유
CI는 지속적 통합이라는 뜻으로, 개발을 진행하면서도 품질을 관리할 수 있도록 여러명이 하나의 코드에 대해서 수정을 진행해도 지속적으로 통합하면서 관리할 수 있음을 의미한다.
지속적으로 빌드와 테스트를 돌림으로써 통합되는 코드에 대해 문제가 없는지 확인하는 것이 CI의 중요한 요소 중 하나라고 생각한다. 따라서 빌드와 테스트에 대한 피드백 시간 또한 중요하다. 빠르게 피드백을 받게 된다면 불과 몇 초일지라도 개발자 조금이나마 편하게 개발을 진행할 수 있을 것이다.
기존의 Workflow
처음 공식 프로젝트에서 적용한 workflow 는 아래와 같다.
name: gongseek backend CI
# Workflow를 실행시키기 위한 이벤트 목록
# 모든 브랜치에 대한 PR이 열릴 때 마다
# backend 하위 패키지에 변경이 일어날 때만 작동
on:
pull_request:
branches:
- '*'
paths:
- backend/**
jobs:
# job의 이름
build:
# 실행될 환경
runs-on: ubuntu-22.04
# 작업을 수행하기 위한 기본 경로
defaults:
run:
working-directory: ./backend
steps:
# 소스코드로 체크아웃
- uses: actions/checkout@v2
# 서버에 자바 버전 설치
- name: Set up JDK 11
uses: actions/setup-java@v1
with:
java-version: 11
# Gradle 권한 변경
- name: Grant execute permission for gradlew
run: chmod +x ./gradlew
# 빌드 진행
- name: Test with Gradle
run: ./gradlew build
# 테스트 결과를 커멘트로 등록
- name: Register test results as comments in PR
uses: EnricoMi/publish-unit-test-result-action@v1
if: always()
with:
files: '**/build/test-results/test/TEST-*.xml'
# 테스트 실패 시, 코드라인에 체크 커멘트 등록
- name: If the test fails, register a Check comment in the failed code line
uses: mikepenz/action-junit-report@v3
if: failure()
with:
report_paths: '**/build/test-results/test/TEST-*.xml'
token: ${{ github.token }}
# 빌드 실패 시, 슬랙으로 알람
- name: If build fails, notify Slack
uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
author_name: 백엔드 빌드 실패 알림
fields: repo, message, commit, author, action, eventName, ref, workflow, job, took
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
if: failure()
Step 확인하기
각각의 Step들의 소요 시간을 확인해보았다. 예상대로 Gradle로 빌드/테스트를 돌리는 부분이 가장 많은 시간을 소요하고 있었다.
Test with Gradle Step을 확인해보니 매번 gradle을 다운받는 것을 확인할 수 있었다. 이는 Github Actions 특성상 매번 workflow를 새로운 가상 서버에서 실행하기 때문이었다. 이를 해결하기 위해 Gradle을 캐싱을 적용하였다.
캐싱 적용하기
name: gongseek backend CI
# Workflow를 실행시키기 위한 이벤트 목록
# 모든 브랜치에 대한 PR이 열릴 때 마다
# backend 하위 패키지에 변경이 일어날 때만 작동
on:
pull_request:
branches:
- '*'
paths:
- backend/**
jobs:
# job의 이름
build:
# 실행될 환경
runs-on: ubuntu-22.04
# 작업을 수행하기 위한 기본 경로
defaults:
run:
working-directory: ./backend
steps:
# 소스코드로 체크아웃
- uses: actions/checkout@v2
# 서버에 자바 버전 설치
- name: Set up JDK 11
uses: actions/setup-java@v1
with:
java-version: 11
# Gradle 캐싱
- name: Gradle Caching
uses: actions/cache@v3
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-
# Gradle 권한 변경
- name: Grant execute permission for gradlew
run: chmod +x ./gradlew
# 빌드 진행
- name: Test with Gradle
run: ./gradlew build
# 테스트 결과를 커멘트로 등록
- name: Register test results as comments in PR
uses: EnricoMi/publish-unit-test-result-action@v1
if: always()
with:
files: '**/build/test-results/test/TEST-*.xml'
# 테스트 실패 시, 코드라인에 체크 커멘트 등록
- name: If the test fails, register a Check comment in the failed code line
uses: mikepenz/action-junit-report@v3
if: failure()
with:
report_paths: '**/build/test-results/test/TEST-*.xml'
token: ${{ github.token }}
# 빌드 실패 시, 슬랙으로 알람
- name: If build fails, notify Slack
uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
author_name: 백엔드 빌드 실패 알림
fields: repo, message, commit, author, action, eventName, ref, workflow, job, took
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
if: failure()
캐싱을 적용한 코드는 위와 같다. 너무 길어서 잘 안보일 수 있으니 아래에서 자세히 살펴보도록 하자.
# Gradle 캐싱
- name: Gradle Caching
uses: actions/cache@v3
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-
- path : 캐시의 복원에 사용되는 runner 내 파일 경로다.
- key : 캐시를 저장, 복원에 사용되는 키. 여러 값들을 조합해서 512자 제한으로 생성할 수 있다.
- restore-keys : 내가 설정한 key로 cache miss가 발생할때 사용할 수 있는 후보군 키들이다.
Github 레포지토리의 Actions에 접근해보면 다음과 같이 Gradle 캐시가 되어있는 것을 확인할 수 있다.
중요한 점은 PR마다 Gradle 캐싱이 발생한다는 것이다. 따라서 PR을 올린 첫 순간에는 Gradle 캐시를 불러올 수 없다. 하지만 해당 PR의 코드리뷰를 통한 수정사항에 대해서는 캐싱을 통해 Gradle을 불러올 수 있다.
Test with Gradle Step의 수행 속도만 확인해 보면 대략 30초 정도 성능향상을 이루어낼 수 있었다.
참고
https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows
'DevOps' 카테고리의 다른 글
Jenkins PR 라벨별로 빌드 유발하기 (0) | 2022.11.13 |
---|---|
Github Actions 알아보기 (0) | 2022.11.10 |
Jenkins Host key verification failed (0) | 2022.11.02 |
Jenkins Pipeline 구축하기 - 프론트엔드편 (0) | 2022.10.26 |
Swap Memory로 메모리 문제 해결하기 (0) | 2022.10.26 |