极狐 GitLab

对象存储

Tier: 基础版,专业版,旗舰版

Offering: 私有化部署

极狐GitLab 支持使用对象存储服务存储多种类型的数据。推荐优先于 NFS,并且在大多数大型部署中,对象存储通常更高效、更可靠且更具可扩展性。

要配置对象存储,您有两种选择:

如果您的数据存储在本地,请参阅如何迁移至对象存储

对象存储提供商支持#

极狐GitLab 使用 Fog 库进行对象存储,并支持以下三种连接类型。不支持其他 Fog 提供商。

连接类型provider使用场景
S3 兼容AWSAmazon S3 及任何与 S3 兼容的服务
Google Cloud StorageGoogleGoogle Cloud Storage
Azure Blob StorageAzureRMAzure Blob Storage

如果您的对象存储服务与其中一种连接类型兼容,请使用下方相应的连接设置进行配置。提供商的选择由您决定。

有活跃测试覆盖的提供商#

极狐GitLab 积极测试以下提供商:

社区记录的提供商#

以下提供商由社区用户记录。极狐GitLab 未对它们进行测试。提供的配置示例仅为方便起见。如果您使用这些提供商并遇到问题,极狐GitLab 支持可能无法提供帮助。

为所有对象类型配置一个统一的存储连接(合并表单)#

大多数对象类型,如 CI 产物、LFS 文件和上传附件,都可以通过为对象存储指定一个统一的凭据并配置多个存储桶来存储。

对于极狐GitLab Helm Charts,请参阅如何配置合并表单

使用合并表单配置对象存储有许多优点:

使用合并表单时,直接上传会自动启用。因此,只能使用以下提供商:

合并表单配置不能用于备份或 Mattermost。备份可以单独配置服务器端加密。请参阅完整支持列表的表格以了解支持的对象存储类型。

启用合并表单会为所有对象类型启用对象存储。如果并非所有存储桶都已指定,您可能会看到类似以下的错误:

plaintext
对象存储类型为 <object type> 的必须指定存储桶

如果您想对特定对象类型使用本地存储,可以为特定功能禁用对象存储

配置通用参数#

在合并表单中,object_store 部分定义了一组通用参数。

设置描述
enabled启用或禁用对象存储。
proxy_download设为 true启用所有已提供文件的代理服务。可减少出站流量,因为这允许客户端直接从远程存储下载,而不是代理所有数据。
connection下述的各种连接选项
storage_options保存新对象时使用的选项,例如服务器端加密
objects对象类型的特定配置

有关示例,请参阅使用合并表单和 Amazon S3 的完整示例

配置每种对象类型的参数#

每种对象类型必须至少定义其存储的存储桶名称。

下表列出了可以使用的有效 objects

每种对象类型下,可以定义三个参数:

设置是否必需?描述
bucket 是*对象类型的存储桶名称。如果 enabled 设为 false 则不需要。
enabled覆盖通用参数
proxy_download覆盖通用参数

有关示例,请参阅使用合并表单和 Amazon S3 的完整示例

为特定功能禁用对象存储#

如前所述,可以通过将 enabled 标志设置为 false 来为特定类型禁用对象存储。例如,为 CI 产物禁用对象存储:

ruby
gitlab_rails['object_store']['objects']['artifacts']['enabled'] = false

如果该功能被完全禁用,则不需要存储桶。例如,若使用以下设置禁用 CI 产物,则无需存储桶:

ruby
gitlab_rails['artifacts_enabled'] = false

为每种对象类型分别定义其自己的存储连接(存储特定表单)#

使用存储特定表单时,每种对象类型都定义自己的对象存储连接和配置。您应该改用合并表单,除非某些存储类型不受合并表单支持。当使用极狐GitLab Helm Charts 时,请参考 Charts 如何处理对象存储的合并表单

不支持在非合并表单中使用加密 S3 存储桶。如果使用,可能会遇到 ETag 不匹配错误

对于存储特定表单, 直接上传可能成为默认方式 因为它不需要共享文件夹。

对于合并表单不支持存储类型的,请参考以下指南:

对象存储类型是否受合并表单支持?
备份
容器镜像仓库(可选功能)
Mattermost
自动缩放 Runner 缓存(可选,用于提升性能)
安全文件
作业产物(包括归档的作业日志)
LFS 对象
上传
合并请求差异
软件包(可选功能)
依赖代理(可选功能)
Terraform 状态文件
Pages 内容

配置连接设置#

合并表单和存储特定表单都必须配置连接。以下部分描述了可以在 connection 设置中使用的参数。

兼容 S3 的提供商#

