7

我有一个 5 节点集群(1-master/4-worker)。是否可以配置一个 StatefulSet,让我可以在给定节点上运行一个或多个 pod,因为它知道它有足够的容量,而不是 Kubernetes 调度程序做出这个决定?

可以说,我的 StatefulSet 创建了 4 个 pod(副本:4)作为 myapp-0、myapp-1、myapp-2 和 myapp-3。现在我正在寻找的是:

myapp-0 pod--被安排到---> worker-1

myapp-1 pod--被安排到---> worker-2

myapp-2 pod--被安排到---> worker-3

myapp-3 pod--被安排到---> worker-4

请让我知道它是否可以以某种方式实现?因为如果我向 StatefulSet 的 pod 添加容错,所有 pod 都将是相同的,并且所有这些都将被安排在与 taint 匹配的单个节点上。

谢谢,J

4

5 回答 5

6

您可以将调度任意 pod 子集的责任委托给您自己的自定义调度程序,该调度程序与默认 Kubernetes 调度程序一起运行,或者代替默认 Kubernetes 调度程序运行。

您可以编写自己的自定义调度程序。自定义调度程序可以用任何语言编写,可以根据需要简单或复杂。下面是一个非常简单的用 Bash 编写的自定义调度程序示例,它随机分配一个节点。请注意,您需要与 kubectl 代理一起运行它才能工作。

SERVER='localhost:8001'

while true;

do

    for PODNAME in $(kubectl --server $SERVER get pods -o json | jq '.items[] | select(.spec.schedulerName == "my-scheduler") | select(.spec.nodeName == null) | .metadata.name' | tr -d '"')

;

    do

        NODES=($(kubectl --server $SERVER get nodes -o json | jq '.items[].metadata.name' | tr -d '"'))


        NUMNODES=${#NODES[@]}

        CHOSEN=${NODES[$[$RANDOM % $NUMNODES]]}

        curl --header "Content-Type:application/json" --request POST --data '{"apiVersion":"v1", "kind": "Binding", "metadata": {"name": "'$PODNAME'"}, "target": {"apiVersion": "v1", "kind"

: "Node", "name": "'$CHOSEN'"}}' http://$SERVER/api/v1/namespaces/default/pods/$PODNAME/binding/

        echo "Assigned $PODNAME to $CHOSEN"

    done

    sleep 1

done

然后只需在规范部分下的 StatefulSet 配置文件中添加schedulerName: your-scheduler行。

您还可以使用pod 亲和性: .

例子:

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: redis-cache
spec:
  selector:
    matchLabels:
      app: store
  replicas: 3
  template:
    metadata:
      labels:
        app: store
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - store
            topologyKey: "kubernetes.io/hostname"
      containers:
      - name: redis-server
        image: redis:3.2-alpine

以下 webserver statefuset 的 yaml 片段配置了 podAntiAffinity 和 podAffinity。这会通知调度程序,它的所有副本都将与具有选择器标签app=store的 pod 位于同一位置。这还将确保每个 Web 服务器副本不会位于单个节点上。

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web-server
spec:
  selector:
    matchLabels:
      app: web-store
  replicas: 3
  template:
    metadata:
      labels:
        app: web-store
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - web-store
            topologyKey: "kubernetes.io/hostname"
        podAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - store
            topologyKey: "kubernetes.io/hostname"
      containers:
      - name: web-app
        image: nginx:1.12-alpine

如果我们创建上述两个部署,我们的三节点集群应该如下所示。

node-1              node-2           node-3
webserver-1     webserver-2          webserver-3
cache-1             cache-2          cache-3

上面的例子使用 PodAntiAffinity 规则和 topologyKey:"kubernetes.io/hostname"来部署 redis 集群,使得没有两个实例位于同一主机上

您可以简单地定义特定 pod 的三个副本,并定义特定的 pod 配置文件 egg.: 有 label: nodeName是最简单的节点选择约束形式,但由于其局限性,通常不使用。nodeName 是 PodSpec 的一个字段。如果它非空,则调度程序会忽略该 pod,并且在命名节点上运行的 kubelet 会尝试运行该 pod。因此,如果 PodSpec 中提供了 nodeName,则它优先于上述节点选择方法。

下面是一个使用 nodeName 字段的 pod 配置文件示例:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx
  nodeName: kube-worker-1

有关调度程序的更多信息:custom-scheduler

看看这篇文章:assigining-pods-kubernetes

于 2020-02-17T13:46:29.593 回答
1

您可以使用nodeSelector并且node affinity(查看本指南https://kubernetes.io/docs/concepts/configuration/assign-pod-node/)来执行此操作,任何人都可以用来在特定节点上运行 pod。但是如果节点有污点(限制),那么您需要为这些节点添加容忍度(更多可以在这里找到https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/)。使用这种方法,您可以指定要用于 pod 调度的节点列表,如果您指定 ex. 3 个节点,您有 5 个 pod,那么您无法控制每个节点上将运行多少个 pod。它们按照 kube-schedular 分发。另一个相关用例:如果你想在每个指定的节点上运行一个 pod,你可以创建一个 daemonset 并使用 nodeSelector 选择节点。

于 2020-02-16T02:10:43.067 回答
1

您可以使用 podAntiAffinity 将副本分发到不同的节点。

apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  serviceName: "nginx"
  replicas: 4
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: k8s.gcr.io/nginx-slim:0.8
        ports:
        - containerPort: 80
          name: web
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - nginx
            topologyKey: "kubernetes.io/hostname"

这将在 worker1 中部署 web-0,在 worker2 中部署 web-1,在 worker3 中部署 web-2,在 worker4 中部署 web-3。

于 2020-02-16T08:19:30.347 回答
1

您可以使用以下KubeMod ModRule:

apiVersion: api.kubemod.io/v1beta1
kind: ModRule
metadata:
  name: statefulset-pod-node-affinity
spec:
  type: Patch

  match:
    # Select pods named myapp-xxx.
    - select: '$.kind'
      matchValue: Pod
    - select: '$.metadata.name'
      matchRegex: myapp-.*

  patch:
    # Patch the selected pods such that their node affinity matches nodes that contain a label with the name of the pod.
    - op: add
      path: /spec/affinity/nodeAffinity/requiredDuringSchedulingIgnoredDuringExecution
      value: |-
        nodeSelectorTerms:
          - matchExpressions:
            - key: accept-pod/{{ .Target.metadata.name }}
              operator: In
              values:
                - 'true'

上面的 ModRule 将监视命名的 Pod 的创建,并在部署之前myapp-*将一个部分注入到它们的资源清单中。nodeAffinity这将指示调度程序将 pod 调度到标签accept-pod/<pod-name>设置为的节点true

然后,您可以通过向节点添加标签来将未来的 pod 分配给节点:

kubectl label node worker-1 accept-pod/myapp-0=true
kubectl label node worker-2 accept-pod/myapp-1=true
kubectl label node worker-3 accept-pod/myapp-2=true
...

上述 ModRule 部署完成后,创建 StatefulSet 会触发其 Pod 的创建,这会被 ModRule 拦截。ModRule 将nodeAffinity使用 pod 的名称动态注入该部分。

如果稍后删除 StatefulSet,再次部署它会导致 Pod 被安排在与之前相同的节点上。

于 2021-06-01T21:01:23.767 回答
0

看看这个指南https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ 但是,你正在寻找的是nodeSelector应该放在 pod 规范中的指令。

于 2020-02-15T21:26:35.327 回答