You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

部署Firebase Cloud Function遇Cloud Run健康检查失败求助

部署Firebase Cloud Function时的端口监听错误排查

问题描述

我尝试部署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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 14:39:55