5. pod调度策略

一般而言pod的调度都是通过RC、Deployment等控制器自动完成,但是仍可以通过手动配置的方式进行调度,目的就是让pod的调度符合我们的预期。

5.1 节点调度

Pod.spec.nodeName用于强制约束将Pod调度到指定的Node节点上,这里说是“调度”,但其实指定了nodeName的Pod会直接跳过Scheduler的调度逻辑,直接写入PodList列表,该匹配规则是强制匹配。

5.1.1 创建资源清单
vi pod-node-dispatch.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
        app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      nodeName: node1 #指定调度节点为node1
      containers:
      - name: nginx
        image: nginx:1.20
        ports:
        - containerPort: 80
5.1.2 应用部署
kubectl apply -f pod-node-selector-dispatch.yml

kubectl get pods -o wide
创建部署后,我们发现节点在node1的节点上

5.1.3 删除pod
删除pod应用,pod控制器会重建pod

kubectl delete pod nginx-deployment-5dc7769598-n25q5
我们发现节点删除后,重新的应用还是落在了node1节点上,说明我们的节点调度生效的

5.2 定向调度(标签调度)

定向调度是把pod调度到具有特定标签的node节点的一种调度方式,比如把MySQL数据库调度到具有SSD的node节点以优化数据库性能。此时需要首先给指定的node打上标签,并在pod中设置nodeSelector属性以完成pod的指定调度。

5.2.1 创建标签

给指定的node打上标签

5.2.1.1 添加标签
给node2添加上disk=ssd的标签

kubectl label nodes node2 disk=ssd

5.2.1.2 显示标签
显示node2的所有标签

kubectl label node node2 --list=true
我们发现我们的标签已经添加上去了

3bf1945e73c24f9db3e7c8902b426da9.png

5.2.3 创建资源清单
vi pod-node-selector-dispatch.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 2
  selector:
    matchLabels:
        app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      nodeSelector:
        disk: ssd # 节点调度到
      containers:
      - name: nginx
        image: nginx:1.20
        ports:
        - containerPort: 80
5.2.4 应用部署
kubectl apply -f pod-node-selector-dispatch.yml

kubectl get pods -o wide
创建部署后,我们发现两个pod都在node2的节点上

23b8ce6644ba4f9e982a2a46bdf9d649.png

5.2.5 删除pod
删除pod应用,pod控制器会重建pod

kubectl delete pod nginx-deployment-5dc7769598-n25q5
我们发现节点删除后,重新的应用还是落在了node2节点上,说明我们的定向调度生效的

ea6032b0199f4a149bbb53bf9dd0ec89.png

注意:定向调度可以把pod调度到特定的node节点,但随之而来的缺点就是如果集群中不存在响应的node,即使有基本满足条件的node节点,pod也不会被调度

6. Service

Kubernetes service 定义了这样一种抽象:一个 Pod 的逻辑分组,一种可以访问它们的策略 —— 通常称为微服务,这一组 pod 能够被 Service 访问到,通常是通过 Label selector

 6d29f3a75fc141889e8f0daed1944a34.png

Service能够提供负载均衡的能力,但是在使用上有以下限制:

  • 只提供4层负载均衡能力,而没有7层功能,但有时我们可能需要更多的匹配规则来转发请求,这点上4层负载均衡是不支持的

6.1 Service概述

Kubernetes Service 从逻辑上代理了一组 Pod,具体是哪些 Pod 则是由 label 来挑选,Service 有自己 IP,而且这个 IP 是不变的。

  • 客户端只需要访问 Service 的 IP,Kubernetes 则负责建立和维护 Service 与 Pod 的映射关系。
  • 无论后端 Pod 如何变化,对客户端不会有任何影响,因为 Service 没有变

6.1.1 为什么要有Service

当Pod宕机后重新生成时,其IP等状态信息可能会变动,Service会根据Pod的Label对这些状态信息进行监控和变更,保证上游服务不受Pod的变动而影响。

Kubernetes Pods 是有生命周期的,他们可以被创建,而且销毁不会再启动, 如果您使用Deployment来运行您的应用程序,则它可以动态创建和销毁 Pod。

一个Kubernetes的Service是一种抽象,它定义了一组Pods的逻辑集合和一个用于访问它们的策略 - 有的时候被称之为微服务,一个Service的目标Pod集合通常是由Label Selector 来决定的。

