极客油画

pod是k8s的最小调度单元

  1. 容器是进程,pod是进程组,而k8s就是分布式的systemd

有多个进程必须调度在同一台机器的场景,而pod就是把多个进程封装起来作为一个原子调度单位。

在一个真正的操作系统里,进程并不是"孤苦伶仃"地独立运行的,而是以进程组的方式,“有原则地"组织在一起。

类似的,在k8s中,pod相当于进程组,docker容器相当于进程,进程组中的进程间是公用同一个文件系统和网络的,那k8s是如何实现多个docker容器共享同一个文件系统和网络呢?

假设有A、B两个容器,可以先运行B容器,然后使用命令docker run --net=B --volumes-from=B --name=A image-A ...运行A容器,这样A就会共享B的文件系统和网络了。

但是,这有个问题,就是A必须在B之后执行,如果解决这个问题呢?

所以,在Kubernetes项目里,Pod的实现需要使用一个中间容器,这个容器叫作Infra容器。在这个Pod中,Infra容器永远都是第一个被创建的容器,而其他用户定义的容器,则通过Join Network Namespace的方式,与Infra容器关联在一起。而对于同一个Pod里面的所有用户容器来说,它们的进出流量,也可以认为都是通过Infra容器完成的。所以网络插件其实是和infra容器打交道的,由于infra容器的rootfs几乎什么都没有,所以网络插件无法在infra容器中存放任何的配置,只能关注于Infra容器的Network Namespace。

infra容器的镜像是k8s.gcr.io/pause,这个镜像是一个用汇编语言编写的、永远处于“暂停”状态的容器,解压后的大小也只有100~200KB。

  1. pod的常用字段

https://github.com/kubernetes/api/blob/master/core/v1/types.go 下的Pod结构体中包含了所有Pod字段。

  1. shareProcessNamespace pod内的所有容器间共享PID命名空间(默认是不共享的)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  shareProcessNamespace: true
  containers:
  - name: nginx
    image: nginx
  - name: shell
    image: busybox
    stdin: true
    tty: true

由于busybox容器开启了stdin和tty,我们就可以使用kubectl attach命令就可以直接进入busybox容器,而不用重复使用-t -i选项了。

1
2
3
4
5
6
7
8
$ kubectl attach -it nginx -c shell
/ # ps ax
PID   USER     TIME  COMMAND
    1 root      0:00 /pause
    8 root      0:00 nginx: master process nginx -g daemon off;
   14 101       0:00 nginx: worker process
   15 root      0:00 sh
   21 root      0:00 ps ax

可以看到,在这个容器里,我们不仅可以看到它本身的ps ax指令,还可以看到nginx容器的进程,以及Infra容器的/pause进程。这就意味着,整个Pod里的每个容器的进程,对于所有容器来说都是可见的

  1. pod内共享宿主机的Namespace
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  hostNetwork: true
  hostIPC: true
  hostPID: true
  containers:
  - name: nginx
    image: nginx
  - name: shell
    image: busybox
    stdin: true
    tty: true
  1. 对于需要预先执行的操作,可以在pod中的initContainers中定义
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
apiVersion: v1
kind: Pod
metadata:
  name: javaweb-2
spec:
  initContainers:
  - image: geektime/sample:v2
    name: war
    command: ["cp", "/sample.war", "/app"]
    volumeMounts:
    - mountPath: /app
      name: app-volume
  containers:
  - image: geektime/tomcat:7.0
    name: tomcat
    command: ["sh","-c","/root/apache-tomcat-7.0.42-v2/bin/start.sh"]
    volumeMounts:
    - mountPath: /root/apache-tomcat-7.0.42-v2/webapps
      name: app-volume
    ports:
    - containerPort: 8080
      hostPort: 8001
  volumes:
  - name: app-volume
    emptyDir: {}

