您的位置:首页 > 运维架构 > Kubernetes

kubernetes系列08—service资源详解

伊甸之恋 2019-01-28 14:25 10 查看 https://www.cnblogs.com/along2

本文收录在容器技术学习系列文章总目录

1、认识service

1.1 为什么要使用service

  Kubernetes Pod 是有生命周期的,它们可以被创建,也可以被销毁,然而一旦被销毁生命就永远结束。 通过 ReplicationController 能够动态地创建和销毁 Pod(例如,需要进行扩缩容,或者执行 滚动升级)。 每个 Pod 都会获取它自己的 IP 地址,即使这些 IP 地址不总是稳定可依赖的。 这会导致一个问题:在 Kubernetes 集群中,如果一组 Pod(称为 backend)为其它 Pod (称为 frontend)提供服务,那么那些 frontend 该如何发现,并连接到这组 Pod 中的哪些 backend 呢?答案是:Service。

 

1.2 service介绍

  Kubernetes Service 定义了这样一种抽象:一个 Pod 的逻辑分组,一种可以访问它们的策略 —— 通常称为微服务。 这一组 Pod 能够被 Service 访问到,通常是通过 Label Selector(下面我们会讲到我们为什么需要一个没有label selector的服务)实现的。

  举个例子,考虑一个图片处理 backend,它运行了3个副本。这些副本是可互换的 —— frontend 不需要关心它们调用了哪个 backend 副本。 然而组成这一组 backend 程序的 Pod 实际上可能会发生变化,frontend 客户端不应该也没必要知道,而且也不需要跟踪这一组 backend 的状态。 Service 定义的抽象能够解耦这种关联。

  对 Kubernetes 集群中的应用,Kubernetes 提供了简单的 Endpoints API,只要 Service 中的一组 Pod 发生变更,应用程序就会被更新。 对非 Kubernetes 集群中的应用,Kubernetes 提供了基于 VIP 的网桥的方式访问 Service,再由 Service 重定向到 backend Pod。

 

1.3 三种代理模式

  •  userspace 代理模式(K8S 1.1之前版本)
  •  iptables 代理模式(K8S 1.10之前版本)
  •  ipvs 代理模式(K8S 1.11 之后版本,激活ipvs需要修改配置)

1.3.1 userspace 代理模式

  这种模式,kube-proxy 会监视 Kubernetes master 对 Service 对象和 Endpoints 对象的添加和移除。 对每个 Service,它会在本地 Node 上打开一个端口(随机选择)。 任何连接到“代理端口”的请求,都会被代理到 Service 的backend Pods 中的某个上面(如 Endpoints 所报告的一样)。 使用哪个 backend Pod,是基于 Service 的 SessionAffinity 来确定的。 最后,它安装 iptables 规则,捕获到达该 Service 的 clusterIP(是虚拟 IP)和 Port 的请求,并重定向到代理端口,代理端口再代理请求到 backend Pod。

  网络返回的结果是,任何到达 Service 的 IP:Port 的请求,都会被代理到一个合适的 backend,不需要客户端知道关于 Kubernetes、Service、或 Pod 的任何信息。

  默认的策略是,通过 round-robin 算法来选择 backend Pod。 实现基于客户端 IP 的会话亲和性,可以通过设置 service.spec.sessionAffinity 的值为 "ClientIP" (默认值为 "None")。

 

 1.3.2 iptables 代理模式

  这种模式,kube-proxy 会监视 Kubernetes master 对 Service 对象和 Endpoints 对象的添加和移除。 对每个 Service,它会安装 iptables 规则,从而捕获到达该 Service 的 clusterIP(虚拟 IP)和端口的请求,进而将请求重定向到 Service 的一组 backend 中的某个上面。 对于每个 Endpoints 对象,它也会安装 iptables 规则,这个规则会选择一个 backend Pod。

  默认的策略是,随机选择一个 backend。 实现基于客户端 IP 的会话亲和性,可以将 service.spec.sessionAffinity 的值设置为 "ClientIP" (默认值为 "None")。

  和 userspace 代理类似,网络返回的结果是,任何到达 Service 的 IP:Port 的请求,都会被代理到一个合适的 backend,不需要客户端知道关于 Kubernetes、Service、或 Pod 的任何信息。 这应该比 userspace 代理更快、更可靠。然而,不像 userspace 代理,如果初始选择的 Pod 没有响应,iptables 代理能够自动地重试另一个 Pod,所以它需要依赖 readiness probes。

 

