{{< details >}}

  • Tier: 基础版, 专业版, 旗舰版
  • Offering: 私有化部署

{{< /details >}}

在管理作业产物时,你可能会遇到以下问题。

作业产物使用过多的磁盘空间

作业产物可能会比预期更快地填满你的磁盘空间。可能的原因包括:

在这些和其他情况下,确定最占用磁盘空间的项目,找出使用最多空间的产物类型,并在某些情况下手动删除作业产物以回收磁盘空间。

产物维护

产物维护是识别哪些产物已过期并可以删除的过程。

极狐GitLab 15.0 到 15.2 中禁用的维护

产物维护在极狐GitLab 15.0 中得到了显著改进,默认情况下通过功能标志引入。默认情况下,在极狐GitLab 15.3 中启用了这些标志

如果在极狐GitLab 15.0 到极狐GitLab 15.2 中产物维护似乎不起作用,你应该检查功能标志是否已启用。

要检查功能标志是否已启用:

  1. 启动一个 Rails 控制台

  2. 检查功能标志是否已启用。

    Feature.enabled?(:ci_detect_wrongly_expired_artifacts)
    Feature.enabled?(:ci_update_unlocked_job_artifacts)
    Feature.enabled?(:ci_job_artifacts_backlog_work)
    
  3. 如果任何功能标志被禁用,请启用它们:

    Feature.enable(:ci_detect_wrongly_expired_artifacts)
    Feature.enable(:ci_update_unlocked_job_artifacts)
    Feature.enable(:ci_job_artifacts_backlog_work)
    

这些更改包括在应该保留时将产物从 unlocked 切换到 locked

状态为 unknown 的产物

在更新维护之前创建的产物状态为 unknown。在它们过期后,这些产物不会被新的维护处理。

你可以检查数据库以确认你的实例是否有状态为 unknown 的产物:

  1. 启动一个数据库控制台:

    {{< tabs >}}

    {{< tab title=”Linux package (Omnibus)” >}}

    sudo gitlab-psql
    

    {{< /tab >}}

    {{< tab title=”Helm chart (Kubernetes)” >}}

    # Find the toolbox pod
    kubectl --namespace <namespace> get pods -lapp=toolbox
    # Connect to the PostgreSQL console
    kubectl exec -it <toolbox-pod-name> -- /srv/gitlab/bin/rails dbconsole --include-password --database main
    

    {{< /tab >}}

    {{< tab title=”Docker” >}}

    sudo docker exec -it <container_name> /bin/bash
    gitlab-psql
    

    {{< /tab >}}

    {{< tab title=”Self-compiled (source)” >}}

    sudo -u git -H psql -d gitlabhq_production
    

    {{< /tab >}}

    {{< /tabs >}}

  2. 运行以下查询:

    select expire_at, file_type, locked, count(*) from ci_job_artifacts
    where expire_at is not null and
    file_type != 3
    group by expire_at, file_type, locked having count(*) > 1;
    

如果返回记录,则表示有产物无法被维护作业处理。例如:

           expire_at           | file_type | locked | count
-------------------------------+-----------+--------+--------
 2021-06-21 22:00:00+00        |         1 |      2 |  73614
 2021-06-21 22:00:00+00        |         2 |      2 |  73614
 2021-06-21 22:00:00+00        |         4 |      2 |   3522
 2021-06-21 22:00:00+00        |         9 |      2 |     32
 2021-06-21 22:00:00+00        |        12 |      2 |    163

状态为 2 的产物为 unknown

清理 unknown 产物

处理所有 unknown 产物的 Sidekiq worker 在极狐GitLab 15.3 及更高版本中默认启用。它分析上面数据库查询返回的产物并确定哪些应该是 lockedunlocked。如果需要,产物将由该 worker 删除。

在极狐GitLab 私有化部署中可以启用该 worker:

  1. 启动一个 Rails 控制台

  2. 检查功能是否已启用。

    Feature.enabled?(:ci_job_artifacts_backlog_work)
    
  3. 如果需要,启用该功能:

    Feature.enable(:ci_job_artifacts_backlog_work)
    

该 worker 每七分钟处理 10,000 个 unknown 产物,或者大约 24 小时内处理两百万个。

有一个相关的 ci_job_artifacts_backlog_large_loop_limit 功能标志,导致 worker 处理 unknown 产物出现问题。不建议使用该标志。