在 Pod 中,所有 Init Container 定义的容器,都会比 spec.containers 定义的用户容器先启动。并且,Init Container 容器会按顺序逐一启动,而直到它们都启动并且退出了,用户容器才会启动。

  1. ImagePullPolicy
  • Always(默认): 每次创建Pod都重新拉取一次镜像。
  • Never: 永远不会主动拉取这个镜像
  • IfNotPresent: 只在宿主机上不存在这个镜像时才拉取
  1. lifecycle(Container Lifecycle Hooks)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
apiVersion: v1
kind: Pod
metadata:
  name: lifecycle-demo
spec:
  containers:
  - name: lifecycle-demo-container
    image: nginx
    lifecycle:
      postStart:
        exec:
          command: ["/bin/sh", "-c", "echo Hello from the postStart handler > /usr/share/message"]
      preStop:
        exec:
          command: ["/usr/sbin/nginx","-s","quit"]
  • postStart: 在容器启动后,立刻执行一个指定的操作。需要明确的是,postStart定义的操作,虽然是在Docker容器ENTRYPOINT执行之后,但它并不严格保证顺序(非阻塞的)。也就是说,在postStart启动时,ENTRYPOINT有可能还没有结束。
  • preStop: 容器被杀死之前(比如,收到了SIGKILL信号)。而需要明确的是,preStop操作的执行,是阻塞的。所以,它会阻塞当前容器杀死进程,直到这个Hook定义操作完成之后,才允许容器被杀死,这跟postStart不一样。
  1. Pod对象在Kubernetes中的生命周期
  • Pending: 这个状态意味着,Pod的YAML文件已经提交给了Kubernetes,API对象已经被创建并保存在Etcd当中。但是,这个Pod里有些容器因为某些原因而不能被顺利创建。比如,调度不成功。
  • Running: 这个状态下,Pod已经调度成功,跟一个具体的节点绑定。它包含的容器都已经创建成功,并且至少有一个正在运行中。
  • Succeeded: 这个状态意味着,Pod里的所有容器都正常运行完毕,并且已经退出了。这种情况在运行一次性任务时最为常见。
  • Failed: 这个状态下,Pod里至少有一个容器以不正常的状态(非0的返回码)退出。这个状态的出现,意味着你得想办法Debug这个容器得应用,比如查看Pod的Events和日志。
  • Unknown: 这是一个异常状态,意味着Pod的状态不能持续地被kubelet汇报给kube-apiserver,这很有可能是主从节点(Master和kubelet)间的通信出现了问题。

Pod对象的Status字段,还可以再细化出一组Conditions。这些细分状态的值包括:PodScheduled、Ready、Initialized,以及Unschedulable。它们主要用于描述造成当前Status的具体原因是什么。比如,Pod当前的Status是Pending,对应的Condition是Unschedulable,这就意味着它的调度出现了问题。

  1. 探针和restartPolicy
apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: test-liveness-exec
spec:
  containers:
  - name: liveness
    image: busybox
    args:
    - /bin/sh
    - -c
    - touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
    livenessProbe:
      exec:
      - cat
      - /tmp/healthy
    initialDelaySeconds: 5
    periodSeconds: 5

在这个Pod中,我们定义了一个有趣的容器。它在启动之后做的第一件事,就是在/tmp目录下创建了一个healthy文件,以此作为自己已经正常运行的标志。而30s过后,它会把这个文件删除掉。

于此同时,我们定义了一个这样的livenessProbe(健康检查)。它的类型是exec,这意味着,它会在容器启动后,在容器里面执行一条我们指定的命令,比如:“cat /tmp/healthy”。

这时,如果这个文件存在,这条命令的返回值就是0,Pod就会认为这个容器不仅已经启动,而且是健康的。这个健康检查,在容器启动5s后开始执行(initialDelaySeconds:5),每5s执行一次(periodSeconds: 5)。

现在,让我们来具体实践一下这个过程。

首先,创建这个Pod:

$ kubectl create -f test-liveness-exec.yaml

然后,查看这个Pod的状态:

$ kubectl get pod
NAME             READY      STATUS   RESTARTS   AGE
test-liveness-exec 1/1      Running   0         10s

可以看到,由于已经通过了健康检查,这个Pod就进入了Running状态。

