玩转tke的混合网络模式

2021-02-05 19:05:16 浏览数 (1)

tke上腾讯云有提供2中网络模式,分别是Global Router(下面我们简称GR)和vpc-cni,这2种网络模式的优劣,如何选型可以参考https://cloud.tencent.com/document/product/457/44966,那么什么是tke的混合网络模式呢,首先我们看看tke的网络模式有哪几种。

tke中可以存在以下几种网络模式:

  • GR类型
  • vpc-cni类型
  • GR vpc-cni类型(注意这里只能是创建集群时候选择GR,后续再开启vpc-cni。如果创建集群选择的是vpc-cni,后续是无法再时区GR)

其实混合网络模式就是创建集群时候网络选择GR,然后后续开启vpc-cni这个网络模式附加到集群上,今天我们重点讲讲GR vpc-cni的混合网络模式下如何使用,什么场景下我们需要用到它,使用的时候需要注意什么。

我们在部署应用到k8s中有个非常常见的场景,就是希望从应用程序中获取到真实的客户端ip信息,但是如果你的应用部署在GR模式的集群中,这点就无法实现,程序提供给外界访问通常是通过service或者ingress暴露,在腾讯云上都是创建一个lb来关联你的service和ingress,在GR的模式下,pod所在的容器网络是平行于vpc的一个虚拟网络,当客户端通过lb请求后端的是否,lb也是处于vpc网络中,所以lb只能将请求转发到后端cvm的nodeport上,然后再通过cni组件的虚拟网卡cbr0讲请求转发到pod里面,这样从pod端来看,客户端就变成了虚拟网卡cbr0的ip了,因此在GR上无法获取真是客户端ip,其实真实流量转发如下:

client----->clb----->cvm(nodeport)----->pod

那么如果我想在获取到客户端的真实ip那应该怎么办呢,其实去掉nodeport到pod直接这一层转发就可以,也就是让clb能直接访问到pod,这样clb可以直接将客户端ip透传给后端pod,pod就能拿到真实的客户端ip,但是要想clb直接将流量转发到pod,那么需要pod和clb处于同一个网络层面,都在vpc内,很明显在GR上这个是实现不了的,所以这里需要用到vpc-cni模式,其实vpc-cni模式就是从vpc中划分出一部分子网给pod作为ip,这样就可以让pod和clb都在vpc这个网络层面上了,那样流量转发就是下面这样:

client----->clb----->pod

如果你创建集群选的就是vpc-cni,那么这个问题就不用担心了,但是如果创建你选择的GR类型,那么想获取客户端ip,就需要做一些额外的操作了,就需要用到GR vpc-cni这种混合网络模式了,下面我们来具体讲讲这种模式怎么使用。

1. 启用混合网络模式

首先我们创建一个GR模式的tke集群,然后在集群的基本信息中找到开启vpc-cni模式的按钮,点击开启

这里会让你选择一个空的子网来用于vpc-cni的模式下,pod ip也都是从这个子网中获取,混合模式下默认是启用固定ip功能的,如果你创建集群选择的是vpc-cni,那么这个功能可选可不选,如果你希望你的pod销毁后ip可以重复使用,可以配置ip回收策略,默认是不回收,pod销毁后最小回收时间是300s,这里我们配置成300,这里建议配置这个策略,避免后续子网ip不够分配给pod。

由于混合网络下只能添加一个子网的现在,我们后面就会面临一个问题,那就是我们节点只能部署在vpc-cni模式的子网相同可用区,为什么会有这个限制,其实你看下vpc-cni的网络架构就明白了

vpc-cni其实就是给每个节点分配一个辅助网卡,然后从网卡中分配ip给pod,由于腾讯云上弹性网卡需要和cvm处于同一个可用区,我这里选择的是广州4区的子网作为vpc-cni的子网,那么弹性网卡也是从广州4区分配,这也就意味着你的cvm也是要在广州4区,但是tke这边为了高可用,通常是多可用去部署,那么这个问题该怎么解决呢?

首先我么测试下看看,如果添加一个其他可用区的节点会发生什么情况,这里我们选择加入一个广州6区的节点

点击添加后会出现报错checkClusterNetworkScale failed, err:DashboardError,Code : -1100000 , Msg : Network scale error[IP for pods in Zone ap-guangzhou-6 is not enough to scale out 1 nodes.], err : nil