@final 产物没有从对象存储中删除

在极狐GitLab 16.1 及更高版本中,产物直接上传到其最终存储位置,即 @final 目录,而不是先使用临时位置。

极狐GitLab 16.1 和 16.2 中的一个问题导致产物没有从对象存储中删除在它们过期时。过期产物的清理过程不会从 @final 目录中移除产物。此问题在极狐GitLab 16.3 及更高版本中已修复。

运行过极狐GitLab 16.1 或 16.2 一段时间的极狐GitLab 实例管理员可能会看到产物使用的对象存储增加。按照以下步骤检查并删除这些产物。

删除文件是一个两阶段过程:

  1. 识别哪些文件已成为孤儿
  2. 从对象存储中删除识别出的文件

列出孤立的作业产物

{{< tabs >}}

{{< tab title=”Linux package (Omnibus)” >}}

sudo gitlab-rake gitlab:cleanup:list_orphan_job_artifact_final_objects

{{< /tab >}}

{{< tab title=”Docker” >}}

docker exec -it <container-id> bash
gitlab-rake gitlab:cleanup:list_orphan_job_artifact_final_objects

要么写入挂载在容器中的持久卷中,要么在命令完成时将输出文件复制出会话。

{{< /tab >}}

{{< tab title=”Self-compiled (source)” >}}

sudo -u git -H bundle exec rake gitlab:cleanup:list_orphan_job_artifact_final_objects RAILS_ENV=production

{{< /tab >}}

{{< tab title=”Helm chart (Kubernetes)” >}}

# find the pod
kubectl get pods --namespace <namespace> -lapp=toolbox

# open the Rails console
kubectl exec -it -c toolbox <toolbox-pod-name> bash
gitlab-rake gitlab:cleanup:list_orphan_job_artifact_final_objects

命令完成时,将文件复制出会话到持久存储。

{{< /tab >}}

{{< /tabs >}}

Rake 任务有一些适用于所有类型极狐GitLab 部署的附加功能:

  • 可以中断扫描对象存储。进度记录在 Redis 中,用于从该点继续扫描产物。
  • 默认情况下,Rake 任务生成一个 CSV 文件: /opt/gitlab/embedded/service/gitlab-rails/tmp/orphan_job_artifact_final_objects.csv
  • 设置环境变量以指定不同的文件名:

    # Packaged GitLab
    sudo su -
    FILENAME='custom_filename.csv' gitlab-rake gitlab:cleanup:list_orphan_job_artifact_final_objects
    
  • 如果输出文件已经存在(默认或指定的文件),它会将条目附加到文件中。
  • 每行包含 object_path,object_size 字段,以逗号分隔,没有文件头。例如:

    35/13/35135aaa6cc23891b40cb3f378c53a17a1127210ce60e125ccf03efcfdaec458/@final/1a/1a/5abfa4ec66f1cc3b681a4d430b8b04596cbd636f13cdff44277211778f26,201
    

删除孤立的作业产物

{{< tabs >}}

{{< tab title=”Linux package (Omnibus)” >}}

sudo gitlab-rake gitlab:cleanup:delete_orphan_job_artifact_final_objects

{{< /tab >}}

{{< tab title=”Docker” >}}

docker exec -it <container-id> bash
gitlab-rake gitlab:cleanup:delete_orphan_job_artifact_final_objects
  • 命令完成时,将输出文件复制出会话,或将其写入已挂载的卷。

{{< /tab >}}

{{< tab title=”Self-compiled (source)” >}}

sudo -u git -H bundle exec rake gitlab:cleanup:delete_orphan_job_artifact_final_objects RAILS_ENV=production

{{< /tab >}}

{{< tab title=”Helm chart (Kubernetes)” >}}

# find the pod
kubectl get pods --namespace <namespace> -lapp=toolbox

# open the Rails console
kubectl exec -it -c toolbox <toolbox-pod-name> bash
gitlab-rake gitlab:cleanup:delete_orphan_job_artifact_final_objects
  • 当命令完成时,将文件复制出会话到持久存储。

{{< /tab >}}

{{< /tabs >}}

