Computer Engineering/Python

Github Workflow에서 Python 패키지 설치 시간 단축하기

jordan.bae 2023. 5. 14. 23:56

GitHub에 설정된 다양한 Workflow들의 파이썬 패키지 설치 과정은 파이썬 개발자들에게는 지루한 기다림의 시간입니다. 특히, 배포를 위해 Test Job이 통과하기를 기다리는 것은 Python을 사용하는 개발자들에게 익숙한 상황 중 하나입니다. 이 과정에서 CI(Test, Linter)가 언제 끝나는지 기다리는 시간은 개발 작업의 흐름을 방해하고, 프로젝트의 배포 및 유지 관리를 느리게 만들 수 있습니다.

이 포스팅에서는 GitHub Actions의 Workflow 내에서 Python 패키지 설치 시간을 단축하는 방법을 소개하려고 합니다. 이 방법들은 GitHub에 종속되는 방법뿐만 아니라 독립적으로 사용할 수 있는 방법들도 포함되어 있습니다. 이 글에서는 총 3가지 방법을 소개하며, 이러한 방법들을 사용하여 실제로 시간이 얼마나 단축되는지도 함께 공유합니다

  1. Github Workflow Cache 이용하기
  2. 패키지관리 파일 분리하기 (ex. linter.txt 분리)
  3. 여러번 반복 설치되는 종속성 패키지의 버전을 고정하기 (pipdeptree 와 graphviz 이용해서 패키지간의 종속성 패키지 설치 시간 줄이기)

 

Github workflow cache 이용하기

Python에서 cache를 사용하기 위해서 setup-python@v4 를 사용합니다.
이 액션은 종속성을 캐싱하고 복원하는 기능을 내장하고 있습니다. 캐싱에는 toolkit/cache를 내부적으로 사용하지만 구성 설정이 적게 필요합니다. 지원되는 패키지 관리자는 pip, pipenv 및 poetry입니다. 캐시 입력은 선택 사항이며, 기본적으로 캐싱이 비활성화되어 있습니다.. cache-dependency-path 입력은 여러 종속성 파일을 사용하거나 서브디렉터리에 위치한 경우 또는 사용하려는 해시와 다른 파일을 사용해야 하는 경우에 사용됩니다.

# .github/workflows/linter.yml
jobs:
  check_linters:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v3
      - name: Set up Python
        uses: actions/setup-python@v4
        # here
        with: 
          python-version: 3.10.7
          cache: pip
          cache-dependency-path: requirements/lint.txt
      - run: python -m pip install -r requirements/lint.txt
      - name: check lint
        run: |
          make check

Cache 설정을 한 후에는 아래와 같이 cache를 확인할 수 있습니다.

 

Linter같은 경우 따로 필요한 패키지 파일 분리하기

기존에는 Test와 Linter가 같은 package requirements.txt를 이용했었습니다. requirements.txt에 포함된 패키지들이 크지 않았었는데 커지다 보니 분리하는 게 좋다고 판단되었습니다. 특히, lint 작업은 자주 수행되기 때문에 가볍게 유지하는 게 좋습니다.
linter용 라이브러리 파일만 아래와 같이 분리하여 관리합니다.

# requirements/lint.txt

black==22.10.0
flake8==5.0.4
isort==5.10.1

# .github/workflows/linter.yml
jobs:
  check_linters:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v3
      - name: Set up Python
        uses: actions/setup-python@v4
      - run: python -m pip install -r requirements/lint.txt
      - name: check lint
        run: |
          make check

 
 

종속성 관리 및 불필요한 패키지 제거

특정 package의 버전을 명시하지 않고, 해당 package를 depedency로 가지고 있는 여러 pakcage가 있는 경우에 여러번 설치되는 경우가 발생합니다. 이는 병렬로 여러 패키지를 설치하기 때문에 발생합니다. 패키지 버전을 고정하여 설치 시간과 호환성 문제를 줄일 수 있습니다. 버전을 명시적으로 지정하거나, 패키지 관리 도구의 잠금 파일을 사용하여 의존성 버전을 고정합니다.
ex) 여러 라이브러리에서 boto3를 종속성 패키지로 가지고 있었습니다.

requirementst.txt에 boto3의 버전을 명시적으로 추가한 후에는 여러 번 캐시로부터 설치하지 않게 돼서 속도가 빨라졌습니다.

 

pipdeptree 와 graphviz 사용하기

이와 같은 패키지를 확인하기 위해서 pipdeptree 와 graphviz 를 이용할 수 있습니다. 해당 라이브러리들을 이용해서 각 패키지의 종속성을 시각화해서 여러 번 설치가 될 수 있는 패키지들을 확인할 수 있습니다.
설치 (Mac 기준)

# Mac 
brew install graphviz

pip install pipdeptree
pip install graphviz
brew install graphviz 를 설치하지 않으면 dot을 실행할 수 없어서 에러가 발생. graphviz.backend.execute.ExecutableNotFound: failed to execute PosixPath('dot'), make sure the Graphviz executables are on your systems' PATH)

 
이미지로 확인

pipdeptree --graph-output pdf > dependency.pdf
이미지 결과물

 

Result

생각보다 Workflow의 캐시를 사용한다고 빨라지지 않았습니다. 네트워크로 설치하는 것과 캐시로 설치하는 것의 속도 차이가 크지 않았던 것 같습니다. linter.txt로 분리는 당연하게도 엄청나게 시간이 단축(3m -> 45s)되었습니다. 항상 커지면 분리가 필요한 것 같습니다. 마지막으로 종속성 패키지들을 고정하는것 또한 굉장히 효과가 좋았습니다. (모든 패키지를 고정하진 않았고, 여러 번 재설치되는 패키지들의 버전을 고정해 주었습니다.)
결과적으로 결과는 아래와 같습니다. 
AS-IS

TO-BE

반응형