1.3.3 ipvs代理模式

  ipvs (IP Virtual Server) 实现了传输层负载均衡,也就是我们常说的4层LAN交换,作为 Linux 内核的一部分。ipvs运行在主机上,在真实服务器集群前充当负载均衡器。ipvs可以将基于TCP和UDP的服务请求转发到真实服务器上,并使真实服务器的服务在单个 IP 地址上显示为虚拟服务。

  在kubernetes v1.8 中引入了 ipvs 模式,在 v1.9 中处于 beta 阶段,在 v1.11 中已经正式可用了。 iptables 模式在 v1.1 中就添加支持了,从 v1.2 版本开始 iptables 就是 kube-proxy 默认的操作模式,ipvs 和 iptables 都是基于netfilter的, ipvs 模式和 iptables 模式之间的差异:

  •  ipvs 为大型集群提供了更好的可扩展性和性能
  •  ipvs 支持比 iptables 更复杂的复制均衡算法(最小负载、最少连接、加权等等)
  •  ipvs 支持服务器健康检查和连接重试等功能

  同时ipvs 也依赖 iptables,ipvs 会使用 iptables 进行包过滤、SNAT、masquared(伪装)。具体来说,ipvs 将使用ipset来存储需要DROP或masquared的流量的源或目标地址,以确保 iptables 规则的数量是恒定的,这样我们就不需要关心我们有多少服务了

ipvs虽然在v1.1版本中已经支持,但是想要使用,还需激活ipvs:

① 修改配置文件

[root@master ~]# vim /etc/sysconfig/kubelet
KUBE_PROXY=MODE=ipvs

② 编写脚本,让kubelet所在的主机,启动时装入以下几个模块:

ip_vs,ip_vs_rr,ip_vs_wrr,ip_vs_sh,nf_conntrack_ipv4

 