这个报错就是提示我们广州6区没有可用的ip分配给pod,因为我们vpc-cni模式没有广州6区的子网。

难道我们就无法添加其他可用区的机器了,其实了解一下vpc-cni的插件部署你就明白可以怎么处理了

从上图我们可以看出,vpc-cni其实就是部署一个server端和每个节点部署agent客户端,tke-eni-agent从tke-eni-ipamd获取可用的子网信息,然后挂上弹性网卡,从子网中分配一部分ip信息给节点作为pod ip,既然是这样,那就说明子网信息都是存在tke-eni-ipamd这个服务端pod里面的,如果我们加一个广州6区的子网到服务端中,那么后面就可以加广州6区的机器到集群了。

kube-system下有个cm记录这vpc-cni模式的子网信息,我们将广州6区的子网加到这个cm中,看看能不能加广州6区的节点到集群,我们在vpc下找到建好的广州6区的空子网,然后通过kubectl命令将子网信息加到cm中,kube-system只能通过命令操作

代码语言:javascript复制
[root@VM-0-51-tlinux ~]# kubectl edit cm -n kube-system tke-eni-ipamd
configmap/tke-eni-ipamd edited

# Please edit the object below. Lines beginning with a '#' will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file will be
# reopened with the relevant failures.
#
apiVersion: v1
data:
  TKE_ENI_IPAMD_SUBNET_ID: subnet-f3m8i0xi:subnet-ef66oja6
  TKE_ENI_IPAMD_VPC_ID: vpc-p1v99tgl
  TKE_ENI_IPAMD_ZONE: ap-guangzhou-4:ap-guangzhou-6
kind: ConfigMap
metadata:
  creationTimestamp: "2021-02-05T09:16:25Z"
  managedFields:
  - apiVersion: v1
    fieldsType: FieldsV1
    fieldsV1:
      f:data:
        .: {}
        f:TKE_ENI_IPAMD_SUBNET_ID: {}
        f:TKE_ENI_IPAMD_VPC_ID: {}
        f:TKE_ENI_IPAMD_ZONE: {}
    manager: tke-controller-manager
    operation: Update
    time: "2021-02-05T09:16:25Z"
  name: tke-eni-ipamd
  namespace: kube-system
  resourceVersion: "1034266769"
  selfLink: /api/v1/namespaces/kube-system/configmaps/tke-eni-ipamd
  uid: 20392b09-3c24-4c4b-bd18-aaa0bef2b90a
代码语言:javascript复制
[root@VM-0-51-tlinux ~]# kubectl delete pod tke-eni-ipamd-6ffbcf6f44-p6wk7 -n kube-system
pod "tke-eni-ipamd-6ffbcf6f44-p6wk7" deleted
[root@VM-0-51-tlinux ~]# kubectl get pod -n kube-system | grep eni
tke-eni-agent-7h6jm                 1/1     Running   0          28m
tke-eni-ipamd-6ffbcf6f44-mwphj      1/1     Running   0          100s

修改之后重建下让cm挂载生效,等待pod重启成功后,现在我们再加一下广州6区的节点到集群

这里我们再添加节点就没有校验报错了,说明我们添加广州6区节点到集群成功了。

2. deployment类型pod获取客户端ip

上面已经说过了,获取客户端ip只需要pod在vpc-cni的模式就行,那么deployment类型如果让pod部署在vpc-cni模式下呢?

首先我们正常创建一个pod,看看是什么样,如果直接通过service访问,能否获取到客户端ip。

代码语言:javascript复制
[root@VM-0-3-centos ~]# ip addr | grep eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    inet 10.0.0.3/24 brd 10.0.0.255 scope global eth0
[root@VM-0-3-centos ~]# curl 10.0.8.11
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

直接在控制台创建一个pod,然后创建service是没有直连的选项,因为pod默认还是在GR下,我们在客户端ip为10.0.0.3的机器上访问service,在pod的日志中发现ip并不是真是客户端ip,我们前面讲过这里请求到了node节点,再由cbr0这虚拟网卡将请求转发到pod,那么192.168.0.65这个ip就是虚拟网卡的ip,所以pod中日志的客户端ip就是虚拟网卡cbr0的ip

