GitLab의 파일 스토리지
GitLab v19.1파일 업로드, 저장 및 검색 처리를 위해 CarrierWave gem을 사용합니다. 파일 업로드는 workhorse에 의해 가속화되어야 합니다. 파일 업로드가 사용되는 곳은 컨텍스트에 따라 다양합니다: 인스턴스 로고 (로그인/회원가입 페이지에 표시되는 로고)
파일 업로드, 저장 및 검색 처리를 위해 CarrierWave gem을 사용합니다.
파일 업로드는 workhorse에 의해 가속화되어야 합니다. 자세한 내용은 업로드 개발 문서를 참조하세요.
파일 업로드가 사용되는 곳은 컨텍스트에 따라 다양합니다:
- 시스템
인스턴스 로고 (로그인/회원가입 페이지에 표시되는 로고)
-
헤더 로고 (내비게이션 바에 표시되는 로고)
-
그룹
그룹 아바타
- 사용자
사용자 아바타
-
사용자 스니펫 첨부 파일
-
프로젝트
프로젝트 아바타
-
이슈/머지 리퀘스트/노트 Markdown 첨부 파일
-
이슈/머지 리퀘스트/노트 레거시 Markdown 첨부 파일
-
CI 아티팩트 (아카이브, 메타데이터, 트레이스)
-
LFS 오브젝트
-
머지 리퀘스트 diff
-
Design Management 디자인 썸네일
-
토픽
토픽 아바타
디스크 스토리지#
GitLab은 처음부터 모든 것을 로컬 디스크에 저장했습니다. 이전 버전에서 디렉터리 위치가 변경되었지만, 아직 100% 표준화되지는 않았습니다. 아래에서 확인할 수 있습니다:
| 설명 | DB 저장 여부 | 상대 경로 (CarrierWave.root 기준) | 업로더 클래스 | 모델 타입 |
|---|---|---|---|---|
| 인스턴스 로고 | yes | uploads/-/system/appearance/logo/:id/:filename | AttachmentUploader | Appearance |
| 헤더 로고 | yes | uploads/-/system/appearance/header_logo/:id/:filename | AttachmentUploader | Appearance |
| 그룹 아바타 | yes | uploads/-/system/group/avatar/:id/:filename | AvatarUploader | Group |
| 사용자 아바타 | yes | uploads/-/system/user/avatar/:id/:filename | AvatarUploader | User |
| 사용자 스니펫 첨부 파일 | yes | uploads/-/system/personal_snippet/:id/:random_hex/:filename | PersonalFileUploader | Snippet |
| 프로젝트 아바타 | yes | uploads/-/system/project/avatar/:id/:filename | AvatarUploader | Project |
| 토픽 아바타 | yes | uploads/-/system/projects/topic/avatar/:id/:filename | AvatarUploader | Topic |
| 이슈/머지 리퀘스트/노트 Markdown 첨부 파일 | yes | uploads/:hash_project_id/:random_hex/:filename | FileUploader | Project |
| Design Management 디자인 썸네일 | yes | uploads/-/system/design_management/action/image_v432x230/:id/:filename | DesignManagement::DesignV432x230Uploader | DesignManagement::Action |
| CI 아티팩트 (CE) | yes | shared/artifacts/:disk_hash[0..1]/:disk_hash[2..3]/:disk_hash/:year_:month_:date/:job_id/:job_artifact_id (:disk_hash는 project_id의 SHA256 다이제스트) | JobArtifactUploader | Ci::JobArtifact |
| LFS 오브젝트 (CE) | yes | shared/lfs-objects/:hex/:hex/:object_hash | LfsObjectUploader | LfsObject |
| 외부 머지 리퀘스트 diff | yes | shared/external-diffs/merge_request_diffs/mr-:parent_id/diff-:id | ExternalDiffUploader | MergeRequestDiff |
| 이슈어블 메트릭 이미지 | yes | uploads/-/system/issuable_metric_image/file/:id/:filename | IssuableMetricImageUploader | IssuableMetricImage |
CI 아티팩트와 LFS 오브젝트는 CE와 EE에서 다르게 동작합니다. CE에서는 GitlabUploader를 상속하고,
EE에서는 ObjectStorage를 상속하며 S3 API 호환 오브젝트 스토어에 파일을 저장합니다.
이슈, 머지 리퀘스트(MR), 노트의 Markdown 첨부 파일은 프로젝트 ID의 해시를 사용하는 해시 스토리지를 사용합니다.
모든 업로드를 한 번에 오브젝트 스토리지로 마이그레이션하기 위한 올인원 Rake 태스크를 제공합니다. 새로운 업로더 클래스 또는 모델 타입이 도입되는 경우, 해당 Rake 태스크 호출을 카테고리 목록에 반드시 추가하세요.
경로 세그먼트#
파일은 여러 위치에 저장되며 다양한 경로 체계를 사용합니다.
GitlabUploader에서 파생된 모든 클래스는 다음 경로 세그먼트 스키마를 따라야 합니다:
| GitlabUploader
| ----------------------- + ------------------------- + --------------------------------- + -------------------------------- |
| `<gitlab_root>/public/` | `uploads/-/system/` | `user/avatar/:id/` | `:filename` |
| ----------------------- + ------------------------- + --------------------------------- + -------------------------------- |
| `CarrierWave.root` | `GitlabUploader.base_dir` | `GitlabUploader#dynamic_segment` | `CarrierWave::Uploader#filename` |
| | `CarrierWave::Uploader#store_dir` | |
| FileUploader
| ----------------------- + ------------------------- + --------------------------------- + -------------------------------- |
| `<gitlab_root>/shared/` | `artifacts/` | `:year_:month/:id` | `:filename` |
| `<gitlab_root>/shared/` | `snippets/` | `:secret/` | `:filename` |
| ----------------------- + ------------------------- + --------------------------------- + -------------------------------- |
| `CarrierWave.root` | `GitlabUploader.base_dir` | `GitlabUploader#dynamic_segment` | `CarrierWave::Uploader#filename` |
| | `CarrierWave::Uploader#store_dir` | |
| | | `FileUploader#upload_path` |
| ObjectStore::Concern (store = remote)
| ----------------------- + ------------------------- + ----------------------------------- + -------------------------------- |
| `<bucket_name>` | <ignored> | `user/avatar/:id/` | `:filename` |
| ----------------------- + ------------------------- + ----------------------------------- + -------------------------------- |
| `#fog_dir` | `GitlabUploader.base_dir` | `GitlabUploader#dynamic_segment` | `CarrierWave::Uploader#filename` |
| | | `ObjectStorage::Concern#store_dir` | |
| | | `ObjectStorage::Concern#upload_path` |
RecordsUploads::Concern 컨선은 GitlabUploader가 저장하는 모든 파일에 대해 Upload 항목을 생성하며,
GitlabUploader#dynamic_path를 사용하여 경로의 동적 부분을 유지합니다.
이후 Upload#build_uploader 메서드를 사용하여 파일을 조작할 수 있습니다.
오브젝트 스토리지#
GitlabUploader에서 파생된 클래스에 ObjectStorage::Concern을 포함시키면 해당 업로더에 대한 오브젝트 스토리지를 활성화할 수 있습니다.
업로더에서 오브젝트 스토리지를 활성화하려면 1) RecordsUpload::Concern을 포함하고 ObjectStorage::Extension::RecordsUploads를 prepend하거나,
2) 업로더를 마운트하고 <mount>_store라는 새 필드를 생성해야 합니다.
CarrierWave::Uploader#store_dir은 다음과 같이 재정의됩니다:
-
스토어가 LOCAL인 경우:
GitlabUploader.base_dir+GitlabUploader.dynamic_segment -
스토어가 REMOTE인 경우:
GitlabUploader.dynamic_segment(네임스페이스에 버킷 이름이 사용됨)
ObjectStorage::Extension::RecordsUploads 사용#
이 컨선은 아직 포함되지 않은 경우 RecordsUploads::Concern을 포함합니다.
ObjectStorage::Concern 업로더는 올바른 오브젝트 스토어를 선택하기 위해 일치하는 Upload를 검색합니다.
Upload는 각 스토어(LOCAL/REMOTE)에 대해 #store_dirs + identifier를 사용하여 매핑됩니다.
class SongUploader < GitlabUploader
include RecordsUploads::Concern
include ObjectStorage::Concern
prepend ObjectStorage::Extension::RecordsUploads
...
end
class Thing < ActiveRecord::Base
mount :theme, SongUploader # we have a great theme song!
...
end
마운트된 업로더 사용#
ObjectStorage::Concern은 model.<mount>_store 속성을 쿼리하여 올바른 오브젝트 스토어를 선택합니다.
이 칼럼은 모델 스키마에 반드시 존재해야 합니다.
class SongUploader < GitlabUploader
include ObjectStorage::Concern
...
end
class Thing < ActiveRecord::Base
attr_reader :theme_store # this is an ActiveRecord attribute
mount :theme, SongUploader # we have a great theme song!
def theme_store
super || ObjectStorage::Store::LOCAL
end
...
end