目录:网上冲浪指南

VPS 上自建 k3s 集群如何获取真实客户端的 IP ?

2020/10/18

大多数的云服务厂商都提供了非常时髦的 Kubernetes 服务,但是迫于没钱,所以只能用轻量应用服务器来搭建自己的轻量 k3s 集群。直接按照官网的方式来创建 k3s 集群的话,就会遇到集群内的服务无法获取客户端真实 IP 的情况。经过一段时间的摸索与反复的重建集群,我终于让搭建在 Tencent Cloud 轻量应用服务器上的 k3s 集群中的服务获取到了客户端的真实 IP。

替换 LB

首先需要修改的就是 systemd unit 文件中 k3s 的启动参数,我们需要禁用自带的 LB 以及 traefik:

/usr/local/bin/k3s server --disable servicelb --disable traefik

与 k8s 不同,k3s 自带了一个 LoadBalancerk3s 文档中提到了,这个 LB 会为每个服务都创建一个 Proxy,由 Proxy 转发外部流量到其背后的服务中,这样我们的 IngressController 就没法获取到真实的客户端 IP 了。

由于干掉了自带的 LB,所以需要找一个替代品,迫于成本的压力,我并不想购买厂商提供的 LB 以及 k8s 服务,而是选择自己部署一个 MetalLB。MetalLB 的功能就俩:

  • 给 LoadBalancer 类型的 Service 分配外部 IP

  • 网络中通过 ARP 宣告外部 IP 所在节点的 MAC 地址

不过 metal-lb 并不是必需的,将需要对外暴露的服务创建成 ExternalIP 类型也能实现一样的效果,不过用 metal-lb 的好处就是在我的使用场景中可以集中的为不同节点上的对外服务设置地址。

安装 metal-lb 按照 官方文档 就可以了。

kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.9.4/manifests/namespace.yaml
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.9.4/manifests/metallb.yaml
# On first install only
kubectl create secret generic -n metallb-system memberlist --from-literal=secretkey="$(openssl rand -base64 128)"

安装完成之后配置并不会立即生效,还需要我们部署一个 ConfigMap,由于腾讯云的轻量服务器是通过弹性 IP 暴露到公网的,所以我只能使用 Layer2 模式的 metal-lb:

apiVersion: v1
kind: ConfigMap
metadata:
  namespace: metallb-system
  name: config
data:
  config: |
    address-pools:
    - name: default # 这个地址池的名字稍后会用到
      protocol: layer2
      addresses:
      - 192.168.1.240-192.168.1.250 # 在这里填写可用的“公网”地址
最开始我配置的时候填的就是腾讯云的弹性公网 IP,不过腾讯云的弹性 IP 似乎会经过一次 NAT,最终到达轻量服务器的 IP 协议的目标地址为 10.0.0.x ,所以 metal-lb 的公网地址配置也需要改为这个内网地址,否则在访问 HTTP 服务的时候你会遇到 No route to host 的错误。

替换 Ingress Controller

在上个步骤中,k3s 自带的 traefik helm 被禁用了,之所以禁用它,是因为修改 traefik 的默认 traefik 配置真的很麻烦,不如手动部署一份。我用的 traefik 官方提供的 traefik helm 模板,具体的使用方式参考相关的说明文档即可。下面给出的是我在使用的部分参数配置:

# Configure ports
ports:
  # The name of this one can't be changed as it is used for the readiness and
  # liveness probes, but you can adjust its config to your liking
  traefik:
    port: 9000
    expose: false
    exposedPort: 9000
    protocol: TCP
  web:
    port: 8000
    expose: true (1)
    exposedPort: 80
    protocol: TCP
  websecure:
    port: 8443
    expose: true (1)
    exposedPort: 443
    protocol: TCP

# Options for the main traefik service, where the entrypoints traffic comes
# from.
service:
  enabled: true
  type: LoadBalancer (2)
  annotations:
    metallb.universe.tf/address-pool: default (3)
  spec:
    externalTrafficPolicy: Local (4)
1 web 与 websecure 两个服务端口需要设置成对外暴露
2 Traefik 的服务类型需要设置为 LoadBalancer 这样才能自动的由 metal-lb 分配外部 IP 地址
3 Traefik 服务需要设置 address-pool annotation,这样 metal-lb 才能检测到这个服务的存在,annotions 的值设置为 metal-lb 配置文件的地址池名称就行了
4 externalTrafficPolicy 需要设置为 Local,这样外部的流量可以直接到达所在节点的 traefik 服务,而不会经过 k3s 内置的 kube-proxy,从而可以保留外部来源 IP。