以下设置适用于使用 AWS 连接类型的 Amazon S3 和任何兼容 S3 的服务。当不直接使用 AWS 时,将 endpoint 设置为您提供商的 URL。

兼容 S3 的服务在实现 AWS S3 API 方面存在差异。极狐GitLab 会使用特定的 S3 行为,包括预签名 URL、多部分上传以及可选的分块签名流式传输,并非所有兼容 S3 的实现都能相同地支持这些行为。如果某个提供商在其他工具中可以工作但在极狐GitLab 中不行,最可能需要调整的设置是:

  • aws_signature_version
  • enable_signature_v4_streaming

连接设置与 fog-aws 提供的匹配:

设置描述默认值
provider对于兼容的主机始终为 AWSAWS
aws_access_key_idAWS 凭据,或兼容的。
aws_secret_access_keyAWS 凭据,或兼容的。
aws_signature_version要使用的 AWS 签名版本。24 均为有效选项。某些兼容 S3 的提供商可能需要 24
enable_signature_v4_streaming设置为 true 以启用使用 AWS V4 签名的 HTTP 分块传输。某些兼容 S3 的提供商需要将其设为 false。极狐GitLab 17.4 将默认值从 true 改为 falsefalse
regionAWS 区域。
host已弃用:请改用 endpoint。当不使用 AWS 时,用于兼容 S3 的主机。例如,localhoststorage.example.com。默认使用 HTTPS 和 443 端口。s3.amazonaws.com
endpoint当配置兼容 S3 的服务时,可以输入 URL,例如 http://127.0.0.1:9000。此设置优先于 host。对于合并表单,请始终使用 endpoint(可选)
path_style设置为 true 以使用 host/bucket_name/object 样式的路径,而非 bucket_name.host/object。对于需要路径样式寻址的兼容 S3 的服务,请设置为 true。对于 AWS S3,请保留为 falsefalse
use_iam_profile设置为 true 以使用 IAM 实例配置文件而非访问密钥。false
aws_credentials_refresh_threshold_seconds当在 IAM 中使用临时凭据时,设置自动刷新阈值(以秒为单位)。15
disable_imds_v2强制使用 IMDS v1,通过禁用检索 X-aws-ec2-metadata-token 的 IMDS v2 端点。false

S3 兼容性及已知故障模式#

声称兼容 S3 并不意味着该提供商能与极狐GitLab 正常工作。如果您在使用兼容 S3 的提供商时遇到错误,在提出支持请求前,请尝试以下调整:

  • 签名流式传输:某些提供商拒绝 AWS 签名 V4 流式传输使用的分块传输编码。请设置 enable_signature_v4_streaming: false
  • 签名版本:某些提供商不完全支持签名版本 4。请设置 aws_signature_version: 2
  • 路径样式 URL:某些提供商要求路径样式的存储桶寻址。请设置 path_style: true
  • ETag 验证:某些提供商返回的 ETag 与上传对象的 MD5 不匹配,而极狐GitLab 会验证此匹配。请参阅 ETag 不匹配

极狐GitLab 支持可以帮助排查配置问题,但无法保证解决已测试集合之外提供商的特定问题。

使用 Amazon 实例配置文件#

无需在对象存储配置中提供 AWS 访问密钥和秘密密钥,您可以将极狐GitLab 配置为使用 Amazon 身份和访问管理 (IAM) 角色来设置 Amazon 实例配置文件。使用此方法时,极狐GitLab 在每次访问 S3 存储桶时获取临时凭据,因此配置中无需硬编码的值。

前提条件:

设置实例配置文件:

  1. 创建具有必要权限的 IAM 角色。以下示例是为名为 test-bucket 的 S3 存储桶创建的角色示例:

    json
    1{ 2 "Version": "2012-10-17", 3 "Statement": [ 4 { 5 "Effect": "Allow", 6 "Action": [ 7 "s3:PutObject", 8 "s3:GetObject", 9 "s3:DeleteObject" 10 ], 11 "Resource": "arn:aws:s3:::test-bucket/*" 12 }, 13 { 14 "Effect": "Allow", 15 "Action": [ 16 "s3:ListBucket" 17 ], 18 "Resource": "arn:aws:s3:::test-bucket" 19 } 20 ] 21}
  2. 将此角色关联到托管您极狐GitLab 实例的 EC2 实例。

  3. 将极狐GitLab 的 use_iam_profile 配置选项设置为 true

加密 S3 存储桶#

当通过实例配置文件或合并表单配置时,极狐GitLab Workhorse 会将文件正确上传到启用了默认 SSE-S3 或 SSE-KMS 加密的 S3 存储桶。不支持 AWS KMS 密钥和 SSE-C 加密,因为这需要在每个请求中发送加密密钥

服务器端加密标头#