以下内容适用于所有类型的极狐GitLab 部署:

  • 使用 FILENAME 变量指定输入文件名。默认情况下,脚本查找: /opt/gitlab/embedded/service/gitlab-rails/tmp/orphan_job_artifact_final_objects.csv
  • 脚本删除文件时,它会输出一个 CSV 文件,包含已删除的文件:
    • 文件与输入文件位于同一目录
    • 文件名前缀为 deleted_from--。例如:deleted_from--orphan_job_artifact_final_objects.csv
    • 文件中的行是:object_path,object_size,object_generation/version,例如:

      35/13/35135aaa6cc23891b40cb3f378c53a17a1127210ce60e125ccf03efcfdaec458/@final/1a/1a/5abfa4ec66f1cc3b681a4d430b8b04596cbd636f13cdff44277211778f26,201,1711616743796587
      

列出具有特定过期时间(或无过期时间)产物的项目和构建

使用 Rails 控制台,你可以找到具有以下情况的作业产物的项目:

  • 没有过期日期。
  • 过期日期超过未来 7 天。

类似于删除产物,使用以下示例时间范围并根据需要进行更改:

  • 7.days.from_now
  • 10.days.from_now
  • 2.weeks.from_now
  • 3.months.from_now
  • 1.year.from_now

以下每个脚本也限制搜索结果为 50 个结果,使用 .limit(50),但此数字也可以根据需要更改:

# Find builds & projects with artifacts that never expire
builds_with_artifacts_that_never_expire = Ci::Build.with_downloadable_artifacts.where(artifacts_expire_at: nil).limit(50)
builds_with_artifacts_that_never_expire.find_each do |build|
  puts "Build with id #{build.id} has artifacts that don't expire and belongs to project #{build.project.full_path}"
end

# Find builds & projects with artifacts that expire after 7 days from today
builds_with_artifacts_that_expire_in_a_week = Ci::Build.with_downloadable_artifacts.where('artifacts_expire_at > ?', 7.days.from_now).limit(50)
builds_with_artifacts_that_expire_in_a_week.find_each do |build|
  puts "Build with id #{build.id} has artifacts that expire at #{build.artifacts_expire_at} and belongs to project #{build.project.full_path}"
end

按存储的作业产物总大小列出项目

通过在 Rails 控制台中运行以下代码,列出按存储的作业产物总大小排序的前 20 个项目:

include ActionView::Helpers::NumberHelper
ProjectStatistics.order(build_artifacts_size: :desc).limit(20).each do |s|
  puts "#{number_to_human_size(s.build_artifacts_size)} \t #{s.project.full_path}"
end

你可以通过修改 .limit(20) 来更改列出的项目数量为你想要的数量。

列出单个项目中最大的产物

通过在 Rails 控制台中运行以下代码,列出单个项目中 50 个最大的作业产物:

include ActionView::Helpers::NumberHelper
project = Project.find_by_full_path('path/to/project')
Ci::JobArtifact.where(project: project).order(size: :desc).limit(50).map { |a| puts "ID: #{a.id} - #{a.file_type}: #{number_to_human_size(a.size)}" }

你可以通过修改 .limit(50) 来更改列出的作业产物数量为你想要的数量。

列出单个项目中的产物

列出单个项目的产物,按产物大小排序。输出包括:

  • 创建产物的作业 ID
  • 产物大小
  • 产物文件类型
  • 产物创建日期
  • 产物的磁盘位置
p = Project.find_by_id(<project_id>)
arts = Ci::JobArtifact.where(project: p)

list = arts.order(size: :desc).limit(50).each do |art|
    puts "Job ID: #{art.job_id} - Size: #{art.size}b - Type: #{art.file_type} - Created: #{art.created_at} - File loc: #{art.file}"
end

要更改列出的作业产物数量,请更改 limit(50) 中的数字。

删除旧的构建和产物

{{< alert type=”warning” >}}

这些命令永久删除数据。在生产环境中运行它们之前,你应该先在测试环境中尝试,并备份实例,以便在需要时恢复。

{{< /alert >}}

删除项目的旧产物

此步骤还会删除用户选择保留的产物

project = Project.find_by_full_path('path/to/project')
builds_with_artifacts =  project.builds.with_downloadable_artifacts
builds_with_artifacts.where("finished_at < ?", 1.year.ago).each_batch do |batch|
  batch.each do |build|
    Ci::JobArtifacts::DeleteService.new(build).execute
  end

  batch.update_all(artifacts_expire_at: Time.current)
end

在极狐GitLab 15.3 及更早版本中,使用以下代码代替:

