一、Service 发布应用 ¶
"什么是 Service?" ¶
Kubernetes Service 定义了:Service 是一种可以访问 Pod 逻辑分组的策略, Service 通常是通过 Label Selector访问 Pod 组; Service 能够提供负载均衡的能力,但是在使用上有些限制:只提供 4 层负载均衡能力,而没有 7 层功能,但有时我们可能需要更多的匹配规则来转发请求,这点上 4 层负载均衡是不支持的。
"为什么 Pod 的 IP 会变化?" ¶
Kubernetes 具有强大的副本控制能力,能保证在任意副本(Pod)挂掉时自动从其他机器启动一个新的,所以说,这个 Pod 可能在任何时刻出现在任何节点上,也可能在任何时刻死在任何节点上;那么自然随着 Pod 的创建和销毁,Pod IP 肯定会动态变化
"如何解决 Pod 的 IP 会变的问题?" ¶
答:当 Pod 宕机后重新生成时,其 IP 等状态信息可能会变动,Service 会根据 Pod 的 Label 对这些状态信息进行监控和变更,保证上游服务不受 Pod 的变动而影响。
是运行 Pod Ip 是 Docker 网桥的 IP 地址段进行分配的,通常是一个虚拟的二层网络,外部网络并没有办法访问,并且,Pod Ip是随时会变的,不是固定的,k8s 引入了 Service 的概念,通过 Service 管理这些 Pod,Service 创建后的 Service IP 是固定的。但是 Service IP(Cluster IP) 是一个虚拟的 IP ,由 Kubernetes 管理和分配 IP 地址,外部网络无法访问。k8s 有三种方式暴露 Service 给外部网络访问。
创建 service 与 deployment 流程

-
当创建
Deployment时,Kubernetes控制器会根据Deployment的定义 的replicas数创建相应数量的Pod副本。这些Pod副本将根据Deployment的模板template进行配置,比如labels为app: web并分配唯一独立的IP地址,并且具有自己的网络标识。这些Pod的IP地址是由Kubernetes集群的网络插件负责分配的。 -
在创建
Deployment时,Deployment控制器会监控Pod的状态,并根据所需的replicas副本数来管理它们的运行状态。如果 Pod 发生故障、被删除或需要扩展副本数,Deployment控制器将负责启动新的Pod副本或终止不需要的Pod副本,以确保所需的Pod数目处于活动状态。 -
Service与Deployment是两个独立的资源对象,它们之间没有直接的关联。但可使用Service的标签选择器来选择与Deployment关联的Pod,并将请求路由到这些Pod上。 -
当创建
Service并指定与Deployment中Pod相关联的标签选择器时,Service将使用该选择器来查找与Deployment相关联的Pod,并将它们作为Service的后端目标。当前端客户端发送请求到Service时,Service将使用负载均衡机制将请求转发到与Deployment关联的Pod上。 -
总结: 创建
Deployment时,Kubernetes控制器会根据Deployment的定义创建Pod副本,并为每个Pod分配唯一的IP地址。Deployment控制器负责管理Pod副本的运行状态。Service与Deployment是独立的资源对象,但可以使用Service的标签选择器来选择与Deployment关联的Pod,并将请求路由到这些Pod上。
-
当创建一个 名为
my-service的Service时,Kubernetes会根据Service的标签选择器(selector)来查找匹配的Pod,并为该Service创建一个与其同名的Endpoint对象。Endpoint对象存储了与Service相关联的 就绪完成的Pod的网络地址信息。当创建或删除Pod时,Kubernetes控制器会检测到这些变化,并相应地更新与这些Pod相关联的Endpoint对象。这意味着当Pod的地址发生变化时,Endpoint对象也会相应地发生变化。 -
Service接收来自前端客户端的请求时,它会通过Endpoint对象查找要转发到哪个Pod的地址进行访问。负载均衡(例如 kube-proxy)会根据一定的算法从Endpoint对象中选择一个Pod的地址,并将请求转发到该地址上的Pod。这种机制确保了在Pod地址发生变化时,Service可以动态地更新其所使用的Endpoint对象,并将流量正确地路由到新的Pod地址上。这使得您可以在集群中动态添加或删除Pod,并且Service能够自动适应这些变化。
总结起来,当创建 Service 时,Kubernetes 会为该 Service 创建一个与其同名的 Endpoint 对象,并根据 Service 的标签选择器来匹配 Pod。Endpoint 对象存储了与 Service 相关联的 Pod 的网络地址信息。负载均衡通过 Endpoint 对象确定要转发请求的 Pod 地址,并将流量正确地路由到该地址上的 Pod。当 Pod 地址发生变化时,Endpoint 对象会相应地更新,以确保 Service 可以正确地路由请求到新的 Pod 地址上。
Service 有以下几种类型: ¶
默认类型,自动分配一个仅 ClusterIP 内部可以访问的虚拟服务 IP,集群内部有效,适用于集群内多个 service 之间互相通讯;

在 ClusterIP 基础上为 Service 在每台机器上绑定一个端口,这样就可以通过: NodePort 来访问该服务,外层配合 Nginx 对用户提供访问。

在 NodePort 的基础上,借助 Cloud Provider 创建一个外部负载均衡器,并将请求转发到 NodePort

