Services 和 Pods
KubernetesPods是有生命周期的。他们可以被创建,而且销毁不会再启动。如果您使用Deployment来运行您的应用程序,则它可以动态创建和销毁 Pod。
一个Kubernetes的Service是一种抽象,它定义了一组Pods的逻辑集合和一个用于访问它们的策略 - 有的时候被称之为微服务。一个Service的目标Pod集合通常是由Label Selector 来决定的(下面有讲一个没有选择器的Service 有什么用处)。
举个例子,想象一个处理图片的后端运行了三个副本。这些副本都是可以替代的 - 前端不关心它们使用的是哪一个后端。尽管实际组成后端集合的Pod可能会变化,前端的客户端却不需要知道这个变化,也不需要自己有一个列表来记录这些后端服务。Service抽象能让你达到这种解耦。
不像 Pod 的 IP 地址,它实际路由到一个固定的目的地,Service 的 IP 实际上不能通过单个主机来进行应答。相反,我们使用 iptables(Linux 中的数据包处理逻辑)来定义一个虚拟IP地址(VIP),它可以根据需要透明地进行重定向。当客户端连接到 VIP 时,它们的流量会自动地传输到一个合适的 Endpoint。环境变量和 DNS,实际上会根据 Service 的 VIP 和端口来进行填充。
kube-proxy支持三种代理模式: 用户空间,iptables和IPVS;它们各自的操作略有不同。
Userspace
作为一个例子,考虑前面提到的图片处理应用程序。当创建 backend Service 时,Kubernetes master 会给它指派一个虚拟 IP 地址,比如 10.0.0.1。假设 Service 的端口是 1234,该 Service 会被集群中所有的 kube-proxy 实例观察到。当代理看到一个新的 Service, 它会打开一个新的端口,建立一个从该 VIP 重定向到新端口的 iptables,并开始接收请求连接。
当一个客户端连接到一个 VIP,iptables 规则开始起作用,它会重定向该数据包到 Service代理 的端口。Service代理 选择一个 backend,并将客户端的流量代理到 backend 上。
这意味着 Service 的所有者能够选择任何他们想使用的端口,而不存在冲突的风险。客户端可以简单地连接到一个 IP 和端口,而不需要知道实际访问了哪些 Pod。
iptables
再次考虑前面提到的图片处理应用程序。当创建 backend Service 时,Kubernetes 控制面板会给它指派一个虚拟 IP 地址,比如 10.0.0.1。假设 Service 的端口是 1234,该 Service 会被集群中所有的 kube-proxy 实例观察到。当代理看到一个新的 Service, 它会配置一系列的 iptables 规则,从 VIP 重定向到 per-Service 规则。该 per-Service 规则连接到 per-Endpoint 规则,该 per-Endpoint 规则会重定向(目标 NAT)到 backend。
当一个客户端连接到一个 VIP,iptables 规则开始起作用。一个 backend 会被选择(或者根据会话亲和性,或者随机),数据包被重定向到这个 backend。不像 userspace 代理,数据包从来不拷贝到用户空间,kube-proxy 不是必须为该 VIP 工作而运行,并且客户端 IP 是不可更改的。当流量打到 Node 的端口上,或通过负载均衡器,会执行相同的基本流程,但是在那些案例中客户端 IP 是可以更改的。
IPVS
在大规模集群(例如10,000个服务)中,iptables 操作会显着降低速度。IPVS 专为负载平衡而设计,并基于内核内哈希表。因此,您可以通过基于 IPVS 的 kube-proxy 在大量服务中实现性能一致性。同时,基于 IPVS 的 kube-proxy 具有更复杂的负载平衡算法(最小连接,局部性,加权,持久性)。
下面我们详细说下k8s支持的4种类型的Service。
在 Kubernetes 中 Service 主要有4种不同的类型,其中的 ClusterIP 是最基础的,如下图所示:
当我们创建一个 NodePort 的 Service 时,它也会创建一个 ClusterIP,而如果你创建一个 LoadBalancer,它就会创建一个 NodePort,然后创建一个 ClusterIP
此外我们还需要明白 Service 是指向 pods 的,Service 不是直接指向 Deployments 或 ReplicaSets,而是直接使用 labels 标签指向 Pod,这种方式就提供了极大的灵活性,因为通过什么方式创建的 Pod 其实并不重要。接下来我们通过一个简单的例子开始,我们用不同的 Service 类型来逐步扩展,看看这些 Service 是如何建立的。
No Services
最开始我们没有任何的 Services。
我们有两个节点,一个 Pod,节点有外网(4.4.4.1、4.4.4.2)和内网(1.1.1.1、1.1.1.2)的 IP 地址,pod-python 这个 Pod 只有一个内部的 IP 地址。
现在我们添加第二个名为 pod-nginx 的 Pod,它被调度在 node-1 节点上。在 Kubernetes 中,所有的 Pod 之间都可以通过 Pod 的 IP 进行通信,不管它们运行在哪个节点上。这意味着 pod-nginx 可以使用其内部IP 1.1.1.3 来 ping 和连接 pod-python 这个 Pod。
现在如果 pod-python 挂掉了重新创建了一个新的 pod-python 出来(本文不涉及如何管理和控制 pods),重新分配了一个新的 1.1.1.5 的 Pod IP 地址,这个时候 pod-nginx 就无法再达到 1.1.1.3 这个之前的地址了,为了防止这种情况发生,我们就需要创建一个 Service 服务了!
ClusterIP
和上面同样的场景,但是我们创建了一个名为 service-python 类型为 ClusterIP 的 Service 服务,一个 Service 并不像 Pod 那样运行在一个特定的节点上,这里我们可以假设一个 Service 只是在整个集群内部的内存中可用就可以了。
pod-nginx 可以安全地连接到 1.1.10.1 这个 ClusterIP 或直接通过 dns 名service-python 进行通信,并被重定向到后面一个可用的 Pod 上去。
现在我们来稍微扩展下这个示例,启动3个 python 实例,现在我们来显示所有 Pod 和 Service 内部 IP 地址的端口。
集群内部的所有 Pods 都可以通过 http://1.1.10.1:3000
或者 http://service-python:3000
来访问到后面的 python pods 的443端口。
service-python 这个 Service 是随机或轮询的方式来转发请求的,这个就是 ClusterIP Service 的作用,它通过一个名称和一个 IP 让集群内部的 Pods 可用。
上图中的 service-python 这个 Service 可以用下面的 yaml 文件来创建:
创建后,可以用 kubectl get svc
命令来查看:
NodePort
现在我们想让 ClusterIP Service 可以从集群外部进行访问,为此我们需要把它转换成 NodePort 类型的 Service,在我们的例子中,我们只需要简单修改上面的 service-python 这个 Service 服务即可:
更新完成后,如下图所示:
这意味着我们的内部的 service-python 这个 Service 现在也可以通过30080 端口从每个节点的内部和外部 IP 地址进行访问了。
集群内部的 Pod 也可以通过内网节点 IP 连接到 30080 端口。
运行 kubectl get svc
命令来查看这个 NodePort 的 Service,可以看到同样有一个 ClusterIP,只是类型和额外的节点端口不同。在内部,NodePort 服务仍然像之前的 ClusterIP 服务一样。
LoadBalancer
如果我们希望有一个单独的 IP 地址,将请求分配给所有的外部节点IP(比如使用 round robin),我们就可以使用 LoadBalancer 服务,所以它是建立在 NodePort 服务之上的。
一个 LoadBalancer 服务创建了一个 NodePort 服务,NodePort 服务创建了一个 ClusterIP 服务。我们也只需要将服务类型更改为 LoadBalancer 即可。
LoadBalancer 服务所做的就是创建一个 NodePort 服务,此外,它还会向托管 Kubernetes 集群的提供商发送一条消息,要求设置一个指向所有外部节点 IP 和特定 nodePort 端口的负载均衡器,当然前提条件是要提供商支持。
现在运行 kubectl get svc
可以看到新增了 external-IP 和 LoadBalancer 的类型。
LoadBalancer 服务仍然像和以前一样在节点内部和外部 IP 上打开 30080 端口。
ExternalName
最后是 ExternalName 服务,这个服务和前面的几种类型的服务有点分离。它创建一个内部服务,其端点指向一个 DNS 名。
我们假设 pod-nginx 运行在 Kubernetes 集群中,但是 python api 服务在集群外部。
这里 pod-nginx 这个 Pod 可以直接通过 http://remote.server.url.com 连接到外部的 python api 服务上去,但是如果我们考虑到以后某个时间节点希望把这个 python api 服务集成到 Kubernetes 集群中去,还不希望去更改连接的地址,这个时候我们就可以创建一个 ExternalName 类型的 Service 服务了。
对应的 YAML 资源清单文件如下所示:
现在 pod-nginx 就可以很方便地通过 http://service-python:3000
进行通信了,就像使用 ClusterIP 服务一样,当我们决定将 python api 这个服务也迁移到我们 Kubernetes 集群中时,我们只需要将服务改为 ClusterIP 服务,并设置正确的标签即可,其他都不需要更改了。
到这里我们就用几张图将 Kubernetes 中的 Service 解释得明明白白清清楚楚真真切切了~~~原文链接:http://suo.im/5YIo27 翻译:http://suo.im/6dLh7S