簡介
Calico 從 v3.13 開始,集成了 eBPF
數據平面。
關於什么是 eBPF
, 以及 Calico
為什么引入了 eBPF
, 並不是本篇文章的重點,感興趣的朋友可以自行閱讀相關文檔。
相比於 Calico 的默認基於 iptables
數據平面,eBPF
具有更高的吞吐量以外, 還具有 source IP preservation
這個功能。
在 K8s 中通常都是直接或者間接以 NodePort
方式對外暴露接口的。而對於 K8s 這個分布式集群來講,通常情況下,客戶端連接 Node Port 端口的節點和負責響應請求的后端業務 Pod 所在的節點不是同一個節點,為了打通整個數據鏈路,就不可避免的引入了 SNAT
。但是這樣顯然也會帶來一個副作用,即業務 Pod 在收到 Packet 以后,其 SRC IP 已經不再是客戶端的實際 IP(被偽裝成節點的內網 IP )。另一方面,對於一些業務應用來講,獲取客戶端 IP 是一個實實在在的剛需。比如:業務應用需要通過客戶端 IP 來獲取客戶登陸的 geo 信息。
目前 K8s 主要是通過設置 externaltrafficpolicy
來規避這個問題的,但是這個方案本身並不能完全令人滿意。Calico 從 v3.13 開始通過集成 eBPF
優雅地解決了這個問題。
在本篇文章中,我們將首先演示通過 KubeKey 創建一個標准的 K8s 集群,並切換數據平面到 eBPF
,最后基於該數據平面做一個簡單的演示。
前提條件
較新的內核, 一般 v4.18+ 即可。
筆者的測試集群:
部署 K8s 集群
Kubekey 默認的 CNI 插件為 Calico(ipip模式)。這里為了部署方便,直接使用 KubeKey 部署了一個全新的 K8s 集群,版本為 v1.18.6 。KubeKey 的詳細用法參見文檔。
切換 Calico 數據平面
Calico 支持多種數據平面,通過修改配置可以方便地進行切換,詳細信息可以參見官方文檔。
主要分為以下幾步:
- 確認 BPF 文件系統已經掛載:
mount | grep "/sys/fs/bpf"
如果能看到以下信息,則代表 BPF 文件系統已經掛載:
- 創建 Calico 配置文件:
- 首先獲取 ks-apiserver endpoints 信息:
kubectl get endpoints kubernetes -o wide
- 由於 KubeKey 是通過 manifest 方式安裝的 Calico,這里我們只需要創建一個 cm 即可:
kind: ConfigMap
apiVersion: v1
metadata:
name: kubernetes-services-endpoint
namespace: kube-system
data:
KUBERNETES_SERVICE_HOST: "<API server host>"
KUBERNETES_SERVICE_PORT: "<API server port>"
- 重啟 Calico pods,並等待 Calico Pod 重新變為 Running 狀態
kubectl delete pod -n kube-system -l k8s-app=calico-node
kubectl delete pod -n kube-system -l k8s-app=calico-kube-controllers
- 關閉 kube-proxy
kubectl patch ds -n kube-system kube-proxy -p '{"spec":{"template":{"spec":{"nodeSelector":{"non-calico": "true"}}}}}'
- 開啟 eBPF 模式
calicoctl patch felixconfiguration default --patch='{"spec": {"bpfEnabled": true}}'
- 由於我們需要保留客戶端 IP,所以需要開啟
DSR
模式。
calicoctl patch felixconfiguration default --patch='{"spec": {"bpfExternalServiceMode": "DSR"}}'
至此,Calico 的整個網絡環境已經配置完畢。
體驗
為了驗證 Calico 切換到 eBPF
數據平面以后,后端確實可以拿到客戶端的真實 IP ,下面我們會在集群中部署一個 Nginx 服務,並通過 nodeport 方式暴露接口。
創建 Nginx 實例並暴露外部接口:
master:~$ kubectl apply -f - <<EOF
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
selector:
matchLabels:
app: nginx
replicas: 1
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx
spec:
type: NodePort
selector:
app: nginx
ports:
- protocol: TCP
port: 80
nodePort: 30604
EOF
等待 Pod 變為 Running 狀態:
外部調用 Nginx 服務:
curl http://<external-ip>:30604
查詢 Nginx 日志,查看 client IP:
注意:如果集群本身部署在雲平台環境中,如果節點位於 VPC 網絡當中,需要設置相應的端口轉發規則,並開啟相應的防火牆端口。
本文由博客一文多發平台 OpenWrite 發布!