14 min read

파이썬 가상 환경

파이썬을 다루다 보면 필연적으로 가상 환경 이야기를 듣게 된다. 그런데 거기에서 각종 툴이 나오기 시작하면 머리가 너무 아파진다. virtualenv - venv - pipenv 에, pyenv 는 또 무엇이며 conda 는 또 뭐고 여하튼 뭐가 너무 많고 혼란스럽다. 나중에 좀 찾아보기 위해 정리를 해 보자.

파이썬 가상 환경?

시작하기 전에 파이썬 가상 환경이 무엇인지 정의하고 넘어가자. 파이썬 공식 문서의 용어 설명을 보면 다음과 같이 정의되어 있다.

A cooperatively isolated runtime environment that allows Python users and applications to install and upgrade Python distribution packages without interfering with the behaviour of other Python applications running on the same system.
  • 상호 협조적으로 격리된 런타임 환경
  • 파이썬 사용자와 애플리케이션이 파이썬 배포 패키지를 설치하거나 업그레이드 할 수 있게 함
  • 이 과정에서 같은 시스템에 설치된 다른 파이썬 애플리케이션의 동작을 방해하지 않음

즉, 가상 환경이란 다른 가상 환경과 영향을 주고받지 않도록 격리된 파이썬 실행 환경을 의미한다. 실행 환경에는 1)특정 버전의 파이썬 실행 파일과 기본 라이브러리, 그리고 2)사용자 코드 또는 애플리케이션이 요구하는 특정 버전의 패키지 및 모듈이 포함된다.

물론 더 넓은 범위로는 시스템에 설치된 외부 시스템 라이브러리 등도 포함되겠지만, 여기까지 관리하려면 docker 같은 프로세스 수준 컨테이너를 사용해야 한다.

파이썬 가상 환경의 원리

파이썬 인터프리터를 실행할 때, 각종 패키지를 불러오는 경로는 sys.path 변수에 저장된다. 이 목록에는 기본으로 진입점 스크립트(.py 파일)의 경로와 PYTHONPATH 환경 변수에 지정된 경로, 그리고 파이썬 기본 라이브러리 경로와 패키지 디렉터리(site-packages 디렉터리) 경로를 포함하게 된다.

그렇다면 site-packages 디렉터리 경로를 적절히 변경해 주면 어떻게 될까. 예를 들어 A 프로젝트를 실행할 때에는 A/lib/site-packages 디렉터리를 사용하고, B 프로젝트를 실행할 때에는 B/lib/site-packages 디렉터리를 사용하는 것이다. 두 디렉터리는 서로 다른 경로이므로 서로 영향을 주지 않는다. 위에서 기술한 대로 가상 환경이 되는 것이다. 즉, 파이썬 가상 환경 구현의 기본은 sys.path 환경 변수를 조작하는 작업이다.

sys.path 환경 변수를 애플리케이션 진입점 스크립트에서 직접 조작할 수도 있다. 파이썬 1.x 때, 패키지라는 개념 없이 외부 의존성을 모조리 프로젝트 내부에 때려 넣었던 시절에 쓰던 방법이다. 파이썬 버전이 올라가면서 패키지 디렉터리 개념이 도입되고, 이를 자동으로 sys.path 에 추가해 주는 site 모듈이 도입되었다.

이후 파이썬 인터프리터 자체에서 pyvenv.cfg 를 이용한 가상 환경 지원이 추가되어 현재에 이르고 있다. 현재는 최소한의 파이썬 실행 파일을 복사하고, 전용 site-packages 디렉터리를 생성한 뒤 pyvenv.cfg 파일을 만들어서 파이썬 가상 환경을 구성할 수 있다.


하나의 파이썬 버전에서 가상 환경 만들기

시스템에 설치된 파이썬 설치본을 바탕으로, 가상 환경을 여러 개 만들어서 사용하려는 경우이다.

관리해 주는 툴 : virtualenv, python-venv, virtualenvwrapper, pipenv, conda

tl; dr

pipenv 쓰자.

virtualenv / python-venv

virtualenv 는 파이썬에서 가상 환경을 지원하기 전부터 개발되어 온 가상 환경 관리 애플리케이션이다. 파이썬 버전 관리 애플리케이션을 파이썬으로 만들다니 일종의 순환 참조라 할 수 있겠다. (이런 순환 참조의 최초 설치 과정을 부트스트래핑이라고 한다.)

virtualenv 가 파이썬 개발자에게 거의 상식처럼 통용되다 보니, 파이썬에서 가상 환경에 대한 지원을 추가하면서 virtualenv 의 핵심 기능을 따온 python-venv 시스템 모듈을 만들었다. 둘 다 pyvenv.cfg 에 의존하기에, 어느 것을 사용하건 가상 환경 생성이라는 핵심 기능에는 차이가 없다.

아래에 나오는 virtualenvwrapperpipenv 는 가상 환경을 생성할 때 내부적으로  virtualenv 를 사용한다.

virtualenvwrapper