在 S3 存储桶上设置默认加密是启用加密的最简单方式,但您可能想设置存储桶策略以确保只上传加密的对象。为此,您必须在 storage_options 配置部分配置极狐GitLab 以发送适当的加密标头:

设置描述
server_side_encryption加密模式(AES256aws:kms)。
server_side_encryption_kms_key_idAmazon 资源名称。仅当在 server_side_encryption 中使用 aws:kms 时才需要。请参阅关于使用 KMS 加密的 Amazon 文档

与默认加密一样,这些选项仅在启用 Workhorse S3 客户端时有效。必须满足以下两个条件之一:

  • 连接设置中 use_iam_profiletrue
  • 正在使用合并表单。

如果在未启用 Workhorse S3 客户端的情况下使用服务器端加密标头,将会出现 ETag 不匹配错误

Google Cloud Storage (GCS)#

版本历史
  • universe_domain 设置在 极狐GitLab 18.9 中引入。

以下是 GCS 的有效连接参数:

设置描述示例
provider提供商名称。Google
google_projectGCP 项目名称。gcp-project-12345
google_json_key_locationJSON 密钥路径。/path/to/gcp-project-12345-abcde.json
google_json_key_stringJSON 密钥字符串。{ "type": "service_account", "project_id": "example-project-382839", ... }
google_application_default设置为 true 以使用 Google Cloud 应用程序默认凭据来定位服务账号凭据。
universe_domain用于 Google Cloud 请求的 Universe 域。使用此选项连接至 Google Cloud Dedicated 或其他非默认 Universe 域。googleapis.com

极狐GitLab 会按顺序读取 google_json_key_locationgoogle_json_key_string,最后是 google_application_default 的值。 它会使用其中第一个有值的设置。

服务账号必须拥有存储桶的访问权限。更多信息,请参阅 Cloud Storage 身份验证文档

Google Cloud 应用程序默认凭据#

Google Cloud 应用程序默认凭据 (ADC) 通常用于极狐GitLab,以使用默认服务账号或工作负载身份联合。将 google_application_default 设置为 true,并省略 google_json_key_locationgoogle_json_key_string

如果使用 ADC,请确保:

  • 您使用的服务账号拥有 iam.serviceAccounts.signBlob 权限。通常通过向服务账号授予 Service Account Token Creator 角色来实现。

  • 如果使用 Google Compute 虚拟机,确保它们具有正确的访问范围以访问 Google Cloud API。如果虚拟机没有正确的范围,错误日志可能会显示:

    shell
    Google::Apis::ClientError (insufficientPermissions: Request had insufficient authentication scopes.)

要使用客户管理的加密密钥的存储桶加密,请使用合并表单

  1. 编辑 /etc/gitlab/gitlab.rb 并添加以下行,替换为您的值:

    ruby
    gitlab_rails['object_store']['connection'] = { 'provider' => 'Google', 'google_project' => '<GOOGLE PROJECT>', 'google_json_key_location' => '<FILENAME>' }

    要使用 ADC,请改用 google_application_default

    ruby
    gitlab_rails['object_store']['connection'] = { 'provider' => 'Google', 'google_project' => '<GOOGLE PROJECT>', 'google_application_default' => true }

    要使用非默认 Universe 域(例如 Google Cloud Dedicated):

    ruby
    1gitlab_rails['object_store']['connection'] = { 2 'provider' => 'Google', 3 'google_project' => '<GOOGLE PROJECT>', 4 'google_application_default' => true, 5 'universe_domain' => '<UNIVERSE DOMAIN>' 6}
  2. 保存文件并重新配置极狐GitLab:

    shell
    sudo gitlab-ctl reconfigure

Azure 工作负载和托管标识#

版本历史
  • 在极狐GitLab 17.9 中引入。

要使用 Azure 工作负载标识托管标识,请从配置中省略 azure_storage_access_key。当 azure_storage_access_key 为空时,极狐GitLab 会尝试:

  1. 使用工作负载标识获取临时凭据。环境变量中应包含 AZURE_TENANT_IDAZURE_CLIENT_IDAZURE_FEDERATED_TOKEN_FILE
  2. 如果工作负载标识不可用,则从 Azure 实例元数据服务请求凭据。
  3. 获取用户委托密钥
  4. 使用该密钥生成 SAS 令牌以访问存储账户 Blob。

确保该标识已分配 Storage Blob Data Contributor 角色。

特定提供商的配置示例#

以下示例展示了需要非默认设置的特定 S3 兼容提供商的配置。对于此处未列出的任何 S3 兼容提供商,请使用基础 S3 兼容配置,并为您提供商设置适当的 endpoint

Oracle 云基础设施#

