摘要:本文属于原创,欢迎转载,转载请保留出处:https://github.com/jasonGeng88/blogphp
文章一:带着问题学 Kubernetes 架构git
文章二:带着问题学 Kubernetes 基本单元 Podgithub
Mac OS 10.11.xdocker
kubectl == v1.6.4api
minikube == v0.19.1架构
docker == 1.11.1app
Service 的 Selector 与 Label 匹配机制负载均衡
Service 与 Pods 的地址映射关系性能
kube-proxy 的 iptables 代理机制学习
Service 的服务发现机制
Service 的服务暴露方式
上一篇讲述了 Pod 的相关内容,了解了 Pod 的定义、生命周期以及通讯机制等。正如上文说的,Pod 是存在生命周期的,它的崩溃、更新都是以建立新 Pod 替换原有的 Pod 的方式进行的,因此经过固定 Pod 地址的访问变得不太可行。咱们须要经过一种上层调用的方式,来解决底层 Pod 的动态变化的场景。
庆幸,K8S 引入了 Service 这个抽象的概念。Service 会建立一个虚拟的服务,由它来整合集群内的 Pod。Service 会虚拟出一个 VIP,并在它销毁以前保持该 VIP 地址保持不变。经过对它的访问,以代理的方式负载到对应的 Pod 上,同时 Pod 生命周期的变换,也会及时反应在代理上。
下面咱们几种常见的场景,来具体看看 Service 是如何工做的。
镜像名:jasonn/php-echoserver
做用:打印当前容器的 IP
文件名:deploy-echoserver.yml (这里以 Deployment 的方式来建立与管理 Pod)
文件内容:
apiVersion: apps/v1beta1 kind: Deployment metadata: # Deployment 实例名称 name: echoserver spec: # 设置 Pod 个数 replicas: 2 template: metadata: # 设置 Pod 标签 labels: app: echoserver spec: # 运行 docker 镜像 containers: - name: echoserver image: jasonn/php-echoserver
启动命令:
kubectl create -f deploy-echoserver.yml
至此,准备工做所有完成。短暂的等待后,Pod 建立成功,而且也由 deployment 管理着。
查看 deployment 启动状况:
对 Pod 的访问状况以下(经过kubectl describe pods
获取 Pod 的 IP 地址):
这里所说的 Pod 默认都是带有标签(label)的,咱们以前建立的两个 Pod 所赋予的标签是 app: echoserver
,因此咱们在建立 Service 时,就要经过选择器(selector)来获取符合条件的 Pod 进行整合。
Service 建立脚本内容以下(service-echoserver.yml):
apiVersion: v1 kind: Service metadata: # Service 实例名称 name: svc-echoserver spec: ports: - protocol: TCP # Service 端口地址 port: 8080 # Pod 端口地址 targetPort: 80 selector: # 匹配符合标签条件的 Pod app: echoserver
建立 Service 命令:
kubectl create -f service-echoserver.yml
由此,咱们建立好了一个 Service,同时也生成了一个对应的 VIP。
查看 Serivce 建立状况:
下面,咱们来验证下是否如以前所说,对 VIP 的访问能访问到 Pod 的内容。
咱们发现不只能成功访问,并且还提供了负载均衡的功能。后面会讲负载是怎么实现的
PS: 标签筛选查找范围仅在同个命名空间(namespace)内。
这里引出了另外一个概念 Endpoints。咱们先来看看它的一个具体状况。
发如今 Service 建立的同时,还生成了一个 Endpoints。 该 Endpoints 与 Service 同名,它所暴露的地址信息正是对应 Pod 的地址。由此猜想是 Endpoints 维护了 Service 与 Pod 的映射关系。
为了验证咱们的猜想,咱们手动删除 Endpoints,发现以前能成功访问到 Pod 的 VIP,如今已经已经访问不到了。
咱们在手动把 Endpoints 建立回来,建立脚本以下(endpoint-echoserver.yml):
apiVersion: v1 kind: Endpoints metadata: # Endpoints 实例的名称 name: svc-echoserver subsets: - addresses: - ip: 172.17.0.5 - ip: 172.17.0.6 ports: - port: 80
建立命令:
kubectl create -f endpoint-echoserver.yml
注意:Endpoints 与 Service 的绑定关系经过名称来关联的,因此这二者的名称(name)必定要一致。
若是建立失败,出现的错误信息是“...endpoints "svc-echoserver" already exists”,说明 Service 已经更新了 Endpoints。这里就说到了 Service 会按期去检查 Pod 的状态,而且将结果更新到 Endpoints 上。
VIP 再次访问时又能成功访问到,如图:
如今咱们已经能轻松的解决场景2的问题了,在建立 Service 时,只要不设置 Selector
属性,那么将不会自动建立 Endpoints,这是咱们能够根据需求手动的建立指定地址(address)的 Endpoints,来解决标签没法实现的整合。
咱们如今要作的呢,是将 VIP 请求给转发到对应的 Pod 上。而实现此的正是 iptables。
了解 iptables 的同窗都知道四表五链的概念,而作端口地址转发的呢,主要是在 nat 表中实现。咱们下面看一下一个 VIP 请求在 nat 表中是如何一步步被转发到 Pod 上的。
根据 iptables 的机制,请求是先到 nat 表的 PREROUTING 链(chain)上的,它的规则以下:
从图中发现,请求首先通过 KUBE-SERVICE 链,其次再到 DOCKER 链上的。
咱们看一下 KUBE-SERVICE 的状况:
咱们发现 KUBE-SERVICE 中包含了一系列 Service 的规则。根据咱们请求的 VIP 的目的地址,对应到了下一个名叫 KUBE-SVC-PRQ3AXYQLQGIVVIU 的 Service 链上。
KUBE-SVC-PRQ3AXYQLQGIVVIU 规则以下:
从规则的名字上能够看出,该条 Service 链上记录的是2个 Endpoints 链,具体的选择是经过 50% 的随机性的进行决定(这也是它的一个负载规则)。
咱们来看第一个名叫 KUBE-SEP-JSFY3ZFM2EVD64VQ 的 Endpoints 链的状况:
从图中,咱们已经很清晰的看到了它转发到 Pod 的具体规则。
下面以一张简单的流程图,看一下请求的转发状况:
关于 DOCKER 链的跟踪,方法是差很少的,请求 从 nat 表结束后,在到 filter 表中,这里就不加以说明了。
而这些规则的实现正是由 Service、Endpoints 来完成的。咱们在建立、更新、以及其自身的检测机制,都会对这些规则进行更新。
对于服务与服务间的调用,实际上就是 Pod 对 Servie 的调用。而 Pod 是如何发现 Service 的,这里可选择的方式有2种。
咱们经过启动一个名为 busybox 的 Pod 来观察这两种方式:
kubectl run -i --tty busybox --image=busybox --restart=Never -- sh
环境变量:
在 Pod 中,集群中的 Service 会以环境变量的方式赋值在容器中,咱们能够经过 {SERVICE_NAME}_SERVICE_HOST
和 {SERVICE_NAME}_SERVICE_PORT
进行获取(对于有多个 Port 的,能够经过带指定 PORT 名称的变量得到。)。
busybox 中 环境变量以下:
查看访问状况:
dns 解析:
第二种方式是经过 kube-dns 对 Service 进行域名解析,一样能达到服务发现的目的。
查看 DNS 域名解析配置:
经过 nslookup 查询 dns 记录:
查看访问结果:
在 Service 的配置文件中,属性spec.type
就是用来设置服务暴露的方式,它提供的三种方式以下:
ClusterIP: 提供一个集群内部的虚拟IP以供Pod访问(默认类型,咱们上述使用的正是这种方式)。
NodePort: 在每一个Node上打开一个端口以供外部访问。
LoadBalancer: 经过外部的负载均衡器来访问(通常须要云提供商提供 LB 支持)。
咱们这里简单起见,仍是经过 NodePort 方式进行。
修改 Service 配置文件,并从新启动:
apiVersion: v1 kind: Service metadata: # Service 实例名称 name: svc-echoserver spec: ports: - protocol: TCP # Service 端口地址 port: 8080 # Pod 端口地址 targetPort: 80 selector: # 匹配符合标签条件的 Pod app: echoserver type: NodePort
注意:这里若是要以kubecrl replace -f service-echoserver.yml
方式进行平滑更新,配置中需添加spec.clusterIP
属性,值为当前 Service 的 VIP,不然更新会失败。这也符合了一开始说的 Service 在它终止以前,VIP 是不会改变的。
查看 Service 更新状况:
外部访问(该 Node 地址是:192.168.64.6):
文本从 Service 的标签与选择器开始,讲了 Service 整合 Pod 的过程,引出了 Service, Endpoints, Pods 三者的关系状况。随后又经过 iptables 详细展开了 kube-proxy 的代理机制。最后,以 Service 的集群内与集群外的访问设置,讲述了 Service 的服务发现与服务暴露机制。
关于 Service 的有遗漏重要的知识点,或者有讲的不对的地方,也欢迎提出和指正!最后,但愿本篇对你学习 K8S 有所帮助~