Dependency Proxy
의존성 프록시(Dependency Proxy)는 DockerHub의 공용 레지스트리 이미지를 위한 pull-through-cache입니다. 이 문서는 GitLab에서 이 기능이 구성된 방법에 대해 설명합니다.
참고: 개인 레지스트리 이미지 지원은 이슈 331741에서 제안됩니다.
컨테이너 레지스트리
컨테이너 레지스트리의 의존성 프록시는 원격 컨테이너 레지스트리를 대신하는 역할을 합니다. 우리의 경우에는 공용 DockerHub 레지스트리가 원격 레지스트리입니다.
사용자의 관점에서는 GitLab 인스턴스가 그들이 docker login gitlab.com
을 사용하여 이미지를 끌어오는 컨테이너 레지스트리로만 보입니다.
docker login gitlab.com
을 사용하면 도커 클라이언트가 v2 API를 사용하여 요청을 합니다.
인증을 지원하기 위해 다음과 같은 경로를 포함해야 합니다: - API 버전 확인
docker pull
요청을 지원하기 위해 두 가지 추가 경로를 포함해야 합니다:
- 이미지 매니페스트 가져오기
- 이미지 레이어(블롭) 가져오기
이러한 경로는 gitlab-org/gitlab/config/routes/group.rb
에서 정의됩니다.
가장 간단한 형태로 의존성 프록시는 세 가지 요청을 관리합니다: - 로그인/ JWT 반환 - 매니페스트 가져오기 - 블롭 가져오기
의존성 프록시의 일반적인 요청 순서는 다음과 같습니다:
mermaid
sequenceDiagram
Client->>+GitLab: 로그인? / 토큰 요청
GitLab->>+Client: JWT
Client->>+GitLab: 이미지의 매니페스트 요청
GitLab->>+ExternalRegistry: JWT 요청
ExternalRegistry->>+GitLab : JWT
GitLab->>+ExternalRegistry : 매니페스트 요청
ExternalRegistry->>+GitLab : 매니페스트 반환
GitLab->>+GitLab : 매니페스트 저장
GitLab->>+Client : 매니페스트 반환
loop 이미지 레이어 요청
Client->>+GitLab: 매니페스트에서 블롭 요청
GitLab->>+ExternalRegistry: JWT 요청
ExternalRegistry->>+GitLab : JWT
GitLab->>+ExternalRegistry : 블롭 요청
ExternalRegistry->>+GitLab : 블롭 반환
GitLab->>+GitLab : 블롭 저장
GitLab->>+Client : 블롭 반환
end
인증 및 권한
Docker 클라이언트가 레지스트리와 인증할 때, 레지스트리는 클라이언트에게 JSON 웹 토큰(JWT)을 얻을 위치 및 이를 이후의 모든 요청에 사용하도록 지시합니다. 이를 통해 인증 서비스가 레지스트리와 별도의 응용 프로그램에서 실행될 수 있습니다. 예를 들어, GitLab 컨테이너 레지스트리는 Docker 클라이언트에게 https://gitlab.com/jwt/auth
에서 토큰을 얻도록 지시합니다. 이 엔드포인트는 gitlab-org/gitlab
프로젝트의 일부이고, 레일즈 프로젝트 또는 웹 서비스로도 알려져 있습니다.
사용자가 Docker 클라이언트와 의존성 프록시에 로그인하려고 할 때, 우리는 그에게 JWT를 얻을 위치를 알려줘야 합니다. 우리는 컨테이너 레지스트리에서 사용하는 것과 동일한 엔드포인트인 https://gitlab.com/jwt/auth
를 사용할 수 있습니다. 그러나 우리의 경우, 파라미터에 service=dependency_proxy
를 명시하도록 Docker 클라이언트에게 지시하여 별도의 기반이 되는 서비스를 사용할 수 있습니다.
이 시퀀스 다이어그램은 의존성 프록시에 로그인하는 요청 흐름을 보여줍니다.
의존성 프록시는 UI(ApplicationController
) 및 API(ApiGuard
)에서 관리되는 인증과 별도의 인증 서비스를 사용합니다. 서비스가 JWT를 생성하면, DependencyProxy::ApplicationController
가 나머지 요청의 인증과 권한을 관리합니다. 이는 GitLab::Auth::Result
를 사용하여 사용자를 관리하고, GitHttpClientController
에서 구현된 Git 클라이언트 요청과 유사합니다.
캐싱
블롭은 주변 로직이 없는 캐시된 아티팩트입니다. 우리는 다이제스트로 그들을 캐시합니다. 새로운 블롭에 대한 요청을 받으면, 요청된 다이제스트로 블롭을 확인하고 반환합니다. 그렇지 않다면 외부 레지스트리에서 가져와 캐시합니다.
매니페스트는 더 복잡하며, 부분적으로 DockerHub의 속도 제한 때문입니다. 매니페스트는 본질적으로 이미지를 만들기 위한 레시피입니다. 특정 이미지를 만들기 위한 블롭 목록을 가지고 있습니다. 따라서 alpine:latest
는 alpine:latest
이미지를 만들기 위해 필요한 블롭을 지정하는 매니페스트가 있습니다. 흥미로운 부분은 alpine:latest
이 시간이 지남에 따라 변경될 수 있기 때문에, 우리가 매니페스트를 캐시하고 영원히 사용할 수 있다고 가정할 수 없다는 것입니다. 대신에, 매니페스트의 다이제스트를 확인해야 합니다. 이는 ETag입니다. 이는 매니페스트에 대한 요청에서 많은 경우 다이제스트를 포함하지 않기 때문에 흥미롭습니다. 그래서 우리는 캐시된 매니페스트가 여전히 가장 최신인지 어떻게 아는 것일까요? DockerHub는 속도 제한에 포함되지 않는 무료 HEAD 요청을 허용합니다. HEAD 요청은 매니페스트 다이제스트를 반환하여 우리가 캐시된 매니페스트가 여전히 가장 최신인지를 확인할 수 있게 합니다.
이러한 지식을 바탕으로 매니페스트 요청을 관리하는 다음과 같은 로직을 구축했습니다:
mermaid
graph TD
A[매니페스트 요청 받음] --> | 캐시된 매니페스트가 있음.| B{Docker 매니페스트 HEAD 요청}
A --> | 캐시된 매니페스트가 없음.| C{Docker 매니페스트 GET 요청}
B --> | 다이제스트가 DB에 있는 것과 일치함 | D[캐시에서 매니페스트 가져옴]
B --> | HEAD 요청 에러, 네트워크 실패, DockerHub에 연결할 수 없음 | D[캐시에서 매니페스트 가져옴]
B --> | 다이제스트가 DB에 있는 것과 일치하지 않음 | C
C --> E[매니페스트를 캐시에 저장, 다이제스트를 데이터베이스에 저장]
D --> F
E --> F[매니페스트 반환]
파일 처리를 위한 Workhorse
파일 업로드 및 캐싱 관리는 Workhorse에서 처리됩니다. 여기에는 의존성 프록시를 위한 추가적인 POST
routes에 대한 설명이 포함되어 있습니다.
send_dependency
메서드는 외부 레지스트리에서 사전에 가져온 JWT를 포함하여 Workhorse에 요청을 수행합니다. 그런 다음 Workhorse는 해당 토큰을 사용하여 사용자가 원래 요청한 매니페스트 또는 blob을 요청할 수 있습니다. Workhorse 코드는 workhorse/internal/dependencyproxy/dependencyproxy.go
에 있습니다.
모두 종합하면, 이미지 파일을 요청하는 시퀀스는 다음과 같습니다:
정리 정책
의존성 프록시의 정리 정책은 시간이 지남에 따라 캐시에 유지되는 파일의 일수로 작동합니다. 사용자는 읽히지 않은 파일이 얼마 동안 캐시에 남아 있을 수 있는지를 설정할 수 있습니다. 이미지와 관련된 블롭을 연관시키는 방법이 없기 때문에(이를 위해서는 컨테이너 레지스트리 직원들이 구축한 메타데이터 데이터베이스를 구축해야 함), “이 블롭이 90일 동안 검색되지 않았다면 삭제”와 같은 규칙을 설정할 수 있습니다. 이는 계속해서 검색되는 파일은 캐시에서 제거되지 않지만, 예를 들어 alpine:latest
가 변경되고 그 하위 블롭 중 하나가 더 이상 사용되지 않는 경우, 결국 정리될 것임을 의미합니다. 우리는 read_at
속성을 사용하여 특정 dependency_proxy_blob
또는 dependency_proxy_manifest
의 마지막 검색 시간을 추적합니다.
이들은 DependencyProxy::CleanupDependencyProxyWorker와 같은 크론 워커를 사용하여 작동하며, 이 워커는 블롭 삭제 및 매니페스트 삭제를 담당하는 두 limited capacity 워커를 실행합니다. capacity는 application setting에서 설정됩니다.
과거 참조 링크
- 개인 그룹을 위한 의존성 프록시 - 초기 인증 구현
- 매니페스트 캐싱 - 초기 매니페스트 캐싱 구현
- 블롭을 위한 Workhorse - 초기 Workhorse 구현
- 매니페스트를 위한 Workhorse - 매니페스트 캐시 로직을 Workhorse로 이동
- 배포 토큰 지원 - 권한 부여 대폭 업데이트
- SSO 지원 - 정책 확인 방법 변경