一、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
