部署至Kubernetes后Fetch请求出现ERR_CONNECTION_REFUSED问题排查
我开发了一个简单的Node.js Express应用,包含一个返回静态响应的端点,通过JavaScript的fetch方法在HTML页面展示响应信息,本地运行测试正常。但部署到Kubernetes后,使用Node_Public_IP:Node_Port的NodePort Service访问UI时,出现如下错误:
GET http://localhost:3000/live net::ERR_CONNECTION_REFUSED
TypeError: Failed to fetch
at window.onload (app-controller.js:6:5)
我尝试使用http://0.0.0.0:3000/live和http://127.0.0.1:3000/live也出现相同错误,请问需要修改什么?当尝试http://<name-of-kubernetes-service>:3000/live时,出现net::ERR_NAME_NOT_RESOLVED错误。
相关代码
Node.js 代码
const express = require('express'); const path = require('path'); const cors = require('cors') const app = express(); app.use(cors()) app.use(express.static(path.join(__dirname, '/'))); app.get('/', async (req, res) => { res.sendFile(path.join(__dirname, '/', 'index.html')); }); app.get('/live', function(req, res) { res.setHeader('Content-Type', 'application/json'); res.send({ "status": "OK, it is working!!!" }); }) app.listen(3000, () => { console.log("App running on port - 3000"); }) module.exports = app;
客户端代码及HTML
JavaScript(app-controller.js)
console.log('We are inside client.js'); /* on page load */ window.onload = function() { fetch("http://localhost:3000/live", { method: "GET" }) .then(function(res) { if (res.ok) { return res.json(); } throw new Error('Request failed'); }).catch(function(error) { console.log(error); }) .then(function(data) { document.getElementById('appStatus').innerHTML = ` ${data.status} ` }); };
HTML(index.html)
<html> <head> <meta name='viewport' content='width=device-width, initial-scale=1'> </head> <body> <div> <div class="middle"> <h1>Application Status</h2> <h3 id="appStatus">**app status placeholder**</h3> </div> </div> <script type="text/javascript" src="app-controller.js" charset="utf-8"></script> </body> </html>
Dockerfile
FROM node:18-alpine3.17 WORKDIR /usr/app COPY package*.json /usr/app/ RUN npm install COPY . . EXPOSE 3000 CMD [ "npm", "start" ]
解决方案
核心问题
前端代码硬编码了http://localhost:3000/live作为请求地址:
- 当页面通过Kubernetes NodePort(
Node_Public_IP:Node_Port)访问时,前端代码运行在用户浏览器中,localhost指向用户自己的本地机器,而非Kubernetes集群内的服务,因此出现连接拒绝错误。 - Kubernetes服务名仅能在集群内部解析,浏览器处于集群外部无法识别该域名,导致
net::ERR_NAME_NOT_RESOLVED错误。
修改步骤
使用相对路径发起请求
将fetch的请求地址改为/live,浏览器会自动使用当前页面的域名和端口拼接请求地址,适配本地、Kubernetes NodePort、Ingress等多种访问场景。修改后的客户端代码如下:console.log('We are inside client.js'); /* on page load */ window.onload = function() { fetch("/live", { method: "GET" }) .then(function(res) { if (res.ok) { return res.json(); } throw new Error('Request failed'); }).catch(function(error) { console.log(error); }) .then(function(data) { document.getElementById('appStatus').innerHTML = ` ${data.status} ` }); };验证CORS配置
当前Express应用已启用cors()中间件,允许跨域请求,使用相对路径后无需额外调整CORS设置。重新部署应用
修改代码后,重新构建Docker镜像,更新Kubernetes中的Deployment,确保新代码生效。
内容的提问来源于stack exchange,提问作者John Seen