Oracle 云基础设施 S3 需要以下设置:

设置
enable_signature_v4_streamingfalse
path_styletrue

如果 enable_signature_v4_streaming 设置为 true,您可能会在 production.log 中看到以下错误:

plaintext
不支持 STREAMING-AWS4-HMAC-SHA256-PAYLOAD

Storj 网关 (SJ)#

Storj 网关不支持多线程复制(参见表中的 UploadPartCopy)。 虽然已计划实现,但在完成之前,您必须禁用多线程复制

Storj 网络提供了一个 S3 兼容的 API 网关。使用以下配置示例:

ruby
1gitlab_rails['object_store']['connection'] = { 2 'provider' => 'AWS', 3 'endpoint' => 'https://gateway.storjshare.io', 4 'path_style' => true, 5 'region' => 'eu1', 6 'aws_access_key_id' => 'ACCESS_KEY', 7 'aws_secret_access_key' => 'SECRET_KEY', 8 'aws_signature_version' => 2, 9 'enable_signature_v4_streaming' => false 10}

签名版本必须为 2。使用 v4 会导致 HTTP 411 Length Required 错误。 有关更多信息,请参阅议题 #4419

Hitachi Vantara HCP#

连接到 HCP 可能会返回错误,提示 SignatureDoesNotMatch - The request signature we calculated does not match the signature you provided. Check your HCP Secret Access key and signing method.。在这些情况下,请将 endpoint 设置为租户的 URL 而不是命名空间,并确保存储桶路径配置为 <namespace_name>/<bucket_name>

HCP 提供了一个 S3 兼容的 API。使用以下配置示例:

ruby
1gitlab_rails['object_store']['connection'] = { 2 'provider' => 'AWS', 3 'endpoint' => 'https://<tenant_endpoint>', 4 'path_style' => true, 5 'region' => 'eu1', 6 'aws_access_key_id' => 'ACCESS_KEY', 7 'aws_secret_access_key' => 'SECRET_KEY', 8 'aws_signature_version' => 4, 9 'enable_signature_v4_streaming' => false 10} 11 12# <namespace_name/bucket_name> 格式示例 13gitlab_rails['object_store']['objects']['artifacts']['bucket'] = '<namespace_name>/<bucket_name>'

Ceph RGW#

Ceph RGW 是 Ceph 的 S3 兼容 API。 使用以下配置示例:

ruby
1gitlab_rails['object_store']['connection'] = { 2 'provider' => 'AWS', 3 'endpoint' => 'https://rgw-ceph.example.com', 4 'region' => 'us-west-1', 5 'aws_access_key_id' => 'ACCESS_KEY', 6 'aws_secret_access_key' => 'SECRET_KEY', 7 'path_style': true 8}

要启用 Ceph RGW 的服务端加密,您必须使用 HTTPS 连接。Ceph 会拒绝通过非安全连接的加密请求。

使用统一形式和 Amazon S3 的完整示例#

以下示例使用 AWS S3 为所有支持的服务启用对象存储:

  1. 编辑 /etc/gitlab/gitlab.rb 并添加以下行,替换为您想要的值:

    ruby
    1# 统一对象存储配置 2gitlab_rails['object_store']['enabled'] = true 3gitlab_rails['object_store']['proxy_download'] = false 4gitlab_rails['object_store']['connection'] = { 5 'provider' => 'AWS', 6 'region' => 'eu-central-1', 7 'aws_access_key_id' => '<AWS_ACCESS_KEY_ID>', 8 'aws_secret_access_key' => '<AWS_SECRET_ACCESS_KEY>' 9} 10# 可选:以下行仅在需要服务端加密时才需要 11gitlab_rails['object_store']['storage_options'] = { 12 'server_side_encryption' => '<AES256 或 aws:kms>', 13 'server_side_encryption_kms_key_id' => '<arn:aws:kms:xxx>' 14} 15gitlab_rails['object_store']['objects']['artifacts']['bucket'] = 'gitlab-artifacts' 16gitlab_rails['object_store']['objects']['external_diffs']['bucket'] = 'gitlab-mr-diffs' 17gitlab_rails['object_store']['objects']['lfs']['bucket'] = 'gitlab-lfs' 18gitlab_rails['object_store']['objects']['uploads']['bucket'] = 'gitlab-uploads' 19gitlab_rails['object_store']['objects']['packages']['bucket'] = 'gitlab-packages' 20gitlab_rails['object_store']['objects']['dependency_proxy']['bucket'] = 'gitlab-dependency-proxy' 21gitlab_rails['object_store']['objects']['terraform_state']['bucket'] = 'gitlab-terraform-state' 22gitlab_rails['object_store']['objects']['ci_secure_files']['bucket'] = 'gitlab-ci-secure-files' 23gitlab_rails['object_store']['objects']['pages']['bucket'] = 'gitlab-pages'

    如果您使用的是 AWS IAM 实例配置文件,请省略 AWS 访问密钥和秘密访问密钥/值对。例如:

    ruby
    gitlab_rails['object_store']['connection'] = { 'provider' => 'AWS', 'region' => 'eu-central-1', 'use_iam_profile' => true }
  2. 保存文件并重新配置极狐GitLab:

    shell
    sudo gitlab-ctl reconfigure