如下图所示,当Nginx Pod作为客户端访问Tomcat Pod中的应用时,IP的变动或应用规模的缩减会导致客户端访问错误。而Pod规模的扩容又会使得客户端无法有效的使用新增的Pod对象,从而影响达成规模扩展之目的。为此,Kubernetes特地设计了Service资源来解决此类问题。

3db53f1a2a9b49559ed90b56b476c9d3.png

6.1.2 Service实现原理

Service资源基于标签选择器将一组Pod定义成一个逻辑组合,并通过自己的IP地址和端口调度代理请求至组内的Pod对象之上,如下图所示,它向客户端隐藏了真实的、处理用户请求的Pod资源,使得客户端的请求看上去就像是由Service直接处理并响应一样。

7f1a45d967804cf7b570c6ddc57baa44.png

 

Service对象的IP地址也称为Cluster IP,它位于Kubernetes集群配置指定专用IP地址的范围之内,是一种虚拟IP地址,它在Service对象创建后既保持不变,并且能够被同一集群中的Pod资源所访问。Service端口用于接收客户端请求并将其转发至其后端的Pod中的相应端口之上,因此,这种代理机构也称为“端口代理”(port proxy)或四层代理,工作于TCP/IP协议栈的传输层。

Service资源会通过API Server持续监视着(watch)标签选择器匹配到的后端Pod对象,并实时跟踪各对象的变动,例如,IP地址变动、对象增加或减少等。Service并不直接链接至Pod对象,它们之间还有一个中间层——Endpoints资源对象,它是一个由IP地址和端口组成的列表,这些IP地址和端口则来自由Service的标签选择器匹配到的Pod资源。当创建service对象时,其关联的Endpoints对象会自动创建。

6.2 Service 的类型

Service 在 K8s 中有以下四种类型

  • Clusterlp:默认类型,自动分配一个仅 Cluster 内部可以访问的虚拟 IP
  • NodePort:在 ClusterIP 基础上为 Service 在每台机器上绑定一个端口,这样就可以通过:NodePort 来访问该服务
  • LoadBalancer:在 NodePort 的基础上,借助 cloud provider 创建一个外部负载均衡器,并将请求转发到:NodePort
  • ExternalName:把集群外部的服务引入到集群内部来,在集群内部直接使用。没有任何类型代理被创建,这只有kubernetes 1.7或更高版本的 kube-dns 才支持

6.3 Service示例

6.3.1 准备工作

6.3.1.1 创建deployment
vi nginx-deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 1 # 只有一个副本
  selector:
    matchLabels:
        app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      volumes:    #定义存储卷
      - name: html # 定义存储名称
        hostPath:  # 定义存储类型
          path: /tmp/k8s/data/volumn1 # 宿主机存储路径
          type: DirectoryOrCreate     # 不存在路径创建路径
      containers:
      - name: nginx
        image: nginx:1.20
        volumeMounts:    #在容器中定义挂载存储卷的名和路径
        - name: html
          mountPath: /usr/share/nginx/html
        ports:
        - containerPort: 80
6.3.1.2 启动deployment
kubectl apply -f nginx-deployment.yml

kubectl get pods -o wide

6.3.1.3 访问测试
curl 10.244.2.54

6.3.2 ClusterlP类型

类型为ClusterIP的service,这个service有一个Cluster-IP,其实就一个VIP,具体实现原理依靠kubeproxy组件,通过iptables或是ipvs实现。

注意:这种类型的service 只能在集群内访问

eb5ec7a3d689435b8afe20c38a2d1ab7.png 

6.3.2.1 编辑资源清单
vi cluster-iP-service.yml
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
  namespace: default
  labels:
    app: nginx-service
spec:
  type: ClusterIP
  ports:
  - port: 8000      # Service 暴漏端口
    targetPort: 80  # 代理的对象的端口
  selector:
    app: nginx # 绑定标签是nginx的对象
6.3.2.2 应用Service
kubectl apply -f cluster-iP-service.yml

kubectl get service -o wide

6.3.2.3 访问测试
curl 10.1.206.66:8000

6.3.2.4 删除Pod
删除pod让Pod的节点漂移再次访问Service节点