virtualenv 로 가상 환경을 만든 건 좋은데, 사용 하다 보니 불편한 점이 발생한다. 프로젝트 디렉터리 아래에 가상 환경을 생성하게 되면, IDE나 버전 관리에서 이를 수동으로 제외해 주어야 한다. 프로젝트 디렉터리에 진입할 때마다 수동으로 activate 스크립트를 실행해야 한다. 현재 생성해 둔 가상 환경의 목록을 한 번에 확인할 방법이 없다. 기타등등...

virtualenvwrapper 는 이런 불편함을 해소해 주는 쉘 스크립트이다. 가상 환경을 홈 디렉터리 아래의 일정한 경로(기본값으로 ~/.virtualenvs/)에 생성하고, 가상 환경 활성화와 동시에 프로젝트 경로로 이동하는 workon {venv} 명령어를 제공하는 등 편의 기능을 구현하고 있다.

pipenv

virtualenv 는 가상 환경을 관리하고, pip 는 패키지를 관리한다. 그런데 가상 환경이 패키지 목록을 포함하는 개념인데, 굳이 두 개의 툴을 나누어 사용할 필요가 있을까?

그 둘을 합친게 pipenv 이다. 가상 환경 관리와 패키지 버전 관리 기능을 동시에 제공한다. 가상 환경은 virtualenvwrapper 와 같이 ~/.virtualenvs/ 디렉터리로 모아 주며, 프로젝트의 경로를 해싱을 사용해서 가상 환경을 자동으로 선택해 준다. 가상 환경을 활성화 하려면 프로젝트 디렉터리 내에서 pipenv shell 명령어를 사용하면 된다. 연결되는 가상 환경 이름을 지정하지 않아도 되므로 편리하다.

또한 pip 에서는 지원하지 않는 패키지 버전 잠금(locking)을 지원하므로, 가상 환경을 다시 만들 때 의존성 패키지 버전을 완전히 재현할 수 있다.


파이썬 버전을 여러 개 써야 할 때

관리해 주는 툴 : 시스템 패키지 관리자, pyenv

개발을 하다 보면 파이썬 버전 자체를 여러 개 설치해야 하는 때가 있다. 예를 들어 라이브러리 패키지를 개발하는 경우, 여러 파이썬 버전에서 잘 작동하는지 테스트 해야 하니 파이썬 버전이 서로 다른 가상 환경이 필요하다.

tl; dr

pyenv-virtualenv 를 쓰거나, pyenv + pipenv 쓰자.

패키지 관리자 / 직접 파이썬 빌드하기

데비안 계열 배포판은 apt, 레드햇 계열 배포판은 yum 등의 패키지 관리자를 사용하여 미리 빌드된 파이썬을 손쉽게 설치할 수 있다. 이 때 버전을 명시하여 파이썬 버전을 여러 개 설치할 수 있다. 원하는 파이썬 버전이 제공되지 않는다면 소스 코드를 직접 다운로드 받아서 빌드하는 방법도 있다.

이렇게 시스템에 여러 버전의 파이썬이 설치된 경우, 특정 버전의 파이썬을 실행하고자 한다면 pythonX.Y 명령어로 실행할 수 있다. 예를 들어 파이썬 3.9를 명시하여 실행하려면 python3.9 로 실행하면 된다.

다만 패키지 관리자를 사용하려면 시스템 관리자 권한을 필요로 하며, 또 직접 빌드한 실행 파일을 /usr/local 아래에 설치하는 경우 시스템 전역에서 접근 가능하게 된다. 즉, 다른 애플리케이션에 영향을 줄 수 있다. 이렇게 설치한 파이썬 설치본은 가상 환경이 아니므로 주의하자.

시스템에 파이썬 버전이 여러 개 설치되어 있는 경우, virtualenv 또는 pipenv 에 옵션으로 파이썬 버전을 명시하여 가상 환경을 생성할 수 있다. python-venv 는 애당초 파이썬 모듈로 실행되니까 버전을 명시할 수밖에 없다.

pyenv

pyenv 는 여러 파이썬 버전을 설치하는 과정을 안전하게 자동화 해 준다. 홈 디렉터리 아래(기본값 ~/.pyenv/versions)에 여러 버전의 파이썬을 설치할 수 있으며, 파이썬 버전을 자유롭게 선택하여 사용할 수 있다.

또한 PATH 환경 변수를 조작하는 activation 과정이 필요 없다. 파이썬 실행 명령어를 중간에 낚아 채서 어느 버전을 실행할 지 결정하기 때문이다. 이런 방법을 shim 이라고 한다.

프로젝트 디렉터리 내에 .python-version 파일을 만들어 파이썬 버전을 지정해 두면 자동으로 이를 인식하여 적절한 파이썬 버전을 선택해 준다. .python-version 파일이 지정된 디렉터리로 진입하면 자동으로 설정이 반영되는 것을 볼 수 있다.

pyenv 자체는 쉘 스크립트로 작성되어 부트스트래핑이 필요 없다. 그리고 쉘 스크립트라 윈도우에서는 작동하지 않는다. 이를 윈도우 용으로 포팅한 pyenv-win 프로젝트가 따로 존재한다.

