部署Firebase Cloud Function遇Cloud Run健康检查失败求助
问题描述
我尝试部署Firebase Cloud Function,但遇到如下错误:
Could not create or update Cloud Run service workwithfile, Container Healthcheck failed. Revision 'workwithfile-00004-jej' is not ready and cannot serve traffic. The user-provided container failed to start and listen on the port defined provided by the PORT=8080 environment variable. Logs for this revision might contain more information.
执行firebase emulators:start时代码运行正常,但执行firebase deploy --only functions时出现上述错误,请求帮助排查原因。
部署的代码
/* eslint-disable no-unused-vars */ /* eslint-disable require-jsdoc */ /** * Import function triggers from their respective submodules: * * const {onCall} = require("firebase-functions/v2/https"); * const {onDocumentWritten} = require("firebase-functions/v2/firestore"); * * See a full list of supported triggers at https://firebase.google.com/docs/functions */ // Create and deploy your first functions // https://firebase.google.com/docs/functions/get-started // exports.helloWorld = onRequest((request, response) => { // logger.info("Hello logs!", {structuredData: true}); // response.send("Hello from Firebase!"); // }); // The Cloud Functions for Firebase SDK to create Cloud Functions and triggers. // const {logger} = require("firebase-functions"); const {onRequest} = require("firebase-functions/v2/https"); // The Firebase Admin SDK to access Firestore. const {initializeApp} = require("firebase-admin/app"); const {createClient} = require("@supabase/supabase-js"); initializeApp(); const subaBaseURL = "the url removed for secuirty "; // eslint-disable-next-line max-len const subaBaseKey = "the key removed for secuirty"; const supabase = createClient(subaBaseURL, subaBaseKey); const {setGlobalOptions} = require("firebase-functions/v2"); setGlobalOptions({maxInstances: 10}); // Take the text parameter passed to this HTTP endpoint and insert it into // Firestore under the path /messages/:documentId/original exports.workwithfile = onRequest(async (req, res) => { // Grab the text parameter. const name = req.query.text; const lastSlashIndex = name.lastIndexOf("/"); const result = lastSlashIndex !== -1 ? name.substring(lastSlashIndex + 1) : name; if (result == "countries.json") { const publicUrl = supabase.storage .from("uploads") .getPublicUrl(`geojsons/countries/${result}`); const finalURL = publicUrl.data.publicUrl; const response = await fetch(finalURL); if (response.ok) { await deleteOldCountries().then(async (e) => { const jsonData = await response.json(); const toSendData = []; for (const item of jsonData.features) { const objectToAdd = { name_en: item.properties.name_en, name_ar: item.properties.name_ar, file_id: item.properties.file_id, title_ar: item.properties.title_ar, title_en: item.properties.title_en, object_id: item.properties.OBJECTID, file_url: finalURL, }; toSendData.push(objectToAdd); } await updateCountriesData(toSendData); }); } else { console.error( `Failed to fetch JSON from ${finalURL}. Status: ${response.status}`, ); } } if (result == "governorates.json") { const publicUrl = supabase.storage .from("uploads") .getPublicUrl(`geojsons/governorates/${result}`); const finalURL = publicUrl.data.publicUrl; const response = await fetch(finalURL); if (response.ok) { await deleteOldGovernorates().then(async (e) => { const jsonData = await response.json(); const toSendData = []; for (const item of jsonData.features) { const objectToAdd = { name_en: item.properties.name_en, name_ar: item.properties.name_ar, file_id: item.properties.file_id, title_ar: item.properties.title_ar, title_en: item.properties.title_en, parent_id: item.properties.parent_id, object_id: item.properties.OBJECTID, file_url: finalURL, }; toSendData.push(objectToAdd); } await updateGovernoratesData(toSendData); }); } else { console.error( `Failed to fetch JSON from ${finalURL}. Status: ${response.status}`, ); } } if (result.includes("level1")) { const publicUrl = supabase.storage .from("uploads") .getPublicUrl(`geojsons/level1/${result}`); const finalURL = publicUrl.data.publicUrl; const response = await fetch(finalURL); if (response.ok) { await deleteOldLevel1().then(async (e) => { const jsonData = await response.json(); const toSendData = []; for (const item of jsonData.features) { const objectToAdd = { name_en: item.properties.name_en, name_ar: item.properties.name_ar, file_id: item.properties.file_id, title_ar: item.properties.title_ar, title_en: item.properties.title_en, parent_id: item.properties.parent_id, object_id: item.properties.OBJECTID, file_url: finalURL, }; toSendData.push(objectToAdd); } await updateLevel1Data(toSendData); }); } else { console.error( `Failed to fetch JSON from ${finalURL}. Status: ${response.status}`, ); } } res.json({result: `file done proccing: ${result}.`}); }); async function updateCountriesData(toSendData) { const {data, error} = await supabase .from("countries") .insert(toSendData) .select(); } async function updateGovernoratesData(toSendData) { const {data, error} = await supabase .from("govs") .insert(toSendData) .select(); } async function updateLevel1Data(toSendData) { const {data, error} = await supabase .from("gov_sub_level_1") .insert(toSendData) .select(); } async function deleteOldCountries() { const {data: toDeleteData} = await supabase .from("countries") .select("*"); for (const singleCountry of toDeleteData) { const {data, error} = await supabase .from("countries") .delete() .eq("id", singleCountry.id); } } async function deleteOldGovernorates() { const {data: toDeleteData} = await supabase .from("govs") .select("*"); for (const singleGov of toDeleteData) { const {data, error} = await supabase .from("govs") .delete() .eq("id", singleGov.id); } } async function deleteOldLevel1() { const {data: toDeleteData} = await supabase .from("gov_sub_level_1") .select("*"); for (const singleItem of toDeleteData) { const {data, error} = await supabase .from("gov_sub_level_1") .delete() .eq("id", singleItem.id); } }
排查方案及解决方法
1. 补充fetch依赖
Firebase Cloud Functions运行的Node.js环境部分版本未内置fetch API,但本地模拟器使用的是你本地的Node.js环境(新版本已内置),这会导致本地正常、部署失败。
- 执行命令安装依赖:
npm install node-fetch - 在代码顶部添加导入语句:
const fetch = require('node-fetch');
2. 修正Supabase配置
代码中subaBaseURL末尾存在空格:"the url removed for secuirty ",这会导致Supabase客户端初始化失败,进而影响服务启动。
- 移除Supabase URL和密钥中的多余空格,确保配置参数准确无误。
3. 处理异步操作错误
代码中所有Supabase异步操作都未处理错误,一旦出现异常会直接导致函数崩溃,影响健康检查。
- 为所有Supabase操作添加错误处理,例如:
async function updateCountriesData(toSendData) { const {data, error} = await supabase .from("countries") .insert(toSendData) .select(); if (error) { console.error('更新countries数据失败:', error); throw error; } } - 在主函数外层添加try-catch,捕获未处理的异常:
exports.workwithfile = onRequest(async (req, res) => { try { // 原有的所有业务逻辑 res.json({result: `file done proccing: ${result}.`}); } catch (err) { console.error('函数执行出错:', err); res.status(500).json({error: '处理失败'}); } });
4. 查看详细日志
错误提示明确提到日志包含更多信息,可通过两种方式查看:
- 打开Firebase控制台,进入对应Cloud Function页面,点击「日志」标签页
- 使用命令行查看:
firebase functions:log
日志会显示容器启动时的具体错误(如依赖缺失、初始化失败等),是定位问题的核心依据。
内容的提问来源于stack exchange,提问作者Ahmed Wagdi

