Firebase 存储工件
Posted
技术标签:
【中文标题】Firebase 存储工件【英文标题】:Firebase storage artifacts 【发布时间】:2020-12-14 03:06:11 【问题描述】:我试图了解eu.artifacts.%PROJECT NAME%.appspot.com
是什么。它目前占用了我每天 5GB 限制的 800MB 存储空间。它仅包含 application/octet-stream 类型的文件。此存储桶是自动创建的,文件路径为 eu.artifacts....appspot.com/containers/images。 2 个最重的文件有 200mb 和 130mb。我尝试删除它,但它又自动创建了。用户可以在我的网站上上传图片,但该存储桶目前只需要大约 10mb 即可包含所有用户图片。
所以我的问题是:这个桶是干什么用的,为什么这么重?
【问题讨论】:
遇到同样的问题并开始影响我的结算,有什么想法吗? 正如弗兰克已经指出的那样,这是函数存储方式最近发生的变化。以下是更多信息的链接:firebase.google.com/support/faq#expandable-10 我找到的最有用的解释:cloud.google.com/functions/pricing?authuser=0#deployment_costs 【参考方案1】:firebaser 在这里
如果您使用 Cloud Functions,您看到的文件与最近构建运行时(适用于 Node 10 及更高版本)的方式发生的变化有关。
Cloud Functions 现在使用 Cloud Build 为您的 Cloud Functions 创建运行时(适用于节点 10 及更高版本)。 Cloud Build 反过来使用 Container Registry 存储这些运行时,将它们存储在您项目下的新 Cloud Storage 存储桶中。
如需了解更多信息,另请参阅 Why will I need a billing account to use Node.js 10 or later for Cloud Functions for Firebase?Firebase 定价常见问题解答中的此条目
另请参阅thread on the firebase-talk mailing list,了解这些工件。
? 更新:其他一些答案建议从存储桶中删除工件,甚至设置生命周期管理以自动执行此操作。这会导致对 Container Registry 中这些工件的引用悬空,从而破坏未来的构建。
要安全地删除工件,请从容器注册表 console(它位于 gcf
文件夹下)或使用 script 删除容器。然后,这也会从您的存储桶中删除工件。
自 CLI 9.14 版起,firebase deploy
进程会在部署后自动清理其容器映像。因此,如果您升级到最新版本,则不应再在存储桶中获得额外的工件。
【讨论】:
奇怪的是,firebase 没有给我们任何控制权,只是增加了存储的使用,几乎迫使我们在不知不觉中付款。 剩下的问题是:如何删除那些过时的工件? Firebase 中没有文档与这些相关。我有一个使用 18 GB 存储空间的项目,因为我的团队最近致力于云功能。在我看来,这不是一个好的开发者体验。 好问题。我们如何删除未使用的? 我的项目的工件文件每天使用 500MB,当免费层高达 5GB 时,我将被收取 0.01 美元的费用。有人可以解释为什么会这样吗?除了这些自动生成的文件,我没有将云存储用于其他用途。 据我了解(自撰写此答案以来),免费套餐仅适用于您的默认存储桶。由于容器存储在其他存储桶中,因此它们不属于免费层。另请参阅 Doug 的答案:***.com/questions/63893413/… 和此处:***.com/questions/63884429/…【参考方案2】:我咨询了 GCP 支持,这里有一些事情
Cloud Functions 导致存储使用量激增 由于这些工件未存储在默认存储桶中,即使您存储的总字节数未达到免费层限制,它们也会向您收费 删除https://console.cloud.google.com/storage/browser 处的工件桶。据支持人员介绍关于工件存储桶,您实际上可以摆脱它们,因为它们存储的是函数的先前版本。但是,我不建议删除 "gcf-sources..." 存储桶,因为它包含当前图像,因此删除此存储桶会破坏您的功能。
我尝试将其全部删除,到目前为止它没有造成麻烦。如果以后有问题我会更新。
编辑 201118:请参阅下面的评论,您可能需要保留存储桶,同时删除其中的所有内容。
【讨论】:
在我的例子中,gcf-sources 大约需要 99.3KB,主要问题是另一个“us.artifacts...”,到目前为止它使用了大约 500MB。那么这是在每个功能部署中生成的吗? @yo1995 @Mr.DMX 我不确定,但我认为是的。此外,在清理工件存储桶后,Firebase 仪表板需要 3 天时间才能刷新……但最终它显示出相当低的使用率。 @WeijingJayLin 他们应该,但似乎 Firebase 开发人员仍在努力。每个支持人员 > 我们的工程团队正在努力进行自动删除,我建议您关注发行说明或我们的博客,了解平台的新功能和改进。 我删除了工件,现在我无法再部署新功能。我得到:部署错误。构建失败:构建错误详细信息不可用。此外,在日志中它告诉我工件上有 404。有什么解决办法吗? @samueldple Waiting 为我解决了这个问题。但我联系了支持人员,得到的回复是:“解决此问题的一种选择是单独部署功能。然后在设置功能映像后,您可以再次部署所有这些。删除映像是可选的,一天对象生命周期很好,您可以通过单独部署函数来解决此问题。请记住,由于未找到函数映像,因此部署有时可能会遇到类似这样的问题。”【参考方案3】:添加到@yo1995 我咨询了 Firebase 支持,他们确认不应删除工件存储桶。基本上,工件用于帮助构建要存储在“gcf-sources”存储桶中的最终图像。
直接引用它们 "您可以随意删除"XX.artifacts"中的内容,但请不要动bucket,它将在接下来的部署周期中使用。"
如果您完全删除工件存储桶,可能会出现一些意外行为。 此外“团队正在努力自动清理此存储桶,但在发布解决方案之前需要解决一些限制。”
我暂时将存储桶设置为自动删除超过 1 天的文件。
【讨论】:
你不应该删除这些。我有一个 7 天的删除生命周期窗口,我在部署时遇到如下错误:ERROR: build step 3 "us.gcr.io/fn-img/buildpacks/nodejs12/builder:nodejs12_20201201_20_RC00" failed: step exited with non-zero status: 46
@xaphod 这真的很奇怪。我将我的删除生命周期设置为 1 天,并且我的功能部署良好(澳大利亚地区、美国多地区、亚洲多地区)。我什至特意用新擦拭的工件桶进行了测试,部署仍然不受影响。 (云功能也可以正常运行)我认为您的错误原因可能是其他原因。
你使用的是节点 12 的功能吗?
你使用的是节点 12 的功能吗?
@xaphod 是的,我的功能是节点 12【参考方案4】:
添加到@yo1995 的响应中,您可以删除存储桶中的工件,而无需进入 GCP。留在 Firebase,你去存储,然后“添加一个桶”。从那里,您将看到导入 gcp 和工件存储桶的选项。接下来,您可以相应地删除存储桶中的工件。
根据收到的一些 cmets,重要的是 不要删除存储桶。而是只删除存储桶中的工件!
【讨论】:
谢谢。我不明白他们为什么要这样隐藏它。我很高兴我在调试时查看了我的用法。我的活动文件存储空间可能是 5mb,但工件存储空间远远超过 700mb。 @elersong 同样在这里,我距离部署还有一周的时间,我看到 1.7GB 的存储使用量我震惊地发现它是工件。 不要这样做,它会导致错误。见上文 删除成功,目前没有看到任何错误 我尝试先删除一些旧的并保留一些其他的,这导致构建失败。但是,如果您删除所有文件,则不会有问题。【参考方案5】:如何减少存储空间
所以有一个很好的answer 来解决这个问题,但如何解决它需要进一步深入研究。
为了帮助未来的开发者切入正题,以下是在 GCP 中将以下规则添加到您的项目后应该看到的结果
橙色线是us-artifacts.<your-project>.appspot.com
存储桶。
解决问题的步骤
-
导航到https://console.cloud.google.com/
打开与 Firebase 项目对应的 GCP 项目
在菜单中,选择存储 -> 浏览器
点击有问题的
us-artifacts.<your-project>.appspot.com
桶
转到“生命周期”标签并添加 3 天的生命周期
警告
Firebase 使用的容器会反向引用以前的容器,因此如果您设置了 3 天的时间段并且您的 Firebase 部署函数开始失败,您将需要更新函数的本地名称以包含版本控制,并指定构建标志删除旧版本,从您的 firebase.json 中删除它们,或手动删除过时的函数。
使用版本化 API 类型函数
在您的入口点中,假设 index.ts
,并假设您已使用
admin.initializeApp(functions.config().firebase)
import * as functions from 'firebase-functions'
// define the app as a cloud function called APIv1 build xxxxxx
export const APIv1b20201202 = functions.https.onRequest(main)
main
是您应用的名称
在你的firebase.json
...
"hosting":
"public": "dist",
"ignore": ["firebase.json", "**/.*", "**/node_modules/**", "**/tests/**"],
"rewrites": [
"source": "/api/v1/**",
"function": "APIv1b2021202"
]
,
...
或者,手动更新
# Deploy new function called APIv11
$ firebase deploy --only functions:APIv11
# Wait until deployment is done; now both APIv11 and APIv10 are running
# Delete APIv10
$ firebase functions:delete APIv10
【讨论】:
警告部分很有趣。在什么情况下,firebase 部署功能会开始失败?我想避免这种版本控制魔法。 构建容器使用分层文件来有效地缓存您的函数执行环境。其中一些缓存似乎有几天或几周的有效期,因此如果它仍然有效,函数 deploy 将查找缓存的版本。如果您将其删除(并且您无法告诉 firebase 您已将其删除),则构建将失败。版本控制只是强制完全重建执行环境【参考方案6】:在与 firebase 团队进行一些研究并通过电子邮件发送后,这是向我提出的建议。
我们知道 Cloud Build 不会自动删除旧工件,因此它的大小会不断增加,作为一种解决方法,我建议删除存储桶中的文件以减少任何可能的费用。
您可以将文件删除到上述存储桶中,转到 GCP 控制台(使用与 Firebase 控制台相同的凭据)-> 选择正确的项目 -> 从左上角菜单中选择存储 -> 浏览器。 您将看到属于您项目的所有存储桶,单击您喜欢的存储桶,您可以从那里删除文件。
您可以尝试的另一个选项是管理存储桶的对象生命周期。当对象满足生命周期规则中指定的所有条件时,可以选择删除对象,这里是link,其中有一个关于此选项的示例。这样bucket对象就会被自动删除。
【讨论】:
【参考方案7】:我创建了一个名为storage_artifacts_lifecycle.json
的配置文件,内容如下:
"lifecycle":
"rule": [
"action": "type": "Delete" ,
"condition":
"age": 21
]
我使用以下命令配置我的存储生命周期:
gsutil lifecycle set ./firebase/storage_artifacts_lifecycle.json gs://us.artifacts.$MY_PROJECT_ID.appspot.com
我在运行后验证它的结果
gsutil lifecycle get gs://us.artifacts.$MY_PROJECT_ID.appspot.com
希望对大家有所帮助!
【讨论】:
【参考方案8】:Firebase 表示他们已发布修复程序(截至 2021 年 6 月):
https://github.com/firebase/firebase-tools/issues/3404#issuecomment-865270081
Fix 在下一个版本的 firebase-tools 中,应该会在今天发布。
修复:
运行npm i -g firebase-tools
。
在 Cloud Storage https://console.cloud.google.com/storage/browser/ 中浏览您的包含(查找名为 gcf-sources-*****-us-central1
的存储桶)
任何通过firebase deploy --only functions
删除的函数似乎都会自动删除工件,但如果您通过 UI 删除它们,它们仍会保留。
【讨论】:
【参考方案9】:我对该主题进行了一些研究,并找到了最适合我的解决方案 - 我在每次部署 Firebase 函数之前运行的脚本。该脚本扫描我的容器图像并:
保留带有latest
标签的那些。
也删除除最后一张以外的所有图片。
这种方法是半自动化的。无论如何,存储只有在我部署时才会增长,所以它对我来说非常有效。
脚本是用 javascript 编写的,适用于 node 和 gcloud cli 可用的环境。
const spawn = require("child_process").spawn;
const KEEP_AT_LEAST = 2;
const CONTAINER_REGISTRIES = [
"gcr.io/<your project name>",
"eu.gcr.io/<your project name>/gcf/europe-west3"
];
async function go(registry)
console.log(`> $registry`);
const images = await command(`gcloud`, [
"container",
"images",
"list",
`--repository=$registry`,
"--format=json",
]);
for (let i = 0; i < images.length; i++)
console.log(` $images[i].name`);
const image = images[i].name;
let tags = await command(`gcloud`, [
"container",
"images",
"list-tags",
image,
"--format=json",
]);
const totalImages = tags.length;
// do not touch `latest`
tags = tags.filter(( tags ) => !tags.find((tag) => tag === "latest"));
// sorting by date
tags.sort((a, b) =>
const d1 = new Date(a.timestamp.datetime);
const d2 = new Date(b.timestamp.datetime);
return d2.getTime() - d1.getTime();
);
// keeping at least X number of images
tags = tags.filter((_, i) => i >= KEEP_AT_LEAST);
console.log(` For removal: $tags.length/$totalImages`);
for (let j = 0; j < tags.length; j++)
console.log(
` Deleting: $formatImageTimestamp(tags[j]) | $tags[j].digest`
);
await command("gcloud", [
"container",
"images",
"delete",
`$image@$tags[j].digest`,
"--format=json",
"--quiet",
"--force-delete-tags",
]);
function command(cmd, args)
return new Promise((done, reject) =>
const ps = spawn(cmd, args);
let result = "";
ps.stdout.on("data", (data) =>
result += data;
);
ps.stderr.on("data", (data) =>
result += data;
);
ps.on("close", (code) =>
if (code !== 0)
console.log(`process exited with code $code`);
try
done(JSON.parse(result));
catch (err)
done(result);
);
);
function formatImageTimestamp(image)
const year, month, day, hour, minute = image.timestamp;
return `$year-$month-$day $hour:$minute`;
(async function ()
for (let i = 0; i < CONTAINER_REGISTRIES.length; i++)
await go(CONTAINER_REGISTRIES[i]);
)();
它运行以下命令:
# finding images
gcloud container images list --repository=<your repository>
# getting metadata
gcloud container images list-tags <image name>
# deleting images
gcloud container images delete <image name>@<digest> --quiet --force-delete-tags
描述我的发现的博客文章可在此处找到https://krasimirtsonev.com/blog/article/firebase-gcp-saving-money
【讨论】:
【参考方案10】:作为替代方案,您可以创建生命周期规则来删除文件夹内的对象。 将年龄设置为 1 天。因此它将删除文件夹中超过 1 天老化的所有对象。 lifeCycle rulw
SetCondition
【讨论】:
这肯定会破坏以后的事情。您将在函数部署时遇到错误,如下所示:“错误:构建步骤 3”us.gcr.io/fn-img/buildpacks/nodejs12/builder:nodejs12_20201201_20_RC00“失败:步骤以非零状态退出:46” 我添加了一个 1 天的生命周期规则并得到了@xaphod 提到的错误。不建议这样做,因为我必须删除所有功能并一一重新部署它们 - 停机时间很多:(以上是关于Firebase 存储工件的主要内容,如果未能解决你的问题,请参考以下文章
未找到具有提供的路径的文件:build.不会上传任何工件。 - 将 React App 部署到 Firebase 的 Github Action
将图像链接从 Firebase 存储存储到 Firebase 数据库时出错