美文网首页云计算
kubernetes 服务访问

kubernetes 服务访问

作者: 梅_梅 | 来源:发表于2018-05-13 16:19 被阅读0次

    简介

    kubernetes 使用service和ingress共同构建了,外部访问k8s内部容器的通道。

    Service

    概念

    Service是定义一系列Pod以及访问这些Pod的策略的一层抽象

    Pods是短暂的,那么重启时IP地址可能会改变,为了能从前端容器正确可靠地指向后台容器

    Service通过Label找到Pod组。因为Service是抽象的,所以在图表里通常看不到它们的存在,这也就让这一概念更难以理解。

    现在,假定有2个后台Pod,并且定义后台Service的名称为‘backend-service’,lable选择器为(tier=backend, app=myapp)。backend-service 的Service会完成如下两件重要的事情:

    • 会为Service创建一个本地集群的DNS入口,因此前端Pod只需要DNS查找主机名为 ‘backend-service’,就能够解析出前端应用程序可用的IP地址。
    • 现在前端已经得到了后台服务的IP地址,但是它应该访问2个后台Pod的哪一个呢?Service在这2个后台Pod之间提供透明的负载均衡,会将请求分发给其中的任意一个(如下面的动画所示)。通过每个Node上运行的代理(kube-proxy)完成。

    下述动画展示了Service的功能。注意该图作了很多简化。如果不进入网络配置,那么达到透明的负载均衡目标所涉及的底层网络和路由相对先进。


    k8s-service.gif

    在实际生产环境中,对Service的访问可能会有两种来源:Kubernetes集群内部的程序(Pod)和Kubernetes集群外部,为了满足上述的场景,Kubernetes service有以下三种类型:

    • ClusterIP:提供一个集群内部的虚拟IP以供Pod访问。
    • NodePort:在每个Node上打开一个端口以供外部访问。
    • LoadBalancer:通过外部的负载均衡器来访问。

    ClusterIP

    此模式会提供一个集群内部的虚拟IP(与Pod不在同一网段),以供集群内部的pod之间通信使用。
    ClusterIP也是Kubernetes service的默认类型。

    clusterip.png

    为了实现图上的功能主要需要以下几个组件的协同工作

    • apiserver
      用户通过kubectl命令向apiserver发送创建service的命令,apiserver接收到请求以后将数据存储到etcd中。
    • kube-proxy
      kubernetes的每个节点中都有一个叫做kube-proxy的进程,这个进程负责感知service,pod的变化,并将变化的信息写入本地的
      iptables中。
    • iptables
      使用NAT等技术将virtualIP的流量转至endpoint中。
      下面我们实际发布一个Service,能够更清晰的了解到Service是如何工作的。

    发布一个rc,并指定replices count为3.

    yancey@ yancey-macbook kubernetes-1$kubectl create -f rc_nginx.yaml
    yancey@ yancey-macbook kubernetes-1$kubectl get pods
    NAME                     READY     STATUS    RESTARTS   AGE
    k8s-master-core-v2-01    4/4       Running   0          8m
    k8s-proxy-core-v2-01     1/1       Running   0          8m
    nginx-controller-6bovu   1/1       Running   0          4m
    nginx-controller-iowux   1/1       Running   0          4m
    nginx-controller-o7m6u   1/1       Running   0          4m
    yancey@ yancey-macbook kubernetes-1$kubectl describe pod nginx-controller-6bovu
    Name:       nginx-controller-6bovu
    Namespace:  default
    Node:       core-v2-01/172.17.8.101
    Start Time: Fri, 17 Jun 2016 15:22:20 +0800
    Labels:     app=nginx
    Status:     Running
    IP:     10.1.13.3
    Controllers:    ReplicationController/nginx-controller
    

    发布一个service,并指定步骤1中的label

    yancey@ yancey-macbook kubernetes-1$kubectl create -f service_nginx.yaml
    yancey@ yancey-macbook kubernetes-1$kubectl get svc
    NAME            CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
    kubernetes      10.0.0.1     <none>        443/TCP    9m
    nginx-service   10.0.0.252   <none>        8000/TCP   4m
    

    kubernetes为nginx-server这个service创建了一个10.0.0.252这个ClusterIP,也可以称为VirtualIP.

    yancey@ yancey-macbook kubernetes-1$kubectl get ep
    NAME            ENDPOINTS                                AGE
    kubernetes      172.17.8.101:6443                        9m
    nginx-service   10.1.13.2:80,10.1.13.3:80,10.1.13.4:80   4m
    

    查看endpoint信息,发现nginx-service一共有三个endpoint地址,分别对应nginx的三个pod。

    查看iptables,观察其NAT表中的信息(只截取了部分和这个service有关的信息)
    查看iptables中NAT表的命令: iptables -L -v -n -t nat

    Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes)
     pkts bytes target     prot opt in     out     source               destination
       37  2766 KUBE-SERVICES  all  --  *      *       0.0.0.0/0            0.0.0.0/0            /* kubernetes service portals */
       33  2112 DOCKER     all  --  *      *       0.0.0.0/0            0.0.0.0/0            ADDRTYPE match dst-type LOCAL
    

    在PREROUTING链中会先匹配到KUBE-SERVICES这个Chain。

    Chain KUBE-SERVICES (2 references)
     pkts bytes target     prot opt in     out     source               destination
        0     0 KUBE-SVC-GKN7Y2BSGW4NJTYL  tcp  --  *      *       0.0.0.0/0            10.0.0.252           /* default/nginx-service: cluster IP */ tcp dpt:8000
       18  1080 KUBE-NODEPORTS  all  --  *      *       0.0.0.0/0            0.0.0.0/0            /* kubernetes service nodeports; NOTE: this must be the last rule in this chain */ ADDRTYPE match dst-type LOCAL
    

    所有destinationIP为10.0.0.252的包都转到KUBE-SVC-GKN7Y2BSGW4NJTYL这个Chain

    Chain KUBE-SVC-GKN7Y2BSGW4NJTYL (1 references)
     pkts bytes target     prot opt in     out     source               destination
        0     0 KUBE-SEP-7ROBBXFV7SD4AIRW  all  --  *      *       0.0.0.0/0            0.0.0.0/0            /* default/nginx-service: */ statistic mode random probability 0.33332999982
        0     0 KUBE-SEP-XY3F6VJIZ7ELIF4Z  all  --  *      *       0.0.0.0/0            0.0.0.0/0            /* default/nginx-service: */ statistic mode random probability 0.50000000000
        0     0 KUBE-SEP-JIDZHFC4A3T535AK  all  --  *      *       0.0.0.0/0            0.0.0.0/0            /* default/nginx-service: */
    

    这里能看到请求会平均执行KUBE-SEP-7ROBBXFV7SD4AIRW,KUBE-SEP-XY3F6VJIZ7ELIF4Z,KUBE-SEP-XY3F6VJIZ7ELIF4Z这三个Chain.最后我们看下KUBE-SEP-7ROBBXFV7SD4AIRW这个Chain做了什么

    Chain KUBE-SEP-7ROBBXFV7SD4AIRW (1 references)
     pkts bytes target     prot opt in     out     source               destination
        0     0 KUBE-MARK-MASQ  all  --  *      *       10.1.13.2            0.0.0.0/0            /* default/nginx-service: */
        0     0 DNAT       tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            /* default/nginx-service: */ tcp to:10.1.13.2:80
    

    这个Chain使用了DNAT规则将流量转发到10.1.13.2:80这个地址。至此从Pod发出来的流量通过本地的iptables将流量转至了service背后的pod上。

    NodePort

    Kubernetes将会在每个Node上打开一个端口并且每个Node的端口都是一样的,通过<NodeIP>:NodePort的方式Kubernetes集群外部的程序可以访问Service。

    apiVersion: v1
    kind: Service
    metadata:
      name: nginx-service
    spec:
      type: NodePort
      ports:
      - port: 8000
        targetPort: 80
        protocol: TCP
      # just like the selector in the replication controller,
      # but this time it identifies the set of pods to load balance
      # traffic to.
      selector:
        app: nginx
    

    发布这个service,可以看到已经随机分配了一个NodePort端口。

    yancey@ yancey-macbook kubernetes-1$kubectl get svc nginx-service -o yaml
    apiVersion: v1
    kind: Service
    metadata:
      creationTimestamp: 2016-06-18T03:29:42Z
      name: nginx-service
      namespace: default
      resourceVersion: "1405"
      selfLink: /api/v1/namespaces/default/services/nginx-service
      uid: e50ba23a-3504-11e6-a94f-080027a75e9e
    spec:
      clusterIP: 10.0.0.229
      ports:
      - nodePort: 30802
        port: 8000
        protocol: TCP
        targetPort: 80
      selector:
        app: nginx
      sessionAffinity: None
      type: NodePort
    status:
      loadBalancer: {}
    

    观察Iptables中的变化,KUBE-NODEPORTS这个Chain中增加了如下内容

    Chain KUBE-NODEPORTS (1 references)
     pkts bytes target     prot opt in     out     source               destination
        0     0 KUBE-MARK-MASQ  tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            /* default/nginx-service: */ tcp dpt:30802
        0     0 KUBE-SVC-GKN7Y2BSGW4NJTYL  tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            /* default/nginx-service: */ tcp dpt:30802
    

    可以看到流量会转至KUBE-SVC-GKN7Y2BSGW4NJTYL这个Chain中处理一次,也就是ClusterIP中提到的通过负载均衡将流量平均分配到3个endpoint上。

    LoadBalancer

    Ingress

    概念

    通常情况下,service和pod仅可在集群内部网络中通过IP地址访问。所有到达边界路由器的流量或被丢弃或被转发到其他地方。从概念上讲,可能像下面这样:

        internet
            |
      ------------
      [ Services ]
    

    Ingress是授权入站连接到达集群服务的规则集合。

        internet
            |
       [ Ingress ]
       --|-----|--
       [ Services ]
    

    你可以给Ingress配置提供外部可访问的URL、负载均衡、SSL、基于名称的虚拟主机等。用户通过POST Ingress资源到API server的方式来请求ingress。 Ingress controller负责实现Ingress,通常使用负载平衡器,它还可以配置边界路由和其他前端,这有助于以HA方式处理流量。

    你需要一个Ingress Controller来实现Ingress,单纯的创建一个Ingress没有任何意义。

    GCE/GKE会在master节点上部署一个ingress controller。你可以在一个pod中部署任意个自定义的ingress controller。你必须正确地annotate每个ingress,比如 运行多个ingress controller 和 关闭glbc

    为了使Ingress正常工作,集群中必须运行Ingress controller。 这与其他类型的控制器不同,其他类型的控制器通常作为kube-controller-manager二进制文件的一部分运行,在集群启动时自动启动。 你需要选择最适合自己集群的Ingress controller或者自己实现一个。

    实践

    单Service Ingress

    apiVersion: extensions/v1beta1
    kind: Ingress
    metadata:
      name: test-ingress
    spec:
      backend:
        serviceName: testsvc
        servicePort: 80
    

    使用kubectl create -f命令创建,然后查看ingress:

    $ kubectl get ing
    NAME                RULE          BACKEND        ADDRESS
    test-ingress        -             testsvc:80     107.178.254.228
    

    107.178.254.228就是Ingress controller为了实现Ingress而分配的IP地址。RULE列表示所有发送给该IP的流量都被转发到了BACKEND所列的Kubernetes service上。

    简单展开

    如前面描述的那样,kubernete pod中的IP只在集群网络内部可见,我们需要在边界设置一个东西,让它能够接收ingress的流量并将它们转发到正确的端点上。这个东西一般是高可用的loadbalancer。使用Ingress能够允许你将loadbalancer的个数降低到最少,例如,嫁入你想要创建这样的一个设置:

    foo.bar.com -> 178.91.123.132 -> / foo    s1:80
                                     / bar    s2:80
    

    你需要一个这样的ingress:

    apiVersion: extensions/v1beta1
    kind: Ingress
    metadata:
      name: test
    spec:
      rules:
      - host: foo.bar.com
        http:
          paths:
          - path: /foo
            backend:
              serviceName: s1
              servicePort: 80
          - path: /bar
            backend:
              serviceName: s2
              servicePort: 80
    

    使用kubectl create -f创建完ingress后:

    $ kubectl get ing
    NAME      RULE          BACKEND   ADDRESS
    test      -
              foo.bar.com
              /foo          s1:80
              /bar          s2:80
    

    只要服务(s1,s2)存在,Ingress controller就会将提供一个满足该Ingress的特定loadbalancer实现。 这一步完成后,您将在Ingress的最后一列看到loadbalancer的地址。

    基于名称的虚拟主机

    Name-based的虚拟主机在同一个IP地址下拥有多个主机名。

    foo.bar.com --|                 |-> foo.bar.com s1:80
                  | 178.91.123.132  |
    bar.foo.com --|                 |-> bar.foo.com s2:80
    

    下面这个ingress说明基于 Host header的后端loadbalancer的路由请求:

    apiVersion: extensions/v1beta1
    kind: Ingress
    metadata:
      name: test
    spec:
      rules:
      - host: foo.bar.com
        http:
          paths:
          - backend:
              serviceName: s1
              servicePort: 80
      - host: bar.foo.com
        http:
          paths:
          - backend:
              serviceName: s2
              servicePort: 80
    

    默认backend:一个没有rule的ingress,如前面章节中所示,所有流量都将发送到一个默认backend。你可以用该技巧通知loadbalancer如何找到你网站的404页面,通过制定一些列rule和一个默认backend的方式。如果请求header中的host不能跟ingress中的host匹配,并且/或请求的URL不能与任何一个path匹配,则流量将路由到你的默认backend。

    TLS

    你可以通过指定包含TLS私钥和证书的 secret 来加密Ingress。 目前,Ingress仅支持单个TLS端口443,并假定TLS termination。 如果Ingress中的TLS配置部分指定了不同的主机,则它们将根据通过SNI TLS扩展指定的主机名(假如Ingress controller支持SNI)在多个相同端口上进行复用。 TLS secret中必须包含名为tls.crttls.key的密钥,这里面包含了用于TLS的证书和私钥,例如:

    apiVersion: v1
    data:
      tls.crt: base64 encoded cert
      tls.key: base64 encoded key
    kind: Secret
    metadata:
      name: testsecret
      namespace: default
    type: Opaque
    

    在Ingress中引用这个secret将通知Ingress controller使用TLS加密从将客户端到loadbalancer的channel:

    apiVersion: extensions/v1beta1
    kind: Ingress
    metadata:
      name: no-rules-map
    spec:
      tls:
        - secretName: testsecret
      backend:
        serviceName: s1
        servicePort: 80
    

    各种Ingress controller支持的TLS功能之间存在差距。 请参阅有关
    nginx,gce 或任何其他平台特定Ingress controller的文档,以了解TLS在你的环境中的工作原理。

    Ingress controller启动时附带一些适用于所有Ingress的负载平衡策略设置,例如负载均衡算法,后端权重方案等。更高级的负载平衡概念(例如持久会话,动态权重)尚未在Ingress中公开。 你仍然可以通过 service loadbalancer获取这些功能。 随着时间的推移,我们计划将适用于跨平台的负载平衡模式加入到Ingress资源中。

    还值得注意的是,尽管健康检查不直接通过Ingress公开,但Kubernetes中存在并行概念,例如准备探查,可以使你达成相同的最终结果。 请查看特定控制器的文档,以了解他们如何处理健康检查。

    相关文章

      网友评论

        本文标题:kubernetes 服务访问

        本文链接:https://www.haomeiwen.com/subject/iortdftx.html