project = Project.find_by_full_path('path/to/project')
builds_with_artifacts =  project.builds.with_downloadable_artifacts
builds_with_artifacts.where("finished_at < ?", 1.year.ago).each_batch do |batch|
  batch.each do |build|
    build.artifacts_expire_at = Time.current
    build.erase_erasable_artifacts!
  end
end

全实例删除旧产物

此步骤还会删除用户选择保留的产物

builds_with_artifacts = Ci::Build.with_downloadable_artifacts
builds_with_artifacts.where("finished_at < ?", 1.year.ago).each_batch do |batch|
  batch.each do |build|
    Ci::JobArtifacts::DeleteService.new(build).execute
  end

  batch.update_all(artifacts_expire_at: Time.current)
end

在极狐GitLab 15.3 及更早版本中,使用以下代码代替:

builds_with_artifacts =  Ci::Build.with_downloadable_artifacts
builds_with_artifacts.where("finished_at < ?", 1.year.ago).each_batch do |batch|
  batch.each do |build|
    build.artifacts_expire_at = Time.current
    build.erase_erasable_artifacts!
  end
end

删除项目的旧作业日志和产物

project = Project.find_by_full_path('path/to/project')
builds =  project.builds
admin_user = User.find_by(username: 'username')
builds.where("finished_at < ?", 1.year.ago).each_batch do |batch|
  batch.each do |build|
    print "Ci::Build ID #{build.id}... "

    if build.erasable?
      Ci::BuildEraseService.new(build, admin_user).execute
      puts "Erased"
    else
      puts "Skipped (Nothing to erase or not erasable)"
    end
  end
end

全实例删除旧作业日志和产物

builds = Ci::Build.all
admin_user = User.find_by(username: 'username')
builds.where("finished_at < ?", 1.year.ago).each_batch do |batch|
  batch.each do |build|
    print "Ci::Build ID #{build.id}... "

    if build.erasable?
      Ci::BuildEraseService.new(build, admin_user).execute
      puts "Erased"
    else
      puts "Skipped (Nothing to erase or not erasable)"
    end
  end
end

在极狐GitLab 15.3 及更早版本中,将 Ci::BuildEraseService.new(build, admin_user).execute 替换为 build.erase(erased_by: admin_user)

1.year.ago 是 Rails ActiveSupport::Duration 方法。从较长的时间开始,以减少意外删除仍在使用的产物的风险。根据需要重新运行删除操作,使用较短的时间段,例如 3.months.ago2.weeks.ago7.days.ago

方法 erase_erasable_artifacts! 是同步的,执行时产物会立即被删除; 它们不会被安排到后台队列中。

删除产物不会立即回收磁盘空间

删除产物时,过程分为两个阶段:

  1. 标记为准备删除Ci::JobArtifact 记录从数据库中删除,并转换为具有未来 pick_up_at 时间戳的 Ci::DeletedObject 记录。
  2. 从存储中移除:产物文件保留在磁盘上,直到 Ci::ScheduleDeleteObjectsCronWorker worker 处理 Ci::DeletedObject 记录并物理删除文件。

删除是有意限制的,以防止系统资源过载:

  • worker 每小时运行一次,在第 16 分钟标记。
  • 它按批次处理对象,最大并发作业数为 20。
  • 每个删除的对象都有一个 pick_up_at 时间戳,用于确定何时变得 符合物理删除的条件

对于大规模删除,物理清理可能需要相当长的时间 才能完全回收磁盘空间。对于非常大的删除,清理可能需要几天时间。

如果你需要快速回收磁盘空间,可以加快产物删除。

加快产物移除

如果你需要在删除大量产物后快速回收磁盘空间, 你可以绕过标准的调度限制并加快删除过程。

{{< alert type=”warning” >}}

这些命令会给系统带来很大的负担,如果你正在删除大量产物。

{{< /alert >}}

# Set the pick_up_date to the current time on all artifacts
# This will mark them for immediate deletion
Ci::DeletedObject.update_all(pick_up_at: Time.current)

# Get the count of artifacts marked for deletion
Ci::DeletedObject.where("pick_up_at < ?", Time.current)

# Delete the artifacts from disk
while Ci::DeletedObject.where("pick_up_at < ?", Time.current).count > 0
  Ci::DeleteObjectsService.new.execute
  sleep(10)
end

