在前面第四篇中,我介绍 Service 的那篇文章(Kubernetes 入门四:Service)里面,我描述了 Service 的主要用途,其中一个用途就是多实例负载均衡,那么,怎么启动多实例呢?很简单,如果根据之前了解过的知识的话,那不是可以简单地通过创建多个 Pod 来实现吗?这是一个很正常的想法,事实上也是推荐这么做的,就是运行多个 Pod。

但是,至于怎么运行多个 Pod 是个有技巧的活,因为如果是你手动启动多个 Pod 的话,那么还需要保持 Pod 的健康和异常恢复等问题,于是乎 Kubernetes 内置提供了解决方案,对于 Pod 的一些操作可以由内置的一些所谓的 Controller 来解决。

ReplicationController

假设如果你要创建一个 3 实例的应用,用来供用户访问,那么你可以尝试一下 ReplicationController,通过它,你可以简单地通过一个 YAML 配置文件就稳定得运行你想要的 Pod 实例个数,例如我这里运行一个简单的应用,它的作用也很简单,就是打印出它自己实例的 ID 标示:

  1. [root@liqiang.io]# cat 01-replication-controller.yaml
  2. ---
  3. apiVersion: v1
  4. kind: ReplicationController
  5. metadata:
  6. name: simple-pod-rc
  7. spec:
  8. replicas: 3
  9. template:
  10. metadata:
  11. name: simple-pod
  12. labels:
  13. app: simple-pod
  14. spec:
  15. containers:
  16. - image: lukelau/rest-docker:0.0.1
  17. name: timemachine
  18. args:
  19. - -server.addr=0.0.0.0:8080

那么这里我们要认识的第三种 Resource 出现了,那就是 ReplicationController,它的作用就是维护 Pod 的数量如资源所定义的那样,例如我这里定义的是 3 个,那么它就会确保我们的 Pod 的数量就是稳定是 3 个。那么我们怎么着也得验证一下是吧,下面我就来操作一波:

  1. [root@liqiang.io]# kubectl apply -f 01-replication-controller.yaml
  2. [root@liqiang.io]# kubectl get pods
  3. NAME READY STATUS RESTARTS AGE
  4. simple-pod-rc-2xsgp 1/1 Running 0 10s
  5. simple-pod-rc-gjxpt 1/1 Running 0 19s
  6. simple-pod-rc-q2g6g 1/1 Running 0 30s
  7. [root@liqiang.io]# kubectl delete pods simple-pod-rc-q2g6g
  8. pod "simple-pod-rc-q2g6g" deleted
  9. [root@liqiang.io]# kubectl get pods
  10. NAME READY STATUS RESTARTS AGE
  11. simple-pod-rc-2xsgp 1/1 Running 0 59s
  12. simple-pod-rc-gjxpt 1/1 Running 0 68s
  13. simple-pod-rc-lx6wh 1/1 Running 0 4s

从这里可以发现,当我们删除掉一个 Pod 之后,很快 ReplicationController 就帮助我们创建出了一个新的 Pod 来维持 Pod 的数量,那么这里问题就来了,ReplicationController 是如何知道我们的 Pod 已经被删除了呢?难道是去查看 K8S 的资源列表看对应的 Pod 在不在?或者状态是不是 Running 的?那如果我们的应用内部卡死了怎么办?岂不是很没有保障。

事实上,RC 之所以会发现我们的 Pod 已经挂掉了是因为有探针的存在,在 Kubernetes 中,kubelet 会通过指定的探针方式去探测容器是否存活,当前最新版本中(1.15),有三种探针方式,分别是:

所以当你的 Pod 的健康探针探测发现 Pod 的不健康的次数超过设定的次数的时候,那么 RC 就会将这个有问题的 Pod 删除(没有 restart 操作),然后再创建出一个新的出来;同样的,并不是只有健康检查不通过才会进行 Pod 操作,从我前面的操作演示中可以发现,其实 RC 还会检测 Pod 的当前数量,如果 Pod 的数量不足 replicas,那么也会创建出新的;如果 Pod 的数量超过 replicas,那么也会被关掉一些,从而符合 replicas 的设置。

ReplicaSet

这里你可能会稍微奇怪一下,为什么文章的标题叫 ReplicaSet 而上来就先说 ReplicationController 呢?原因是其实这两种资源本质上是一致的,根据 Kubernetes 的文档介绍,它们没有太大区别,细微的区别之处在于 ReplicaSet 的 Selector 会比 ReplicationController 要强大一些,例如从这个例子中可以看到:

  1. [root@liqiang.io]# cat 01-replication-controller.yaml
  2. ---
  3. apiVersion: v1
  4. kind: ReplicationController
  5. metadata:
  6. name: simple-pod-rc
  7. spec:
  8. replicas: 3
  9. template:
  10. metadata:
  11. name: simple-pod
  12. labels:
  13. app: simple-pod
  14. spec:
  15. containers:
  16. - image: lukelau/rest-docker:0.0.1
  17. name: timemachine
  18. args:
  19. - -server.addr=0.0.0.0:8080

只有一个简单的 Labels 的选择:app: simple-pod,但是,通过 ReplicaSet 选择就变得多样化起来了,这个对应的 ReplicaSet 是这样的:

  1. [root@liqiang.io]# cat 02-replicaset.yaml
  2. ---
  3. apiVersion: apps/v1
  4. kind: ReplicaSet
  5. metadata:
  6. name: simple-pod-rc
  7. spec:
  8. selector:
  9. matchLabels:
  10. app: simple-pod
  11. replicas: 3
  12. template:
  13. metadata:
  14. name: simple-pod
  15. labels:
  16. app: simple-pod
  17. spec:
  18. containers:
  19. - image: lukelau/rest-docker:0.0.1
  20. name: timemachine
  21. args:
  22. - -server.addr=0.0.0.0:8080

可以发现,这里的 selector 变得稍微有点复杂了,除了可以 matchLabels 之外,还支持 matchExpressions,所以这算是一种改进;通过这两种对比,可能在大多数情况下你都会选择 ReplicationController,但是,接下来要介绍的一个资源 Deployment 已经将内置的 replicas 集从 ReplicationController 转成 ReplicaSet 了,所以很多地方都会推荐你使用 ReplicaSet,不要再使用 ReplicationController 了。