而30s之后,我们再查看一下Pod的Events:

$ kubectl describe pod test-liveness-exec

你会发现,这个Pod在Events报告了一个异常:

FirstSeen  LastSeen   Count   From          SubobjectPath              Type
---------  --------   -----   ----          -------------              -----
2s         2s         1  {kubelet worker0}  spec.containers{liveness}  Warning

显然,这个健康检查探查到/tmp/healthy已经不存在了,所以它报告容器是不健康的。那么接下来会发生什么呢?

我们不妨再次查看一下这个Pod的状态:

$ kubectl get pod test-livenes-exec
NAME             READY         STATUS       RESTARTS      AGE
liveness-exec    1/1           Running      1             1m

这时我们发现,Pod并没有进入Failed状态,而是保持了Running状态。这是为什么呢?

其实,如果你注意到RESTARTS字段从0到1的变化,就明白原因了:这个异常的容器已经被Kubernetes重启了 。在这个过程中,Pod保持Running状态不变。

需要注意的是:Kubernetes中并没有Docker的Stop语义。所以虽然是Restart(重启),但实际却是重新创建了容器。

这个功能就是Kubernetes里的Pod恢复机制,叫作restartPolicy。它是Pod的Spec部分的一个标准字段(pod.spec.restartPolicy)。

  • Always(默认):任何时候这个容器发生了异常,它一定会被重新创建
  • OnFailure: 只在容器异常时才自动重启容器;
  • Never: 从来不重启容器

pod的重启遵循两个基本的设计原则:

  • 只要Pod的restartPolicy指定的策略允许重启异常的容器(比如:Always),那么这个Pod就会保持Running状态,并进行容器重启。否则,Pod就会进入Failed状态。
  • 对于包含多个容器的Pod,只要它里面所有的容器都计入异常状态后,Pod才会进入Failed状态。在此之前,Pod都是Running状态。此时,Pod的READY字段会显示正常容器的个数。

假设一个Pod里只有一个容器,然后这个容器异常退出了。那么,只有当restartPolicy=Never时,这个Pod才会进入Failed状态。而其他情况下,由于Kubernetes都可以重启这个机器,所以Pod的状态保持Running不变。

而如果这个Pod有多个容器,仅有一个容器异常退出,它就始终保持Running状态,哪怕即使restartPolicy=Never。只有当所有容器也异常退出之后,这个Pod才会进入Failed状态。

  1. readniessProbe探针(探测该Pod能否被Service访问到)

readinessProbe检查结果的成功与否,决定的这个Pod是不是能被通过Service的方式访问到,而并不影响Pod的生命周期。

  1. NodeName 一旦 Pod 的这个字段被赋值,Kubernetes 项目就会被认为这个 Pod 已经经过了调度,调度的结果就是赋值的节点名字。所以,这个字段一般由调度器负责设置,但用户也可以设置它来“骗过”调度器,当然这个做法一般是在测试或者调试的时候才会用到。

kube-controller-manager

所有的控制器都由kube-controller-manager这个组件管理,在kubernetes项目的pkg/controller目录下可以查看所有的控制器:

BUILD                          deployment                     podautoscaler
OWNERS                         disruption                     podgc
apis                           doc.go                         replicaset
bootstrap                      endpoint                       replication
certificates                   endpointslice                  resourcequota
client_builder_dynamic.go      endpointslicemirroring         serviceaccount
clusterroleaggregation         garbagecollector               statefulset
controller_ref_manager.go      history                        storageversiongc
controller_ref_manager_test.go job                            testutil
controller_utils.go            lookup_cache.go                ttl
controller_utils_test.go       namespace                      ttlafterfinished
cronjob                        nodeipam                       util
daemon                         nodelifecycle                  volume

这些控制器之所以被统一放在pkg/controller目录下,就是因为它们被遵循Kubernetes项目中的一个通用编排模式,即:控制循环(control loop)。

