回響

Blog & Thoughts

2026年01月09日

记一次 Kubernetes Ingress + FRP 问题解决

记一次 Kubernetes Ingress + FRP 问题解决

环境:两台 Ubuntu 24.04 组成的 Kubernetes 集群及其内部运行的 Frpc;公网服务器 Docker Frps;

需求:通过反代将内网 Pod 服务暴露到公网。

首先通过配置 Frps 和 Frpc 并暴露相关端口完成反向代理端口的配置:

# docker-compose.yaml
services:
  frps:
    image: fatedier/frps:v0.61.2
    container_name: frps
    restart: always
    ports:
    	# ... 控制台、连接等等其他需要暴露的端口  
    	- "8081:8081" # 用于反代转发 http 流量
    volumes:
      - /root/frps:/opt/frps
    environment:
      - TZ=Asia/Shanghai
    command: -c /opt/frps/frps.toml
# ... 省略与客户端建立连接的配置
vhostHTTPPort = 8081 # 配置 http 流量入口

在公网服务器配置 Nginx 作为默认网关入口并配置反代转发:

server {
    listen 80;
    # 这里列出所有需要穿透的子域名
    server_name tool.konpeki.top board.konpeki.top pic.konpeki.top;

    access_log /var/log/nginx/tunnel_access.log;
    error_log /var/log/nginx/tunnel_error.log;

    location / {
        # 转发给本地运行的 FRP 服务端
        proxy_pass http://127.0.0.1:8081;

        # =================================================
        # 核心头信息传递 (由外层 Nginx 传给内层 FRP/Ingress)
        # =================================================
        
        proxy_set_header Host $host; # 传递 Host 便于 Ingress 区分流量: 
        proxy_set_header X-Real-IP $remote_addr; # 传递 IP
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme; # HTTP(S)
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        client_max_body_size 100m; # 上传大文件限制
    }
}

集群内以 Deployment + ConfigMap 配置 frpc:

# frpc-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: frpc
  labels:
    app: frpc
spec:
  replicas: 1
  selector:
    matchLabels:
      app: frpc
  template:
    metadata:
      labels:
        app: frpc
    spec:
      containers:
      - name: frpc
        image: fatedier/frpc:v0.61.2
        args: 
        - "-c"
        - "/opt/frpc/frpc.toml"
        volumeMounts:
        - name: config-volume
          mountPath: /opt/frpc/frpc.toml
          subPath: frpc.toml
        env:
        - name: TZ
          value: Asia/Shanghai
      volumes:
      - name: config-volume
        configMap:
          name: frpc-config
      restartPolicy: Always
---
# frpc-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: frpc-config
  namespace: default
data:
  frpc.toml: |
    # ... 与服务端建立连接
    # ... 配置Token鉴权
    # ... 配置其他代理
    
    [[proxies]]
    name = "k8s-ingress-http"
    type = "http"  
    localIP = "ingress-nginx-controller.ingress-nginx.svc.cluster.local"
    # localIP = "10.100.23.114"
    localPort = 80
    customDomains = ["tool.konpeki.top", "board.konpeki.top", "pic.konpeki.top"]    

这里给出 tool.konpeki.top 对应的 Ingress 配置:(为了方便管理,我将网站相关资源都放入了单独的命名空间 konpeki

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: tool-ingress
  namespace: konpeki        # ⚠️ 
  annotations:
    nginx.ingress.kubernetes.io/proxy-body-size: "10m"
spec:
  ingressClassName: nginx   # 指定使用 nginx 控制器
  rules:
  - host: tool.konpeki.top  
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: tool-service  
            port:
              number: 80        

理论上,上述内容不出问题的话,我便可以成功从浏览器输入 tool.konpeki.top 并看到服务。但是我看到的却是 Frp 的 Not Found 页面。

仔细回顾整个流量的链路:

-> 公网服务器 -> 公网 Nginx -> 公网 Frps 容器 -> 集群内 Frpc Pod -> 集群 Nginx Ingress Controller -> 集群内 Service 与 Pod

由于浏览器已显示 Frp 404 且可以从 Dashboard 看到 Frp 已正常连接,因此问题不在集群内 Frpc 之前。仔细排查 frpc-config.yaml 后发现存在一些小问题如端口冲突,修改解决。但是最大的问题是流量能够到达 frpc,但是不知道下一步的走向。既然 vhostHTTPPort 已经设置好,Custom Domains 也配置得没有问题,那么问题就出在这里了:

    localIP = "ingress-nginx-controller.ingress-nginx.svc.cluster.local"
    # localIP = "10.100.23.114" # 排查问题的时候本来是没有这一行的

怀疑是指向 Ingress Controller 的流量找不到目标,于是尝试 kubectl get service -A | grep ingress 得到 Ingress Controller 的 IP 后把 localIP 改成集群内 IP,在一小段时间内跑通了。这说明了集群内 CoreDNS 存在问题。

此时尝试部署应用 Pod 和 Service,本来看到 Pod 处于 Running 状态时以为已经万事大吉了。结果在别的地方排查一段时间后顺手 kubectl get endpoints tool-service -n konpeki 发现 Service 找不到 Endpoint。紧随着查看 Pod 状态时,发现 Pod 总是在一段时间后 CrashLoopBackOff,于是通过 kubectl logs <pod-name> -n konpeki -p 可以看到当前 Pod 运行之前的上一个 Pod 在被销毁前的日志。经排查发现是集群内 Service 解析仍然在出错导致的数据库连接超时,从而导致 Pod 无法正常初始化并异常退出。

2026/01/08 19:28:29 failed to connect database: dial tcp: lookup mini on 10.96.0.10:53: read udp 10.244.247.26:39610->10.96.0.10:53: i/o timeout

暂时硬编码 IP 发现能成功上线跑通,进一步验证得知 CoreDNS 有问题。

后续过了一小段时间后,我又一次见到了 Frp 404。在此之后我反复尝试了许多种可能的原因和解决方案。包括但不限于:

  • 关闭硬件校验和卸载
  • 将 Calico 的网络覆盖模式在 IPIP 和 VXLAN 之间反复修改与重启
  • 修改 Nginx Ingress Controller 的暴露方式

每一种解决方案的尝试都会造成短时间内链路的打通,但过一段时间后还是回到 Frp 404。最后为了让链路稳定下来,我选择了一个不太优雅的办法:

  1. 在 Nginx Ingress Controller 中暴露 hostPort (http:80, https:443)
  2. 在 Frpc 中直接指定本地主机 ip

tool.konpeki.top

此时 Frp 不用在 Kubernetes 的网络环境内穿行,但这种方式实在太粗暴,可是这已经是我与 k8s 鏖战 30 个小时后得到的唯一相对稳定的解决方案了(- -|||)以后再优化吧。