迁移到对象存储#

要将现有本地数据迁移到对象存储,请参阅以下指南:

过渡到统一配置形式#

在存储特定配置中:

  • 所有类型对象(如 CI/CD 产物、LFS 文件和上传附件)的对象存储配置是独立进行的。
  • 对象存储连接参数(如密码和端点 URL)对于每种类型都会重复出现。

例如,Linux 软件包安装可能具有以下配置:

ruby
1# 原始对象存储配置 2gitlab_rails['artifacts_object_store_enabled'] = true 3gitlab_rails['artifacts_object_store_direct_upload'] = true 4gitlab_rails['artifacts_object_store_proxy_download'] = false 5gitlab_rails['artifacts_object_store_remote_directory'] = 'artifacts' 6gitlab_rails['artifacts_object_store_connection'] = { 'provider' => 'AWS', 'aws_access_key_id' => 'access_key', 'aws_secret_access_key' => 'secret' } 7gitlab_rails['uploads_object_store_enabled'] = true 8gitlab_rails['uploads_object_store_direct_upload'] = true 9gitlab_rails['uploads_object_store_proxy_download'] = false 10gitlab_rails['uploads_object_store_remote_directory'] = 'uploads' 11gitlab_rails['uploads_object_store_connection'] = { 'provider' => 'AWS', 'aws_access_key_id' => 'access_key', 'aws_secret_access_key' => 'secret' }

尽管这种方式提供了灵活性,使极狐GitLab 能够将对象存储在不同的云提供商上,但它也增加了不必要的复杂性和冗余。因为极狐GitLab Rails 和 Workhorse 组件都需要访问对象存储,统一配置形式避免了凭证的过度重复。

统一配置形式仅在所有原始形式的配置行均被省略时才会使用。要迁移到统一配置形式,请删除原始配置(例如 artifacts_object_store_enableduploads_object_store_connection)。

将对象迁移到不同的对象存储提供商#

你可能需要将极狐GitLab 在对象存储中的数据迁移到不同的对象存储提供商。以下步骤展示了如何使用 Rclone 完成此操作。

这些步骤假设你正在迁移 uploads 存储桶,但相同的流程也适用于其他存储桶。

先决条件:

  • 选择运行 Rclone 的计算机。根据你要迁移的数据量,Rclone 可能需要运行很长时间,因此应避免使用可能进入省电模式的笔记本电脑或台式机。你可以使用极狐GitLab 服务器来运行 Rclone。
  1. 安装 Rclone。

  2. 通过运行以下命令配置 Rclone:

    shell
    rclone config

    配置过程是交互式的。至少添加两个“远程存储”:一个是你当前数据所在的对象存储提供商(old),另一个是你要迁移到的提供商(new)。

  3. 确认你可以读取旧数据。以下示例引用 uploads 存储桶,但你的存储桶可能具有不同的名称:

    shell
    rclone ls old:uploads | head

    这应该会打印出当前存储在 uploads 存储桶中的部分对象列表。如果你收到错误,或者列表为空,请返回并使用 rclone config 更新你的 Rclone 配置。

  4. 执行初始复制。在此步骤中你无需将极狐GitLab 服务器下线。

    shell
    rclone sync -P old:uploads new:uploads
  5. 首次同步完成后,使用新对象存储提供商的 Web UI 或命令行界面验证新存储桶中是否有对象。如果没有,或者运行 rclone sync 时遇到错误,请检查你的 Rclone 配置并重试。

在你至少成功完成一次从旧位置到新位置的 Rclone 复制后,安排维护并将极狐GitLab 服务器下线。在维护窗口期间,你必须完成两件事:

  1. 执行一次最终的 rclone sync 运行,确保用户无法添加新对象,这样就不会在旧存储桶中留下任何数据。
  2. 更新极狐GitLab 服务器的对象存储配置,以使用新的 uploads 提供商。

文件系统存储的替代方案#

如果你正在 扩展 极狐GitLab 实施,或增加容错和冗余,你可能希望消除对块存储或网络文件系统的依赖。请参阅以下附加指南:

  1. 确保 git 用户主目录 位于本地磁盘上。
  2. 配置 SSH 密钥的数据库查找,以消除对共享 authorized_keys 文件的需求。
  3. 防止作业日志使用本地磁盘
  4. 禁用 Pages 本地存储

故障排除#

对象未包含在极狐GitLab 备份中#

正如 备份文档 中所指出的,对象不会包含在极狐GitLab 备份中。你可以启用对象存储提供商提供的备份来代替。

使用独立的存储桶#

为每种数据类型使用独立的存储桶是极狐GitLab 的推荐方法。这样可以确保极狐GitLab 存储的各种类型数据之间不会发生冲突。借助 Linux 软件包和自编译安装,可以将一个真实存储桶拆分为多个虚拟存储桶。如果你的对象存储桶名为 my-gitlab-objects,你可以将上传配置到 my-gitlab-objects/uploads,产物配置到 my-gitlab-objects/artifacts 等。应用程序会将其视为独立的存储桶。

基于 Helm 的安装需要独立的存储桶来 处理备份恢复

S3 API 兼容性问题#

如果你在使用与 S3 兼容的提供商时遇到错误,请参阅 S3 兼容性及已知故障模式 了解常见原因和配置调整。production.log 中出现 411 Length Required 错误通常是由签名流式传输引起的。将 enable_signature_v4_streaming: false 设置为 false 以解决此问题。

产物始终以 download 文件名下载#

下载的产物文件名是通过 GetObject 请求 中的 response-content-disposition 标头设置的。如果 S3 提供商不支持此标头,则下载的文件始终保存为 download

代理下载#

客户端可以通过接收预签名、有时限的 URL,或由极狐GitLab 代理将数据从对象存储传输给客户端来下载对象存储中的文件。直接从对象存储下载文件有助于减少极狐GitLab 需要处理的出口流量。

当文件存储在本地块存储或 NFS 上时,极狐GitLab 必须充当代理。这不是对象存储的默认行为。

proxy_download 设置控制此行为:默认值为 false。请在每个用例的文档中验证这一点。

如果你希望极狐GitLab 代理文件,请将 proxy_download 设置为 true。如果 proxy_download 设置为 true,极狐GitLab 服务器的性能可能会受到很大影响。极狐GitLab 的服务器部署将 proxy_download 设置为 false

proxy_downloadfalse 时,极狐GitLab 会返回一个 HTTP 302 重定向,包含一个预签名、有时限的对象存储 URL。这可能导致以下一些问题:

  • 如果极狐GitLab 使用非安全 HTTP 访问对象存储,客户端可能会产生 https->http 降级错误并拒绝处理重定向。解决方法是让极狐GitLab 使用 HTTPS。例如,LFS 会生成此错误:

    plaintext
    LFS: lfsapi/client: refusing insecure redirect, https->http
  • 客户端需要信任颁发对象存储证书的证书颁发机构,否则可能会返回常见的 TLS 错误,例如:

    plaintext
    x509: certificate signed by unknown authority
  • 客户端需要能够网络访问对象存储。网络防火墙可能会阻止访问。如果未建立此访问,可能出现的错误包括:

    plaintext
    Received status code 403 from server: Forbidden
  • 对象存储桶需要允许来自极狐GitLab 实例 URL 的跨源资源共享(CORS)访问。尝试在仓库页面加载 PDF 时可能会显示以下错误:

    plaintext
    An error occurred while loading the file. Please try again later.

    有关更多详细信息,请参阅 LFS 文档

预签名 URL 具有时限性,但不会与特定用户绑定。任何获得预签名 URL 的用户都可以在 URL 有效期内无需认证即可访问对象。直接下载还可能会在你的对象存储提供商与客户端之间产生带宽费用。

ETag 不匹配#

使用默认的极狐GitLab 设置时,某些与 S3 兼容的对象存储后端(例如阿里云)可能会生成 ETag mismatch 错误。

Amazon S3 加密#

如果你在 Amazon Web Services S3 上遇到此 ETag 不匹配错误,很可能是由于 存储桶上的加密设置 所致。要解决此问题,你有两个选择:

对于与 S3 兼容的服务,建议使用统一配置形式。某些服务还可能需要额外的服务器端配置,例如启用兼容模式,以解决 ETag 不匹配错误。

如果不使用统一配置形式或实例配置文件,极狐GitLab Workhorse 会使用未计算 Content-MD5 HTTP 标头的预签名 URL 将文件上传到 S3。为了确保数据未被损坏,Workhorse 会检查发送数据的 MD5 哈希值是否等于 S3 服务器返回的 ETag 标头。启用加密后,情况并非如此,这会导致 Workhorse 在上传过程中报告 ETag mismatch 错误。