for {
  实际状态 := 获取集群中对象x的实际状态(Actual State)
  期望状态 := 获取集群中对象x的期望状态(Desired State)
  if 实际状态 == 期望状态 { // 第三步,执行对比,也被称之为Reconcile Loop(调谐循环)
    什么都不做
  } else {
    执行编排动作,将实际状态调整为期望状态
  }
}

1. Deployment/ReplicaSet

接下来,以 Deployment 为例,我和你简单描述一下它对控制器模型的实现:

  1. Deployment 控制器从 Etcd 中获取到所有携带了“app: nginx”标签的 Pod,然后统计它们的数量,这就是实际状态;
  2. Deployment 对象的 Replicas 字段的值就是期望状态;
  3. Deployment 控制器将两个状态做比较,然后根据比较结果,确定是创建 Pod,还是删除已有的 Pod。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2
-------------------------
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80

类似 Deployment 这样的一个控制器,实际上都是由上半部分的控制器定义(包括期望状态),加上下半部分的被控制对象的模板组成的。 这就是为什么,在所有 API 对象的 Metadata 里,都有一个字段叫作 ownerReference,用于保存当前这个 API 对象的拥有者(Owner)的信息。 对于Deployment管理的Pod来说,它的ownerReference其实是ReplicaSet。 Deployment 控制器实际操纵的,正是这样的 ReplicaSet 对象,而不是 Pod 对象。

Pod的"水平扩展/收缩”(horizontal scaling out/in) 举个例子,如果你更新了 Deployment 的 Pod 模板(比如,修改了容器的镜像),那么 Deployment 就需要遵循一种叫作“滚动更新”(rolling update)的方式,来升级现有的容器。 而这个能力的实现,依赖的是 Kubernetes 项目中的一个非常重要的概念(API 对象):ReplicaSet。 ReplicaSet 的结构非常简单,我们可以通过这个 YAML 文件查看一下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: nginx-set
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9

其实,Deployment、ReplicaSet和Pod之间,是层层控制的关系。其中,ReplicaSet 负责通过“控制器模式”,保证系统中 Pod 的个数永远等于指定的个数(比如,3 个)。 这也正是 Deployment 只允许容器的 restartPolicy=Always 的主要原因:只有在容器能保证自己始终是 Running 状态的前提下,ReplicaSet 调整 Pod 的个数才有意义。 而Deployment 同样通过“控制器模式”,来操作 ReplicaSet 的个数和属性,进而实现“水平扩展 / 收缩”和“滚动更新”这两个编排动作。

  1. Deployment通过修改它所控制的ReplicaSet的Pod副本个数来实现"水平扩展/收缩" 可以通过直接修改Deployment的yaml,或者使用下面的命令实现:
1
$ kubectl scale deployment nginx-deployment --replicas=4
  1. 滚动更新就相对要复杂些 接下来,我们以下面这个yaml来演示"滚动更新"的过程
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80

创建nginx-deployment,并使用–record参数来记录每次操作所执行的命令(会在metadata.annotations的kubernetes.io/change-cause中记录下每次的操作):

1
2
3
4
$ kubectl create -f nginx-deployment.yaml --record
$ kubectl get deployments
NAME               READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   3/3     3            3           60s

在返回结果中,我们可以看到三个状态字段,它们的含义如下所示。

  • READY: 通过健康检查的Pod的个数,可能包含就版本的Pod。
  • UP-TO-DATE: 当前处于最新版本的 Pod 的个数,所谓最新版本指的是 Pod 的 Spec 部分与 Deployment 里 Pod 模板里定义的完全一致;
  • AVAILABLE:当前已经可用的 Pod 的个数,即:既是 Running 状态,又是最新版本,并且已经处于 Ready(健康检查正确)状态的 Pod 的个数。
  • CURRENT: 当前处于 Running 状态的 Pod 的个数;

可以使用kubectl rollout statu命令来实时查看Deployment对象的状态变化: $ kubectl rollout status deployment/nginx-deployment

查看该Deployment所控制的ReplicaSet:

1
2
3
$ kubectl get rs
NAME                          DESIRED   CURRENT   READY   AGE
nginx-deployment-5bf87f5f59   3         3         3       6m6s