kubectl delete pod nginx-deployment-6897679c4b-2hqmk

6.3.2.5 访问测试
我们发现虽然pod的IP以及节点变化了但是Service访问IP没有任何变化

curl 10.1.206.66:8000

6.3.3 NodePort

当我们需要集群外业务访问,那么ClusterIP就满足不了了,NodePort当然是其中的一种实现方案

d955942b84104823a160bb0bcebbbed6.png 

6.3.3.1 编辑资源清单
vi node-port-service.yml
apiVersion: v1
kind: Service
metadata:
  name: nginx-node-port-service
  namespace: default
  labels:
    app: nginx-service
spec:
  type: NodePort
  ports:
  - port: 8000      # Service 暴漏端口
    targetPort: 80  # 代理的对象的端口
  selector:
    app: nginx # 绑定标签是nginx的对象
6.3.3.2 应用Service
kubectl apply -f node-port-service.yml

kubectl get service -o wide

6.3.3.3 访问测试
curl 10.1.137.200:8000

6.3.3.4 删除Pod
删除pod让Pod的节点漂移再次访问Service节点

kubectl delete pod nginx-deployment-6897679c4b-2hqmk

6.3.3.5 访问测试
我们发现虽然pod的IP以及节点变化了但是Service访问IP没有任何变化

curl 10.1.137.200:8000

6.3.3.6 外网访问
NodePort的最主要功能是进行外网访问,我们是不能通过访问8000端口访问的,他是内网端口,外网访问需要访问映射出来的端口8000:31337/TCP这里映射出来的nodeport是31337,还可以通过指定nodePort来指定端口,要求端口必须大于30000

curl 192.168.64.160:31337

 

6.4 端口号区分

k8s 中 portnodePorttargetPort有什么区别呢

6.4.1 应用位置不同

  • port:是service的的端口
  • targetport:是pod也就是容器的端口
  • nodeport:是容器所在宿主机的端口(实质上也是通过service暴露给了宿主机,而port却没有)

6.4.1 port

k8s集群内部服务之间访问service的入口,即clusterIP:port是service暴露在clusterIP上的端口

主要作用是集群内其他pod访问本pod的时候,需要的一个port,如nginx的pod访问mysql的pod,那么mysql的pod的service可以如下定义,由此可以这样理解,port是service的port,nginx访问service的33306

apiVersion: v1
 kind: Service
 metadata:
  name: mysql-service
 spec:
  ports:
  - port: 33306
    targetPort: 3306
  selector:
   name: mysql-pod

6.4.2 targetport

容器的端口(最终的流量端口)。targetPort是pod上的端口,从port和nodePort上来的流量,经过kube-proxy流入到后端pod的targetPort上,最后进入容器。

看上面的targetport,targetport说过是pod暴露出来的port端口,当nginx的一个请求到达service的33306端口时,service就会将此请求根据selector中的name,将请求转发到mysql-pod这个pod的3306端口上

6.4.3 nodeport

nodeport就很好理解了,它是集群外的客户访问,集群内的服务时,所访问的port,比如客户访问下面的集群中的nginx,就是这样的方式,ip:30001

外部流量访问k8s集群中service入口的一种方式(另一种方式是LoadBalancer),即nodeIP:nodePort是提供给外部流量访问k8s集群中service的入口

比如外部用户要访问k8s集群中的一个Web应用,那么我们可以配置对应service的type=NodePort,nodePort=30001。其他用户就可以通过浏览器http://node:30001访问到该web服务。而数据库等服务可能不需要被外界访问,只需被内部服务访问即可,那么我们就不必设置service的NodePort。

apiVersion: v1 
kind: Service 
metadata: 
    name: nginx-service 
spec: 
    type: NodePort # 有配置NodePort,外部流量可访问k8s中的服务 
    ports: 
    - port: 30080 # 服务访问端口 
      targetPort: 80 # 容器端口 
      nodePort: 30001 # NodePort 
    selector: name: nginx-pod 

6.4.4 小结

nodeport是集群外流量访问集群内服务的端口类型,比如客户访问nginx,apache,port是集群内的pod互相通信用的端口类型,比如nginx访问mysql,而mysql是不需要让客户访问到的,最后targetport,顾名思义,目标端口,也就是最终端口,也就是pod的端口。

 

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