Kubernetes 201 涵盖了 Kubernetes 101 未涉及的领域和更高级的话题,如应用生产化,部署和扩展。

大致的知识树如下:

  • Kubernetes 201 - Labels, Replication Controllers, Services and Health Checking
    • Labels
    • Replication Controllers
      • Replication Controller Management
    • Services
      • Service Management
    • Health Checking
      • Process Health Checking
      • Application Health Checking

Labels

了解 Pods 概念并会创建后,你一定想创建原来越多的 pod。 加油干吧!

但是你逐渐会需要一个体系来组织这些 pod,做好分组。

Kubernetes 中实现这一目的的系统是 Labels。 Labels 是 key-value 对,附属于 Kubernetes 中每一个对象。 可以使用 RESTful 的 list 操作做 Label 选择,发送到 api 服务器,获取满足 label 选择条件的对象列表。

增加一个 label,只要在 pod 定义的元数据部分以下写 labels 即可:

labels:
    app: nginx

以下是一个 nginx pod 定义示例,包含了 label:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  containers:
  - name: nginx
    image: nginx
    ports:
    - containerPort: 80

创建一个有 label 的 pod:

$ kubectl create -f docs/user-guide/walkthrough/pod-nginx-with-label.yaml

列出所有具有 label app=nginx 的 pod:

$ kubectl get pods -l app=nginx

关于 Labels

Label 是 Kubernetes 构建链上两个部分(Replication Controllers 和 Services)的核心概念。

Replication Controllers

OK,你知道如何构建超棒的、多容器、有标签的 pod, 想用他们构建应用程序, 也许已经开始建立了一堆独立的 pod, 但如果你这么做了, 一系列操作问题会出现。

比如:如何向上或向下扩展 pod 的数量,如何保证所有 pod 同质同源?

Replication controller (副本控制器)回答了这一问题。 Replicatin controller 将 pod 创建的模版和所需副本数,结合到单一的 Kubernetes 对象里。 它还包含一个标签选择器,表示副本控制器管理的对象集合。

副本控制器持续测量对象集合大小与所需大小的差异,采取行动创建或删除 pod。

例如,一个副本控制器,初始化两个 nginx pod:

apiVersion: v1
kind: ReplicationController
metadata:
  name: nginx-controller
spec:
  replicas: 2
  # selector identifies the set of Pods that this
  # replication controller is responsible for managing
  selector:
    app: nginx
  # podTemplate defines the 'cookie cutter' used for creating
  # new pods when necessary
  template:
    metadata:
      labels:
        # Important: these labels need to match the selector above
        # The api server enforces this constraint.
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80

Replication Controller Management

创建一个副本控制器:

$ kubectl create -f docs/user-guide/walkthrough/replication-controller.yaml

列出所有副本控制器:

$ kubectl get rc

通过名字删除副本控制器:

$ kubectl delete rc nginx-controller

Replication Controllers

Services

一旦有一套 pod 的副本,就需要一个抽象层,使应用的各层之间联系起来。 例如,如果有复制控制器管理后台任务,就不希望每次后端扩展后都需要重现配置前端程序。 同样,如果后台任务被计划调度到不同机器上,也不应该被要求重新配置前端程序。

在 Kubernetes 里,Service(服务)实现这些目标。

Service 提供了通过静态 ip 指向一组(用 label 选择出来的) pod 的方式。 如果提供商支持,也可以提供负载均衡。

例如,基于前文 nginx 副本控制器例子创建的 pod,这个 service 提供了负载均衡:

apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  ports:
  - port: 8000 # the port that this service should serve on
    # the container on each pod to connect to, can be a name
    # (e.g. 'www') or a number (e.g. 80)
    targetPort: 80
    protocol: TCP
  # just like the selector in the replication controller,
  # but this time it identifies the set of pods to load balance
  # traffic to.
  selector:
    app: nginx

Service Management

创建一个 nginx 服务:

$ kubectl create -f docs/user-guide/walkthrough/service.yaml

列出所有服务:

$ kubectl get services

大多数提供商的服务 IP 无法从外部访问。 测试服务工作的最简单方式是创建一个 busybox pod,通过它执行命令。

服务 IP 可用时,能够用 curl 在 80 端口访问:

$ export SERVICE_IP=$(kubectl get service nginx-service -o=template -t={{.spec.clusterIP}})
$ export SERVICE_PORT=$(kubectl get service nginx-service -o=template '-t={{(index .spec.ports 0).port}}')
$ curl http://${SERVICE_IP}:${SERVICE_PORT}

通过名字删除服务:

$ kubectl delete service nginx-controller

每个服务创建时,都分配了唯一的 IP 地址。 这个地址绑定在 Service 的整个生命周期,并且不回改变。

Pod 可以配置与 Service 进行通讯,与 Service 的数据交换会自动负载均衡到 Service 选择条件所选择出来的一个成员 pod 上。

Services

Health Checking

我写的代码永远不会崩溃,是吗? 可悲的是 Kubernetes 邮件列表里显示正好相反……

与其试图写出无 bug 的代码,不如使用一个管理工具来定期做健康检查并修复。 由一个应用外部的系统负责监视应用程序和采取修复措施。 在外部做检查很重要,因为如果健康检查是应用的一部分,应用程序失效时它也可能也失效,而你并不知晓。

在 Kubernetes 中,健康监控程序是 Kubelet 客服端。

Process Health Checking

监控检查最简单的形式是进程级别的监控检查。 Kubelet 持续向 Docker 服务询问容器进程是否在运行,如果失败,容器进程被重新启动。

目前我们所运行的 Kubernetes 例子中,健康检查都已经启用。 Kubernetes 中每一个容器,健康检查都是开启的状态。

Application Health Checking

然而,许多情况下,这种低级别的健康检查是不够的。

考虑下面的代码:

lockOne := sync.Mutex{}
lockTwo := sync.Mutex{}

go func() {
  lockOne.Lock();
  lockTwo.Lock();
  ...
}()

lockTwo.Lock();
lockOne.Lock();

这是计算机科学经典问题“死锁”的例子。 从 Docker 的角度来看进程仍然在运行,但是从应用程序的角度代码被死锁,永远不会再正确响应。

为解决这个问题,Kubernetes 支持应用层次的健康检查。 这些检查由 Kubelet 执行,确保应用程序运行的“正确性”。

目前有三种类型的应用健康检查可供选择:

  • HTTP 健康检查 - Kubelet 调用 web hook。如果返回 200-399 之间的状态码,则认为成功。反之则失败。
  • Container Exec - Kubelet 在容器内执行命令。如果退出状态码为 0 则认为成功。
  • TCP Socket - Kubelet 尝试向容器建立一个 socket。如果能建立成功,则任务是健康的,否则认为失败。

任何情况,只要 Kubelet 发现容器故障,都会重启它。

容器健康检查在容器配置文件的 livenessProbe 部分配置。 还可以指定 initialDelaySeconds,即服务启动最初的一个宽限期,使容器执行必要的初始化后才执行健康检查。

以下是包含 HTTP 健康检查的示例:

apiVersion: v1
kind: Pod
metadata:
  name: pod-with-healthcheck
spec:
  containers:
  - name: nginx
    image: nginx
    # defines the health checking
    livenessProbe:
      # an http probe
      httpGet:
        path: /_status/healthz
        port: 80
      # length of time to wait for a pod to initialize
      # after pod startup, before applying health checking
      initialDelaySeconds: 30
      timeoutSeconds: 1
    ports:
    - containerPort: 80

Container Probes