可以看到,在用户提交了一个Deployment对象后,Deployment Controller就会立即创建一个ReplicaSet,这个ReplicaSet的名字,有Deployment的名字和一个随机字符串组成。 将一个集群中正在运行的多个 Pod 版本,交替地逐一升级的过程,就是“滚动更新”。Deployment每执行一次滚动更新,都会新建一个新的ReplicaSet,新的ReplicaSet就绪后才会删除旧的ReplicaSet。 可以通过spec.strategy字段来控制滚动更新的策略,例如:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
...
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1

maxSurge 指定的是除了 DESIRED 数量之外,在一次“滚动”中,Deployment 控制器还可以创建多少个新 Pod。值还可以是百分比。 maxUnavailable 指的是,在一次“滚动”中,Deployment 控制器可以删除多少个旧 Pod。值还可以是百分比。

可以通过直接修改Deployment的yaml,或者使用下面的命令实现滚动更新: $ kubectl set image deployment/nginx-deployment nginx=nginx:1.91

可以使用下面的命令把整个Deployment回滚到上一个版本: $ kubectl rollout undo deployment/nginx-deployment

进一步地,如果我想回滚到更早之前的版本,要怎么办呢? 首先,我需要使用 kubectl rollout history 命令,查看该Deployment 变更的所有历史版本。 如果在创建Deployment的时候指定了–record选项,就不需要再指定–revision=<版本号>来查看具体的变更细节了。 $ kubectl rollout history deployment/nginx-deployment 然后,就可以使用–to-revision=<版本号>来执行回滚了。 $ kubectl rollout history deployment/nginx-deployment –to-revision=2

如何实现对Deployment的多次更新操作,最后只生成一个ReplicaSet呢? 首先,使用如下的命令,暂停该Deployment: $ kubectl rollout pause deployment/nginx-deployment 在对Deployment的所有修改都完成之后,再恢复该Deployment: $ kubectl rollout resume deploy/nginx-deployment

可以通过spec.revisionHistoryLimit字段来设置Deployment能保留的最大ReplicaSet的个数。

2. DaemonSet

DaemonSet 的主要作用,是让你在 Kubernetes 集群里,运行一个 Daemon Pod。 所以,这个 Pod 有如下三个特征:

  1. 这个 Pod 运行在 Kubernetes 集群里的每一个节点(Node)上;
  2. 每个节点上只有一个这样的 Pod 实例;
  3. 当有新的节点加入 Kubernetes 集群后,该 Pod 会自动地在新节点上被创建出来;而当旧节点被删除后,它上面的 Pod 也相应地会被回收掉。

这个机制听起来很简单,但 Daemon Pod 的意义确实是非常重要的。我随便给你列举几个例子:

  1. 各种网络插件的 Agent 组件,都必须运行在每一个节点上,用来处理这个节点上的容器网络;
  2. 各种存储插件的 Agent 组件,也必须运行在每一个节点上,用来在这个节点上挂载远程存储目录,操作容器的 Volume 目录;
  3. 各种监控组件和日志组件,也必须运行在每一个节点上,负责这个节点上的监控信息和日志搜集。

更重要的是,跟其他编排对象不一样,DaemonSet 开始运行的时机,很多时候比整个 Kubernetes 集群出现的时机都要早。

这个乍一听起来可能有点儿奇怪。但其实你来想一下:如果这个 DaemonSet 正是一个网络插件的 Agent 组件呢?这个时候,整个 Kubernetes 集群里还没有可用的容器网络,所有 Worker 节点的状态都是 NotReady(NetworkReady=false)。这种情况下,普通的 Pod 肯定不能运行在这个集群上。

而DaemonSet 的“过人之处”,其实就是依靠 Toleration 实现的。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
...
template:
    metadata:
      labels:
        name: network-plugin-agent
    spec:
      tolerations:
      - key: node.kubernetes.io/network-unavailable
        operator: Exists
        effect: NoSchedule

