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

为何通过Kubernetes LoadBalancer可建立Active FTP连接,NodePort却失败?

Active FTP通过NodePort无法建立会话的问题

我目前使用Active FTP(通过PORT命令)传输文件:

  • 使用LoadBalancer IP和LoadBalancer Service的Target Port可以成功建立Active FTP会话
  • 但通过Node外部IP和NodePort尝试建立会话时失败

我使用的是npm的basic-ftp模块,相关连接代码如下:

let client = new ftp.Client(ftpTimeout * 1000);
client.prepareTransfer = prepareTransfer;

prepareTransfer的实现如下:

export async function prepareTransfer(ftp: FTPContext): Promise<FTPResponse> {
  // 获取LoadBalancer的IP(对应LoadBalancer服务)或节点的IP(对应NodePort服务)
   const ip = await getIp();
  // 获取LoadBalancer服务的TargetPort或NodePort服务的NodePort
  const port = await getFtpPort();

  // 示例命令: PORT 192,168,150,80,14,178
  // 前四个八位组是IP地址,最后两个八位组组成数据连接使用的端口
  // 实际端口计算方式: 第五个八位组乘256再加第六个八位组
  // 示例中的端口为 (14*256) + 178 = 3762
  const p1 = Math.floor(port / 256);
  const p2 = port % 256;
  const command = `PORT ${ip.replace(/\./g, ',')},${p1},${p2}`;

  // 数据套接字在连接前就需要管道处理,因此使用模拟套接字
  const mockSocket = new MockSocket();
  ftp.dataSocket = mockSocket as any;

  let ftpResponse = new Promise<FTPResponse>((resolve, reject) => {
    let response: FTPResponse;
    const server = createServer(function (socket) {
      console.log('FTP Client connected');
      mockSocket.setSocket(socket, server, port);
    });

    server.on('error', err => {
      reject(err);
    });
    server.listen(port, async () => {
      console.log(`socket server for FTP started at port ${port}`);
      // 发送PORT请求
      response = await ftp.request(command);
      if (response.code !== 200) {
        reject('Could not connect');
        server.close();
      }
      resolve(response);
    });
  });

  return ftpResponse;
}

NodePort的Helm配置如下:

apiVersion: v1
kind: Service
metadata:
  name: "ftp-service"
spec:
  type: NodePort
  externalTrafficPolicy: Local
  selector:
    statefulset.kubernetes.io/pod-name: "pod1"
  ports:
  - protocol: TCP
    name: ftp-8000
    port: 8000
    targetPort: 8000
    nodePort: 30014

LoadBalancer的Helm配置如下:

apiVersion: v1
kind: Service
metadata:
  name: "ftp-service-load"
spec:
  type: LoadBalancer 
  externalTrafficPolicy: Local
  selector:
    statefulset.kubernetes.io/pod-name: "pod1"
  ports:
  - protocol: TCP
    name: ftp-8000
    port: 8000
    targetPort: 8000
    nodePort: 30014

问题原因分析

1. PORT命令与内部监听端口不匹配

Active FTP的PORT命令需要客户端告知FTP服务器数据连接要对接的IP和端口。你的代码中存在核心错误:

  • 使用NodePort时,getFtpPort()获取的是节点上暴露的NodePort(30014),但你在Pod内部启动的socket server直接监听这个NodePort——而Pod无法监听节点的端口,它只能监听容器自身的端口(也就是配置中的TargetPort 8000)
  • 当FTP服务器收到PORT命令后,会尝试连接NodeIP:30014,但这个端口的流量是转发到Pod的8000端口的,而Pod内的socket server并没有监听8000,导致数据连接无法建立

2. NodePort的流量转发逻辑限制

你的NodePort Service设置了externalTrafficPolicy: Local,意味着只有当前节点上的Pod会接收流量。但Active FTP的数据连接是由服务器(Pod)主动发起的,这里存在两个矛盾:

  • PORT命令指定的是NodeIP和NodePort,但Pod内的服务并没有监听NodePort,无法接收这个端口的连接请求
  • NodePort的映射规则是节点端口→Pod内部端口,而PORT命令让服务器直接连接NodePort,跳过了映射逻辑,流量无法正确进入Pod

3. LoadBalancer能正常工作的原因

使用LoadBalancer时,getFtpPort()获取的是TargetPort(8000),Pod内的socket server也监听8000端口。PORT命令告知服务器连接LoadBalancerIP:8000,而LoadBalancer会将8000端口的流量转发到Pod的8000端口,控制连接和数据连接的端口逻辑匹配,因此能正常建立会话。


解决建议

  1. 修正端口逻辑:当使用NodePort服务时,getFtpPort()应返回Pod的TargetPort(8000),而非NodePort。同时确保节点的8000端口在安全组/防火墙中开放,或者为数据连接额外配置一个NodePort(因为Active FTP的控制端口和数据端口是独立的)
  2. 调整内部监听端口:Pod内的socket server必须监听容器内部的端口(如8000),不能直接监听NodePort
  3. 切换为Passive FTP:Active FTP在Kubernetes环境下因端口映射和网络规则限制,配置复杂度高。Passive FTP由服务器指定数据连接端口,更容易通过Service暴露,避免PORT命令的端口不匹配问题

内容的提问来源于stack exchange,提问作者Yashasvi Raj Pant

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 10:57:22