作者:BGbiao ,來源:簡書,原文鏈接
背景:眾所周知的是在構建一個Kubernetes集群時,容器網絡通常會使用一個獨立的私有子網來構建Kubernetes集群內部的pod網絡和service網絡,但在實際的業務場景中,沒有企業會在一段時間內將內部全部的服務都遷移到Kubernetes集群中(因為涉及到業務架構以及整體業務的可靠性),因而會產生一些Kubernetes集群內部服務和集群外部服務互相調用的場景,當然如果是HTTP服務,我們可以采用LVS、Nginx、HAProxy之類的代理工具工具進行集群內外的流量轉發,但如果是TCP服務,比如使用Dubbo框架時,生產者和消費者需要直連,當生產者和消費者不在一個可以互聯互通的網絡下會比較麻煩,這也就是為什么大廠在規模化使用Kubernetes時首先需要解決的就是網絡問題的原因了。比如我們在數科的時候就采用的是Contiv+BGP的模式來實現容器網絡和容器外網絡的互聯互通的,而這通常需要一個比較專業的SDN團隊來構建和維護。而作為創業公司通常會使用公有雲來承載自己的業務,這種輕資產模式的好處就是底層會有專業的團隊來提供保障,因此考慮到業務需求我們采用了阿里雲的terway網絡插件來實現內部的Kubernetes集群網絡.
現有網絡插件
- Flannel: Flannel是最早CoreOS團隊開源的網絡插件,用於讓集群中不同節點創建的容器都具有集群內全局唯一的網絡(集群外無法感知),也是當前Kubernetes開源方案中比較成熟的方案,支持HostGW和VXLAN模式
- Calico: Calico是一個純3層的數據中心網絡方案,支持IPIP和BGP模式,后者可以無縫集成像OpenStack這種IaaS雲架構,能夠提供可控的VM、容器、裸機之間的IP通信,但是需要網絡設備對BGP的支持(阿里雲vpc子網內應該是不支持BGP的); 同時可以支持基於iptables的網絡策略控制
- Contiv: Contiv是思科開源的用於跨虛擬機、裸機、公有雲或私有雲的異構容器部署的開源容器網絡架構,可支持2層、3層網絡(通常也需要BGP的支持)
- Terway: Terway是阿里雲開源的基於VPC網絡的CNI插件,支持VPC和ENI模式,后者可實現容器網絡使用vpc子網網絡
以上就是當前開源Kubernetes集群中使用較多的集中網絡方案,我們的業務需求中也是需要打通容器內外的網絡,因此在成本、效率以及穩定性上優先選擇采用阿里雲的Terway網絡方案來滿足我們的Kubernetes集群需求.
基於阿里雲ECS搭建Terway網絡的Kubernetes集群
注意:
阿里雲容器服務ACK默認也支持兩種網絡,Flannel和Terway,前者和開源插件基本一致,后者支持VPC模式和ENI模式,VPC模式可實現容器網絡使用vpc內交換機子網地址,但是默認無法和其他交換機下的ecs主機通信,ENI模式會給pod容器組分配一塊彈性網卡來實現和集群外網絡的互聯互通,但Terway網絡下的ENI模式需要部分特殊機型才可以支持。
由於ACK下Terway的ENI模式對機型的要求,我們采用購買ECS來自己搭建單節點集群測試Terway
網絡下容器的互聯互通.
前提條件:
- 已經創建了VPC子網
- 在VPC子網下創建2個虛擬交換機(模擬Kubernetes集群網絡和ECS網絡)
- 分別在兩個子網購買兩台ECS主機(模擬ECS到容器的互聯互通)
注意:
Terway網絡插件官方驗證過的os鏡像為Centos 7.4/7.6
,購買ecs時需要注意
1. 使用kubeadm安裝k8s單節點集群
注意:
因為要使用terway網絡將pod和ecs網絡打通,因此需要將內核參數rp_filter
全部設置為0
(對數據包源地址不進行校驗)
# 更新yum源並安裝k8s相關組件 $ yum update $ cat <<EOF > /etc/yum.repos.d/kubernetes.repo [kubernetes] name=Kubernetes baseurl=http://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64 enabled=1 gpgcheck=0 repo_gpgcheck=0 gpgkey=http://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg http://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg EOF $ yum clean all $ yum install kubelet kubeadm kubectl --disableexcludes=kubernetes -y $ yum install docker -y # 啟動kubelet ## 此時kubelet會無限重試,因為會鏈接apiserver $ systemctl restart kubelet # 啟動docker ## 注意:需要注意kubelet中的cgroupfs類型要和docker的cgroupfs一致 $ systemctl restart docker # 查看kubeadm 啟動集群時所需鏡像 # 注意:kubeadm默認使用的是谷歌的鏡像倉庫,可將鏡像倉庫換成阿里雲鏡像倉庫 # 將k8s.gcr.io 替換成registry.cn-hangzhou.aliyuncs.com/google_containers 即可 $ kubeadm config images list k8s.gcr.io/kube-apiserver:v1.16.2 k8s.gcr.io/kube-controller-manager:v1.16.2 k8s.gcr.io/kube-scheduler:v1.16.2 k8s.gcr.io/kube-proxy:v1.16.2 k8s.gcr.io/pause:3.1 k8s.gcr.io/etcd:3.3.15-0 k8s.gcr.io/coredns:1.6.2 # 初始化集群 ## 注意:初始化時需要指定vpc的子網,否則后期可能會發現無法識別vpc子網 $ kubeadm init --pod-network-cidr=172.16.48.0/20 .... .... Your Kubernetes control-plane has initialized successfully! To start using your cluster, you need to run the following as a regular user: mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config You should now deploy a pod network to the cluster. Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at: https://kubernetes.io/docs/concepts/cluster-administration/addons/ Then you can join any number of worker nodes by running the following on each as root: kubeadm join 172.16.62.70:6443 --token j4b3xp.78izi2bmitxxx \ --discovery-token-ca-cert-hash sha256:fd1ff50cbabd4fb22cb9a866052fbdc0db7da662168cda702exxxxxxxx # 接下來按照上述提示創建配置文件 $ mkdir -p $HOME/.kube $ sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config $ sudo chown $(id -u):$(id -g) $HOME/.kube/config # 查看k8s的node節點(當前處於NotReady狀態,因為kubelet還沒有成功啟動) $ # kubectl version Client Version: version.Info{Major:"1", Minor:"16", GitVersion:"v1.16.2", GitCommit:"c97fe5036ef3df2967d086711e6c0c405941e14b", GitTreeState:"clean", BuildDate:"2019-10-15T19:18:23Z", GoVersion:"go1.12.10", Compiler:"gc", Platform:"linux/amd64"} Server Version: version.Info{Major:"1", Minor:"16", GitVersion:"v1.16.2", GitCommit:"c97fe5036ef3df2967d086711e6c0c405941e14b", GitTreeState:"clean", BuildDate:"2019-10-15T19:09:08Z", GoVersion:"go1.12.10", Compiler:"gc", Platform:"linux/amd64"} $ kubectl get nodes NAME STATUS ROLES AGE VERSION izbp18diszrt8m41b2fbpsz NotReady master 7m19s v1.16.2
2. 給k8s集群創建terway網絡
注意:
使用kubeadm創建的k8s集群是v1.16的,官方提供的yaml文件中需要稍微修改下DaemonSet的相關部分。
# 給集群創建k8s的cni網絡插件,也就是前面說的terway插件 # 需要修改阿里雲相關的配置(ak,as,subnet,security_group) $ curl -O https://raw.githubusercontent.com/BGBiao/k8s-ansible-playbooks/master/manifest/cni/terway/podnetwork.yaml # 修改podnetwork.yaml中的配置(指定阿里雲的ak和as認證信息以及vpc子網和安全組信息) $ cat podnetwork.yaml ... ... eni_conf: | { "version": "1", "access_key": "your ak", "access_secret": "your as", "service_cidr": "your vpc subnet", "security_group": "your 安全組id", "max_pool_size": 5, "min_pool_size": 0 } .... .... - name: Network value: "your vpc subnet" .... # 創建terway網絡 $ kubectl apply -f podnetwork.yaml serviceaccount/terway created clusterrole.rbac.authorization.k8s.io/terway-pod-reader created clusterrolebinding.rbac.authorization.k8s.io/terway-binding created configmap/eni-config created daemonset.apps/terway created customresourcedefinition.apiextensions.k8s.io/felixconfigurations.crd.projectcalico.org created customresourcedefinition.apiextensions.k8s.io/bgpconfigurations.crd.projectcalico.org created customresourcedefinition.apiextensions.k8s.io/ippools.crd.projectcalico.org created customresourcedefinition.apiextensions.k8s.io/hostendpoints.crd.projectcalico.org created customresourcedefinition.apiextensions.k8s.io/clusterinformations.crd.projectcalico.org created customresourcedefinition.apiextensions.k8s.io/globalnetworkpolicies.crd.projectcalico.org created customresourcedefinition.apiextensions.k8s.io/globalnetworksets.crd.projectcalico.org created customresourcedefinition.apiextensions.k8s.io/networkpolicies.crd.projectcalico.org created # 查看cni相關容器以及node狀態 $ kubectl get nodes NAME STATUS ROLES AGE VERSION izbp18diszrt8m41b2fbpsz Ready master 28m v1.16.2 $ kubectl get pods -A | grep terway kube-system terway-b9vm8 2/2 Running 0 6m53s
至此,我們就已經完成了kubernetes的terway網絡單節點集群,接下來就可以嘗試讓k8s集群中的pod來使用vpc的網絡了,以便可以實現k8s集群內部的容器網絡和其他ecs主機的網絡是平行的.
3. 測試terway網絡
注意:
我們使用kubeadm構建的k8s單節點集群,而kubeadm默認給master節點設置了taint,因此測試前需要去除taint。
# 去除taint $ kubectl taint nodes --all node-role.kubernetes.io/master- node/izbp18diszrt8m41b2fbpsz untainted # 默認創建一個vpc模式的deployment $ kubectl apply -f https://raw.githubusercontent.com/BGBiao/k8s-ansible-playbooks/master/manifest/cni/terway/nginx.yaml namespace/myapp configured deployment.apps/nginx-test created # 可以看到容器網絡地址其實是指定的vpc子網內地址 $ kubectl get pods -n myapp -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES nginx-test-d56c87dd9-26mzs 1/1 Running 0 2m40s 172.16.48.5 izbp18diszrt8m41b2fbpsz <none> <none> nginx-test-d56c87dd9-hp2rv 1/1 Running 0 2m40s 172.16.48.4 izbp18diszrt8m41b2fbpsz <none> <none> $ curl 172.16.48.4 -I HTTP/1.1 200 OK Server: nginx/1.17.5 Date: Sat, 26 Oct 2019 08:21:28 GMT Content-Type: text/html Content-Length: 612 Last-Modified: Tue, 22 Oct 2019 14:30:00 GMT Connection: keep-alive ETag: "5daf1268-264" Accept-Ranges: bytes $ curl 172.16.48.5 -I HTTP/1.1 200 OK Server: nginx/1.17.5 Date: Sat, 26 Oct 2019 08:21:31 GMT Content-Type: text/html Content-Length: 612 Last-Modified: Tue, 22 Oct 2019 14:30:00 GMT Connection: keep-alive ETag: "5daf1268-264" Accept-Ranges: bytes
可以發現,在集群內部使用terway網絡已經沒有任何問題了,但是我們在其他ECS主機去訪問pod網絡時發現依然無法訪問(因為默認使用的是terway的VPC模式,其實就是類似於calico的模式了.這個時候就需要用到eni模式了,即給k8s節點增加eni彈性網卡,然后pod的網絡流量統一通過node節點的eni網卡傳輸,此時就可以很好的和整個內網vpc打通了)
4. 測試ENI模式
注意:
在上面的nginx配置中增加limits: aliyun/eni: N
即可,需要注意的是N表示node節點上eni彈性網卡的數量,該數量取決於阿里雲ecs不同規格對eni的限制。
# 注意: # 由於實驗中采用的是4c8g的k8s單節點集群,因此只能創建2個彈性網卡,這也就意味着如果不增加任何網絡配置,該node節點最多只能運行2個和整個VPC網絡中其他ecs主機互聯互通的pod $ cat nginx.yaml --- apiVersion: apps/v1 kind: Deployment metadata: name: nginx-v2 namespace: myapp spec: revisionHistoryLimit: 10 strategy: type: RollingUpdate rollingUpdate: maxSurge: 1 maxUnavailable: 1 replicas: 2 selector: matchLabels: app: nginx-v2 profile: prod template: metadata: labels: app: nginx-v2 profile: prod spec: containers: - name: nginx-v2 image: nginx:latest imagePullPolicy: IfNotPresent resources: requests: cpu: 200m memory: 215Mi limits: cpu: 200m memory: 215Mi aliyun/eni: 1 # 創建帶eni的pod $ kubectl apply -f nginx.yaml deployment.apps/nginx-v2 configured # 查看pod狀態 $ kubectl get pods -n myapp -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES nginx-test-d56c87dd9-26mzs 1/1 Running 0 19m 172.16.48.5 izbp18diszrt8m41b2fbpsz <none> <none> nginx-test-d56c87dd9-hp2rv 1/1 Running 0 19m 172.16.48.4 izbp18diszrt8m41b2fbpsz <none> <none> nginx-v2-7548466fc8-d4klv 1/1 Running 0 61s 172.16.62.74 izbp18diszrt8m41b2fbpsz <none> <none> nginx-v2-7548466fc8-x7ft9 1/1 Running 0 61s 172.16.62.75 izbp18diszrt8m41b2fbpsz <none> <none> # 在k8snode節點訪問 $ curl 172.16.62.75 -I HTTP/1.1 200 OK Server: nginx/1.17.5 Date: Sat, 26 Oct 2019 08:38:20 GMT Content-Type: text/html Content-Length: 612 Last-Modified: Tue, 22 Oct 2019 14:30:00 GMT Connection: keep-alive ETag: "5daf1268-264" Accept-Ranges: bytes $ curl 172.16.62.74 -I HTTP/1.1 200 OK Server: nginx/1.17.5 Date: Sat, 26 Oct 2019 08:38:23 GMT Content-Type: text/html Content-Length: 612 Last-Modified: Tue, 22 Oct 2019 14:30:00 GMT Connection: keep-alive ETag: "5daf1268-264" Accept-Ranges: bytes # 此時發現創建的帶eni和不帶eni的兩個pod在k8s集群內部已經完全可以訪問
5. 測試集群內外部網絡互聯互通
注意:
k8s集群使用的是vpc網絡,因此默認集群訪問外部ECS網絡默認是沒有問題,這里主要測試外部ECS網絡是否可以直連pod網絡進行通信。
# 在同vpc環境下其他ecs主機上訪問 # 首先分別ping 上述四個pod的網絡(可以發現eni模式下容器默認可以ping通) $ for i in 172.16.48.5 172.16.48.4 172.16.62.74 172.16.62.75 ;do ping -c 1 -w 1 $i;done PING 172.16.48.5 (172.16.48.5) 56(84) bytes of data. --- 172.16.48.5 ping statistics --- 2 packets transmitted, 0 received, 100% packet loss, time 999ms PING 172.16.48.4 (172.16.48.4) 56(84) bytes of data. --- 172.16.48.4 ping statistics --- 1 packets transmitted, 0 received, 100% packet loss, time 0ms PING 172.16.62.74 (172.16.62.74) 56(84) bytes of data. 64 bytes from 172.16.62.74: icmp_seq=1 ttl=64 time=0.782 ms --- 172.16.62.74 ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 0ms rtt min/avg/max/mdev = 0.782/0.782/0.782/0.000 ms PING 172.16.62.75 (172.16.62.75) 56(84) bytes of data. 64 bytes from 172.16.62.75: icmp_seq=1 ttl=64 time=0.719 ms --- 172.16.62.75 ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 0ms rtt min/avg/max/mdev = 0.719/0.719/0.719/0.000 ms # 測試nginx服務(依然是帶eni的網絡可達) $ for i in 172.16.48.5 172.16.48.4 172.16.62.74 172.16.62.75 ;do curl --connect-timeout 1 -I $i;done curl: (28) Connection timed out after 1001 milliseconds curl: (28) Connection timed out after 1001 milliseconds HTTP/1.1 200 OK Server: nginx/1.17.5 Date: Sat, 26 Oct 2019 08:44:21 GMT Content-Type: text/html Content-Length: 612 Last-Modified: Tue, 22 Oct 2019 14:30:00 GMT Connection: keep-alive ETag: "5daf1268-264" Accept-Ranges: bytes HTTP/1.1 200 OK Server: nginx/1.17.5 Date: Sat, 26 Oct 2019 08:44:21 GMT Content-Type: text/html Content-Length: 612 Last-Modified: Tue, 22 Oct 2019 14:30:00 GMT Connection: keep-alive ETag: "5daf1268-264" Accept-Ranges: bytes
此時,我們查看該node節點上的網卡信息時可以看到,增加了兩塊輔助網卡。
6. 其他問題
注意:
前面我們提到過,如果使用eni
模式,不同的ECS規格可以綁定的ENI
彈性網卡是有限的,也就是說可以創建互聯互通的容器是有限的,我們這里驗證下。
# 如果我們這個時候再創建帶eni的pod時,就會發現無法創建成功(因為4c8g的ecs最大只支持兩個eni) $ kubectl apply -f nginx-v3.yaml deployment.apps/nginx-v3 created [root@iZbp18diszrt8m41b2fbpsZ ~]# kubectl get pods -n myapp -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES nginx-test-d56c87dd9-26mzs 1/1 Running 0 48m 172.16.48.5 izbp18diszrt8m41b2fbpsz <none> <none> nginx-test-d56c87dd9-hp2rv 1/1 Running 0 48m 172.16.48.4 izbp18diszrt8m41b2fbpsz <none> <none> nginx-v2-7548466fc8-d4klv 1/1 Running 0 29m 172.16.62.74 izbp18diszrt8m41b2fbpsz <none> <none> nginx-v2-7548466fc8-x7ft9 1/1 Running 0 29m 172.16.62.75 izbp18diszrt8m41b2fbpsz <none> <none> nginx-v3-79dd8fb956-4ghgb 0/1 Pending 0 2s <none> <none> <none> <none> nginx-v3-79dd8fb956-str2k 0/1 Pending 0 2s <none> <none> <none> <none> # 查看Pending的詳情 $ kubectl describe pods -n myapp nginx-v3-79dd8fb956-4ghgb .... .... Events: Type Reason Age From Message ---- ------ ---- ---- ------- Warning FailedScheduling <unknown> default-scheduler 0/1 nodes are available: 1 Insufficient aliyun/eni.
可以發現,當使用terway網絡的ENI
模式時,如果該ecs可支持的彈性網卡達到限制,k8s就會調度失敗。
所以問題就來了,通常情況下,我們是希望使用k8s來彈性擴容,我們會希望k8s節點上運行更多的pod,但用了terway網絡之后我們發現,創建和k8s集群外ecs主機通信的pod數量竟然受eni的限制,這可得了?
其實不用擔心,阿里雲同學的回復是,這種情況下在vpc上設置靜態路由即可實現node節點上的多pod和集群外ecs主機互通,此時ecs主機上的eni僅相當於是整個容器的網絡出口,到這里其實我們就可以放心了,因為使用terway后,及時不用eni模式,pod網絡也是全局唯一的,這個時候適當增加一些靜態路由,即可實現整個vpc內k8s容器網絡和容器外的ecs主機網絡互聯互通,很好的解決了我們一開始的問題。
注意:
阿里雲容器服務ACK的terway網絡模式下的集群會默認創建一些路由規則,因此當你使用ACK集群時,只要購買了支持terway
規格的節點,默認創建的容器都可以實現和外部ecs主機的互聯互通,此時,該ecs上創建的彈性網卡將作為節點上k8s容器的網絡出口,而ecs主機本身的eth0
將僅作為管理網絡而存在,感興趣的同學可以點擊閱讀原文
嘗試使用阿里雲ACK的terway
網絡模式。