文章目录
-
- [1. Service](#1. Service)
- [2. ClusterIP 集群内部使用](#2. ClusterIP 集群内部使用)
- [3. NodePort 每一个Node一个端口](#3. NodePort 每一个Node一个端口)
- [3. LoadBalancer](#3. LoadBalancer)
- [4. ExternalName 映射到DNS](#4. ExternalName 映射到DNS)
- [5. ingress网络](#5. ingress网络)
1. Service
Deployment来创建一组Pod来提供具有高可用性的服务。虽然每 个Pod都会分配一个单独的Pod IP,然而却存在如下两问题:
- Pod IP仅仅是集群内可见的虚拟IP,外部无法访问。
- Pod IP会随着Pod的销毁而消失,当Deployment对Pod进行动态伸缩时,Pod IP可能随时随地都会变化,这样对于我们访问这个服务带来了难度。
- Service能够提供负载均衡的能力,但是在使用上有以下限制。只提供 4 层负载均衡能力,而没有7 层功能,但有时我们可能需要更多的匹配规则来转发请求,这点上 4 层负载均衡是不支持的
Service 在 K8s 中有以下四种类型
- ClusterIp:默认类型,自动分配一个仅 Cluster 内部可以访问的虚拟
- IP NodePort:在 ClusterIP 基础上为 Service 在每台机器上绑定一个端口,这样就可以通过 : NodePort 来访 问该服务
- LoadBalancer:在 NodePort 的基础上,借助 cloud provider 创建一个外部负载均衡器,并将请 求转发 到NodePort。是付费服务,而且价格不菲。
- ExternalName:把集群外部的服务引入到集群内部来,在集群内部直接使用。没有任何类型代理 被创建,这只有 kubernetes 1.7 或更高版本的 kube-dns 才支持
详解4 种 Service 类型 Services 和 Pods
Kubernetes的Pods是有生命周期的。他们可以被创建,而且销毁不会再启动。如果您使用
Deployment来运行您的应用程序,则它可以动态创建和销毁 Pod。
一个Kubernetes的Service是一种抽象,它定义了一组Pods的逻辑集合和一个用于访问它们的策略 -有的时候被称之为微服务。一个Service的目标Pod集合通常是由Label Selector
来决定的举个例子,想象一个处理图片的后端运行了三个副本。这些副本都是可以替代的 -
前端不关心它们使用的是哪一个后端。尽管实际组成后端集合的Pod可能会变化,前端的客户端却不需要知道这个变化,也不需要自己有一个列表来记录这些后端服务。Service抽象能让你达到这种解耦。
不像 Pod 的 IP 地址,它实际路由到一个固定的目的地,Service 的 IP 实际上不能通过单个主机来进行应答。相反,我们使用
iptables(Linux 中的数据包处理逻辑)来定义一个虚拟IP地址(VIP),它可以 根据需要透明地进行重定向。当客户端连接到 VIP
时,它们的流量会自动地传输到一个合适的 Endpoint。环境变量和 DNS,实际上会根据 Service 的 VIP 和端口来进行填充。
kube-proxy支持三种代理模式: 用户空间,iptables和IPVS;它们各自的操作略有不同。
Userspace代理模式
Client Pod要访问Server Pod时,它先将请求发给本机内核空间中的service规则,由它再将请求,转给监 在指定套接字上的kube-proxy,kube-proxy处理完请求,并分发请求到指定Server Pod后,再将请求递交给内核空间中的service,由service将请求转给指定的Server Pod。由于其需要来回在用户空间和内 核空间交互通信,因此效率很差
当一个客户端连接到一个 VIP,iptables 规则开始起作用,它会重定向该数据包到 Service代理 的端 口。Service代理 选择一个 backend,并将客户端的流量代理到 backend 上。
这意味着 Service 的所有者能够选择任何他们想使用的端口,而不存在冲突的风险。客户端可以简单地 连接到一个 IP 和端口,而不需要知道实际访问了哪些 Pod。
iptables代理模式
当一个客户端连接到一个 VIP,iptables 规则开始起作用。一个 backend 会被选择(或者根据会话亲和性,或者随机),数据包被重定向到这个 backend。不像 userspace 代理,数据包从来不拷贝到用户 空间,kube-proxy 不是必须为该 VIP 工作而运行,并且客户端 IP 是不可更改的。当流量打到 Node 的 端口上,或通过负载均衡器,会执行相同的基本流程,但是在那些案例中客户端 IP 是可以更改的。
IPVS代理模式
在大规模集群(例如10,000个服务)中,iptables 操作会显着降低速度。IPVS 专为负载平衡而设计, 并基于内核内哈希表。因此,您可以通过基于 IPVS 的 kube-proxy 在大量服务中实现性能一致性。同 时,基于 IPVS 的 kube-proxy 具有更复杂的负载平衡算法(最小连接,局部性,加权,持久性)。
在 Kubernetes 集群中,每个 Node 运行一个 kube-proxy 进程。 kube-proxy 负责为 Service 实现了一 种VIP(虚拟 IP)的形式,而不是 ExternalName 的形式。 在 Kubernetes v1.0 版本,代理完全在 userspace。在 Kubernetes v1.1 版本,新增了 iptables 代理,但并不是默认的运行模式。 从 Kubernetes v1.2 起, 默认就是
iptables 代理。 在 Kubernetes v1.8.0-beta.0 中,添加了 ipvs 代理在 Kubernetes 1.14 版本开始默认使用 ipvs 代理。在 Kubernetes v1.0 版本, Service 是 "4层"(TCP/UDP over IP)概念。 在 Kubernetes v1.1 版本,新增了Ingress API(beta 版),用来表示 "7层"(HTTP)服务 。
这种模式,kube-proxy 会监视 Kubernetes Service 对象和 Endpoints ,调用 netlink 接口以相应地创建 ipvs 规则并定期与 Kubernetes Service 对象和 Endpoints 对象同步 ipvs 规则,以确保 ipvs 状态与期望一致。访问服务时,流量将被重定向到其中一个后端 Pod与 iptables 类似,ipvs 于 netfilter 的 hook 功 能,但使用哈希表作为底层数据结构并在内核空间中工作。这意味着 ipvs 可以更快地重定向流量,并且在同步代理规则时具有更好的性能。此外,ipvs 为负载均衡算法提供了更多选项。
2. ClusterIP 集群内部使用
类型为ClusterIP的service,这个service有一个Cluster-IP,其实就一个VIP。具体实现原理依靠kubeproxy组件,通过iptables或是ipvs实现。
clusterIP 主要在每个 node 节点使用 iptables,将发向 clusterIP 对应端口的数据,转发到kube-proxy 中。然后 kube-proxy 自己内部实现有负载均衡的方法,并可以查询到这个 service 下对应 pod的地址和端 口,进而把数据转发给对应的 pod 的地址和端口
这种类型的service 只能在集群内访问。
bash
apiVersion:
apps/v1
kind: Deployment 创建一个deployment
metadata:
name: clusteripdemo 定义标签
labels:
app: clusteripdemo
spec:
replicas: 3
template:
metadata:
name: clusteripdemo pod的标签
labels:
app: clusteripdemo
spec: containers:
- name: clusteripdemo
image: tomcat:9.0.20-jre8-alpine
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
restartPolicy: Always
selector:
matchLabels:
app: clusteripdemo 标签
--apiVersion: v1
kind: Service
metadata:
name: clusterip-svc
spec:
selector:
app: clusteripdemo
ports:
- port: 8080
targetPort: 8080
type: ClusterIP
运行service
bash
运行服务
kubectl apply -f clusteripdemo.yml
查看服务
kubectl get svc
访问服务 curl 10.1.15.24:8080
删除服务
kubectl delete -f clusteripdemo.yml
3. NodePort 每一个Node一个端口
NodePort 我们的场景不全是集群内访问,也需要集群外业务访问。那么ClusterIP就满足不了了。NodePort当然 是其中的一种实现方案。nodePort 的原理在于在 node 上开了一个端口,将向该端口的流量导入到
kube-proxy,然后由 kube-proxy 进一步到给对应的 pod 。
bash
--apiVersion: v1
kind: Service
metadata:
name: clusterip-svc
spec:
selector:
app: clusteripdemo
ports:
- port: 8080
targetPort: 8080
nodePort: 30088 指定的是node上面的端口
type: NodePort
运行service
bash
运行服务
kubectl apply -f nodeportdemo.yml
查看服务
kubectl get svc
访问服务
curl 10.1.61.126:8080
浏览器访问服务
http://192.168.198.156:30088
删除服务
kubectl delete -f nodeportdemo.yml
3. LoadBalancer
LoadBalancer类型的service 是可以实现集群外部访问服务的另外一种解决方案。不过并不是所有的 k8s集群都会支持,大多是在公有云托管集群中会支持该类型。负载均衡器是异步创建的,关于被提供的负载均衡器的信息将会通过Service的status.loadBalancer字段被发布出去。
创建 LoadBalancer service 的yaml 如下:
bash
apiVersion: v1
kind: Service
metadata:
name: service-xxx
spec:
ports:
- port: 3000
protocol: TCP
targetPort: 443
nodePort: 30080
selector: run: pod-xxx
type: LoadBalancer
4. ExternalName 映射到DNS
类型为 ExternalName 的service将服务映射到 DNS 名称,而不是典型的选择器,例如my-service或者 cassandra。您可以使用spec.externalName参数指定这些服务。
这种类型的 Service 通过返回 CNAME 和它的值,可以将服务映射到 externalName 字段的内容( 例 如:
hub.xxx.com )。ExternalName Service 是 Service 的特例,它没有 selector,也没有定义任何 的端口和 Endpoint。相反的,对于运行在集群外部的服务,它通过返回该外部服务的cname(别名)这种方式来提 供服务
创建 ExternalName 类型的服务的 yaml 如下:
bash
kind: Service apiVersion: v1
metadata:
name: service-xxx
spec:
ports:
- port: 3000
protocol: TCP
targetPort: 443
type: ExternalName
externalName: www.xxx.com
5. ingress网络
K8s集群对外暴露服务的方式目前只有三种:loadblancer 、nodeport 、ingress。前两种熟悉起来比较快,而且使用起来也比较方便,
ingress由两部分组成:ingress controller和ingress服务。
其中ingress controller目前主要有两种:基于nginx服务的ingress controller和基于traefik的ingress controller。
而其中traefik的ingress controller,目前支持http和https协议。由于对nginx比较熟悉,而且需要使用 TCP负载,所以在此我们选择的是基于nginx服务的ingress controller。
在kubernetes集群中,我们知道service和pod的ip仅在集群内部访问。如果外部应用要访问集群内的服务,集群外部的请求需要通过负载均衡转发到service在Node上暴露的NodePort上,然后再由kubeproxy组件将其转发给相关的pod。
而Ingress就是为进入集群的请求提供路由规则的集合,通俗点就是提供外部访问集群的入口,将外部的HTTP或者HTTPS请求转发到集群内部service上。
Ingress-nginx组成 Ingress-nginx一般由三个组件组成:
反向代理负载均衡器:通常以service的port方式运行,接收并按照ingress定义的规则进行转发, 常用的有nginx,Haproxy,Traefik等,本次实验中使用的就是nginx。
Ingress Controller:监听APIServer,根据用户编写的ingress规则(编写ingress的yaml文件), 动态地去更改nginx服务的配置文件,并且reload重载使其生效,此过程是自动化的(通过lua脚本 来实现)。
Ingress:将nginx的配置抽象成一个Ingress对象,当用户每添加一个新的服务,只需要编写一个新的ingress的yaml文件即可。