Workhorse의 WebSocket 채널 지원
GitLab v19.1경우에 따라 GitLab은 WebSocket을 통해 다음 기능을 제공할 수 있습니다: 환경에 대한 브라우저 내 터미널 접근: 프로젝트가 배포된 실행 중인 서버 또는 컨테이너. CI에서 실행 중인 서비스에 대한 접근. Workhorse는 WebSocket 업그레이드와 장기적인 websocket 연결을 관리하며, 이를 통해 GitLab이 다른 요청을 처리할 수 있게 됩니다.
경우에 따라 GitLab은 WebSocket을 통해 다음 기능을 제공할 수 있습니다:
-
환경에 대한 브라우저 내 터미널 접근: 프로젝트가 배포된 실행 중인 서버 또는 컨테이너.
-
CI에서 실행 중인 서비스에 대한 접근.
Workhorse는 WebSocket 업그레이드와 장기적인 websocket 연결을 관리하며, 이를 통해 GitLab이 다른 요청을 처리할 수 있게 됩니다. 이 문서는 이러한 연결의 아키텍처를 설명합니다.
WebSocket 소개#
WebSocket은 "업그레이드된" HTTP/1.1 요청입니다. WebSocket은 클라이언트와 서버 간의 양방향 통신을 허용합니다. WebSocket은 HTTP가 아닙니다.
클라이언트는 언제든지 서버에 메시지(프레임이라고 알려진)를 보낼 수 있으며,
그 반대도 마찬가지입니다. 클라이언트 메시지가 반드시 요청일 필요는 없으며, 서버 메시지가 반드시 응답일 필요도 없습니다. WebSocket URL은 ws://(암호화되지 않음) 또는 wss://(TLS 보안) 같은 스키마를 사용합니다.
WebSocket으로 업그레이드를 요청할 때, 브라우저는 다음과 같은 HTTP/1.1 요청을 보냅니다:
GET /path.ws HTTP/1.1
Connection: upgrade
Upgrade: websocket
Sec-WebSocket-Protocol: terminal.gitlab.com
# More headers, including security measures
이 시점에서 연결은 여전히 HTTP이므로 이것은 요청입니다.
서버는 404 Not Found 또는 500 Internal Server Error 같은 표준 HTTP 응답을 보낼 수 있습니다.
서버가 업그레이드를 허용하기로 결정하면, HTTP 101 Switching Protocols 응답을 보냅니다. 이 시점부터 연결은 더 이상 HTTP가 아닙니다. 이제 WebSocket이 되며 HTTP 요청이 아닌 프레임이 전송됩니다. 클라이언트 또는 서버가 연결을 닫을 때까지 연결이 유지됩니다.
서브 프로토콜 외에도 개별 websocket 프레임은 다음과 같은 메시지 유형을 지정할 수 있습니다:
-
BinaryMessage -
TextMessage -
Ping -
Pong -
Close
임의 데이터를 포함할 수 있는 것은 바이너리 프레임뿐입니다. 프레임은 서브 프로토콜 요구 사항 외에도 유효한 UTF-8 문자열이어야 합니다.
브라우저에서 Workhorse로#
터미널을 예로 들면:
-
GitLab은
https://gitlab.com/group/project/-/environments/1/terminal과 같은 URL에서 브라우저에 JavaScript 터미널 에뮬레이터를 제공합니다. -
이 URL은
wss://gitlab.com/group/project/-/environments/1/terminal.ws로 websocket 연결을 엽니다. 이 엔드포인트는 Workhorse에만 존재하며 GitLab에는 존재하지 않습니다. -
연결을 수신할 때, Workhorse는 먼저 GitLab에
preauthentication요청을 수행하여 클라이언트가 요청된 터미널에 접근할 권한이 있는지 확인합니다:
클라이언트가 적절한 권한을 보유하고 터미널이 존재하면, GitLab은 클라이언트가 연결해야 할 터미널의 세부 정보를 포함하는 성공 응답으로 응답합니다.
-
그렇지 않으면 Workhorse는 적절한 HTTP 오류 응답을 반환합니다.
-
GitLab이 Workhorse에 유효한 터미널 세부 정보를 반환하면:
지정된 터미널에 연결합니다.
-
브라우저를 WebSocket으로 업그레이드합니다.
-
브라우저의 자격 증명이 유효한 동안 두 연결 사이를 프록시합니다.
-
브라우저가 있는 동안 중간 프록시가 연결을 종료하는 것을 방지하기 위해 브라우저에 정기적인
PingMessage제어 프레임을 전송합니다.
브라우저는 특정 서브 프로토콜로 업그레이드를 요청해야 합니다:
terminal.gitlab.com#
이 서브 프로토콜은 TextMessage 프레임을 유효하지 않은 것으로 간주합니다. PingMessage 또는 CloseMessage 같은 제어 프레임은 일반적인 의미를 가집니다.
-
브라우저에서 서버로 전송된
BinaryMessage프레임은 임의의 텍스트 입력입니다. -
서버에서 브라우저로 전송된
BinaryMessage프레임은 임의의 텍스트 출력입니다.
이 프레임들은 ANSI 텍스트 제어 코드를 포함하며 어떤 인코딩으로든 될 수 있습니다.
base64.terminal.gitlab.com#
이 서브 프로토콜은 BinaryMessage 프레임을 유효하지 않은 것으로 간주합니다.
PingMessage 또는 CloseMessage 같은 제어 프레임은 일반적인 의미를 가집니다.
-
브라우저에서 서버로 전송된
TextMessage프레임은 base64로 인코딩된 임의의 텍스트 입력입니다. 서버는 이를 입력하기 전에 base64 디코딩해야 합니다. -
서버에서 브라우저로 전송된
TextMessage프레임은 base64로 인코딩된 임의의 텍스트 출력입니다. 브라우저는 이를 출력하기 전에 base64 디코딩해야 합니다.
base64로 인코딩된 형태에서 이 프레임들은 ANSI 터미널 제어 코드를 포함하며 어떤 인코딩으로든 될 수 있습니다.
Workhorse에서 GitLab으로#
터미널을 예로 들면, 브라우저를 업그레이드하기 전에
Workhorse는 https://gitlab.com/group/project/environments/1/terminal.ws/authorize와 같은 URL에서 GitLab에 표준 HTTP 요청을 보냅니다.
이는 터미널을 찾을 수 있는 위치와 연결 방법에 대한 세부 정보를 포함하는 JSON 응답을 반환합니다. 특히, 성공 시 다음 세부 정보가 반환됩니다:
-
연결할 WebSocket URL(예:
wss://example.com/terminals/1.ws?tty=1). -
지원할 WebSocket 서브 프로토콜(예:
["channel.k8s.io"]). -
전송할 헤더(예:
Authorization: Token xxyyz). -
선택 사항.
wss연결을 검증하기 위한 인증 기관.
Workhorse는 이 엔드포인트를 주기적으로 재확인합니다. 오류 응답을 수신하거나 터미널의 세부 정보가 변경되면 websocket 세션을 종료합니다.
Workhorse에서 WebSocket 서버로#
GitLab에서 환경 또는 CI job에는 배포 서비스(예: KubernetesService)가 연결될 수 있습니다. 이 서비스는 환경에 대한 터미널 또는 서비스를 찾을 수 있는 위치를 알고 있으며, GitLab은 이 세부 정보를 Workhorse에 반환합니다.
이러한 URL도 WebSocket URL입니다. GitLab은 Workhorse에게 원격 엔드에서 필요한 인증 세부 정보와 함께 연결에서 사용할 서브 프로토콜을 알려줍니다.
브라우저의 연결을 websocket으로 업그레이드하기 전에 Workhorse는:
-
Workhorse에서 제공받은 세부 정보에 따라 HTTP 클라이언트 연결을 엽니다.
-
해당 연결을 websocket으로 업그레이드하려고 시도합니다.
실패하면 브라우저에 오류 응답이 전송됩니다.
- 성공하면 브라우저도 업그레이드됩니다.
이제 Workhorse는 서로 다른 서브 프로토콜을 사용하는 두 개의 websocket 연결을 가지게 되며, 그런 다음:
-
브라우저에서 들어오는 프레임을 디코딩하고, 채널의 서브 프로토콜로 재인코딩하여 채널에 전송합니다.
-
채널에서 들어오는 프레임을 디코딩하고, 브라우저의 서브 프로토콜로 재인코딩하여 브라우저에 전송합니다.
어느 쪽 연결이 닫히거나 오류 상태가 되면 Workhorse는 오류를 감지하고 다른 연결을 닫아 채널 세션을 종료합니다. 브라우저가 연결이 끊어진 경우, Workhorse는 적절한 서브 프로토콜에 따라 인코딩된 ANSI End of Transmission 제어 코드(0x04 바이트)를 채널에 전송합니다. 연결이 끊어지는 것을 방지하기 위해 Workhorse는 채널이 보낸 websocket ping 프레임에 응답합니다.
Workhorse는 다음 서브 프로토콜만 지원합니다:
새로운 배포 서비스를 지원하려면 새로운 서브 프로토콜을 지원해야 합니다.
channel.k8s.io#
쿠버네티스에서 사용하는 이 서브 프로토콜은 멀티플렉스 채널을 정의합니다.
제어 프레임은 일반적인 의미를 가집니다. TextMessage 프레임은 유효하지 않습니다. BinaryMessage 프레임은 특정 파일 디스크립터에 대한 I/O를 나타냅니다.
각 BinaryMessage 프레임의 첫 번째 바이트는 uint8로서 파일 디스크립터(fd) 번호를 나타냅니다. 예를 들면:
-
0x00은fd 0,STDIN에 해당합니다. -
0x01은fd 1,STDOUT에 해당합니다.
나머지 바이트는 임의의 데이터를 나타냅니다. 서버에서 수신된 프레임의 경우 해당 fd에서 수신된 바이트입니다. 서버로 전송된 프레임의 경우 해당 fd에 써야 할 바이트입니다.
base64.channel.k8s.io#
역시 쿠버네티스에서 사용되는 이 서브 프로토콜은 channel.k8s.io와 유사한 멀티플렉스 채널을 정의합니다. 주요 차이점은 다음과 같습니다:
-
BinaryMessage프레임이 아닌TextMessage프레임이 유효합니다. -
각
TextMessage프레임의 첫 번째 바이트는 파일 디스크립터를 숫자 UTF-8 문자로 나타내므로,U+0030문자 즉 "0"은fd 0,STDIN입니다. -
나머지 바이트는 base64로 인코딩된 임의의 데이터를 나타냅니다.