转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具、解决方案和服务,赋能开发者。
在上节中我们介绍了活字格公有云版在 k8s 上部署,以及如何实现容器之间的编排与管理控制。为了进一步实现内外交互调用,则需要实现服务发现的功能。也就是我们前面提到“人与狗”之间的关系。
做过微服务的同学可能了解过什么叫服务发现,spring cloud 项目中的 Eureka 框架就是完成这个功能的,其主要工作就是注册内部的服务,以供其他集群的服务可以调用、访问这个服务。
我们合理猜测 Kubernetes 的存在很有可能激发了各种微服务框架产生服务发现机制。
在 Kubernetes 中服务发现对应的模块是 Service 与 Ingress,接下来,我们分别来说说这两个功能。
Service 类似于服务的注册功能。
其逻辑很简单,在 kubernetes 声明一个服务,从而生成一个 VIP (虚拟网络),所有 Kubernetes 集群中的其他组件,都可以通过这个 VIP 来访问这个服务,并且这个服务是不会随 Service 的改变而改变的,只要创建就是终生存在。
而服务的内容是什么呢?这部分和上述的 Deployment 一样,是通过 selector 选择器确定的。我们可以通过下述 yaml 来创建一个服务:
apiVersion: v1
kind: Service
metadata:
name: hostnames
spec:
selector:
app: hostnames
ports:
- name: default
protocol: TCP
port: 80
targetPort: 9376
通过上一篇的介绍,我们可以了解这个服务所需要代理的内容是 app==hostnames 的 Pod 。同时这里也有一个新的字段 ports,这个字段是说明该代理的服务的请求方式( protocol )、对外暴露的端口( port )、内部的端口( targetPort )分别是什么。
我们可以通过这个 sample-service.yaml 的文件创建一个 Service 并且查看一个 Service:
# 创建
kubectl apply -f sample-service.yaml
# 查看
kubectl get services hostnames
在这个 service 中存在一个 ClusterIP,这个 IP 就是这个 Service 生成的 VIP,在集群内部的其他成员,都可以通过这个 VIP 来访问这个 Service 。但是由于我们现在没有任何的具体服务让这个 Service 代理,因此现在请求这个 IP 是不会成功的。
那么,我们需要为这个 Service 创建一个具体实现:以下的 sample-deployment.yaml 文件是创建一个多副本的 Pod,其 Pod 的功能是返回自己的 podname:
apiVersion: apps/v1
kind: Deployment
metadata:
name: hostnames
spec:
selector:
matchLabels:
app: hostnames
replicas: 3
template:
metadata:
labels:
app: hostnames
spec:
containers:
- name: hostnames
image: k8s.gcr.io/serve_hostname
ports:
- containerPort: 9376
protocol: TCP
~
在这段代码中,我们把容器的 9376 端口暴露的出来,因为这个 Pod 是通过这个端口与外部通行的。同样我们执行以下命令创建和查看这个 pod 副本:
# 创建
kubectl apply -f sample-deployment.yaml
# 查看
kubectl get pods -l app=hostnames
在这部分内容中可以看到这个 pod 副本已经创建成功了。此时,根据我上一节所说的控制器模式,Service 也有对应的处理 Service 的控制器,其内部发现了有满足 app==hostnames 的服务,即将这个服务和 Service 进行了绑定 。此时,我们就可以通过任意一台集群内的主机来请求刚才上文中的 ClusterIP:
在这一部分可以看到,我们进行了很多次请求,但是每次返回的结果都不同,这是因为 Service 在其内部通过网络插件( CNI )做了负载均衡处理,所以我们可以通过 Service 来实现的负载均衡功能。
在学习了解这部分内容的时候,我一直有一个误解:认为 Service 是必须对应 Deployment 这种 Pod 的编排控制器对象才能工作的,所以把 Service --> Deployment --> Pods 这条逻辑关系熟记于心,但这种理解其实是错误的。
在 Kubernetes 中,每个功能组件各司其职,他们只会处理自己该做的事,比如这里,Service 绑定 Pod 所依赖的是选择器中的 app==hostnames,而这个定义是出现在 Deployment 中定义在 Pod 中的,因此 Service 和 Deployment 完全没有关系,它们俩谁也不认识谁,关系可以用下图来描述:
并且,在之前的学习中还错误地认为负载均衡服务是由 Deployment 提供的,其实这个功能是 Service 中的网络插件来处理的,并且用户同样也可以自定义使用的网络查件或者负载均衡算法是什么,Kubernetes 给了用户足够大的自由度。
有了 Service 之后,我们的服务就可以在集群中随意访问达到服务之间的交流关系了, 但是要想让我们的服务让最终的用户访问到,我们还需要最后一个组件 Ingress 。
Ingress 是 Kubernetes 中的反向代理服务,它可以解析配置的域名指向到我们内部的 Service 中,其定义可以通过下述的 yaml 来实现:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: sample-ingress
spec:
rules:
- host: hostname.sample.com
http:
paths:
- path: /
backend:
serviceName: hostnames
servicePort: 80
上述代码中,我们将 hostname.sample.com 这个域名指向了刚才定义的 hostnames 这个 Service 。通过这样的操作,我们的服务便就可以通过定义域名配置供外部的服务进行访问了。而 Ingress 的创建命令,也和前面所说的一样:
kubectl apply -f sample-ingress.yaml
有了这部分配置,我们的功能原则上就能够让外部访问了。但在实际应用中我们本地没有可供测试的环境,本地的 Kubernetes 环境是通过 kindD 生成的,其核心是多个 Docker Container 而不是多台机器。上述内容在 Container 内部运行,是使用 Docker 模拟 Kubernetes 的功能,因此这也是本文中唯一无法验证成功的一个功能模块。
通过上节我们一起学习了 Pod 间的编排控制器的使用,本节中实现了内外交互调用,进一步实现服务发现的功能,我们现在就可以再次回到之前提出的问题: 究竟如何成功部署一个活字格应用。
通过介绍整个 Kubernetes 的基础使用的流程,我们可以看到一个服务在 Kubernetes 变成 Pod,通过 Deployment 部署,通过 Service 服务发现,通过 Ingress 反向代理的全过程,经过这些模块的协力配合之后,我们的活字格应用终于可以部署在这个 Kubernetes 集群中了。
希望这张图片展示,能够为大家带来更加直观的感觉。
截止到本章,我们已经完整介绍了活字格公有云版做 k8s 部署的全过程。下一节将会为大家带来本系列文章的最后一篇——Kubernetes 总览,让大家对 Kubernetes 集群内容部分有一个整体性印象,对一些深层次功能做一个总结。
感兴趣的小伙伴不要错过~我们下篇接着聊。