记一次 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。最后为了让链路稳定下来,我选择了一个不太优雅的办法:
- 在 Nginx Ingress Controller 中暴露 hostPort (http:80, https:443)
- 在 Frpc 中直接指定本地主机 ip
此时 Frp 不用在 Kubernetes 的网络环境内穿行,但这种方式实在太粗暴,可是这已经是我与 k8s 鏖战 30 个小时后得到的唯一相对稳定的解决方案了(- -|||)以后再优化吧。