那么我们如何将pod部署在vpc-cni下呢?这里我们需要在yaml里面配置下才让让eni组件能识别到,这样才会部署到vpc-cni模式下

代码语言:javascript复制
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deploy
  namespace: default
spec:
  progressDeadlineSeconds: 600
  replicas: 1
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      k8s-app: nginx-deploy
      qcloud-app: nginx-deploy
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
    type: RollingUpdate
  template:
    metadata:
      annotations:
        tke.cloud.tencent.com/networks: tke-route-eni
      creationTimestamp: null
      labels:
        k8s-app: nginx-deploy
        qcloud-app: nginx-deploy
    spec:
      containers:
      - image: nginx
        imagePullPolicy: Always
        name: nginx-deploy
        resources:
          limits:
            cpu: 500m
            memory: 1Gi
            tke.cloud.tencent.com/eni-ip: "1"
          requests:
            cpu: 250m
            memory: 256Mi
            tke.cloud.tencent.com/eni-ip: "1"
        securityContext:
          privileged: false
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
      dnsPolicy: ClusterFirst
      imagePullSecrets:
      - name: qcloudregistrykey
      restartPolicy: Always
      schedulerName: default-scheduler
      securityContext: {}
      terminationGracePeriodSeconds: 30

这里我们需要在spec.template.metadata.annotations这个字段加上tke.cloud.tencent.com/networks: tke-route-eni的注解,然后再

resources的requeset和limit加上tke.cloud.tencent.com/eni-ip:"1"的数量,保存后重建下pod

添加了字段后可以发现我们的pod和节点是同一个网段,说明pod在vpc-cni的模式下了。

下面我们分别给负载配置直连的service和ingress,然后通过ingress和service访问pod,看看pod能否获取到我们的客户端ip

代码语言:javascript复制
[root@VM-0-51-tlinux ~]# kubectl get svc
NAME           TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
kubernetes     ClusterIP      192.168.12.1   <none>        443/TCP        86m
nginx-deploy   LoadBalancer   192.168.14.0   10.0.8.10     80:30535/TCP   50s
[root@VM-0-51-tlinux ~]# kubectl get ingress
NAME                   CLASS    HOSTS   ADDRESS    PORTS   AGE
nginx-deploy-ingress   <none>   *       10.0.8.8   80      19s

下面我们在其他vpc内集群访问下service看看,这里我们的客户端ip是10.0.0.13

代码语言:javascript复制
[root@VM-0-13-centos ~]# ip addr | grep eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    inet 10.0.0.13/24 brd 10.0.0.255 scope global eth0
[root@VM-0-13-centos ~]# curl 10.0.8.10
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

然后我们通过访问ingress的ip看看,这里我们的客户是10.0.0.3

代码语言:javascript复制
[root@VM-0-3-centos ~]# ip addr | grep eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    inet 10.0.0.3/24 brd 10.0.0.255 scope global eth0
[root@VM-0-3-centos ~]# curl 10.0.8.8
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

从上面来看,我们通过service和ingress都能在pod获取到客户端的真实ip。

3. StatefulSet类型pod获取客户端ip

下面我们说说StatefulSet的pod应该怎么操作,其实在tke控制提供了选项,是否将pod部署在vpc-cni下,因此StatefulSet就不需要通过修改yaml的方式

创建StatefulSet的时候我们可以在高级选项中勾选使用vpc-cni模式,并且选择固定ip,并配置一个直连的service。

下面我们在10.0.0.3中访问下StatefulSet的pod

代码语言:javascript复制
[root@VM-0-3-centos ~]# ip addr | grep eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    inet 10.0.0.3/24 brd 10.0.0.255 scope global eth0
[root@VM-0-3-centos ~]# curl 10.0.0.239
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

可以发现我们在pod中获取到了客户端的ip,说明pod现在是vpc-cni的模式下,这里我们也可以看看固定ip是怎么配置的

其实就是需要在spec.template.metadata.annotations这个字段加上注解tke.cloud.tencent.com/vpc-ip-claim-delete-policy: Never,这样pod ip就固定了。StatefulSet中配置ingress直连pod这个我们就不配置了,和deployment的一样配置一个直连ingress转发到后端的service就行。

0 人点赞