当统一配置形式:

  • 与 S3 兼容的对象存储或实例配置文件一起使用时,Workhorse 会使用其内部 S3 客户端,该客户端具有 S3 凭证,从而可以计算 Content-MD5 标头。这消除了比较 S3 服务器返回的 ETag 标头的需要。
  • 不与 S3 兼容的对象存储一起使用时,Workhorse 会回退到使用预签名 URL。

Google Cloud Storage 加密#

版本历史
  • 引入于极狐GitLab 16.11。

在启用 客户管理的加密密钥(CMEK) 时,Google Cloud Storage(GCS)中也会发生 ETag 不匹配错误。

要使用 CMEK,请使用 统一配置形式

多线程复制#

极狐GitLab 使用 S3 Upload Part Copy API 来加速存储桶内文件的复制。此功能不受某些 S3 兼容提供商的支持,并且它们在上传期间会返回 404 错误。

要禁用多线程复制,请让具有 Rails 控制台访问权限 的极狐GitLab 管理员运行以下命令:

ruby
Feature.disable(:s3_multithreaded_uploads)

通过 Rails 控制台进行手动测试#

当你怀疑配置错误时,可以使用此方法验证对象存储连接。以下示例测试连接,写入一个测试对象,并将其读回。

  1. 启动 Rails 控制台

  2. 使用你在 /etc/gitlab/gitlab.rb 中设置的相同参数,以下列示例格式设置对象存储连接:

    使用现有上传配置的示例连接:

    ruby
    settings = Gitlab.config.uploads.object_store.connection.deep_symbolize_keys connection = Fog::Storage.new(settings)

    使用访问密钥的示例连接:

    ruby
    1connection = Fog::Storage.new( 2 { 3 provider: 'AWS', 4 region: 'eu-central-1', 5 aws_access_key_id: '<AWS_ACCESS_KEY_ID>', 6 aws_secret_access_key: '<AWS_SECRET_ACCESS_KEY>' 7 } 8)

    使用 AWS IAM 配置文件的示例连接:

    ruby
    1connection = Fog::Storage.new( 2 { 3 provider: 'AWS', 4 use_iam_profile: true, 5 region: 'us-east-1' 6 } 7)
  3. 指定要测试的存储桶名称,写入并最后读取一个测试文件。

    ruby
    dir = connection.directories.new(key: '<bucket-name-here>') f = dir.files.create(key: 'test.txt', body: 'test') pp f pp dir.files.head('test.txt')

启用额外的调试#

版本历史
  • AWS_DEBUG 环境变量支持引入于极狐GitLab 18.3。

你还可以启用额外的调试来查看 HTTP 请求。你应在 Rails 控制台 中执行此操作,以避免凭证泄露到日志文件中。以下展示了如何为不同的提供商启用请求调试:

设置 EXCON_DEBUG 环境变量:

ruby
ENV['EXCON_DEBUG'] = "1"

你还可以通过将 AWS_DEBUG 环境变量设置为 1,在极狐GitLab Workhorse 日志中启用 S3 HTTP 请求和响应标头日志记录。对于 Linux 软件包(Omnibus):

  1. 编辑 /etc/gitlab/gitlab.rb 并添加以下行:

    ruby
    gitlab_workhorse['env'] = { 'AWS_DEBUG' => '1' }
  2. 保存文件并重新配置极狐GitLab:

    shell
    sudo gitlab-ctl reconfigure

    与 S3 兼容的存储请求和响应标头将记录在 /var/log/gitlab/gitlab-workhorse/current 中。

重置 Geo 跟踪数据库以确保完整的对象一致性#

假设以下 Geo 场景:

  • 环境由一个 Geo 主节点和一个辅助节点组成。
  • 你在主节点上 迁移到了对象存储
    • 辅助节点使用独立的对象存储桶。
    • 已激活“允许此辅助站点复制对象存储上的内容”选项。

此类迁移可能导致在跟踪数据库中将对象标记为已同步,但对象存储中却实际缺失这些对象。在这种情况下,重置你的 Geo 辅助站点复制,以确保迁移后对象状态保持一致。

迁移到对象存储后出现不一致#

从本地存储迁移到对象存储时,可能会出现数据不一致。特别是在与 Geo 结合使用时,如果文件在迁移前已被手动删除,就会出现这种情况。

例如,实例管理员在本地文件系统上手动删除了几个产物。此类更改无法正确传播到数据库,从而导致不一致。迁移到对象存储后,这些不一致仍然存在,并可能造成摩擦。Geo 辅助节点可能会继续尝试复制这些文件,因为这些文件仍在数据库中被引用,但已不复存在。

在使用 Geo 时识别不一致#