pyenv-virtualenv

pyenv 는 파이썬 버전을 여러 개 설치할 수 있게 해 줄 뿐, 가상 환경을 만들어 주지는 않는다. 파이썬 버전별로 가상 환경까지 관리하려면 pyenv-virtualenv 플러그인을 사용하면 된다.

pyenv-virtualenv 는 파이썬 버전별로 envs 디렉터리를 만들고 그 밑에 가상 환경을 만들어 주므로, 깔끔하게 정돈된 결과를 얻을 수 있다. 또한 .python-version 파일에 가상 환경 이름을 지정해 두면 해당 디렉터리로 진입할 때 이를 인식하여 잘 반영해 준다. 아주 편리하다. pyenv-virtualenv 는 가상 환경을 생성하는데 virtualenvpython-venv 를 모두 지원한다.

pyenv-win 은 아직 pyenv-virtualenv 플러그인을 지원하지 않는다. 윈도우 사용자는 아직 손가락 빨면서 기다려야 한다.

pyenv-virtualenvwrapper

virtualenvwrapper 쉘 스크립트가 pyenv 와 함께 작동할 수 있도록 해 주는 플러그인이다. pyenv-virtualenvpipenv 로 이전하는 걸 권장한다.

pyenv + pipenv

pyenv 가 설치되어 있으면 pipenv 는 자동으로 이를 인식하여 연동한다. 파이썬 버전을 지정하여 가상 환경을 생성할 때, 해당 버전이 아직 설치되지 않았다면 pyenv 를 이용하여 설치할 지 물어본다.

pyenv 를 지원하지만, pipenv 로 생성한 가상 환경은 여전히 ~/.virtualenvs 디렉터리에 생성된다. 따라서 pipenv 를 사용한다면 일관되게 pipenv shell 명령어를 사용하면 된다. .python-version 파일로 가상 환경을 지정할 수 없다.

pipenv 자체는 윈도우에서도 작동하지만, pyenv-win 과 호환되지는 않는다. 윈도우 사용자는 아직 손가락 빨면서 기다려야 한다.


conda

Anaconda Inc.에서 배포하는 파이썬/R(통계학 언어) 패키지 매니저이다. 일단은 오픈소스로 배포되지만, 일부 확장 기능이나 유료 패키지는 유료 구독에 가입해야 사용할 수 있다. 여하튼, 파이썬 가상 환경 관리와 패키지 매니저 기능은 오픈 소스로 누구나 쓸 수 있다.

conda 명령어를 이용하여 가상 환경을 생성하면서, 파이썬 버전을 지정해 줄 수 있다. 이 때 Anaconda Inc. 에서 배포하는 버전이 설치된다. 사실 conda 는 파이썬의 pyvenv.cfg 를 이용한 가상 환경을 사용하지 않고, 그냥 파이썬 설치본을 통째로 복사하여 가상 환경을 만든다. 이건 파이썬이 pyvenv.cfg 를 지원하기 전에 사용하던 방법이다.

가상 환경은 ~/.conda/envs 디렉터리에 모아서 관리되며, 별도의 메타데이터를 이용하여 파이썬 버전과 패키지를 모두 관리해 준다. 가상 환경 활성화는 conda activate {venv} 명령어를 사용하여 수동으로 진행하여야 한다.

Anaconda Inc. 에서 저장소를 관리하기 때문에, 일부 패키지가 없거나 또는 오래된 버전이 설치될 수 있다. 그리고 conda 패키지 관리자와 pip 가 서로 충돌을 일으키는 경우도 있다. 대체로 문제가 발생하지는 않지만, 일단 문제가 발생하면 해결이 참 어렵다. 그냥 가상 환경을 삭제하고 새로 만드는게 편할 정도다.

참고로 Anaconda에서 배포하는 파이썬 버전도 pyenv 를 이용해서 설치할 수 있고, 그 안에서 conda 명령어를 사용할 수도 있다. pyenv 로 설치한 파이썬 인터프리터에서 virtualenv 를 사용할 수 있는 것과 같은 원리다. 물론 그렇게 써서 얻는 이득은 별로 없다.

윈도우 환경에서 파이썬 버전을 여러 개 설치하려면 아직은 conda 가 가장 편리하다. pyenv-win 이 제약이 많기 때문이다. WSL은 리눅스 환경이니까 별도로 생각하자.


docker

파이썬 가상 환경을 관리하는데 도커는 별로 인기가 없다.  파이썬 애플리케이션 개발에서 도커를 이용한 격리는 주로 배포 단계에서 파이썬 환경까지 통째로 배포하려는 목적으로 사용하는 것 같다.

물론 개발 환경으로 못 쓰는 것도 아니다. 프로세스 수준에서 격리가 이루어지며, 파일 시스템도 완전히 격리가 이루어지므로 가장 깔끔한 환경을 얻을 수 있다. 다만 호스트와 연동이 잘 안 되어 IDE 연동이나 디버거 사용이 잘 안 될 수 있다. 역시 도커는 배포용이다.