1.4 service定义资源清单几个字段

  •  apiVersion: v1  版本
  •  kind: Service  类型
  •  metadata  元数据
  •  spec  期望状态  ports:服务公开的端口列表;把哪个端口和后端建立联系  port:此服务将公开的端口
  •  targetPort:要在服务所针对的pod上访问的端口的编号或名称
  •  nodePort:K8S 集群节点上的端口
  •  selector:标签选择器;关联到哪些pod资源上
  •  clusterIP:服务的IP地址,通常由主服务器随机分配
  •  type:确定服务的公开方式。 默认为ClusterIP
       ClusterIP(默认)
    •  NodePort
    •  LoadBalancer
    •  ExternelName
  •  sessionAffinity:service负载均衡,默认值是None,根据iptables规则随机调度;可使用sessionAffinity保持会话连线;
  •  status  当前状态
  •  

    1.5 service的4中类型

    •  ClusterIP(默认):仅用于集群内通信,集群内部可达,可以被各pod访问,节点本身可访问;
    •  NodePort:构建在ClusterIP上,并在路由到clusterIP的每个节点上分配一个端口;  client ---> NodeIP:NodePort ---> ClusterIP:ServicePort ---> PodIP:containePort
  •  LoadBalancer:构建在NodePort上,并创建一个外部负载均衡器(如果在当前云中受支持),它将路由到clusterIP;
  •  ExternelName:通过CNAME将service与externalName的值(比如:foo.bar.example.com)映射起来. 要求kube-dns的版本为1.7或以上.
  •  

    2、创建clusterIP类型的service

    (1)编写yaml文件并创建名为redis的service

    先创建一个deployment,启动一个redis pod;在使用service绑定这个pod

    [root@master manifests]# vim redis-svc.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: redis
    namespace: default
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: redis
    role: logstor
    template:
    metadata:
    labels:
    app: redis
    role: logstor
    spec:
    containers:
    - name: redis
    image: redis:4.0-alpine
    ports:
    - name: redis
    containerPort: 6379
    ---
    apiVersion: v1
    kind: Service
    metadata:
    name: redis
    namespace: default
    spec:
    selector:
    app: redis
    role: logstor
    clusterIP: 10.99.99.99
    type: ClusterIP
    ports:
    - port: 6380
    targetPort: 6379
    [root@master manifests]# kubectl apply -f redis-svc.yaml
    deployment.apps/redis created
    service/redis created

      

    (2)查询验证

    [root@master ~]# kubectl get svc
    NAME         TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)    AGE
    kubernetes   ClusterIP   10.96.0.1     <none>        443/TCP    142d
    redis        ClusterIP   10.99.99.99   <none>        6380/TCP   12s
    ---查询service详细信息,pod绑定成功
    [root@master ~]# kubectl describe svc redis
    Name:              redis
    Namespace:         default
    Labels:            <none>
    Annotations:       kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"redis","namespace":"default"},"spec":{"clusterIP":"10.99.99.99","ports":[{"por...
    Selector:          app=redis,role=logstor
    Type:              ClusterIP
    IP:                10.99.99.99
    Port:              <unset>  6380/TCP
    TargetPort:        6379/TCP
    Endpoints:         10.244.2.94:6379
    Session Affinity:  None
    Events:            <none>

      

    3、创建NodePort类型的service

    3.1 创建service

    (1)编写yaml文件并创建名为myapp的service

    先创建一个deployment,启动3个myapp pod;在使用service绑定这3个pod

    [root@master manifests]# vim myapp-svc.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: myapp-deploy
    namespace: default
    spec:
    replicas: 3
    selector:
    matchLabels:
    app: myapp
    release: canary
    template:
    metadata:
    labels:
    app: myapp
    release: canary
    spec:
    containers:
    - name: myapp
    image: ikubernetes/myapp:v1
    ports:
    - name: http
    containerPort: 80
    ---
    apiVersion: v1
    kind: Service
    metadata:
    name: myapp
    namespace: default
    spec:
    selector:
    app: myapp
    release: canary
    clusterIP: 10.97.97.97
    type: NodePort
    ports:
    - port: 80
    targetPort: 80
    nodePort: 31180
    [root@master manifests]# kubectl apply -f myapp-svc.yaml
    deployment.apps/myapp-deploy unchanged
    service/myapp created

      

    (2)查询验证

    [root@master ~]# kubectl get svc
    NAME         TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)        AGE
    kubernetes   ClusterIP   10.96.0.1     <none>        443/TCP        145d
    myapp        NodePort    10.97.97.97   <none>        80:31180/TCP   39s
    redis        ClusterIP   10.99.99.99   <none>        6380/TCP       2d
    [root@master ~]# kubectl describe svc myapp
    Name:                     myapp
    Namespace:                default
    Labels:                   <none>
    Annotations:              kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"myapp","namespace":"default"},"spec":{"clusterIP":"10.97.97.97","ports":[{"nod...
    Selector:                 app=myapp,release=canary
    Type:                     NodePort
    IP:                       10.97.97.97
    Port:                     <unset>  80/TCP
    TargetPort:               80/TCP
    NodePort:                 <unset>  31180/TCP
    Endpoints:                10.244.1.96:80,10.244.2.101:80,10.244.2.102:80
    Session Affinity:         None
    External Traffic Policy:  Cluster
    Events:                   <none>

      

    (3)在集群外访问服务

     

    3.2 使用sessionAffinity保持会话连接

    (1)sessionAffinity默认是None,没有修改前,访问业务是随机调度

    [root@master ~]# while true; do curl 192.168.10.103:31180/hostname.html; sleep 1; done
    myapp-deploy-69b47bc96d-mmb5v
    myapp-deploy-69b47bc96d-wtbx7
    myapp-deploy-69b47bc96d-wtbx7
    myapp-deploy-69b47bc96d-cj48v
    ... ...

      

    (2)打补丁修改sessionAffinity为clientip;实现会话连接

    也可以使用exec修改;或者直接修改yaml文件也可以;

    [root@master ~]# kubectl patch svc myapp -p '{"spec":{"sessionAffinity":"ClientIP"}}'
    service/myapp patched

      

    (3)查询验证

    [root@master ~]# kubectl describe svc myapp
    Name:                     myapp
    Namespace:                default
    Labels:                   <none>
    Annotations:              kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"myapp","namespace":"default"},"spec":{"clusterIP":"10.97.97.97","ports":[{"nod...
    Selector:                 app=myapp,release=canary
    Type:                     NodePort
    IP:                       10.97.97.97
    Port:                     <unset>  80/TCP
    TargetPort:               80/TCP
    NodePort:                 <unset>  31180/TCP
    Endpoints:                10.244.1.96:80,10.244.2.101:80,10.244.2.102:80
    Session Affinity:         ClientIP
    External Traffic Policy:  Cluster
    Events:                   <none>

      

    (4)访问业务查询验证;发现同一客户端的请求始终发往同一pod

    [root@master ~]# while true; do curl 192.168.10.103:31180/hostname.html; sleep 1; done
    myapp-deploy-69b47bc96d-cj48v
    myapp-deploy-69b47bc96d-cj48v
    myapp-deploy-69b47bc96d-cj48v
    myapp-deploy-69b47bc96d-cj48v
    ... ...

      

    (5)重新打补丁修改为None,立即恢复为随机调度

    [root@master ~]# kubectl patch svc myapp -p '{"spec":{"sessionAffinity":"None"}}'
    service/myapp patched
    [root@master ~]# while true; do curl 192.168.10.103:31180/hostname.html; sleep 1; done
    myapp-deploy-69b47bc96d-cj48v
    myapp-deploy-69b47bc96d-mmb5v
    myapp-deploy-69b47bc96d-cj48v
    myapp-deploy-69b47bc96d-mmb5v

      

    4、创建无头service

    (1)编写yaml文件并创建名为myapp-svc的service

    绑定上面创建myapp的3个pod

    [root@master manifests]# vim myapp-svc-headless.yaml
    apiVersion: v1
    kind: Service
    metadata:
    name: myapp-svc
    namespace: default
    spec:
    selector:
    app: myapp
    release: canary
    clusterIP: None
    ports:
    - port: 80
    targetPort: 80
    [root@master manifests]# kubectl apply -f myapp-svc-headless.yaml
    service/myapp-svc created

      

    (2)查询验证

    [root@master ~]# kubectl get svc
    NAME         TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)        AGE
    kubernetes   ClusterIP   10.96.0.1     <none>        443/TCP        145d
    myapp        NodePort    10.97.97.97   <none>        80:31180/TCP   2h
    myapp-svc    ClusterIP   None          <none>        80/TCP         6s
    redis        ClusterIP   10.99.99.99   <none>        6380/TCP       2d

      

    (3)和有头正常myapp的service对比

    无头service的解析:

    [root@master manifests]# dig -t A myapp-svc.default.svc.cluster.local. @10.96.0.10
    ... ...
    ;; ANSWER SECTION:
    myapp-svc.default.svc.cluster.local. 5 IN A	10.244.1.96
    myapp-svc.default.svc.cluster.local. 5 IN A	10.244.2.101
    myapp-svc.default.svc.cluster.local. 5 IN A	10.244.2.102
    ... ...

    有头正常myapp的service的解析:

    [root@master manifests]# dig -t A myapp.default.svc.cluster.local. @10.96.0.10
    ... ...
    ;; ANSWER SECTION:
    myapp.default.svc.cluster.local. 5 IN	A	10.97.97.97
    ... ...

     

     

    标签: