PEP 381 – PyPI 미러링 인프라
- Author:
- Tarek Ziadé <tarek at ziade.org>, Martin von Löwis <martin at v.loewis.de>
- Status:
- Withdrawn
- Type:
- Standards Track
- Topic:
- Packaging
- Created:
- 21-Mar-2009
- Post-History:
번역·라이선스 안내
이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판
초록
이 PEP는 PyPI를 위한 미러링 인프라를 설명합니다.
PEP 철회
주 PyPI 웹 서비스는 2013년 5월에 Fastly 캐싱 CDN 뒤로 이전되었습니다: https://mail.python.org/pipermail/distutils-sig/2013-May/020848.html
이후 이 방식은 PSF와의 현물 후원으로 공식화되었으며, PSF는 해당 후원 방식이 중단될 경우에 대비한 위험 관리 업무도 맡았습니다.
이전에 PyPI에서 직접 제공되던 다운로드 통계는 이제 Google Big Query를 통해 간접적으로 공개됩니다: https://packaging.python.org/guides/analyzing-pypi-package-downloads/
따라서 이 PEP에서 설명하는 미러링 제안은 더 이상 필요하지 않으며, 철회(Withdrawn)로 표시되었습니다.
근거
PyPI는 6000개가 넘는 프로젝트를 호스팅하고 있으며, 사람들이 애플리케이션을 만드는 데 매일 사용하고 있습니다. 특히 easy_install과 zc.buildout 같은 시스템은 PyPI를 집중적으로 사용합니다.
PyPI를 집중적으로 사용하는 사람들에게 PyPI는 단일 장애 지점이 될 수 있습니다. 사람들은 비공개 미러와 공개 미러를 몇 개 구축하기 시작했습니다. 이러한 미러는 능동 미러입니다. 즉, 동기화를 위해 PyPI를 탐색합니다.
시스템을 더 안정적으로 만들기 위해 이 PEP는 다음을 설명합니다:
- PyPI에서의 미러 목록 등재 및 등록
- 공개 미러가 유지해야 하는 페이지. 이 페이지들은 PyPI가 조회 수와 최종 수정 날짜를 얻는 데 사용합니다.
- 미러가 PyPI와 동기화하는 방법
- 클라이언트가 페일오버 메커니즘을 구현하는 방법
미러 목록 등재 및 등록
PyPI를 미러링하려는 사람은 catalog-SIG에 제안합니다. 메일링 리스트에 미러가 제안되면, 미러링 규칙을 준수하는지 확인한 후 PyPI 애플리케이션의 미러 목록에 수동으로 추가됩니다.
미러 목록은 다음 형식의 호스트 이름 목록으로 제공됩니다.
X.pypi.python.org
X의 값은 a,b,c,…,aa,ab,… 순서입니다. a.pypi.python.org는 마스터 서버이며, 미러는 b부터 시작합니다. CNAME 레코드 last.pypi.python.org는 마지막 호스트 이름을 가리킵니다. 미러 연산자는 고정 주소를 사용해야 하며, 그 주소에 대해 계획된 변경 사항은 distutils-sig에 미리 알려야 합니다.
새 미러는 http://pypi.python.org/mirrors에도 나타나며, 이 페이지는 사람이 읽을 수 있는 형태로 미러 목록을 제공합니다. 이 페이지는 새 미러를 등록하는 방법도 설명합니다.
통계 페이지
PyPI는 /stats에서 다운로드 통계를 제공합니다. PyPI는 모든 미러의 로컬 통계를 읽어 합산하는 방식으로 이 페이지를 매일 계산합니다.
통계는 /stats/days와 /stats/months 아래에 일별 또는 월별 파일로 제공됩니다. 각 파일은 다음 형식의 이름을 가진 bzip2 파일입니다:
- 일별 파일은 YYYY-MM-DD.bz2
- 월별 파일은 YYYY-MM.bz2
예시:
- /stats/days/2008-11-06.bz2
- /stats/days/2008-11-07.bz2
- /stats/days/2008-11-08.bz2
- /stats/months/2008-11.bz2
- /stats/months/2008-10.bz2
미러의 진위성
분산 미러링 시스템에서는 클라이언트가 미러링된 사본이 진본인지 검증하고 싶어 할 수 있습니다. 고려해야 할 위협은 여러 가지입니다:
- 중앙 색인이 침해될 수 있습니다.
- 중앙 색인은 신뢰할 수 있다고 가정하더라도 미러가 변조될 수 있습니다.
- 중앙 색인과 최종 사용자 사이, 또는 미러와 최종 사용자 사이에 있는 중간자가 데이터그램을 변조할 수 있습니다.
이 명세는 두 번째 위협만 다룹니다. 중간자 공격을 탐지하기 위한 몇 가지 대비책도 마련되어 있습니다. 첫 번째 공격을 탐지하려면 패키지 작성자가 PGP 키로 패키지에 서명하여, 사용자가 그 패키지가 자신이 신뢰하는 작성자로부터 왔는지 확인할 수 있게 해야 합니다.
중앙 색인은 URL /serverkey에서 “openssl dsa -pubout”으로 생성되는 PEM 형식(즉, 알고리즘 1.3.14.3.2.12를 사용하는 RFC 3280 SubjectPublicKeyInfo)의 DSA 키를 제공합니다. 이 URL은 미러링되어서는 안 되며, 클라이언트는 공식 serverkey를 PyPI에서 직접 가져오거나 PyPI 클라이언트 소프트웨어에 함께 제공된 사본을 사용해야 합니다. 그래도 미러는 키 롤오버를 탐지하기 위해 키를 내려받아야 합니다.
각 패키지에 대해 /serversig/<package>에서 미러링된 서명이 제공됩니다. 이것은 이에 대응하는 URL /simple/<package>에 대한 DSA 서명으로, SHA-1 with DSA를 사용한 DER 형식입니다(즉, 알고리즘 1.2.840.10040.4.3으로 생성한 RFC 3279 Dsa-Sig-Value입니다)
미러를 사용하는 클라이언트는 패키지를 검증하기 위해 다음 단계를 수행해야 합니다:
- /simple 페이지를 내려받고 그 SHA-1 해시를 계산합니다.
- 그 해시의 DSA 서명을 계산합니다.
- 대응하는 /serversig를 내려받아 2단계에서 계산한 값과 (바이트 단위로) 비교합니다.
- 미러에서 내려받는 모든 파일의 MD-5 해시를 계산하고 (/simple 페이지와 대조하여) 검증합니다.
검증 알고리즘의 구현은 https://svn.python.org/packages/trunk/pypi/tools/verify.py 에서 구할 수 있습니다.
중앙 색인에서 내려받을 때는 검증이 필요하지 않으며, 계산 오버헤드를 줄이기 위해 검증하지 않아야 합니다.
약 1년에 한 번씩 키가 새 키로 교체됩니다. 미러는 모든 /serversig 페이지를 다시 가져와야 합니다. 미러를 사용하는 클라이언트는 새 서버 키의 신뢰할 수 있는 사본을 찾아야 합니다. 이를 얻는 한 가지 방법은 https://pypi.python.org/serverkey 에서 내려받는 것입니다. 중간자 공격을 탐지하려면 클라이언트는 SSL 서버 인증서를 검증해야 하며, 이 인증서는 CACert 인증 기관이 서명합니다.
미러가 제공해야 하는 특수 페이지
미러는 PyPI의 부분 사본이므로, PyPI의 구조를 복사하여 동일한 구조를 제공합니다.
- simple: 패키지 색인의 REST 버전
- packages: 패키지, Python 버전별 및 문자별로 저장됩니다
- serversig: simple 페이지에 대한 서명
또한 다음 두 가지 특정 요소를 제공해야 합니다:
- last-modified
- local-stats
최종 수정 날짜
CPAN은 미러의 마지막 동기화 날짜를 공개하는 최신성 날짜 시스템을 사용합니다.
PyPI의 경우, 각 미러는 해당 미러의 마지막 동기화 날짜를 나타내는 단순 텍스트 콘텐츠가 담긴 URL을 유지해야 합니다.
날짜는 ISO 8601 형식 [2]을 사용하여 GMT 시간으로 제공됩니다. 각 미러는 자신의 최종 수정 날짜를 유지할 책임이 있습니다.
이 페이지는 /last-modified에 위치해야 하며 text/plain 페이지여야 합니다.
로컬 통계
각 미러는 자신을 통해 이루어진 모든 다운로드를 집계할 책임이 있습니다. PyPI는 전체 합계를 표시할 수 있도록 이 통계를 사용하여 모든 다운로드를 합산합니다.
이 통계는 CSV와 유사한 형식이며, 첫 줄에 헤더가 있습니다. 이는 PEP 305를 준수해야 합니다. 기본적으로, Python의 csv 모듈로 읽을 수 있어야 합니다.
이 파일의 필드는 다음과 같습니다:
- package: 패키지의 distutils id입니다.
- filename: 다운로드된 파일의 이름입니다.
- useragent: 패키지를 다운로드한 클라이언트의 User-Agent입니다.
- count: 다운로드 횟수입니다.
내용은 다음과 같은 형태입니다:
# package,filename,useragent,count
zc.buildout,zc.buildout-1.6.0.tgz,MyAgent,142
...
집계는 미러가 운영을 시작한 날부터 시작되며, 파일은 하루에 하나씩 bzip2 형식으로 압축되어 생성됩니다. 각 파일의 이름은 해당 날짜로 지정됩니다. 예를 들어, 2008-11-06.bz2는 2008년 11월 6일의 파일입니다.
그런 다음 이 파일들은 days라는 폴더에 제공됩니다. 예를 들면 다음과 같습니다:
- /local-stats/days/2008-11-06.bz2
- /local-stats/days/2008-11-07.bz2
- /local-stats/days/2008-11-08.bz2
이 페이지는 /local-stats에 있어야 합니다.
미러가 PyPI와 동기화하는 방법
Simple Index라는 미러링 프로토콜은 Martin v. Loewis와 Jim Fulton이 easy_install의 동작 방식을 바탕으로 기술하고 구현했습니다. 이 절에서는 그 프로토콜을 종합하여 정리하고 몇 가지 관련 링크를 제공하며, User-Agent에 관한 짧은 내용도 덧붙입니다.
미러링 프로토콜
미러는 중앙 서버와 미러 사이에 전송되는 데이터의 양을 줄여야 합니다. 이를 위해 미러는 반드시 changelog() PyPI XML-RPC 호출을 사용해야 하며, 마지막 동기화 이후 변경된 패키지만 다시 가져와야 합니다. 각 패키지 P에 대해 미러는 반드시 /simple/P/ 문서와 /serversig/P 문서를 복사해야 합니다. 중앙 서버에서 패키지가 삭제되면 미러는 반드시 해당 패키지와 관련된 모든 파일을 삭제해야 합니다. 패키지 파일의 변경을 감지하기 위해 미러는 파일의 ETag를 캐시할 수 있으며, If-none-match 헤더를 사용하여 해당 파일을 건너뛰도록 요청할 수도 있습니다.
각 미러링 도구는 반드시 설명적인 User-agent 헤더를 사용하여 자신을 식별해야 합니다.
pep381client 패키지 [1]는 이 프로토콜을 준수하며 PyPI를 탐색하는 애플리케이션을 제공합니다.
User-agent 요청 헤더
PyPI에서 클라이언트가 수행한 동작을 구별할 수 있도록, 모든 미러링 소프트웨어는 고유한 사용자 에이전트 이름을 제공해야 합니다.
이는 다음과 같은 모든 클라이언트에도 해당됩니다:
XXX PyPI의 사용자 에이전트 등록 메커니즘?
클라이언트가 PyPI와 그 미러를 사용하는 방법
PyPI를 탐색하는 클라이언트는 last.pypi.python.org를 사용하여 미러 목록을 가져옴으로써 대체 미러를 사용할 수 있어야 합니다.
코드 예제:
>>> import socket
>>> socket.gethostbyname_ex('last.pypi.python.org')[0]
'h.pypi.python.org'
현재까지 이 메커니즘을 사용할 수 있는 클라이언트는 다음과 같습니다:
- setuptools
- zc.buildout (setuptools를 통해)
- pip
장애 조치 메커니즘
PyPI를 탐색하는 클라이언트는 PyPI나 사용 중인 미러가 응답하지 않을 때 장애 조치 메커니즘을 사용할 수 있어야 합니다.
어떤 미러를 사용할지는 클라이언트가 결정할 몫이며, 예를 들어 지리적 위치와 응답성을 살펴보고 결정할 수 있습니다.
이 PEP는 이 장애 조치 메커니즘이 어떻게 동작해야 하는지는 기술하지 않지만, 클라이언트가 가장 가까운 미러를 사용하도록 시도할 것을 강력히 권장합니다.
지금까지 이 메커니즘을 사용할 수 있는 클라이언트는 다음과 같습니다:
- setuptools
- zc.buildout (setuptools를 통해)
- pip
추가 패키지 색인
일부 패키지가 PyPI에 업로드되지 않으리라는 것은 분명합니다. 비공개 패키지이기 때문일 수도 있고, 프로젝트 유지 관리자가 사람들이 프로젝트 패키지를 받아 갈 수 있는 자체 서버를 운영하기 때문일 수도 있습니다. 그러나 공개 패키지 색인은 PyPI와 Distutils 프로토콜을 따를 것을 강력히 권장합니다.
다시 말해, register 및 upload 명령은 현존하는 어떤 패키지 색인 서버와도 호환되어야 합니다.
지금까지 PyPI 및 Distutils와 호환되는 소프트웨어는 다음과 같습니다:
추가 패키지 색인은 PyPI의 미러가 아니지만, 그 자체로 몇 개의 미러를 가질 수 있습니다.
여러 색인 병합하기
클라이언트가 서로 다른 여러 색인에서 패키지를 가져와야 할 때는, 각 색인을 잠재적인 패키지 출처로 사용할 수 있어야 합니다. 클라이언트가 패키지를 찾을 수 있도록 서로 다른 색인들은 정렬된 목록으로 정의되어야 합니다.
물론 각 독립 색인은 자신의 미러 목록을 제공할 수 있습니다.
XXX 임의의 색인의 미러에 대한 호스트 이름을 얻는 방법을 정의해야 합니다.
이를 통해 클라이언트 수준에서 모든 조합이 가능해지며, 모든 수준의 비공개성을 갖춘 신뢰할 수 있는 패키징 시스템을 구성할 수 있습니다.
병합을 처리하는 것은 클라이언트의 몫입니다.
참고 문헌
감사의 글
Georg Brandl.
Copyright
This document has been placed in the public domain.