假设以下 Geo 场景:

  • 环境由一个 Geo 主节点和一个辅助节点组成
  • 两个系统均已迁移到对象存储
    • 辅助节点使用与主节点相同的对象存储
    • 选项“允许此辅助站点复制对象存储上的内容”已停用
  • 在对象存储迁移之前,手动删除了多个上传文件
    • 在本示例中,有两张上传到议题的图片

在这种情况下,辅助节点不再需要复制任何数据,因为它使用与主节点相同的对象存储。由于存在不一致,管理员可以观察到辅助节点仍在尝试复制数据:

在主站点上:

  1. 在右上角,选择 管理员
  2. 在左侧边栏中,选择 Geo > 站点
  3. 查看 主站点 并检查验证信息。所有上传均已验证: Geo 站点仪表板显示主站点验证成功。
  4. 查看 辅助站点 并检查验证信息。请注意,有两个上传仍在同步,尽管辅助站点应该使用相同的对象存储。也就是说,它本不应该同步任何上传: Geo 站点仪表板显示辅助站点存在不一致。

清理不一致#

在执行任何删除命令之前,请确保你手头有最新且可用的备份。

基于之前的情况,多个 上传 导致了不一致,以下将以它们为例进行说明。

按照以下步骤正确删除潜在的残留数据:

  1. 将已识别的不一致项映射到其对应的模型名称。在后续步骤中需要使用模型名称。

    对象存储类型模型名称
    备份不适用
    容器镜像仓库不适用
    Mattermost不适用
    自动伸缩运行器缓存不适用
    安全文件Ci::SecureFile
    作业产物Ci::JobArtifactCi::PipelineArtifact
    LFS 对象LfsObject
    上传Upload
    合并请求差异MergeRequestDiff
    软件包Packages::PackageFile
    依赖代理DependencyProxy::BlobDependencyProxy::Manifest
    Terraform 状态文件Terraform::StateVersion
    Pages 内容PagesDeployment
  2. 启动一个 Rails 控制台

  3. 根据上一步中的模型名称查询所有仍存储在本地(而非对象存储中)的“文件”。在本例中,由于上传受到影响,因此使用模型名称 Upload。观察 openbao.png 如何仍存储在本地:

    ruby
    Upload.with_files_stored_locally
    ruby
    1#<Upload:0x00007d35b69def68 2 id: 108, 3 size: 13346, 4 path: "c95c1c9bf91a34f7d97346fd3fa6a7be/openbao.png", 5 checksum: "db29d233de49b25d2085dcd8610bac787070e721baa8dcedba528a292b6e816b", 6 model_id: 2, 7 model_type: "Project", 8 uploader: "FileUploader", 9 created_at: Wed, 02 Apr 2025 05:56:47.941319000 UTC +00:00, 10 store: 1, 11 mount_point: nil, 12 secret: "[FILTERED]", 13 version: 2, 14 uploaded_by_user_id: 1, 15 organization_id: nil, 16 namespace_id: nil, 17 project_id: 2, 18 verification_checksum: nil>]
  4. 使用已识别资源的 id 来正确删除它们。首先,通过使用 find 验证其是否为正确的资源,然后运行 destroy

    ruby
    Upload.find(108) Upload.find(108).destroy
  5. 可选地,通过再次运行 find 来验证资源是否已正确删除,此时不应再找到它:

    ruby
    Upload.find(108)
    ruby
    ActiveRecord::RecordNotFound: Couldn't find Upload with 'id'=108

对所有受影响的对象存储类型重复以上步骤。

多节点极狐GitLab 实例中缺少作业日志#

在具有多个 Rails 节点(运行 Web 服务或 Sidekiq 的服务器)的极狐GitLab 实例上,需要有一种机制,使作业日志在从 Runner 发送后可供所有节点使用。作业日志可以存储在本地磁盘或对象存储中。

如果未使用 NFS,并且 增量日志记录功能 尚未启用,则作业日志可能会丢失:

  1. 从运行器接收日志的节点将日志写入本地磁盘。
  2. 当极狐GitLab 尝试归档日志时,通常作业运行在其他服务器上,该服务器无法访问该日志。
  3. 上传到对象存储失败。

以下错误也可能会记录到 /var/log/gitlab/gitlab-rails/exceptions_json.log

yaml
1{ 2 "severity": "ERROR", 3 "exception.class": "Ci::AppendBuildTraceService::TraceRangeError", 4 "extra.build_id": 425187, 5 "extra.body_end": 12955, 6 "extra.stream_size": 720, 7 "extra.stream_class": {}, 8 "extra.stream_range": "0-12954" 9}

如果 CI 产物在多节点环境中写入对象存储,你必须 启用增量日志记录功能