kubernetes系列08—service资源详解

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

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,不需要客户端知道关于 KubernetesService、或 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,不需要客户端知道关于 KubernetesService、或 Pod 的任何信息。 这应该比 userspace 代理更快、更可靠。然而,不像 userspace 代理,如果初始选择的 Pod 没有响应,iptables 代理能够自动地重试另一个 Pod,所以它需要依赖 readiness probes

 

1.3.3 ipvs代理模式

  ipvs (IP Virtual Server) 实现了传输层负载均衡,也就是我们常说的4LAN交换,作为 Linux 内核的一部分。ipvs运行在主机上,在真实服务器集群前充当负载均衡器。ipvs可以将基于TCPUDP的服务请求转发到真实服务器上,并使真实服务器的服务在单个 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 也依赖 iptablesipvs 会使用 iptables 进行包过滤、SNATmasquared(伪装)。具体来说,ipvs 将使用ipset来存储需要DROPmasquared的流量的源或目标地址,以确保 iptables 规则的数量是恒定的,这样我们就不需要关心我们有多少服务了

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

修改配置文件

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

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

ip_vsip_vs_rrip_vs_wrrip_vs_shnf_conntrack_ipv4

 

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

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

 

1.5 service4中类型

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

 

2、创建clusterIP类型的service

1)编写yaml文件并创建名为redisservice

先创建一个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文件并创建名为myappservice

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

[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保持会话连接

1sessionAffinity默认是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)打补丁修改sessionAffinityclientip;实现会话连接

也可以使用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-svcservice

绑定上面创建myapp3pod

[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)和有头正常myappservice对比

无头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
... ...

有头正常myappservice的解析:

[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
... ...

 

 

posted @ 2019-01-28 14:25 alonghub 阅读(...) 评论(...) 编辑 收藏