# Get the count of artifacts marked for deletion (should now be zero)
Ci::DeletedObject.count

删除旧的流水线

{{< alert type=”warning” >}}

这些命令永久删除数据。在生产环境中运行它们之前, 考虑寻求支持工程师的指导。你还应该先在测试环境中尝试它们 并备份实例,以便在需要时恢复。

{{< /alert >}}

删除流水线还会删除该流水线的:

  • 作业产物
  • 作业日志
  • 作业元数据
  • 流水线元数据

删除作业和流水线元数据有助于减少数据库中 CI 表的大小。 CI 表通常是实例数据库中最大的表。

删除项目的旧流水线

project = Project.find_by_full_path('path/to/project')
user = User.find(1)
project.ci_pipelines.where("finished_at < ?", 1.year.ago).each_batch do |batch|
  batch.each do |pipeline|
    puts "Erasing pipeline #{pipeline.id}"
    ::Ci::DestroyPipelineService.new(pipeline.project, user).execute(pipeline)
  end
end

全实例删除旧流水线

user = User.find(1)
Ci::Pipeline.where("finished_at < ?", 1.year.ago).each_batch do |batch|
  batch.each do |pipeline|
    puts "Erasing pipeline #{pipeline.id} for project #{pipeline.project_id}"
    ::Ci::DestroyPipelineService.new(pipeline.project, user).execute(pipeline)
  end
end

作业产物上传失败,错误 500

如果你使用对象存储来存储产物,而作业产物上传失败,请查看:

  • 作业日志中的错误消息类似于:

    WARNING: Uploading artifacts as "archive" to coordinator... failed id=12345 responseStatus=500 Internal Server Error status=500 token=abcd1234
    
  • workhorse 日志中的错误消息类似于:

    {"error":"MissingRegion: could not find region configuration","level":"error","msg":"error uploading S3 session","time":"2021-03-16T22:10:55-04:00"}
    

在这两种情况下,你可能需要将 region 添加到作业产物的对象存储配置

作业产物上传失败,500 Internal Server Error (Missing file)

整合对象存储中,包含文件夹路径的存储桶名称不受支持。 例如,bucket/path。如果存储桶名称中包含路径,你可能会收到类似以下的错误:

WARNING: Uploading artifacts as "archive" to coordinator... POST https://gitlab.example.com/api/v4/jobs/job_id/artifacts?artifact_format=zip&artifact_type=archive&expire_in=1+day: 500 Internal Server Error (Missing file)
FATAL: invalid argument

如果作业产物在使用整合对象存储时上传失败并出现上述错误,请确保你为每种数据类型使用单独的存储桶

作业产物在使用 Windows 挂载时上传失败,FATAL: invalid argument

如果你使用 CIFS 的 Windows 挂载来存储作业产物,当 runner 尝试上传产物时,你可能会看到 invalid argument 错误:

WARNING: Uploading artifacts as "dotenv" to coordinator... POST https://<your-gitlab-instance>/api/v4/jobs/<JOB_ID>/artifacts: 500 Internal Server Error  id=1296 responseStatus=500 Internal Server Error status=500 token=*****
FATAL: invalid argument

要解决此问题,你可以尝试:

  • 切换到 ext4 挂载而不是 CIFS。
  • 升级到至少包含许多与 CIFS 文件租约相关的重要错误修复的 Linux 内核 5.15。
  • 对于较旧的内核,使用 nolease 挂载选项来禁用文件租约。

使用配额显示不正确的产物存储使用情况

有时,产物存储使用情况显示的总存储空间不正确。要为实例中的所有项目重新计算产物使用统计信息,你可以运行此后台脚本:

gitlab-rake gitlab:refresh_project_statistics_build_artifacts_size[https://example.com/path/file.csv]

https://example.com/path/file.csv 文件必须列出你希望重新计算产物存储使用情况的所有项目的项目 ID。使用此格式为文件:

PROJECT_ID
1
2

在脚本运行时,产物使用值可能会波动为 0。重新计算后,使用情况应按预期显示。

产物下载流程图

以下流程图说明了作业产物如何工作。这些 图假设对象存储已配置为作业产物。

禁用代理下载

proxy_download 设置为 false时,极狐GitLab 将 runner 重定向到使用预签名 URL 从对象存储下载产物。通常情况下,runner 从源直接获取数据会更快,因此通常推荐此配置。 它还应减少带宽使用,因为数据不必被极狐GitLab 获取并发送给 runner。然而,它确实需要给予 runner 直接访问对象存储的权限。

请求流程如下所示:

sequenceDiagram autonumber participant C as Runner participant O as Object Storage participant W as Workhorse participant R as Rails participant P as PostgreSQL C->>+W: GET /api/v4/jobs/:id/artifacts?direct_download=true Note over C,W: gitlab-ci-token@<CI_JOB_TOKEN> W-->+R: GET /api/v4/jobs/:id/artifacts?direct_download=true Note over W,R: gitlab-ci-token@<CI_JOB_TOKEN> R->>P: Look up job for CI_JOB_TOKEN R->>P: Find user who triggered job R->>R: Does user have :read_build access? alt Yes R->>W: Send 302 redirect to object storage presigned URL R->>C: 302 redirect C->>O: GET <presigned URL> else No R->>W: 401 Unauthorized W->>C: 401 Unauthorized end

在此图中:

  1. 首先,runner 尝试通过使用 GET /api/v4/jobs/:id/artifacts 端点获取作业产物。runner 在第一次尝试时附加 direct_download=true 查询参数以指示它能够直接从对象存储下载。可以在 runner 配置中通过 FF_USE_DIRECT_DOWNLOAD 功能标志禁用直接下载。此标志默认设置为 true

  2. runner 使用 HTTP 基本身份验证发送 GET 请求使用 gitlab-ci-token 用户名和自动生成的 CI/CD 作业令牌作为密码。此令牌由极狐GitLab 生成并在作业开始时提供给 runner。

  3. GET 请求传递给极狐GitLab API,它在数据库中查找令牌并找到触发作业的用户。

  4. 在步骤 5-8 中:

    • 如果用户有权访问构建,则极狐GitLab 生成一个预签名 URL 并发送 302 重定向,Location 设置为该 URL。runner 跟随 302 重定向并下载产物。

    • 如果找不到作业或用户没有权访问作业,则 API 返回 401 未授权。

    如果 runner 收到以下 HTTP 状态码,则不会重试:

    • 200 OK
    • 401 未授权
    • 403 禁止
    • 404 未找到

    然而,如果 runner 收到其他状态码,例如 500 错误,它会再次尝试下载产物两次,每次尝试之间间隔 1 秒。随后的尝试省略 direct_download=true

启用代理下载

如果 proxy_downloadtrue,极狐GitLab 始终从对象存储获取产物并将数据发送给 runner,即使 runner 发送 direct_download=true 查询参数。代理下载可能是可取的,如果 runner 具有受限的网络访问。

以下图类似于禁用代理下载的示例,除了在步骤 6-9 中,极狐GitLab 不会向 runner 发送 302 重定向。相反,极狐GitLab 指示 Workhorse 获取数据并将其流式传输回 runner。从 runner 的角度来看,最初的 GET 请求到 /api/v4/jobs/:id/artifacts 直接返回二进制数据。

sequenceDiagram autonumber participant C as Runner participant O as Object Storage participant W as Workhorse participant R as Rails participant P as PostgreSQL C->>+W: GET /api/v4/jobs/:id/artifacts?direct_download=true Note over C,W: gitlab-ci-token@<CI_JOB_TOKEN> W-->+R: GET /api/v4/jobs/:id/artifacts?direct_download=true Note over W,R: gitlab-ci-token@<CI_JOB_TOKEN> R->>P: Look up job for CI_JOB_TOKEN R->>P: Find user who triggered job R->>R: Does user have :read_build access? alt Yes R->>W: SendURL with object storage presigned URL W->>O: GET <presigned URL> O->>W: <artifacts data> W->>C: <artifacts data> else No R->>W: 401 Unauthorized W->>C: 401 Unauthorized end

413 Request Entity Too Large 错误

如果产物太大,作业可能会失败并出现以下错误:

Uploading artifacts as "archive" to coordinator... too large archive <job-id> responseStatus=413 Request Entity Too Large status=413" at end of a build job on pipeline when trying to store artifacts to <object-storage>.

你可能需要:

  • 增加最大产物大小
  • 如果你使用 NGINX 作为代理服务器,请增加文件上传大小限制,默认限制为 1 MB。在 NGINX 配置文件中设置更高的 client-max-body-size 值。