基于阿里云 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
网络模式。