如何在Kubernetes中为Node.js服务区分内部与外部API?
方案1:基于Nginx Ingress + NetworkPolicy(无需额外依赖)
不用修改Node.js服务或启动多实例,仅通过Kubernetes原生组件就能实现需求:
统一Service配置
只创建一个ClusterIP类型的Service,指向你的Node.js Pod(假设端口为3000),无需拆分多个Service。Ingress路径控制
在Ingress规则中仅对外暴露/products/*路径,不配置/prices的转发规则,这样外部流量无法通过Ingress访问该私有端点。
示例Ingress配置:apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: nodejs-api-ingress annotations: nginx.ingress.kubernetes.io/rewrite-target: /$1 spec: rules: - host: your-domain.com http: paths: - path: /products/(.*) pathType: Prefix backend: service: name: nodejs-service port: number: 3000NetworkPolicy限制访问
创建NetworkPolicy,仅允许集群内指定Pod/Namespace的流量访问Node.js服务的3000端口,确保/prices只能被内部服务调用。
示例NetworkPolicy配置:apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: restrict-prices-api spec: podSelector: matchLabels: app: nodejs-api # 替换为你的Node.js Pod标签 policyTypes: - Ingress ingress: - from: - podSelector: matchLabels: app: internal-service # 允许访问的内部服务标签 ports: - protocol: TCP port: 3000 - from: - namespaceSelector: matchLabels: name: internal-namespace # 允许访问的Namespace标签 ports: - protocol: TCP port: 3000
方案2:使用Istio(服务网格进阶方案)
如果集群已部署Istio,或者需要更精细化的流量控制,Istio可以提供更灵活的访问策略:
注入Sidecar代理
确保你的Node.js Pod已注入Istio Sidecar(可通过命名空间自动注入或手动配置)。Gateway与VirtualService配置
通过Istio Gateway对外暴露/products/:id路径,VirtualService仅转发该路径到你的服务:apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: nodejs-api-vs spec: hosts: - your-domain.com gateways: - nodejs-gateway http: - match: - uri: prefix: /products/ route: - destination: host: nodejs-service port: number: 3000AuthorizationPolicy控制访问
创建Istio AuthorizationPolicy,拒绝外部流量访问/prices,仅允许集群内部服务调用:apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata: name: prices-api-auth spec: selector: matchLabels: app: nodejs-api action: ALLOW rules: - to: - operation: paths: ["/prices"] from: - source: notRemoteIpBlocks: ["0.0.0.0/0"] # 仅允许集群内部IP访问 - to: - operation: paths: ["/products/*"] from: - source: {} # 允许所有来源访问公开API
方案3:Node.js代码层控制(轻量方案)
如果不想依赖Kubernetes网络组件,可在Express代码中直接判断请求来源,限制/prices的外部访问:
const express = require('express'); const app = express(); const ipaddr = require('ipaddr.js'); // 可选,用于精确IP段匹配 // 集群内部IP段,根据你的Kubernetes配置修改 const INTERNAL_CIDRS = ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16']; // 中间件:验证是否为内部请求 const validateInternalAccess = (req, res, next) => { const clientIp = req.headers['x-forwarded-for']?.split(',')[0].trim() || req.connection.remoteAddress; // 处理IPv4/IPv6格式 const ip = ipaddr.parse(clientIp.startsWith('::ffff:') ? clientIp.slice(7) : clientIp); const isInternal = INTERNAL_CIDRS.some(cidr => { const [range, prefix] = cidr.split('/'); return ip.match(ipaddr.parse(range), parseInt(prefix)); }); if (isInternal) { next(); } else { res.status(403).send('Forbidden'); } }; // 公开API app.get('/products/:id', (req, res) => { res.json({ productId: req.params.id }); }); // 内部API,应用访问控制中间件 app.get('/prices', validateInternalAccess, (req, res) => { res.json({ prices: 'internal data' }); }); app.listen(3000, () => { console.log('Server running on port 3000'); });
内容的提问来源于stack exchange,提问作者Saarang Tiwari