把集群外部的服务引入到集群内部来,在集群内部直接使用。没有任何类型代理被创建,这只有 Kubernetes 1.7或更高版本的kube-dns才支持。
Service 是怎么运行的?
- 客户端访问节点时通过 iptables 实现
- iptables 规则是通过 kube-proxy 写入
- apiserver 通过监控 kube-proxy 去进行对服务和端点的监控
- kube-proxy 通过 pod 的标签( lables)去判断这个断点信息是否写入到 Endpoints 里

暴露服务的三种方式: ¶
ClusterIP 主要在每个 node 节点使用 iptables ,将发向 ClusterIP 对应端口的数据,转发到 kube-proxy 中。然后 kube-proxy 自己内部实现有负载均衡的方法,并可以查询到这个 service 下对应 pod 的地址和端口,进而把数据转发给对应的 pod 的地址和端口。

为了实现图上的功能,主要需要以下几个组件的协同工作:
apiserver:用户通过 kubectl 命令向 apiserver 发送创建 service 的命令, apiserver 接收到请求后将数据存储到 etcd 中 kube-proxy: Kubernetes的每个节点中都有一个叫做 kube-porxy 的进程,这个进程负责感知 service、 pod 的变化,并将变化的信息写入本地的 iptables 规则中iptables:使用 NAT 等技术将 virtualIP 的流量转至 endpoint 中创建 myapp-deploy.yaml 文件
1. 创建 Service 的 YAML 描述文件 ¶
apiVersion: v1
kind: Namespace
metadata:
name: datarc
# 执行创建
kubectl apply -f datarc-namespace.yaml
2. 创建 deployment 资源 ¶
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: datarc
name: datarc-test-pod
labels:
app: datarc
spec:
replicas: 2
selector:
matchLabels:
app: datarc
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
app: datarc
spec:
containers:
- image: nginx:1.13
name: datarc-1
resources: {}
status: {}
# 执行创建
kubectl apply -f datarc-deployment.yaml
3. 创建 Service 资源 ¶
apiVersion: v1
kind: Service
metadata:
namespace: datarc
name: datarc-test-pod
spec:
type: ClusterIP
selector:
app: datarc
ports:
- name: http
port: 80
targetPort: 8080
# 执行创建
kubectl apply -f datarc-service.yaml
4. 查看 Service 资源 ¶
# 查看 SVC
kubectl get svc -n datarc
kubectl describe svc datarc-test-pod -n datarc
将服务的类型设置成 NodePort 每个集群节点都会在节点上打开一个端口, 对于 NodePort 服务, 每个集群节点在节点本身(因此得名叫 NodePort)上打开一个端口,并将在该端口上接收到的流量重定向到基础服务。该服务仅在内部集群 IP 和端口上才可访间, 但也可通过所有节点上的专用端口访问。
在 k8s 上可以给 Service 设置成 NodePort 类型,这样的话可以让 Kubernetes 在其所有节点上开放一个端口给外部访问(所有节点上都使用相同的端口号), 并将传入的连接转发给作为 Service 服务对象的 pod。这样我们的 pod 就可以被外部请求访问。
- (NodePort的原理在于在 Node上开了一个端口,将向该端口的流量导入到 kube-proxy,然后由 kube-proxy进一步到给对应的 pod)
1. 创建 Service 的 YAML 描述文件 ¶
apiVersion: v1
kind: Namespace
metadata:
name: datarc
# 执行创建
kubectl apply -f datarc-namespace.yaml
2. 创建 deployment 资源 ¶
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: datarc
name: datarc-test-pod
labels:
app: datarc
spec:
replicas: 2
selector:
matchLabels:
app: datarc
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
app: datarc
spec:
containers:
- image: nginx:1.13
name: datarc-1
resources: {}
status: {}
# 执行创建
kubectl apply -f datarc-deployment.yaml
3. 创建 Service 资源 ¶
apiVersion: v1
kind: Service
metadata:
namespace: datarc
name: datarc-test-pod
spec:
selector:
app: datarc
type: NodePort #Serive的类型,ClusterIP/NodePort/LoaderBalancer
ports:
- name: http #名字
protocol: TCP #协议类型 TCP/UDP
port: 80 #集群内的其他容器组可通过 80 端口访问 Service
targetPort: 80 #将请求转发到匹配 Pod 的 80 端口
nodePort: 32600 #通过任意节点的 32600 端口访问 Service
# 注:spec.ports.nodePort这个参数是指Node节点对外开放访问的端口号,如果不指定,则Kubernetes会随机选择一个端口号
# 执行创建
kubectl apply -f datarc-service.yaml
4. 查看 Service 资源 ¶
# 查看 SVC
kubectl get svc -n datarc
kubectl describe svc datarc-test-pod -n datarc
NodePort总结
简单易用,但是服务一旦多起来,NodePort 在每个节点上开启的端口会及其庞大,而且难以维护,所以生产环境不建议这么使用。
将服务的类型设置成 LoadBalance , NodePort 类型的一 种扩展,这使得服务可以通过一个专用的负载均衡器来访问, 这是由 Kubernetes 中正在运行的云基础设施提供的。 负载均衡器将流量重定向到跨所有节点的节点端口。客户端通过负载均衡器的 IP 连接到服务。
"创建自定义 ENDPOINTS" ¶
1. 安装 nginx ¶
2. 编写 endpoint 文件 ¶
version: '3'
services:
nginx:
restart: always
container_name: nginx
image: nginx
ports:
- 8080:80