基於阿里雲 Terway 網絡的 Kubernetes 集群實踐


作者: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網絡模式。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM