Poetry 사용의 문제점과 pip-tools를 이용한 모듈 의존성 requirements.txt 에 고정하기 (lock)
레거시 코드에 대한 환경 관리 고민과 poetry 사용의 문제점 관리
회사 프로젝트에서 운영하고 있는 개별 프로젝트의 모듈 버전을 관리하는 것은 무척 중요한 일 중 하나다.
대학생 시절 갓 개발을 배우기 시작할 때, 공부한 기록을 남길 겸 가상환경 관리에 대한 글을 작업했었는데 (https://medium.com/p/8173992f28e2) 이 이후로도 당연히 파이썬을 사용해서 개발하는 프로젝트에 대해 개발자들이 많은 고민과 연구를 하면서 환경 관리하는 툴이 발전하기 시작했고, 근래에는 poetry를 사용한 파이썬 버전관리도 이뤄지는 것으로 보인다.
특히 나의 경우에도 회사에서 Flyte라는 Workflow Pipeline에 프로젝트를 올리기 위해 사용하는 버전들을 poetry로 관리하고 있고, 이 pyproject.toml 파일을 읽어들여서 CI가 실행된 후, flyte로 업로드 된다.
poetry를 처음 사용할 때, 장점으로 꼽았던 것은 프로젝트에서 사용하는 버전의 의존성 관리가 자체적으로 된다는 점이다.
예를 들어, 위에 코드에서 awscli 버전을 1.25.53을 설치할 때, 사용하는 python 버전과 다른 모듈들과의 전반적인 의존성을 체크하고, 최대한 상호 호환이 되는 버전들을 설치할 수 있도록 버전 리스트를 체크하면서 충돌이 발생 안하게끔 지원한다.
새로운 모듈을 설치하거나 기존에 사용하던 모듈의 버전 업그레이드를 할 때, 기존 코드가 늘 실행될 지, 안될 지 걱정할 필요 없이 poetry가 체크해주는 것이 가장 큰 장점이었다.
그리고 데이터 배치를 받거나 스케줄링 작업으로 늘 일정 주기로 실행되어야 하는 프로젝트의 경우, 일반적인 상황에서 버전 업그레이드를 자주할 일도 없어서 사용 중인 모듈의 버전을 고정하고 싶은 경우가 있다. (즉, version lock)
이 때, 지정해 줄 버전들을 pyproject.toml에 작성하고, poetry install을 하게 되면 poetry.lock 파일이 생성 되어서, 프로젝트를 도커 등에 빌드할 경우, 매번 의존성을 poetry가 리스트를 찾아가면서 설치할 필요도 없고, 버전 변경이 있으면 poetry update, 새로운 모듈 설치는 poetry install을 사용해서 반영해주면 된다.
그런데 문제는 이러한 poetry 장점을 잘 사용하지 못할 때 발생한다.
예를 들어, github action을 사용해서 CI를 실행해 도커 이미지를 빌드한다고 가정해보자.
이 때, 모듈 의존성은 pyproject.toml에 작성되어 있고 별도의 lock 파일은 존재하지 않는 상황이다.
이미지를 빌드하는 코드를 .github 폴더 내에 정의했다면, 아래 이미지와 같은 상황이 발생할 수 있다.
평상시에 3분 걸리는 도커 이미지 빌드 작업이 6시간이 걸렸다. 내부적으로 확인해보니 poetry에서 의존성 모듈 리스트를 찾기 위해 각 모듈별로 의존성 확인을 하다보니 도커 이미지 빌드 시간이 6시간이나 걸리고, 마지막에는 timeout으로 실패하는 대참사가 발생한다.
처음에는 네트워크나 서비스 장애 등을 의심해서 github status 페이지에서 github action을 확인했으나, 이 문제는 아니었고, 도커 빌드 로그를 하나씩 살펴보니 poetry에 의한 인재(人災
)였다.
문제는 해당 소스코드가 다른 팀에서 데이터를 받아오기 위해 작업한 배치 레포였기 때문에 pyproject의 version lock은 설정되어 있지 않았고 이 부분을 풀어주기 위해 삽질 아닌 삽질을 하게 되었다.
간단하게 poetry install을 사용해서 현재 pyproject.toml 파일 내에 정의 되어 있는 모듈들만 버전을 고정해주면 되지 않냐고 물어볼 수 있겠지만, 회사에서 사용하는 내부 모듈들의 버전이 올라가면서 함께 사용하는 버전들이 위의 프로젝트의 모듈들과 충돌을 일으켰다.
그래서 poetry로 버전 리스트를 찾으며 버전 고정을 하는 것은 해결이 오래 걸릴 것이라 팀에서 판단하여, pip-tools라는 것을 찾아서 급하게 pyproject에 정의 되어 있는 모듈들을 requirements.txt에 버전을 고정해서 빌드해주는 작업을 수행하게 되었다.
pip-compile -o requirements.txt pyproject.toml 커맨드를 입력하면 해당 pyproject.toml에 정의되어 있는 모듈들을 requirements.txt로 컴파일해서 옮겨준다.
상황에 맞는 적절한 모듈을 찾은 덕에 CI 에서 도커 이미지 생성은 정상적으로 잘 마무리 되었고, 버전을 반영해서 flyte에 작업을 올릴 수 있었다.
무척 식겁했던 상황이었고, 추석 전날에 갑자기 문제가 터진 상황이라 많이 당황했었는데, 다행이도 문제를 잘해결할 수 있었다.
생각해보면 프로젝트에 개발이 투입될 때, 코드 작성하는 일도 분명 중요하지만 버전 관리와 모듈 등 의존성 관리 및 배포 환경과 리소스 관리도 무시하지 못하는 것 같다.
간과하고 넘어가면 늘 기술 부채가 되어서 돌아오기 때문에 경험 많은 엔지니어일 수록 이런 프로젝트 주변 환경에 대한 시야가 넓지 않을까 싶다.
Ryan