这样,哪怕这个Worker节点的状态是NotReady,也可以部署普通的Pod。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluentd-elasticsearch
  namespace: kube-system
  labels:
    k8s-app: fluentd-logging
spec:
  selector:
    matchLabels:
      name: fluentd-elasticsearch
  template:
    metadata:
      labels:
        name: fluentd-elasticsearch
    spec:
      tolerations:
      - key: node-role.kubernetes.io/master
        effect: NoSchedule
      containers:
      - name: fluentd-elasticsearch
        image: k8s.gcr.io/fluentd-elasticsearch:1.20
        resources:
          limits:
            memory: 200Mi
          requests:
            cpu: 100m
            memory: 200Mi
        volumeMounts:
        - name: varlog
          mountPath: /var/log
        - name: varlibdockercontainers
          mountPath: /var/lib/docker/containers
          readOnly: true
      terminationGracePeriodSeconds: 30
      volumes:
      - name: varlog
        hostPath:
          path: /var/log
      - name: varlibdockercontainers
        hostPath:
          path: /var/lib/docker/containers

这个 DaemonSet,管理的是一个 fluentd-elasticsearch 镜像的 Pod。这个镜像的功能非常实用:通过 fluentd 将 Docker 容器里的日志转发到 ElasticSearch 中。

Docker 容器里应用的日志(这里只限stdout和stderr),默认会保存在宿主机的 /var/lib/docker/containers/{{. 容器 ID}}/{{. 容器 ID}}-json.log 文件里,所以这个目录正是 fluentd 的搜集目标。

如果Docker容器将日志放在某个文件中,上面的方案就不可用了, 这个时候就需要使用sidecar把日志文件的数据重新打印到stdout和stderr上才行。

那么,DaemonSet 又是如何保证每个 Node 上有且只有一个被管理的 Pod 呢? DaemonSet Controller,首先从 Etcd 里获取所有的 Node 列表,然后遍历所有的 Node。这时,它就可以很容易地去检查,当前这个 Node 上是不是有一个携带了 name=fluentd-elasticsearch 标签的 Pod 在运行。而检查的结果,可能有这么三种情况:

  1. 没有这种 Pod,那么就意味着要在这个 Node 上创建这样一个 Pod;
  2. 有这种 Pod,但是数量大于 1,那就说明要把多余的 Pod 从这个 Node 上删除掉;
  3. 正好只有一个这种 Pod,那说明这个节点是正常的。

其中,删除节点(Node)上多余的 Pod 非常简单,直接调用 Kubernetes API 就可以了。但是,如何在指定的 Node 上创建新 Pod 呢? 可以使用nodeSelector,但是建议使用nodeAffinity,因为nodeSelector将来会被nodeAffinity所取代。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
apiVersion: v1
kind: Pod
metadata:
  name: with-node-affinity
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: metadata.name
            operator: In
            values:
            - node-geektime

其中:

  1. requiredDuringSchedulingIgnoredDuringExecution:它的意思是说,这个 nodeAffinity 必须在每次调度的时候予以考虑。同时,这也意味着你可以设置在某些情况下不考虑这个 nodeAffinity;
  2. 这个 Pod,将来只允许运行在“metadata.name”是“node-geektime”的节点上。

Kubernetes v1.7 之后添加了一个 API 对象,名叫 ControllerRevision,专门用来记录某种 Controller 对象的版本。比如,你可以通过如下命令查看 fluentd-elasticsearch 对应的 ControllerRevision:

1
2
3
$ kubectl get controllerrevision -n kube-system -l name=fluentd-elasticsearch
NAME                               CONTROLLER                             REVISION   AGE
fluentd-elasticsearch-64dc6799c9   daemonset.apps/fluentd-elasticsearch   2          1h

3. job和cronjob


本文发表于 0001-01-01,最后修改于 0001-01-01。

本站永久域名「 jiavvc.top 」,也可搜索「 极客油画 」找到我。


上一篇 « 下一篇 »

赞赏支持

请我吃鸡腿 =^_^=

i ysf

云闪付

i wechat

微信

推荐阅读

Big Image