GitLab Shell 개발 가이드라인
GitLab v19.1GitLab Shell은 GitLab의 Git SSH 세션을 처리하고 인증된 키 목록을 수정합니다. GitLab은 SSH를 통해 Git LFS 인증을 지원합니다. GitLab Shell은 Go로 작성되어 있으며, 빌드하려면 Go 컴파일러가 필요합니다.
GitLab Shell은 GitLab의 Git SSH 세션을 처리하고 인증된 키 목록을 수정합니다. GitLab Shell은 Unix 셸이 아니며 Bash나 Zsh의 대체제도 아닙니다.
GitLab은 SSH를 통해 Git LFS 인증을 지원합니다.
요구 사항#
GitLab Shell은 Go로 작성되어 있으며, 빌드하려면 Go 컴파일러가 필요합니다. 빌드 및 테스트에는 여전히 Ruby가 필요하지만, 실행에는 필요하지 않습니다.
GitLab Shell은 Linux 패키지 설치 시 port 22에서 실행됩니다. 일반 SSH 서비스를 사용하려면 다른 포트로 구성하세요.
현재 버전의 Go를 다운로드하여 설치하세요. Go 릴리즈 정책을 따르며 다음을 지원합니다:
-
현재 안정 버전.
-
이전 두 메이저 버전.
버전#
GitLab Shell과 관련된 두 가지 버전 파일:
GitLab 팀 구성원은 #announcements 내부 Slack 채널을 모니터링할 수도 있습니다.
GitLab Shell 작동 방식#
SSH를 통해 GitLab 서버에 접근하면 GitLab Shell은 다음을 수행합니다:
-
사전 정의된 Git 명령어(
git push,git pull,git fetch)로 제한합니다. -
GitLab Rails API를 호출하여 인가 여부와 리포지터리가 있는 Gitaly 서버를 확인합니다.
-
SSH 클라이언트와 Gitaly 서버 사이에서 데이터를 복사합니다.
HTTP(S)를 통해 GitLab 서버에 접근하면 gitlab-workhorse로 연결됩니다.
SSH를 통한 git pull#
%%{init: { "fontFamily": "GitLab Sans" }}%%
graph LR
A[Git pull] --> |via SSH| B[gitlab-shell]
B -->|API call| C[gitlab-rails
authorization]
C -->|accept or decline| D[Gitaly session]
SSH를 통한 git push#
git push 명령어는 gitlab-rails가 푸시를 수락한 이후에 실행됩니다:
%%{init: { "fontFamily": "GitLab Sans" }}%% graph LR subgraph User initiates A[Git push] -->|via SSH| B[gitlab-shell] end subgraph Gitaly B -->|establish Gitaly session| C[gitlab-shell pre-receive hook] C -->|API auth call| D[Gitlab-rails] D --> E[accept or decline push] end
authorized_keys 수정#
GitLab Shell은 클라이언트 머신의 authorized_keys 파일을 수정합니다.
GitLab Shell에 기여하기#
GitLab Shell에 기여하려면:
-
GitLab API 접근 및 내부 API를 통한 Redis에 연결 가능한지 확인합니다:
make check -
gitlab-shell바이너리를 컴파일하여bin/에 배치합니다:make compile -
make install을 실행하여gitlab-shell바이너리를 빌드하고 파일 시스템에 설치합니다. 기본 위치는/usr/local입니다. 변경하려면PREFIX와DESTDIR환경 변수를 설정하세요. -
단일 머신에서 소스로부터 GitLab을 설치하려면
make setup을 실행하세요. GitLab Shell 바이너리를 컴파일하고, 파일 시스템의 여러 경로가 올바른 권한으로 존재하는지 확인합니다. 설치 방법 문서에서 이 명령어를 실행하도록 지시하지 않는 한 실행하지 마세요.
자세한 내용은 CONTRIBUTING.md를 참조하세요.
테스트 실행#
기여 시 테스트를 실행하세요:
bundle install 및 make test로 테스트를 실행합니다.
Gofmt 실행: make verify
테스트와 검증을 모두 실행합니다(기본 Makefile 타깃):
bundle install
make validate
필요한 경우 Gitaly를 구성합니다.
로컬 테스트를 위한 Gitaly 구성#
일부 테스트에는 Gitaly 서버가 필요합니다.
docker-compose.yml 파일은 포트 8075에서 Gitaly를 실행합니다.
테스트에 Gitaly 위치를 알려주려면 GITALY_CONNECTION_INFO를 설정하세요:
export GITALY_CONNECTION_INFO='{"address": "tcp://localhost:8075", "storage": "default"}'
make test
GITALY_CONNECTION_INFO가 설정되지 않은 경우 테스트 스위트는 여전히 실행되지만, Gitaly가 필요한 테스트는 건너뜁니다. CI 환경에서는 항상 테스트가 실행됩니다.
속도 제한#
GitLab Shell은 Git 작업에 대해 사용자 계정 및 프로젝트별로 속도 제한을 수행합니다.
GitLab Shell은 Git 작업 요청을 수락한 후 Redis로 지원되는 Rails 속도 제한기를 호출합니다. user + project가 속도 제한을 초과하면, GitLab Shell은 해당 user + project에 대한 추가 연결 요청을 차단합니다.
속도 제한기는 Git 명령어(플러밍) 수준에서 적용됩니다. 각 명령어는 분당 600회의 속도 제한이 있습니다. 예를 들어, git push는 분당 600회이고, git pull은 별도로 분당 600회입니다.
동일한 플러밍 명령어인 git-upload-pack을 사용하기 때문에, git pull과 git clone은 속도 제한 목적상 실질적으로 동일한 명령어입니다.
Gitaly에도 속도 제한기가 있지만, GitLab Shell(Rails)에서 속도 제한이 초과되면 Gitaly에 대한 호출이 이루어지지 않습니다.
GitLab Shell의 로그#
일반적으로 로그를 검사하면 GitLab Shell 또는 gitlab-sshd 세션의 구조는 파악할 수 있지만 내용은 파악할 수 없습니다. 몇 가지 가이드라인:
-
로깅에는
gitlab.com/gitlab-org/labkit/log를 사용합니다. -
항상 상관 관계 ID를 포함하세요.
-
로그 메시지는 불변이고 고유해야 합니다. 부수적인 정보는
log.WithField,log.WithFields, 또는log.WithError를 사용하여 필드에 포함하세요. -
성공 케이스와 오류 케이스 모두 로그를 남기세요.
-
로그가 너무 많은 것이 너무 적은 것보다 낫습니다. 메시지가 너무 장황하다고 느껴지면 메시지를 제거하기 전에 로그 수준을 낮추는 것을 고려하세요.
GitLab.com#
GitLab.com에서 gitlab-shell 흐름 다이어그램:
%%{init: { "fontFamily": "GitLab Sans" }}%%
graph LR
a2 --> b2
a2 --> b3
a2 --> b4
b2 --> c1
b3 --> c1
b4 --> c1
c2 --> d1
c2 --> d2
c2 --> d3
d1 --> e1
d2 --> e1
d3 --> e1
a1[Cloudflare] --> a2[TCP
load balancer]
e1[Git]
subgraph HAProxy Fleet
b2[HAProxy]
b3[HAProxy]
b4[HAProxy]
end
subgraph GKE
c1[Internal TCP<br/> load balancer<br/>port 2222] --> c2[GitLab-shell<br/> pods]
end
subgraph Gitaly
d1[Gitaly]
d2[Gitaly]
d3[Gitaly]
end
GitLab Shell 아키텍처#
%%{init: { "fontFamily": "GitLab Sans" }}%% sequenceDiagram participant Git on client participant SSH server participant AuthorizedKeysCommand participant GitLab Shell participant Rails participant Gitaly participant Git on server
Note left of Git on client: git fetch
Git on client->>+SSH server: ssh git fetch-pack request
SSH server->>+AuthorizedKeysCommand: gitlab-shell-authorized-keys-check git AAAA...
AuthorizedKeysCommand->>+Rails: GET /internal/api/authorized_keys?key=AAAA...
Note right of Rails: Lookup key ID
Rails-->>-AuthorizedKeysCommand: 200 OK, command="gitlab-shell upload-pack key_id=1"
AuthorizedKeysCommand-->>-SSH server: command="gitlab-shell upload-pack key_id=1"
SSH server->>+GitLab Shell: gitlab-shell upload-pack key_id=1
GitLab Shell->>+Rails: GET /internal/api/allowed?action=upload_pack&key_id=1
Note right of Rails: Auth check
Rails-->>-GitLab Shell: 200 OK, { gitaly: ... }
GitLab Shell->>+Gitaly: SSHService.SSHUploadPack request
Gitaly->>+Git on server: git upload-pack request
Note over Git on client,Git on server: Bidirectional communication between Git client and server
Git on server-->>-Gitaly: git upload-pack response
Gitaly -->>-GitLab Shell: SSHService.SSHUploadPack response
GitLab Shell-->>-SSH server: gitlab-shell upload-pack response
SSH server-->>-Git on client: ssh git fetch-pack response