本文收录在容器技术学习系列文章总目录html
Kubernetes Pod 是有生命周期的,它们能够被建立,也能够被销毁,然而一旦被销毁生命就永远结束。 经过 ReplicationController 可以动态地建立和销毁 Pod(例如,须要进行扩缩容,或者执行 滚动升级)。 每一个 Pod 都会获取它本身的 IP 地址,即便这些 IP 地址不老是稳定可依赖的。 这会致使一个问题:在 Kubernetes 集群中,若是一组 Pod(称为 backend)为其它 Pod (称为 frontend)提供服务,那么那些 frontend 该如何发现,并链接到这组 Pod 中的哪些 backend 呢?答案是:Service。node
Kubernetes Service 定义了这样一种抽象:一个 Pod 的逻辑分组,一种能够访问它们的策略 —— 一般称为微服务。 这一组 Pod 可以被 Service 访问到,一般是经过 Label Selector(下面咱们会讲到咱们为何须要一个没有label selector的服务)实现的。redis
举个例子,考虑一个图片处理 backend,它运行了3个副本。这些副本是可互换的 —— frontend 不须要关心它们调用了哪一个 backend 副本。 然而组成这一组 backend 程序的 Pod 实际上可能会发生变化,frontend 客户端不该该也不必知道,并且也不须要跟踪这一组 backend 的状态。 Service 定义的抽象可以解耦这种关联。算法
对 Kubernetes 集群中的应用,Kubernetes 提供了简单的 Endpoints API,只要 Service 中的一组 Pod 发生变动,应用程序就会被更新。 对非 Kubernetes 集群中的应用,Kubernetes 提供了基于 VIP 的网桥的方式访问 Service,再由 Service 重定向到 backend Pod。vim
这种模式,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 的任何信息。api
默认的策略是,经过 round-robin 算法来选择 backend Pod。 实现基于客户端 IP 的会话亲和性,能够经过设置 service.spec.sessionAffinity 的值为 "ClientIP" (默认值为 "None")。bash
这种模式,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。
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 也依赖 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)编写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>
(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)在集群外访问服务
(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
(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 ... ...