From 9ee0e9c07719243c227368c5d07fbffeb93f1eb2 Mon Sep 17 00:00:00 2001 From: Qiming Teng Date: Sun, 14 Feb 2021 20:42:47 +0800 Subject: [PATCH] [zh] Sync changes that remove logging solutions --- .../cluster-administration/logging.md | 290 ++++---- .../events-stackdriver.md | 156 ----- .../logging-elasticsearch-kibana.md | 197 ------ .../stateful-application/zookeeper.md | 652 ++++++++++-------- 4 files changed, 489 insertions(+), 806 deletions(-) delete mode 100644 content/zh/docs/tasks/debug-application-cluster/events-stackdriver.md delete mode 100644 content/zh/docs/tasks/debug-application-cluster/logging-elasticsearch-kibana.md diff --git a/content/zh/docs/concepts/cluster-administration/logging.md b/content/zh/docs/concepts/cluster-administration/logging.md index 2a820144be..44689dcac3 100644 --- a/content/zh/docs/concepts/cluster-administration/logging.md +++ b/content/zh/docs/concepts/cluster-administration/logging.md @@ -14,44 +14,43 @@ weight: 60 应用日志可以让你了解应用内部的运行状况。日志对调试问题和监控集群活动非常有用。 -大部分现代化应用都有某种日志记录机制;同样地,大多数容器引擎也被设计成支持某种日志记录机制。 -针对容器化应用,最简单且受欢迎的日志记录方式就是写入标准输出和标准错误流。 +大部分现代化应用都有某种日志记录机制。同样地,容器引擎也被设计成支持日志记录。 +针对容器化应用,最简单且最广泛采用的日志记录方式就是写入标准输出和标准错误流。 -但是,由容器引擎或运行时提供的原生功能通常不足以满足完整的日志记录方案。 -例如,如果发生容器崩溃、Pod 被逐出或节点宕机等情况,你仍然想访问到应用日志。 -因此,日志应该具有独立的存储和生命周期,与节点、Pod 或容器的生命周期相独立。 -这个概念叫 _集群级的日志_ 。集群级日志方案需要一个独立的后台来存储、分析和查询日志。 -Kubernetes 没有为日志数据提供原生存储方案,但是你可以集成许多现有的日志解决方案到 Kubernetes 集群中。 +但是,由容器引擎或运行时提供的原生功能通常不足以构成完整的日志记录方案。 +例如,如果发生容器崩溃、Pod 被逐出或节点宕机等情况,你可能想访问应用日志。 +在集群中,日志应该具有独立的存储和生命周期,与节点、Pod 或容器的生命周期相独立。 +这个概念叫 _集群级的日志_ 。 -集群级日志架构假定在集群内部或者外部有一个日志后台。 -如果你对集群级日志不感兴趣,你仍会发现关于如何在节点上存储和处理日志的描述对你是有用的。 +集群级日志架构需要一个独立的后端用来存储、分析和查询日志。 +Kubernetes 并不为日志数据提供原生的存储解决方案。 +相反,有很多现成的日志方案可以集成到 Kubernetes 中. +下面各节描述如何在节点上处理和存储日志。 ## Kubernetes 中的基本日志记录 -本节,你会看到一个kubernetes 中生成基本日志的例子,该例子中数据被写入到标准输出。 -这里的示例为包含一个容器的 Pod 规约,该容器每秒钟向标准输出写入数据。 +这里的示例使用包含一个容器的 Pod 规约,每秒钟向标准输出写入数据。 {{< codenew file="debug/counter-pod.yaml" >}} @@ -76,7 +75,7 @@ pod/counter created -使用 `kubectl logs` 命令获取日志: +像下面这样,使用 `kubectl logs` 命令获取日志: ```shell kubectl logs counter @@ -95,10 +94,10 @@ The output is: ``` -一旦发生容器崩溃,你可以使用命令 `kubectl logs` 和参数 `--previous` 检索之前的容器日志。 -如果 pod 中有多个容器,你应该向该命令附加一个容器名以访问对应容器的日志。 +你可以使用命令 `kubectl logs --previous` 检索之前容器实例的日志。 +如果 Pod 中有多个容器,你应该为该命令附加容器名以访问对应容器的日志。 详见 [`kubectl logs` 文档](/docs/reference/generated/kubectl/kubectl-commands#logs)。 容器化应用写入 `stdout` 和 `stderr` 的任何数据,都会被容器引擎捕获并被重定向到某个位置。 例如,Docker 容器引擎将这两个输出流重定向到某个 -[日志驱动](https://docs.docker.com/engine/admin/logging/overview) , +[日志驱动(Logging Driver)](https://docs.docker.com/engine/admin/logging/overview) , 该日志驱动在 Kubernetes 中配置为以 JSON 格式写入文件。 -节点级日志记录中,需要重点考虑实现日志的轮转,以此来保证日志不会消耗节点上所有的可用空间。 -Kubernetes 当前并不负责轮转日志,而是通过部署工具建立一个解决问题的方案。 -例如,在 Kubernetes 集群中,用 `kube-up.sh` 部署一个每小时运行的工具 -[`logrotate`](https://linux.die.net/man/8/logrotate)。 -你也可以设置容器 runtime 来自动地轮转应用日志,比如使用 Docker 的 `log-opt` 选项。 -在 `kube-up.sh` 脚本中,使用后一种方式来处理 GCP 上的 COS 镜像,而使用前一种方式来处理其他环境。 -这两种方式,默认日志超过 10MB 大小时都会触发日志轮转。 +节点级日志记录中,需要重点考虑实现日志的轮转,以此来保证日志不会消耗节点上全部可用空间。 +Kubernetes 并不负责轮转日志,而是通过部署工具建立一个解决问题的方案。 +例如,在用 `kube-up.sh` 部署的 Kubernetes 集群中,存在一个 +[`logrotate`](https://linux.die.net/man/8/logrotate),每小时运行一次。 +你也可以设置容器运行时来自动地轮转应用日志。 例如,你可以找到关于 `kube-up.sh` 为 GCP 环境的 COS 镜像设置日志的详细信息, -相应的脚本在 -[这里](https://github.com/kubernetes/kubernetes/blob/{{< param "githubbranch" >}}/cluster/gce/gci/configure-helper.sh)。 +脚本为 +[`configure-helper` 脚本](https://github.com/kubernetes/kubernetes/blob/{{< param "githubbranch" >}}/cluster/gce/gci/configure-helper.sh)。 当运行 [`kubectl logs`](/docs/reference/generated/kubectl/kubectl-commands#logs) 时, 节点上的 kubelet 处理该请求并直接读取日志文件,同时在响应中返回日志文件内容。 {{< note >}} -当前,如果有其他系统机制执行日志轮转,那么 `kubectl logs` 仅可查询到最新的日志内容。 -比如,一个 10MB 大小的文件,通过`logrotate` 执行轮转后生成两个文件,一个 10MB 大小, -一个为空,所以 `kubectl logs` 将返回空。 +如果有外部系统执行日志轮转,那么 `kubectl logs` 仅可查询到最新的日志内容。 +比如,对于一个 10MB 大小的文件,通过 `logrotate` 执行轮转后生成两个文件, +一个 10MB 大小,一个为空,`kubectl logs` 返回最新的日志文件,而该日志文件 +在这个例子中为空。 {{< /note >}} * 在容器中运行的 kube-scheduler 和 kube-proxy。 -* 不在容器中运行的 kubelet 和容器运行时(例如 Docker)。 +* 不在容器中运行的 kubelet 和容器运行时。 -在使用 systemd 机制的服务器上,kubelet 和容器 runtime 写入日志到 journald。 -如果没有 systemd,他们写入日志到 `/var/log` 目录的 `.log` 文件。 -容器中的系统组件通常将日志写到 `/var/log` 目录,绕过了默认的日志机制。他们使用 -[klog](https://github.com/kubernetes/klog) 日志库。 -你可以在[日志开发文档](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-instrumentation/logging.md)找到这些组件的日志告警级别协议。 +在使用 systemd 机制的服务器上,kubelet 和容器容器运行时将日志写入到 journald 中。 +如果没有 systemd,它们将日志写入到 `/var/log` 目录下的 `.log` 文件中。 +容器中的系统组件通常将日志写到 `/var/log` 目录,绕过了默认的日志机制。 +他们使用 [klog](https://github.com/kubernetes/klog) 日志库。 +你可以在[日志开发文档](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-instrumentation/logging.md) +找到这些组件的日志告警级别约定。 和容器日志类似,`/var/log` 目录中的系统组件日志也应该被轮转。 -通过脚本 `kube-up.sh` 启动的 Kubernetes 集群中,日志被工具 `logrotate` 执行每日轮转, -或者日志大小超过 100MB 时触发轮转。 +通过脚本 `kube-up.sh` 启动的 Kubernetes 集群中,日志被工具 `logrotate` +执行每日轮转,或者日志大小超过 100MB 时触发轮转。 ## 集群级日志架构 -虽然Kubernetes没有为集群级日志记录提供原生的解决方案,但你可以考虑几种常见的方法。以下是一些选项: +虽然Kubernetes没有为集群级日志记录提供原生的解决方案,但你可以考虑几种常见的方法。 +以下是一些选项: * 使用在每个节点上运行的节点级日志记录代理。 -* 在应用程序的 pod 中,包含专门记录日志的 sidecar 容器。 +* 在应用程序的 Pod 中,包含专门记录日志的边车(Sidecar)容器。 * 将日志直接从应用程序中推送到日志记录后端。 -由于日志记录代理必须在每个节点上运行,它可以用 DaemonSet 副本,Pod 或 本机进程来实现。 -然而,后两种方法被弃用并且非常不别推荐。 +由于日志记录代理必须在每个节点上运行,通常可以用 `DaemonSet` 的形式运行该代理。 +节点级日志在每个节点上仅创建一个代理,不需要对节点上的应用做修改。 -对于 Kubernetes 集群来说,使用节点级的日志代理是最常用和被推荐的方式, -因为在每个节点上仅创建一个代理,并且不需要对节点上的应用做修改。 -但是,节点级的日志 _仅适用于应用程序的标准输出和标准错误输出_。 +容器向标准输出和标准错误输出写出数据,但在格式上并不统一。 +节点级代理 +收集这些日志并将其进行转发以完成汇总。 -Kubernetes 并不指定日志代理,但是有两个可选的日志代理与 Kubernetes 发行版一起发布。 -[Stackdriver 日志](/zh/docs/tasks/debug-application-cluster/logging-stackdriver/) -适用于 Google Cloud Platform,和 -[Elasticsearch](/zh/docs/tasks/debug-application-cluster/logging-elasticsearch-kibana/)。 -你可以在专门的文档中找到更多的信息和说明。 -两者都使用 [fluentd](https://www.fluentd.org/) 与自定义配置作为节点上的代理。 - - -### 使用 sidecar 容器和日志代理 +### 使用 sidecar 容器运行日志代理 {#sidecar-container-with-logging-agent} -你可以通过以下方式之一使用 sidecar 容器: +你可以通过以下方式之一使用边车(Sidecar)容器: -* sidecar 容器将应用程序日志传送到自己的标准输出。 -* sidecar 容器运行一个日志代理,配置该日志代理以便从应用容器收集日志。 +* 边车容器将应用程序日志传送到自己的标准输出。 +* 边车容器运行一个日志代理,配置该日志代理以便从应用容器收集日志。 #### 传输数据流的 sidecar 容器 -![数据流容器的 Sidecar 容器](/images/docs/user-guide/logging/logging-with-streaming-sidecar.png) +![带数据流容器的边车容器](/images/docs/user-guide/logging/logging-with-streaming-sidecar.png) -利用 sidecar 容器向自己的 `stdout` 和 `stderr` 传输流的方式, +利用边车容器向自己的 `stdout` 和 `stderr` 传输流的方式, 你就可以利用每个节点上的 kubelet 和日志代理来处理日志。 -sidecar 容器从文件、套接字或 journald 读取日志。 -每个 sidecar 容器打印其自己的 `stdout` 和 `stderr` 流。 +边车容器从文件、套接字或 journald 读取日志。 +每个边车容器向自己的 `stdout` 和 `stderr` 流中输出日志。 -考虑接下来的例子。pod 的容器向两个文件写不同格式的日志,下面是这个 pod 的配置文件: +例如,某 Pod 中运行一个容器,该容器向两个文件写不同格式的日志。 +下面是这个 pod 的配置文件: {{< codenew file="admin/logging/two-files-counter-pod.yaml" >}} -在同一个日志流中有两种不同格式的日志条目,这有点混乱,即使你试图重定向它们到容器的 `stdout` 流。 -取而代之的是,你可以引入两个 sidecar 容器。 -每一个 sidecar 容器可以从共享卷跟踪特定的日志文件,并重定向文件内容到各自的 `stdout` 流。 +不建议在同一个日志流中写入不同格式的日志条目,即使你成功地将其重定向到容器的 +`stdout` 流。相反,你可以创建两个边车容器。每个边车容器可以从共享卷 +跟踪特定的日志文件,并将文件内容重定向到各自的 `stdout` 流。 -这是运行两个 sidecar 容器的 Pod 文件。 +下面是运行两个边车容器的 Pod 的配置文件: {{< codenew file="admin/logging/two-files-counter-pod-streaming-sidecar.yaml" >}} @@ -358,12 +350,18 @@ Here's a configuration file for a pod that has two sidecar containers: Now when you run this pod, you can access each log stream separately by running the following commands: --> -现在当你运行这个 Pod 时,你可以分别地访问每一个日志流,运行如下命令: +现在当你运行这个 Pod 时,你可以运行如下命令分别访问每个日志流: ```shell kubectl logs counter count-log-1 ``` -``` + + +输出为: + +```console 0: Mon Jan 1 00:00:00 UTC 2001 1: Mon Jan 1 00:00:01 UTC 2001 2: Mon Jan 1 00:00:02 UTC 2001 @@ -373,7 +371,13 @@ kubectl logs counter count-log-1 ```shell kubectl logs counter count-log-2 ``` -``` + + +输出为: + +```console Mon Jan 1 00:00:00 UTC 2001 INFO 0 Mon Jan 1 00:00:01 UTC 2001 INFO 1 Mon Jan 1 00:00:02 UTC 2001 INFO 2 @@ -385,7 +389,8 @@ The node-level agent installed in your cluster picks up those log streams automatically without any further configuration. If you like, you can configure the agent to parse log lines depending on the source container. --> -集群中安装的节点级代理会自动获取这些日志流,而无需进一步配置。如果你愿意,你可以配置代理程序来解析源容器的日志行。 +集群中安装的节点级代理会自动获取这些日志流,而无需进一步配置。 +如果你愿意,你也可以配置代理程序来解析源容器的日志行。 -注意,尽管 CPU 和内存使用率都很低(以多个 cpu millicores 指标排序或者按内存的兆字节排序), +注意,尽管 CPU 和内存使用率都很低(以多个 CPU 毫核指标排序或者按内存的兆字节排序), 向文件写日志然后输出到 `stdout` 流仍然会成倍地增加磁盘使用率。 -如果你的应用向单一文件写日志,通常最好设置 `/dev/stdout` 作为目标路径,而不是使用流式的 sidecar 容器方式。 +如果你的应用向单一文件写日志,通常最好设置 `/dev/stdout` 作为目标路径, +而不是使用流式的边车容器方式。 -应用本身如果不具备轮转日志文件的功能,可以通过 sidecar 容器实现。 -该方式的一个例子是运行一个定期轮转日志的容器。 -然而,还是推荐直接使用 `stdout` 和 `stderr`,将日志的轮转和保留策略交给 kubelet。 +应用本身如果不具备轮转日志文件的功能,可以通过边车容器实现。 +该方式的一个例子是运行一个小的、定期轮转日志的容器。 +然而,还是推荐直接使用 `stdout` 和 `stderr`,将日志的轮转和保留策略 +交给 kubelet。 -### 具有日志代理功能的 sidecar 容器 +### 具有日志代理功能的边车容器 -![日志记录代理功能的 sidecar 容器](/images/docs/user-guide/logging/logging-with-sidecar-agent.png) +![含日志代理的边车容器](/images/docs/user-guide/logging/logging-with-sidecar-agent.png) -如果节点级日志记录代理程序对于你的场景来说不够灵活,你可以创建一个带有单独日志记录代理程序的 -sidecar 容器,将代理程序专门配置为与你的应用程序一起运行。 +如果节点级日志记录代理程序对于你的场景来说不够灵活,你可以创建一个 +带有单独日志记录代理的边车容器,将代理程序专门配置为与你的应用程序一起运行。 - -{{< note >}} -在 sidecar 容器中使用日志代理会导致严重的资源损耗。 +在边车容器中使用日志代理会带来严重的资源损耗。 此外,你不能使用 `kubectl logs` 命令访问日志,因为日志并没有被 kubelet 管理。 {{< /note >}} -例如,你可以使用 [Stackdriver](/zh/docs/tasks/debug-application-cluster/logging-stackdriver/), -它使用 fluentd 作为日志记录代理。 -以下是两个可用于实现此方法的配置文件。 -第一个文件包含配置 fluentd 的 +下面是两个配置文件,可以用来实现一个带日志代理的边车容器。 +第一个文件包含用来配置 fluentd 的 [ConfigMap](/zh/docs/tasks/configure-pod-container/configure-pod-configmap/)。 {{< codenew file="admin/logging/fluentd-sidecar-config.yaml" >}} +{{< note >}} -{{< note >}} -配置 fluentd 超出了本文的范围。要进一步了解如何配置 fluentd, -请参考 [fluentd 官方文档](https://docs.fluentd.org/). +要进一步了解如何配置 fluentd,请参考 [fluentd 官方文档](https://docs.fluentd.org/). {{< /note >}} -第二个文件描述了运行 fluentd sidecar 容器的 Pod 。flutend 通过 Pod 的挂载卷获取它的配置数据。 +第二个文件描述了运行 fluentd 边车容器的 Pod 。 +flutend 通过 Pod 的挂载卷获取它的配置数据。 {{< codenew file="admin/logging/two-files-counter-pod-agent-sidecar.yaml" >}} -一段时间后,你可以在 Stackdriver 界面看到日志消息。 - - -记住,这只是一个例子,事实上你可以用任何一个日志代理替换 fluentd ,并从应用容器中读取任何资源。 +在示例配置中,你可以将 fluentd 替换为任何日志代理,从应用容器内 +的任何来源读取数据。 - ### 从应用中直接暴露日志目录 ![直接从应用程序暴露日志](/images/docs/user-guide/logging/logging-from-application.png) -通过暴露或推送每个应用的日志,你可以实现集群级日志记录; -然而,这种日志记录机制的实现已超出 Kubernetes 的范围。 - +从各个应用中直接暴露和推送日志数据的集群日志机制 +已超出 Kubernetes 的范围。 diff --git a/content/zh/docs/tasks/debug-application-cluster/events-stackdriver.md b/content/zh/docs/tasks/debug-application-cluster/events-stackdriver.md deleted file mode 100644 index 04a20a7174..0000000000 --- a/content/zh/docs/tasks/debug-application-cluster/events-stackdriver.md +++ /dev/null @@ -1,156 +0,0 @@ ---- -content_type: concept -title: StackDriver 中的事件 ---- - - - - - - - -Kubernetes 事件是一种对象,它为用户提供了洞察集群内发生的事情的能力, -例如调度程序做出了什么决定,或者为什么某些 Pod 被逐出节点。 -你可以在[应用程序自检和调试](/zh/docs/tasks/debug-application-cluster/debug-application-introspection/) -中阅读有关使用事件调试应用程序的更多信息。 - - -因为事件是 API 对象,所以它们存储在主控节点上的 API 服务器中。 -为了避免主节点磁盘空间被填满,将强制执行保留策略:事件在最后一次发生的一小时后将会被删除。 -为了提供更长的历史记录和聚合能力,应该安装第三方解决方案来捕获事件。 - - -本文描述了一个将 Kubernetes 事件导出为 Stackdriver Logging 的解决方案,在这里可以对它们进行处理和分析。 - - -{{< note >}} -不能保证集群中发生的所有事件都将导出到 Stackdriver。 -事件不能导出的一种可能情况是事件导出器没有运行(例如,在重新启动或升级期间)。 -在大多数情况下,可以将事件用于设置 -[metrics](https://cloud.google.com/logging/docs/view/logs_based_metrics) 和 -[alerts](https://cloud.google.com/logging/docs/view/logs_based_metrics#creating_an_alerting_policy) -等目的,但你应该注意其潜在的不准确性。 -{{< /note >}} - - - - -## 部署 {#deployment} - -### Google Kubernetes Engine - - - -在 Google Kubernetes Engine 中,如果启用了云日志,那么事件导出器默认部署在主节点运行版本为 1.7 及更高版本的集群中。 -为了防止干扰你的工作负载,事件导出器没有设置资源,并且处于尽力而为的 QoS 类型中,这意味着它将在资源匮乏的情况下第一个被杀死。 -如果要导出事件,请确保有足够的资源给事件导出器 Pod 使用。 -这可能会因为工作负载的不同而有所不同,但平均而言,需要大约 100MB 的内存和 100m 的 CPU。 - - -### 部署到现有集群 - -使用下面的命令将事件导出器部署到你的集群: - -```shell -kubectl create -f https://k8s.io/examples/debug/event-exporter.yaml -``` - - - -由于事件导出器访问 Kubernetes API,因此它需要权限才能访问。 -以下的部署配置为使用 RBAC 授权。 -它设置服务帐户和集群角色绑定,以允许事件导出器读取事件。 -为了确保事件导出器 Pod 不会从节点中退出,你可以另外设置资源请求。 -如前所述,100MB 内存和 100m CPU 应该就足够了。 - -{{< codenew file="debug/event-exporter.yaml" >}} - - -## 用户指南 {#user-guide} - -事件在 Stackdriver Logging 中被导出到 `GKE Cluster` 资源。 -你可以通过从可用资源的下拉菜单中选择适当的选项来找到它们: - - -Stackdriver 日志接口中事件的位置 - - -你可以使用 Stackdriver Logging 的 -[过滤机制](https://cloud.google.com/logging/docs/view/advanced_filters) -基于事件对象字段进行过滤。 -例如,下面的查询将显示调度程序中有关 Deployment `nginx-deployment` 中的 Pod 的事件: - -``` -resource.type="gke_cluster" -jsonPayload.kind="Event" -jsonPayload.source.component="default-scheduler" -jsonPayload.involvedObject.name:"nginx-deployment" -``` - -{{< figure src="/images/docs/stackdriver-event-exporter-filter.png" alt="在 Stackdriver 接口中过滤的事件" width="500" >}} - - diff --git a/content/zh/docs/tasks/debug-application-cluster/logging-elasticsearch-kibana.md b/content/zh/docs/tasks/debug-application-cluster/logging-elasticsearch-kibana.md deleted file mode 100644 index 2dbabad039..0000000000 --- a/content/zh/docs/tasks/debug-application-cluster/logging-elasticsearch-kibana.md +++ /dev/null @@ -1,197 +0,0 @@ ---- -content_type: concept -title: 使用 ElasticSearch 和 Kibana 进行日志管理 ---- - - - - - - -在 Google Compute Engine (GCE) 平台上,默认的日志管理支持目标是 -[Stackdriver Logging](https://cloud.google.com/logging/), -在[使用 Stackdriver Logging 管理日志](/zh/docs/tasks/debug-application-cluster/logging-stackdriver/) -中详细描述了这一点。 - - -本文介绍了如何设置一个集群,将日志导入 -[Elasticsearch](https://www.elastic.co/products/elasticsearch),并使用 -[Kibana](https://www.elastic.co/products/kibana) 查看日志,作为在 GCE 上 -运行应用时使用 Stackdriver Logging 管理日志的替代方案。 - - -{{< note >}} -你不能在 Google Kubernetes Engine 平台运行的 Kubernetes 集群上自动部署 -Elasticsearch 和 Kibana。你必须手动部署它们。 -{{< /note >}} - - - - -要使用 Elasticsearch 和 Kibana 处理集群日志,你应该在使用 kube-up.sh -脚本创建集群时设置下面所示的环境变量: - -```shell -KUBE_LOGGING_DESTINATION=elasticsearch -``` - - -你还应该确保设置了 `KUBE_ENABLE_NODE_LOGGING=true` (这是 GCE 平台的默认设置)。 - - -现在,当你创建集群时,将有一条消息将指示每个节点上运行的 fluentd 日志收集守护进程 -以 ElasticSearch 为日志输出目标: - -```shell -cluster/kube-up.sh -``` - -``` -... -Project: kubernetes-satnam -Zone: us-central1-b -... calling kube-up -Project: kubernetes-satnam -Zone: us-central1-b -+++ Staging server tars to Google Storage: gs://kubernetes-staging-e6d0e81793/devel -+++ kubernetes-server-linux-amd64.tar.gz uploaded (sha1 = 6987c098277871b6d69623141276924ab687f89d) -+++ kubernetes-salt.tar.gz uploaded (sha1 = bdfc83ed6b60fa9e3bff9004b542cfc643464cd0) -Looking for already existing resources -Starting master and configuring firewalls -Created [https://www.googleapis.com/compute/v1/projects/kubernetes-satnam/zones/us-central1-b/disks/kubernetes-master-pd]. -NAME ZONE SIZE_GB TYPE STATUS -kubernetes-master-pd us-central1-b 20 pd-ssd READY -Created [https://www.googleapis.com/compute/v1/projects/kubernetes-satnam/regions/us-central1/addresses/kubernetes-master-ip]. -+++ Logging using Fluentd to elasticsearch -``` - - -每个节点的 Fluentd Pod、Elasticsearch Pod 和 Kibana Pod 都应该在集群启动后不久运行在 -kube-system 名字空间中。 - -```shell -kubectl get pods --namespace=kube-system -``` - -``` -NAME READY STATUS RESTARTS AGE -elasticsearch-logging-v1-78nog 1/1 Running 0 2h -elasticsearch-logging-v1-nj2nb 1/1 Running 0 2h -fluentd-elasticsearch-kubernetes-node-5oq0 1/1 Running 0 2h -fluentd-elasticsearch-kubernetes-node-6896 1/1 Running 0 2h -fluentd-elasticsearch-kubernetes-node-l1ds 1/1 Running 0 2h -fluentd-elasticsearch-kubernetes-node-lz9j 1/1 Running 0 2h -kibana-logging-v1-bhpo8 1/1 Running 0 2h -kube-dns-v3-7r1l9 3/3 Running 0 2h -monitoring-heapster-v4-yl332 1/1 Running 1 2h -monitoring-influx-grafana-v1-o79xf 2/2 Running 0 2h -``` - - -`fluentd-elasticsearch` Pod 从每个节点收集日志并将其发送到 `elasticsearch-logging` Pod, -该 Pod 是名为 `elasticsearch-logging` 的 -[服务](/zh/docs/concepts/services-networking/service/)的一部分。 -这些 ElasticSearch pod 存储日志,并通过 REST API 将其公开。 -`kibana-logging` pod 提供了一个用于读取 ElasticSearch 中存储的日志的 Web UI, -它是名为 `kibana-logging` 的服务的一部分。 - - - -Elasticsearch 和 Kibana 服务都位于 `kube-system` 名字空间中,并且没有通过 -可公开访问的 IP 地址直接暴露。要访问它们,请参照 -[访问集群中运行的服务](/zh/docs/tasks/access-application-cluster/access-cluster/#accessing-services-running-on-the-cluster) -的说明进行操作。 - - -如果你想在浏览器中访问 `elasticsearch-logging` 服务,你将看到类似下面的状态页面: - -![Elasticsearch Status](/images/docs/es-browser.png) - - -现在你可以直接在浏览器中输入 Elasticsearch 查询,如果你愿意的话。 -请参考 [Elasticsearch 的文档](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-uri-request.html) -以了解这样做的更多细节。 - - - -或者,你可以使用 Kibana 查看集群的日志(再次使用 -[访问集群中运行的服务的说明](/zh/docs/tasks/access-application-cluster/access-cluster/#accessing-services-running-on-the-cluster))。 -第一次访问 Kibana URL 时,将显示一个页面,要求你配置所接收日志的视图。 -选择时间序列值的选项,然后选择 `@timestamp`。 -在下面的页面中选择 `Discover` 选项卡,然后你应该能够看到所摄取的日志。 -你可以将刷新间隔设置为 5 秒,以便定期刷新日志。 - - - -以下是从 Kibana 查看器中摄取日志的典型视图: - -![Kibana logs](/images/docs/kibana-logs.png) - -## {{% heading "whatsnext" %}} - - -Kibana 为浏览你的日志提供了各种强大的选项!有关如何深入研究它的一些想法, -请查看 [Kibana 的文档](https://www.elastic.co/guide/en/kibana/current/discover.html)。 - diff --git a/content/zh/docs/tutorials/stateful-application/zookeeper.md b/content/zh/docs/tutorials/stateful-application/zookeeper.md index 500259f5fa..3f5baf3c27 100644 --- a/content/zh/docs/tutorials/stateful-application/zookeeper.md +++ b/content/zh/docs/tutorials/stateful-application/zookeeper.md @@ -1,5 +1,10 @@ --- -approvers: +title: 运行 ZooKeeper,一个分布式协调系统 +content_type: tutorial +weight: 40 +--- + @@ -20,8 +25,11 @@ Kubernetes using [StatefulSets](/docs/concepts/workloads/controllers/statefulset [PodDisruptionBudgets](/docs/concepts/workloads/pods/disruptions/#pod-disruption-budget), and [PodAntiAffinity](/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity). --> - -本教程展示了在 Kubernetes 上使用 [StatefulSets](/zh/docs/concepts/workloads/controllers/statefulset/),[PodDisruptionBudgets](/zh/docs/concepts/workloads/pods/disruptions/#pod-disruption-budget) 和 [PodAntiAffinity](/zh/docs/concepts/scheduling-eviction/assign-pod-node/#亲和与反亲和) 特性运行 [Apache Zookeeper](https://zookeeper.apache.org)。 +本教程展示了在 Kubernetes 上使用 +[StatefulSet](/zh/docs/concepts/workloads/controllers/statefulset/), +[PodDisruptionBudget](/zh/docs/concepts/workloads/pods/disruptions/#pod-disruption-budget) 和 +[PodAntiAffinity](/zh/docs/concepts/scheduling-eviction/assign-pod-node/#亲和与反亲和) +特性运行 [Apache Zookeeper](https://zookeeper.apache.org)。 ## {{% heading "prerequisites" %}} @@ -29,44 +37,45 @@ and [PodAntiAffinity](/docs/concepts/scheduling-eviction/assign-pod-node/#affini Before starting this tutorial, you should be familiar with the following Kubernetes concepts. --> - 在开始本教程前,你应该熟悉以下 Kubernetes 概念。 -- [Pods](/zh/docs/concepts/workloads/pods/) -- [Cluster DNS](/zh/docs/concepts/services-networking/dns-pod-service/) -- [Headless Services](/zh/docs/concepts/services-networking/service/#headless-services) -- [PersistentVolumes](/zh/docs/concepts/storage/persistent-volumes/) -- [PersistentVolume Provisioning](https://github.com/kubernetes/examples/tree/{{< param "githubbranch" >}}/staging/persistent-volume-provisioning/) -- [StatefulSets](/zh/docs/concepts/workloads/controllers/statefulset/) -- [PodDisruptionBudgets](/zh/docs/concepts/workloads/pods/disruptions/#pod-disruption-budget) -- [PodAntiAffinity](/zh/docs/concepts/scheduling-eviction/assign-pod-node/#亲和与反亲和) -- [kubectl CLI](/zh/docs/reference/kubectl/kubectl/) +- [Pods](/zh/docs/concepts/workloads/pods/) +- [集群 DNS](/zh/docs/concepts/services-networking/dns-pod-service/) +- [无头服务(Headless Service)](/zh/docs/concepts/services-networking/service/#headless-services) +- [PersistentVolumes](/zh/docs/concepts/storage/persistent-volumes/) +- [PersistentVolume 制备](https://github.com/kubernetes/examples/tree/{{< param "githubbranch" >}}/staging/persistent-volume-provisioning/) +- [StatefulSet](/zh/docs/concepts/workloads/controllers/statefulset/) +- [PodDisruptionBudget](/zh/docs/concepts/workloads/pods/disruptions/#pod-disruption-budget) +- [PodAntiAffinity](/zh/docs/concepts/scheduling-eviction/assign-pod-node/#亲和与反亲和) +- [kubectl CLI](/zh/docs/reference/kubectl/kubectl/) +你需要一个至少包含四个节点的集群,每个节点至少 2 CPUs 和 4 GiB 内存。 +在本教程中你将会隔离(Cordon)和腾空(Drain )集群的节点。 +**这意味着集群节点上所有的 Pods 将会被终止并移除。这些节点也会暂时变为不可调度**。 +在本教程中你应该使用一个独占的集群,或者保证你造成的干扰不会影响其它租户。 + - -你需要一个至少包含四个节点的集群,每个节点至少 2 CPUs 和 4 GiB 内存。在本教程中你将会 cordon 和 drain 集群的节点。**这意味着集群节点上所有的 Pods 将会被终止并移除**。**这些节点也会暂时变为不可调度**。在本教程中你应该使用一个独占的集群,或者保证你造成的干扰不会影响其它租户。 - -本教程假设你的集群配置为动态的提供 PersistentVolumes。如果你的集群没有配置成这样,在开始本教程前,你需要手动准备三个 20 GiB 的卷。 - +本教程假设你的集群配置为动态的提供 PersistentVolumes。 +如果你的集群没有配置成这样,在开始本教程前,你需要手动准备三个 20 GiB 的卷。 ## {{% heading "objectives" %}} - 在学习本教程后,你将熟悉下列内容。 * 如何使用 StatefulSet 部署一个 ZooKeeper ensemble。 @@ -74,11 +83,10 @@ After this tutorial, you will know the following. * 如何在 ensemble 中 分布 ZooKeeper 服务器的部署。 * 如何在计划维护中使用 PodDisruptionBudgets 确保服务可用性。 - +### ZooKeeper {#zookeeper-basics} +[Apache ZooKeeper](https://zookeeper.apache.org/doc/current/) +是一个分布式的开源协调服务,用于分布式系统。 +ZooKeeper 允许你读取、写入数据和发现数据更新。 +数据按层次结构组织在文件系统中,并复制到 ensemble(一个 ZooKeeper 服务器的集合) +中所有的 ZooKeeper 服务器。对数据的所有操作都是原子的和顺序一致的。 +ZooKeeper 通过 +[Zab](https://pdfs.semanticscholar.org/b02c/6b00bd5dbdbd951fddb00b906c82fa80f0b3.pdf) +一致性协议在 ensemble 的所有服务器之间复制一个状态机来确保这个特性。 + + +Ensemble 使用 Zab 协议选举一个领导者,在选举出领导者前不能写入数据。 +一旦选举出了领导者,ensemble 使用 Zab 保证所有写入被复制到一个 quorum, +然后这些写入操作才会被确认并对客户端可用。 +如果没有遵照加权 quorums,一个 quorum 表示包含当前领导者的 ensemble 的多数成员。 +例如,如果 ensemble 有 3 个服务器,一个包含领导者的成员和另一个服务器就组成了一个 +quorum。 +如果 ensemble 不能达成一个 quorum,数据将不能被写入。 + - -### ZooKeeper 基础 - -[Apache ZooKeeper](https://zookeeper.apache.org/doc/current/) 是一个分布式的开源协调服务,用于分布式系统。ZooKeeper 允许你读取、写入数据和发现数据更新。数据按层次结构组织在文件系统中,并复制到 ensemble(一个 ZooKeeper 服务器的集合) 中所有的 ZooKeeper 服务器。对数据的所有操作都是原子的和顺序一致的。ZooKeeper 通过 [Zab](https://pdfs.semanticscholar.org/b02c/6b00bd5dbdbd951fddb00b906c82fa80f0b3.pdf) 一致性协议在 ensemble 的所有服务器之间复制一个状态机来确保这个特性。 - -ensemble 使用 Zab 协议选举一个 leader,在选举出 leader 前不能写入数据。一旦选举出了 leader,ensemble 使用 Zab 保证所有写入被复制到一个 quorum,然后这些写入操作才会被确认并对客户端可用。如果没有遵照加权 quorums,一个 quorum 表示包含当前 leader 的 ensemble 的多数成员。例如,如果 ensemble 有3个服务器,一个包含 leader 的成员和另一个服务器就组成了一个 quorum。如果 ensemble 不能达成一个 quorum,数据将不能被写入。 - -ZooKeeper 在内存中保存它们的整个状态机,但是每个改变都被写入一个在存储介质上的持久 WAL(Write Ahead Log)。当一个服务器故障时,它能够通过回放 WAL 恢复之前的状态。为了防止 WAL 无限制的增长,ZooKeeper 服务器会定期的将内存状态快照保存到存储介质。这些快照能够直接加载到内存中,所有在这个快照之前的 WAL 条目都可以被安全的丢弃。 +ZooKeeper 在内存中保存它们的整个状态机,但是每个改变都被写入一个在存储介质上的 +持久 WAL(Write Ahead Log)。 +当一个服务器出现故障时,它能够通过回放 WAL 恢复之前的状态。 +为了防止 WAL 无限制的增长,ZooKeeper 服务器会定期的将内存状态快照保存到存储介质。 +这些快照能够直接加载到内存中,所有在这个快照之前的 WAL 条目都可以被安全的丢弃。 - ## 创建一个 ZooKeeper Ensemble 下面的清单包含一个 -[Headless Service](/zh/docs/concepts/services-networking/service/#headless-services), +[无头服务](/zh/docs/concepts/services-networking/service/#headless-services), 一个 [Service](/zh/docs/concepts/services-networking/service/), 一个 [PodDisruptionBudget](/zh/docs/concepts/workloads/pods/disruptions/#specifying-a-poddisruptionbudget), 和一个 [StatefulSet](/zh/docs/concepts/workloads/controllers/statefulset/)。 @@ -127,8 +152,8 @@ Open a terminal, and use the [`kubectl apply`](/docs/reference/generated/kubectl/kubectl-commands/#apply) command to create the manifest. --> - -打开一个命令行终端,使用 [`kubectl apply`](/docs/reference/generated/kubectl/kubectl-commands/#apply) +打开一个命令行终端,使用命令 +[`kubectl apply`](/docs/reference/generated/kubectl/kubectl-commands/#apply) 创建这个清单。 ```shell @@ -139,8 +164,8 @@ kubectl apply -f https://k8s.io/examples/application/zookeeper/zookeeper.yaml This creates the `zk-hs` Headless Service, the `zk-cs` Service, the `zk-pdb` PodDisruptionBudget, and the `zk` StatefulSet. --> - -这个操作创建了 `zk-hs` Headless Service、`zk-cs` Service、`zk-pdb` PodDisruptionBudget 和 `zk` StatefulSet。 +这个操作创建了 `zk-hs` 无头服务、`zk-cs` 服务、`zk-pdb` PodDisruptionBudget +和 `zk` StatefulSet。 ``` service/zk-hs created @@ -153,8 +178,9 @@ statefulset.apps/zk created Use [`kubectl get`](/docs/reference/generated/kubectl/kubectl-commands/#get) to watch the StatefulSet controller create the StatefulSet's Pods. --> - -使用 [`kubectl get`](/docs/reference/generated/kubectl/kubectl-commands/#get) 查看 StatefulSet 控制器创建的 Pods。 +使用命令 +[`kubectl get`](/docs/reference/generated/kubectl/kubectl-commands/#get) +查看 StatefulSet 控制器创建的 Pods。 ```shell kubectl get pods -w -l app=zk @@ -163,7 +189,6 @@ kubectl get pods -w -l app=zk - 一旦 `zk-2` Pod 变成 Running 和 Ready 状态,使用 `CRTL-C` 结束 kubectl。 ``` @@ -189,8 +214,8 @@ zk-2 1/1 Running 0 40s The StatefulSet controller creates three Pods, and each Pod has a container with a [ZooKeeper](https://www-us.apache.org/dist/zookeeper/stable/) server. --> - -StatefulSet 控制器创建了3个 Pods,每个 Pod 包含一个 [ZooKeeper](https://www-us.apache.org/dist/zookeeper/stable/) 服务器。 +StatefulSet 控制器创建 3 个 Pods,每个 Pod 包含一个 +[ZooKeeper](https://www-us.apache.org/dist/zookeeper/stable/) 服务器。 +### 促成 Leader 选举 {#facilitating-leader-election} -### 促成 Leader 选举 +由于在匿名网络中没有用于选举 leader 的终止算法,Zab 要求显式的进行成员关系配置, +以执行 leader 选举。Ensemble 中的每个服务器都需要具有一个独一无二的标识符, +所有的服务器均需要知道标识符的全集,并且每个标识符都需要和一个网络地址相关联。 -由于在匿名网络中没有用于选举 leader 的终止算法,Zab 要求显式的进行成员关系配置,以执行 leader 选举。Ensemble 中的每个服务器都需要具有一个独一无二的标识符,所有的服务器均需要知道标识符的全集,并且每个标识符都需要和一个网络地址相关联。 - -使用 [`kubectl exec`](/docs/reference/generated/kubectl/kubectl-commands/#exec) 获取 `zk` StatefulSet 中 Pods 的主机名。 +使用命令 +[`kubectl exec`](/docs/reference/generated/kubectl/kubectl-commands/#exec) +获取 `zk` StatefulSet 中 Pods 的主机名。 ```shell for i in 0 1 2; do kubectl exec zk-$i -- hostname; done @@ -215,8 +243,10 @@ for i in 0 1 2; do kubectl exec zk-$i -- hostname; done The StatefulSet controller provides each Pod with a unique hostname based on its ordinal index. The hostnames take the form of `-`. Because the `replicas` field of the `zk` StatefulSet is set to `3`, the Set's controller creates three Pods with their hostnames set to `zk-0`, `zk-1`, and `zk-2`. --> - -StatefulSet 控制器基于每个 Pod 的序号索引为它们各自提供一个唯一的主机名。主机名采用 `-` 的形式。由于 `zk` StatefulSet 的 `replicas` 字段设置为3,这个 Set 的控制器将创建3个 Pods,主机名为:`zk-0`、`zk-1` 和 `zk-2`。 +StatefulSet 控制器基于每个 Pod 的序号索引为它们各自提供一个唯一的主机名。 +主机名采用 `-<序数索引>` 的形式。 +由于 `zk` StatefulSet 的 `replicas` 字段设置为 3,这个集合的控制器将创建 +3 个 Pods,主机名为:`zk-0`、`zk-1` 和 `zk-2`。 ``` zk-0 @@ -229,8 +259,8 @@ The servers in a ZooKeeper ensemble use natural numbers as unique identifiers, a To examine the contents of the `myid` file for each server use the following command. --> - -ZooKeeper ensemble 中的服务器使用自然数作为唯一标识符,每个服务器的标识符都保存在服务器的数据目录中一个名为 `myid` 的文件里。 +ZooKeeper ensemble 中的服务器使用自然数作为唯一标识符, +每个服务器的标识符都保存在服务器的数据目录中一个名为 `myid` 的文件里。 检查每个服务器的 `myid` 文件的内容。 @@ -241,7 +271,6 @@ for i in 0 1 2; do echo "myid zk-$i";kubectl exec zk-$i -- cat /var/lib/zookeepe - 由于标识符为自然数并且序号索引是非负整数,你可以在序号上加 1 来生成一个标识符。 ``` @@ -256,8 +285,7 @@ myid zk-2 - -获取 `zk` StatefulSet 中每个 Pod 的 FQDN (Fully Qualified Domain Name,正式域名)。 +获取 `zk` StatefulSet 中每个 Pod 的全限定域名(Fully Qualified Domain Name,FQDN)。 ```shell for i in 0 1 2; do kubectl exec zk-$i -- hostname -f; done @@ -267,8 +295,7 @@ for i in 0 1 2; do kubectl exec zk-$i -- hostname -f; done The `zk-hs` Service creates a domain for all of the Pods, `zk-hs.default.svc.cluster.local`. --> - -`zk-hs` Service 为所有 Pods 创建了一个 domain:`zk-hs.default.svc.cluster.local`。 +`zk-hs` Service 为所有 Pods 创建了一个域:`zk-hs.default.svc.cluster.local`。 ``` zk-0.zk-hs.default.svc.cluster.local @@ -281,10 +308,13 @@ The A records in [Kubernetes DNS](/docs/concepts/services-networking/dns-pod-ser ZooKeeper stores its application configuration in a file named `zoo.cfg`. Use `kubectl exec` to view the contents of the `zoo.cfg` file in the `zk-0` Pod. --> +[Kubernetes DNS](/zh/docs/concepts/services-networking/dns-pod-service/) +中的 A 记录将 FQDNs 解析成为 Pods 的 IP 地址。 +如果 Pods 被调度,这个 A 记录将会使用 Pods 的新 IP 地址完成更新, +但 A 记录的名称不会改变。 -[Kubernetes DNS](/zh/docs/concepts/services-networking/dns-pod-service/) 中的 A 记录将 FQDNs 解析成为 Pods 的 IP 地址。如果 Pods 被调度,这个 A 记录将会使用 Pods 的新 IP 地址更新,但 A 记录的名称不会改变。 - -ZooKeeper 在一个名为 `zoo.cfg` 的文件中保存它的应用配置。使用 `kubectl exec` 在 `zk-0` Pod 中查看 `zoo.cfg` 文件的内容。 +ZooKeeper 在一个名为 `zoo.cfg` 的文件中保存它的应用配置。 +使用 `kubectl exec` 在 `zk-0` Pod 中查看 `zoo.cfg` 文件的内容。 ```shell kubectl exec zk-0 -- cat /opt/zookeeper/conf/zoo.cfg @@ -296,8 +326,9 @@ the file, the `1`, `2`, and `3` correspond to the identifiers in the ZooKeeper servers' `myid` files. They are set to the FQDNs for the Pods in the `zk` StatefulSet. --> - -文件底部为 `server.1`、`server.2` 和 `server.3`,其中的 `1`、`2`和`3`分别对应 ZooKeeper 服务器的 `myid` 文件中的标识符。它们被设置为 `zk` StatefulSet 中的 Pods 的 FQDNs。 +文件底部为 `server.1`、`server.2` 和 `server.3`,其中的 `1`、`2` 和 `3` +分别对应 ZooKeeper 服务器的 `myid` 文件中的标识符。 +它们被设置为 `zk` StatefulSet 中的 Pods 的 FQDNs。 ``` clientPort=2181 @@ -317,14 +348,17 @@ server.3=zk-2.zk-hs.default.svc.cluster.local:2888:3888 ``` +### 达成共识 {#achieving-consensus} -### 达成一致 - - 一致性协议要求每个参与者的标识符唯一。在 Zab 协议里任何两个参与者都不应该声明相同的唯一标识符。对于让系统中的进程协商哪些进程已经提交了哪些数据而言,这是必须的。如果有两个 Pods 使用相同的序号启动,这两个 ZooKeeper 服务器会将自己识别为相同的服务器。 + 一致性协议要求每个参与者的标识符唯一。 +在 Zab 协议里任何两个参与者都不应该声明相同的唯一标识符。 +对于让系统中的进程协商哪些进程已经提交了哪些数据而言,这是必须的。 +如果有两个 Pods 使用相同的序号启动,这两个 ZooKeeper 服务器 +会将自己识别为相同的服务器。 ```shell kubectl get pods -w -l app=zk @@ -355,8 +389,10 @@ the FQDNs of the ZooKeeper servers will resolve to a single endpoint, and that endpoint will be the unique ZooKeeper server claiming the identity configured in its `myid` file. --> - -每个 Pod 的 A 记录仅在 Pod 变成 Ready状态时被录入。因此,ZooKeeper 服务器的 FQDNs 只会解析到一个 endpoint,而那个 endpoint 将会是一个唯一的 ZooKeeper 服务器,这个服务器声明了配置在它的 `myid` 文件中的标识符。 +每个 Pod 的 A 记录仅在 Pod 变成 Ready状态时被录入。 +因此,ZooKeeper 服务器的 FQDNs 只会解析到一个端点,而那个端点将会是 +一个唯一的 ZooKeeper 服务器,这个服务器声明了配置在它的 `myid` +文件中的标识符。 ``` zk-0.zk-hs.default.svc.cluster.local @@ -369,7 +405,8 @@ This ensures that the `servers` properties in the ZooKeepers' `zoo.cfg` files represents a correctly configured ensemble. --> -这保证了 ZooKeepers 的 `zoo.cfg` 文件中的 `servers` 属性代表了一个正确配置的 ensemble。 +这保证了 ZooKeepers 的 `zoo.cfg` 文件中的 `servers` 属性代表了 +一个正确配置的 ensemble。 ``` server.1=zk-0.zk-hs.default.svc.cluster.local:2888:3888 @@ -380,8 +417,10 @@ server.3=zk-2.zk-hs.default.svc.cluster.local:2888:3888 - -当服务器使用 Zab 协议尝试提交一个值的时候,它们会达成一致并成功提交这个值(如果 leader 选举成功并且至少有两个 Pods 处于 Running 和 Ready状态),或者将会失败(如果没有满足上述条件中的任意一条)。当一个服务器承认另一个服务器的代写时不会有状态产生。 +当服务器使用 Zab 协议尝试提交一个值的时候,它们会达成一致并成功提交这个值 +(如果领导者选举成功并且至少有两个 Pods 处于 Running 和 Ready状态), +或者将会失败(如果没有满足上述条件中的任意一条)。 +当一个服务器承认另一个服务器的代写时不会有状态产生。 - ### Ensemble 健康检查 -最基本的健康检查是向一个 ZooKeeper 服务器写入一些数据,然后从另一个服务器读取这些数据。 +最基本的健康检查是向一个 ZooKeeper 服务器写入一些数据,然后从 +另一个服务器读取这些数据。 使用 `zkCli.sh` 脚本在 `zk-0` Pod 上写入 `world` 到路径 `/hello`。 @@ -411,8 +450,7 @@ Created /hello - -从 `zk-1` Pod 获取数据。 +使用下面的命令从 `zk-1` Pod 获取数据。 ```shell kubectl exec zk-1 zkCli.sh get /hello @@ -422,8 +460,7 @@ kubectl exec zk-1 zkCli.sh get /hello The data that you created on `zk-0` is available on all the servers in the ensemble. --> - -你在 `zk-0` 创建的数据在 ensemble 中所有的服务器上都是可用的。 +你在 `zk-0` 上创建的数据在 ensemble 中所有的服务器上都是可用的。 ``` WATCHER:: @@ -455,12 +492,15 @@ state machine. Use the [`kubectl delete`](/docs/reference/generated/kubectl/kubectl-commands/#delete) command to delete the `zk` StatefulSet. --> +### 提供持久存储 -### 准备持久存储 +如同在 [ZooKeeper](#zookeeper-basics) 一节所提到的,ZooKeeper 提交 +所有的条目到一个持久 WAL,并周期性的将内存快照写入存储介质。 +对于使用一致性协议实现一个复制状态机的应用来说,使用 WALs 提供持久化 +是一种常用的技术,对于普通的存储应用也是如此。 -如同在 [ZooKeeper 基础](#zookeeper-基础) 一节所提到的,ZooKeeper 提交所有的条目到一个持久 WAL,并周期性的将内存快照写入存储介质。对于使用一致性协议实现一个复制状态机的应用来说,使用 WALs 提供持久化是一种常用的技术,对于普通的存储应用也是如此。 - -使用 [`kubectl delete`](/docs/reference/generated/kubectl/kubectl-commands/#delete) 删除 `zk` StatefulSet。 +使用 [`kubectl delete`](/docs/reference/generated/kubectl/kubectl-commands/#delete) +删除 `zk` StatefulSet。 ```shell kubectl delete statefulset zk @@ -473,7 +513,6 @@ statefulset.apps "zk" deleted - 观察 StatefulSet 中的 Pods 变为终止状态。 ```shell @@ -483,7 +522,6 @@ kubectl get pods -w -l app=zk - 当 `zk-0` 完全终止时,使用 `CRTL-C` 结束 kubectl。 ``` @@ -504,8 +542,7 @@ zk-0 0/1 Terminating 0 11m - -重新应用 `zookeeper.yaml` 中的代码清单。 +重新应用 `zookeeper.yaml` 中的清单。 ```shell kubectl apply -f https://k8s.io/examples/application/zookeeper/zookeeper.yaml @@ -516,7 +553,6 @@ This creates the `zk` StatefulSet object, but the other API objects in the manif Watch the StatefulSet controller recreate the StatefulSet's Pods. --> - `zk` StatefulSet 将会被创建。由于清单中的其他 API 对象已经存在,所以它们不会被修改。 观察 StatefulSet 控制器重建 StatefulSet 的 Pods。 @@ -528,7 +564,6 @@ kubectl get pods -w -l app=zk - 一旦 `zk-2` Pod 处于 Running 和 Ready 状态,使用 `CRTL-C` 停止 kubectl命令。 ``` @@ -554,7 +589,6 @@ zk-2 1/1 Running 0 40s Use the command below to get the value you entered during the [sanity test](#sanity-testing-the-ensemble), from the `zk-2` Pod. --> - 从 `zk-2` Pod 中获取你在[健康检查](#Ensemble-健康检查)中输入的值。 ```shell @@ -564,8 +598,8 @@ kubectl exec zk-2 zkCli.sh get /hello - -尽管 `zk` StatefulSet 中所有的 Pods 都已经被终止并重建过,ensemble 仍然使用原来的数值提供服务。 +尽管 `zk` StatefulSet 中所有的 Pods 都已经被终止并重建过,ensemble +仍然使用原来的数值提供服务。 ``` WATCHER:: @@ -588,8 +622,8 @@ numChildren = 0 - -`zk` StatefulSet 的 `spec` 中的 `volumeClaimTemplates` 字段标识了将要为每个 Pod 准备的 PersistentVolume。 +`zk` StatefulSet 的 `spec` 中的 `volumeClaimTemplates` 字段标识了 +将要为每个 Pod 准备的 PersistentVolume。 ```yaml volumeClaimTemplates: @@ -610,10 +644,9 @@ the `StatefulSet`. Use the following command to get the `StatefulSet`'s `PersistentVolumeClaims`. --> +`StatefulSet` 控制器为 `StatefulSet` 中的每个 Pod 生成一个 `PersistentVolumeClaim`。 -StatefulSet 控制器为 StatefulSet 中的每个 Pod 生成一个 PersistentVolumeClaim。 - -获取 StatefulSet 的 PersistentVolumeClaims。 +获取 `StatefulSet` 的 `PersistentVolumeClaim`。 ```shell kubectl get pvc -l app=zk @@ -622,8 +655,7 @@ kubectl get pvc -l app=zk - -当 StatefulSet 重新创建它的 Pods时,Pods 的 PersistentVolumes 会被重新挂载。 +当 `StatefulSet` 重新创建它的 Pods 时,Pods 的 PersistentVolumes 会被重新挂载。 ``` NAME STATUS VOLUME CAPACITY ACCESSMODES AGE @@ -635,8 +667,8 @@ datadir-zk-2 Bound pvc-bee0817e-bcb1-11e6-994f-42010a800002 20Gi R - -StatefulSet 的容器 `template` 中的 `volumeMounts` 一节使得 PersistentVolumes 被挂载到 ZooKeeper 服务器的数据目录。 +StatefulSet 的容器 `template` 中的 `volumeMounts` 一节使得 +PersistentVolumes 被挂载到 ZooKeeper 服务器的数据目录。 ```shell volumeMounts: @@ -650,11 +682,13 @@ same `PersistentVolume` mounted to the ZooKeeper server's data directory. Even when the Pods are rescheduled, all the writes made to the ZooKeeper servers' WALs, and all their snapshots, remain durable. --> - -当 `zk` StatefulSet 中的一个 Pod 被(重新)调度时,它总是拥有相同的 PersistentVolume,挂载到 ZooKeeper 服务器的数据目录。即使在 Pods 被重新调度时,所有对 ZooKeeper 服务器的 WALs 的写入和它们的全部快照都仍然是持久的。 +当 `zk` StatefulSet 中的一个 Pod 被(重新)调度时,它总是拥有相同的 PersistentVolume, +挂载到 ZooKeeper 服务器的数据目录。 +即使在 Pods 被重新调度时,所有对 ZooKeeper 服务器的 WALs 的写入和它们的 +全部快照都仍然是持久的。 - ## 确保一致性配置 -如同在 [促成 leader 选举](#促成-Leader-选举) 和 [达成一致](#达成一致) 小节中提到的,ZooKeeper ensemble 中的服务器需要一致性的配置来选举一个 leader 并形成一个 quorum。它们还需要 Zab 协议的一致性配置来保证这个协议在网络中正确的工作。在这次的样例中,我们通过直接将配置写入代码清单中来达到该目的。 +如同在[促成领导者选举](#facilitating-leader-election) 和[达成一致](#achieving-consensus) +小节中提到的,ZooKeeper ensemble 中的服务器需要一致性的配置来选举一个领导者并形成一个 +quorum。它们还需要 Zab 协议的一致性配置来保证这个协议在网络中正确的工作。 +在这次的示例中,我们通过直接将配置写入代码清单中来达到该目的。 获取 `zk` StatefulSet。 @@ -677,8 +713,8 @@ Get the `zk` StatefulSet. kubectl get sts zk -o yaml ``` ``` -… -command: + ... + command: - sh - -c - "start-zookeeper \ @@ -699,14 +735,14 @@ command: --max_session_timeout=40000 \ --min_session_timeout=4000 \ --log_level=INFO" -… +... ``` - -用于启动 ZooKeeper 服务器的命令将这些配置作为命令行参数传给了 ensemble。你也可以通过环境变量来传入这些配置。 +用于启动 ZooKeeper 服务器的命令将这些配置作为命令行参数传给了 ensemble。 +你也可以通过环境变量来传入这些配置。 +### 配置日志 {#configuring-logging} -### 配置日志 - -`zkGenConfig.sh` 脚本产生的一个文件控制了 ZooKeeper 的日志行为。ZooKeeper 使用了 [Log4j](http://logging.apache.org/log4j/2.x/) 并默认使用基于文件大小和时间的滚动文件追加器作为日志配置。 +`zkGenConfig.sh` 脚本产生的一个文件控制了 ZooKeeper 的日志行为。 +ZooKeeper 使用了 [Log4j](http://logging.apache.org/log4j/2.x/) 并默认使用 +基于文件大小和时间的滚动文件追加器作为日志配置。 从 `zk` StatefulSet 的一个 Pod 中获取日志配置。 @@ -732,7 +769,6 @@ kubectl exec zk-0 cat /usr/etc/zookeeper/log4j.properties The logging configuration below will cause the ZooKeeper process to write all of its logs to the standard output file stream. --> - 下面的日志配置会使 ZooKeeper 进程将其所有的日志写入标志输出文件流中。 ``` @@ -753,11 +789,13 @@ standard out and standard error do not exhaust local storage media. Use [`kubectl logs`](/docs/reference/generated/kubectl/kubectl-commands/#logs) to retrieve the last 20 log lines from one of the Pods. --> +这是在容器里安全记录日志的最简单的方法。 +由于应用的日志被写入标准输出,Kubernetes 将会为你处理日志轮转。 +Kubernetes 还实现了一个智能保存策略,保证写入标准输出和标准错误流 +的应用日志不会耗尽本地存储媒介。 -这是在容器里安全记录日志的最简单的方法。由于应用的日志被写入标准输出,Kubernetes 将会为你处理日志轮转。Kubernetes 还实现了一个智能保存策略,保证写入标准输出和标准错误流的应用日志不会耗尽本地存储媒介。 - - -使用 [`kubectl logs`](/docs/reference/generated/kubectl/kubectl-commands/#logs) 从一个 Pod 中取回最后几行日志。 +使用命令 [`kubectl logs`](/docs/reference/generated/kubectl/kubectl-commands/#logs) +从一个 Pod 中取回最后 20 行日志。 ```shell kubectl logs zk-0 --tail 20 @@ -766,7 +804,6 @@ kubectl logs zk-0 --tail 20 - 使用 `kubectl logs` 或者从 Kubernetes Dashboard 可以查看写入到标准输出和标准错误流中的应用日志。 ``` @@ -793,18 +830,17 @@ You can view application logs written to standard out or standard error using `k ``` - -Kubernetes 支持与 [Stackdriver](/docs/tasks/debug-application-cluster/logging-stackdriver/) 和 [Elasticsearch and Kibana](/docs/tasks/debug-application-cluster/logging-elasticsearch-kibana/) 的整合以获得复杂但更为强大的日志功能。 -对于集群级别的日志输出与整合,可以考虑部署一个 [sidecar](https://kubernetes.io/blog/2015/06/the-distributed-system-toolkit-patterns) 容器。 +Kubernetes 支持与多种日志方案集成。你可以选择一个最适合你的集群和应用 +的日志解决方案。对于集群级别的日志输出与整合,可以考虑部署一个 +[边车容器](/zh/docs/concepts/cluster-administration/logging#sidecar-container-with-logging-agent) +来轮转和提供日志数据。 - ### 配置非特权用户 -在容器中允许应用以特权用户运行这条最佳实践是值得商讨的。如果你的组织要求应用以非特权用户运行,你可以使用 [SecurityContext](/zh/docs/tasks/configure-pod-container/security-context/) 控制运行容器入口点的用户。 +在容器中允许应用以特权用户运行这条最佳实践是值得商讨的。 +如果你的组织要求应用以非特权用户运行,你可以使用 +[SecurityContext](/zh/docs/tasks/configure-pod-container/security-context/) +控制运行容器入口点所使用的用户。 `zk` StatefulSet 的 Pod 的 `template` 包含了一个 `SecurityContext`。 @@ -833,8 +871,7 @@ corresponds to the zookeeper group. Get the ZooKeeper process information from the `zk-0` Pod. --> - -在 Pods 的容器内部,UID 1000 对应用户 zookeeper,GID 1000对应用户组 zookeeper。 +在 Pods 的容器内部,UID 1000 对应用户 zookeeper,GID 1000 对应用户组 zookeeper。 从 `zk-0` Pod 获取 ZooKeeper 进程信息。 @@ -846,8 +883,8 @@ kubectl exec zk-0 -- ps -elf As the `runAsUser` field of the `securityContext` object is set to 1000, instead of running as root, the ZooKeeper process runs as the zookeeper user. --> - -由于 `securityContext` 对象的 `runAsUser` 字段被设置为1000而不是 root,ZooKeeper 进程将以 zookeeper 用户运行。 +由于 `securityContext` 对象的 `runAsUser` 字段被设置为 1000 而不是 root, +ZooKeeper 进程将以 zookeeper 用户运行。 ``` F S UID PID PPID C PRI NI ADDR SZ WCHAN STIME TTY TIME CMD @@ -860,8 +897,8 @@ By default, when the Pod's PersistentVolumes is mounted to the ZooKeeper server' Use the command below to get the file permissions of the ZooKeeper data directory on the `zk-0` Pod. --> - -默认情况下,当 Pod 的 PersistentVolume 被挂载到 ZooKeeper 服务器的数据目录时,它只能被 root 用户访问。这个配置将阻止 ZooKeeper 进程写入它的 WAL 及保存快照。 +默认情况下,当 Pod 的 PersistentVolume 被挂载到 ZooKeeper 服务器的数据目录时, +它只能被 root 用户访问。这个配置将阻止 ZooKeeper 进程写入它的 WAL 及保存快照。 在 `zk-0` Pod 上获取 ZooKeeper 数据目录的文件权限。 @@ -872,8 +909,9 @@ kubectl exec -ti zk-0 -- ls -ld /var/lib/zookeeper/data - -由于 `securityContext` 对象的 `fsGroup` 字段设置为1000,Pods 的 PersistentVolumes 的所有权属于 zookeeper 用户组,因而 ZooKeeper 进程能够成功的读写数据。 +由于 `securityContext` 对象的 `fsGroup` 字段设置为 1000,Pods 的 +PersistentVolumes 的所有权属于 zookeeper 用户组,因而 ZooKeeper +进程能够成功地读写数据。 ``` drwxr-sr-x 3 zookeeper zookeeper 4096 Dec 5 20:45 /var/lib/zookeeper/data @@ -890,19 +928,19 @@ common pattern. When deploying an application in Kubernetes, rather than using an external utility as a supervisory process, you should use Kubernetes as the watchdog for your application. --> - ## 管理 ZooKeeper 进程 -[ZooKeeper documentation](https://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_supervision) 文档指出“你将需要一个监管程序用于管理每个 ZooKeeper 服务进程(JVM)”。在分布式系统中,使用一个看门狗(监管程序)来重启故障进程是一种常用的模式。 +[ZooKeeper 文档](https://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_supervision) +指出“你将需要一个监管程序用于管理每个 ZooKeeper 服务进程(JVM)”。 +在分布式系统中,使用一个看门狗(监管程序)来重启故障进程是一种常用的模式。 - ### 更新 Ensemble `zk` `StatefulSet` 的更新策略被设置为了 `RollingUpdate`。 @@ -912,6 +950,7 @@ You can use `kubectl patch` to update the number of `cpus` allocated to the serv ```shell kubectl patch sts zk --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/resources/requests/cpu", "value":"0.3"}]' ``` + ``` statefulset.apps/zk patched ``` @@ -919,12 +958,12 @@ statefulset.apps/zk patched - 使用 `kubectl rollout status` 观测更新状态。 ```shell kubectl rollout status sts/zk ``` + ``` waiting for statefulset rolling update to complete 0 pods at revision zk-5db4499664... Waiting for 1 pods to be ready... @@ -943,8 +982,8 @@ This terminates the Pods, one at a time, in reverse ordinal order, and recreates Use the `kubectl rollout history` command to view a history or previous configurations. --> - -这项操作会逆序地依次终止每一个 Pod,并用新的配置重新创建。这样做确保了在滚动更新的过程中 quorum 依旧保持工作。 +这项操作会逆序地依次终止每一个 Pod,并用新的配置重新创建。 +这样做确保了在滚动更新的过程中 quorum 依旧保持工作。 使用 `kubectl rollout history` 命令查看历史或先前的配置。 @@ -962,7 +1001,6 @@ REVISION - 使用 `kubectl rollout undo` 命令撤销这次的改动。 ```shell @@ -974,7 +1012,7 @@ statefulset.apps/zk rolled back ``` - ### 处理进程故障 -[Restart Policies](/zh/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy) 控制 Kubernetes 如何处理一个 Pod 中容器入口点的进程故障。对于 StatefulSet 中的 Pods 来说,Always 是唯一合适的 RestartPolicy,这也是默认值。你应该**绝不**覆盖 stateful 应用的默认策略。 +[重启策略](/zh/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy) +控制 Kubernetes 如何处理一个 Pod 中容器入口点的进程故障。 +对于 StatefulSet 中的 Pods 来说,Always 是唯一合适的 RestartPolicy,也是默认值。 +你应该**绝不**覆盖有状态应用的默认策略。 检查 `zk-0` Pod 中运行的 ZooKeeper 服务器的进程树。 @@ -999,8 +1039,8 @@ kubectl exec zk-0 -- ps -ef The command used as the container's entry point has PID 1, and the ZooKeeper process, a child of the entry point, has PID 27. --> - -作为容器入口点的命令的 PID 为 1,Zookeeper 进程是入口点的子进程,PID 为27。 +作为容器入口点的命令的 PID 为 1,Zookeeper 进程是入口点的子进程, +PID 为 27。 ``` UID PID PPID C STIME TTY TIME CMD @@ -1011,8 +1051,7 @@ zookeep+ 27 1 0 15:03 ? 00:00:03 /usr/lib/jvm/java-8-openjdk-amd6 - -在一个终端观察 `zk` StatefulSet 中的 Pods。 +在一个终端观察 `zk` `StatefulSet` 中的 Pods。 ```shell kubectl get pod -w -l app=zk @@ -1021,7 +1060,6 @@ kubectl get pod -w -l app=zk - 在另一个终端杀掉 Pod `zk-0` 中的 ZooKeeper 进程。 ```shell @@ -1031,8 +1069,8 @@ In another terminal, terminate the ZooKeeper process in Pod `zk-0` with the foll - -ZooKeeper 进程的终结导致了它父进程的终止。由于容器的 RestartPolicy 是 Always,父进程被重启。 +ZooKeeper 进程的终结导致了它父进程的终止。由于容器的 `RestartPolicy` +是 Always,父进程被重启。 ``` NAME READY STATUS RESTARTS AGE @@ -1051,11 +1089,12 @@ that implements the application's business logic, the script must terminate with child process. This ensures that Kubernetes will restart the application's container when the process implementing the application's business logic fails. --> - -如果你的应用使用一个脚本(例如 zkServer.sh)来启动一个实现了应用业务逻辑的进程,这个脚本必须和子进程一起结束。这保证了当实现应用业务逻辑的进程故障时,Kubernetes 会重启这个应用的容器。 +如果你的应用使用一个脚本(例如 `zkServer.sh`)来启动一个实现了应用业务逻辑的进程, +这个脚本必须和子进程一起结束。这保证了当实现应用业务逻辑的进程故障时, +Kubernetes 会重启这个应用的容器。 - ### 存活性测试 -你的应用配置为自动重启故障进程,但这对于保持一个分布式系统的健康来说是不够的。许多场景下,一个系统进程可以是活动状态但不响应请求,或者是不健康状态。你应该使用 liveness probes 来通知 Kubernetes 你的应用进程处于不健康状态,需要被重启。 +你的应用配置为自动重启故障进程,但这对于保持一个分布式系统的健康来说是不够的。 +许多场景下,一个系统进程可以是活动状态但不响应请求,或者是不健康状态。 +你应该使用存活性探针来通知 Kubernetes 你的应用进程处于不健康状态,需要被重启。 `zk` StatefulSet 的 Pod 的 `template` 一节指定了一个存活探针。 ```yaml livenessProbe: - exec: - command: - - sh - - -c - - "zookeeper-ready 2181" - initialDelaySeconds: 15 - timeoutSeconds: 5 + exec: + command: + - sh + - -c + - "zookeeper-ready 2181" + initialDelaySeconds: 15 + timeoutSeconds: 5 ``` - -这个探针调用一个简单的 bash 脚本,使用 ZooKeeper 的四字缩写 `ruok` 来测试服务器的健康状态。 +这个探针调用一个简单的 Bash 脚本,使用 ZooKeeper 的四字缩写 `ruok` +来测试服务器的健康状态。 ``` OK=$(echo ruok | nc 127.0.0.1 $1) @@ -1102,8 +1142,7 @@ fi - -在一个终端窗口观察 `zk` StatefulSet 中的 Pods。 +在一个终端窗口中使用下面的命令观察 `zk` StatefulSet 中的 Pods。 ```shell kubectl get pod -w -l app=zk @@ -1112,7 +1151,6 @@ kubectl get pod -w -l app=zk - 在另一个窗口中,从 Pod `zk-0` 的文件系统中删除 `zookeeper-ready` 脚本。 ```shell @@ -1124,8 +1162,8 @@ When the liveness probe for the ZooKeeper process fails, Kubernetes will automatically restart the process for you, ensuring that unhealthy processes in the ensemble are restarted. --> - -当 ZooKeeper 进程的存活探针探测失败时,Kubernetes 将会为你自动重启这个进程,从而保证 ensemble 中不健康状态的进程都被重启。 +当 ZooKeeper 进程的存活探针探测失败时,Kubernetes 将会为你自动重启这个进程, +从而保证 ensemble 中不健康状态的进程都被重启。 ```shell kubectl get pod -w -l app=zk @@ -1143,28 +1181,32 @@ zk-0 1/1 Running 1 1h ``` +### 就绪性测试 +就绪不同于存活。如果一个进程是存活的,它是可调度和健康的。 +如果一个进程是就绪的,它应该能够处理输入。存活是就绪的必要非充分条件。 +在许多场景下,特别是初始化和终止过程中,一个进程可以是存活但没有就绪的。 + + +如果你指定了一个就绪探针,Kubernetes 将保证在就绪检查通过之前, +你的应用不会接收到网络流量。 -### 就绪性测试 - -就绪不同于存活。如果一个进程是存活的,它是可调度和健康的。如果一个进程是就绪的,它应该能够处理输入。存活是就绪的必要非充分条件。在许多场景下,特别是初始化和终止过程中,一个进程可以是存活但没有就绪的。 - -如果你指定了一个就绪探针,Kubernetes将保证在就绪检查通过之前,你的应用不会接收到网络流量。 - -对于一个 ZooKeeper 服务器来说,存活即就绪。因此 `zookeeper.yaml` 清单中的就绪探针和存活探针完全相同。 +对于一个 ZooKeeper 服务器来说,存活即就绪。 +因此 `zookeeper.yaml` 清单中的就绪探针和存活探针完全相同。 ```yaml readinessProbe: @@ -1182,11 +1224,11 @@ Even though the liveness and readiness probes are identical, it is important to specify both. This ensures that only healthy servers in the ZooKeeper ensemble receive network traffic. --> - -虽然存活探针和就绪探针是相同的,但同时指定它们两者仍然重要。这保证了 ZooKeeper ensemble 中只有健康的服务器能接收网络流量。 +虽然存活探针和就绪探针是相同的,但同时指定它们两者仍然重要。 +这保证了 ZooKeeper ensemble 中只有健康的服务器能接收网络流量。 +## 容忍节点故障 +ZooKeeper 需要一个 quorum 来提交数据变动。对于一个拥有 3 个服务器的 ensemble 来说, +必须有两个服务器是健康的,写入才能成功。 +在基于 quorum 的系统里,成员被部署在多个故障域中以保证可用性。 +为了防止由于某台机器断连引起服务中断,最佳实践是防止应用的多个实例在相同的机器上共存。 + + +默认情况下,Kubernetes 可以把 StatefulSet 的 Pods 部署在相同节点上。 +对于你创建的 3 个服务器的 ensemble 来说,如果有两个服务器并存于 +相同的节点上并且该节点发生故障时,ZooKeeper 服务将中断, +直至至少一个 Pods 被重新调度。 + - -## 容忍节点故障 - -ZooKeeper 需要一个 quorum 来提交数据变动。对于一个拥有 3 个服务器的 ensemble来说,必须有两个服务器是健康的,写入才能成功。在基于 quorum 的系统里,成员被部署在故障域之间以保证可用性。为了防止由于某台机器断连引起服务中断,最佳实践是防止应用的多个示例在相同的机器上共存。 - -默认情况下,Kubernetes 可以把 StatefulSet 的 Pods 部署在相同节点上。对于你创建的 3 个服务器的 ensemble 来说,如果有两个服务器并存于相同的节点上并且该节点发生故障时,ZooKeeper 服务将中断,直至至少一个 Pods 被重新调度。 - -你应该总是提供额外的容量以允许关键系统进程在节点故障时能够被重新调度。如果你这样做了,服务故障就只会持续到 Kubernetes 调度器重新调度某个 ZooKeeper 服务器为止。但是,如果希望你的服务在容忍节点故障时无停服时间,你应该设置 `podAntiAffinity`。 +你应该总是提供多余的容量以允许关键系统进程在节点故障时能够被重新调度。 +如果你这样做了,服务故障就只会持续到 Kubernetes 调度器重新调度某个 +ZooKeeper 服务器为止。 +但是,如果希望你的服务在容忍节点故障时无停服时间,你应该设置 `podAntiAffinity`。 获取 `zk` Stateful Set 中的 Pods 的节点。 @@ -1225,8 +1277,7 @@ for i in 0 1 2; do kubectl get pod zk-$i --template {{.spec.nodeName}}; echo ""; - -`zk` StatefulSe 中所有的 Pods 都被部署在不同的节点。 +`zk` `StatefulSet` 中所有的 Pods 都被部署在不同的节点。 ``` kubernetes-node-cxpk @@ -1238,19 +1289,19 @@ kubernetes-node-2g2d This is because the Pods in the `zk` `StatefulSet` have a `PodAntiAffinity` specified. --> -这是因为 `zk` StatefulSet 中的 Pods 指定了 `PodAntiAffinity`。 +这是因为 `zk` `StatefulSet` 中的 Pods 指定了 `PodAntiAffinity`。 ```yaml - affinity: - podAntiAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - - labelSelector: - matchExpressions: - - key: "app" - operator: In - values: - - zk - topologyKey: "kubernetes.io/hostname" +affinity: + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchExpressions: + - key: "app" + operator: In + values: + - zk + topologyKey: "kubernetes.io/hostname" ``` - -`requiredDuringSchedulingIgnoredDuringExecution` 告诉 Kubernetes 调度器,在以 `topologyKey` 指定的域中,绝对不要把带有键为 `app`,值为 `zk` 的标签的两个 Pods 调度到相同的节点。`topologyKey` -`kubernetes.io/hostname` 表示这个域是一个单独的节点。使用不同的 rules、labels 和 selectors,你能够通过这种技术把你的 ensemble 分布在不同的物理、网络和电力故障域之间。 +`requiredDuringSchedulingIgnoredDuringExecution` 告诉 Kubernetes 调度器, +在以 `topologyKey` 指定的域中,绝对不要把带有键为 `app`、值为 `zk` 的标签 +的两个 Pods 调度到相同的节点。`topologyKey` `kubernetes.io/hostname` 表示 +这个域是一个单独的节点。 +使用不同的规则、标签和选择算符,你能够通过这种技术把你的 ensemble 分布 +在不同的物理、网络和电力故障域之间。 +## 节点维护期间保持应用可用 -## 存活管理 +**在本节中你将会隔离(Cordon)和腾空(Drain)节点。 +如果你是在一个共享的集群里使用本教程,请保证不会影响到其他租户。** -**在本节中你将会 cordon 和 drain 节点。如果你是在一个共享的集群里使用本教程,请保证不会影响到其他租户** +上一小节展示了如何在节点之间分散 Pods 以在计划外的节点故障时保证服务存活。 +但是你也需要为计划内维护引起的临时节点故障做准备。 -上一小节展示了如何在节点之间分散 Pods 以在计划外的节点故障时保证服务存活。但是你也需要为计划内维护引起的临时节点故障做准备。 - -获取你集群中的节点。 +使用此命令获取你的集群中的节点。 ```shell kubectl get nodes @@ -1295,7 +1350,8 @@ Use [`kubectl cordon`](/docs/reference/generated/kubectl/kubectl-commands/#cordo cordon all but four of the nodes in your cluster. --> -使用 [`kubectl cordon`](/docs/reference/generated/kubectl/kubectl-commands/#cordon) cordon 你的集群中除4个节点以外的所有节点。 +使用 [`kubectl cordon`](/docs/reference/generated/kubectl/kubectl-commands/#cordon) +隔离你的集群中除 4 个节点以外的所有节点。 ```shell kubectl cordon @@ -1304,8 +1360,7 @@ kubectl cordon - -获取 `zk-pdb` `PodDisruptionBudget`。 +使用下面的命令获取 `zk-pdb` `PodDisruptionBudget`。 ```shell kubectl get pdb zk-pdb @@ -1315,8 +1370,8 @@ kubectl get pdb zk-pdb The `max-unavailable` field indicates to Kubernetes that at most one Pod from `zk` `StatefulSet` can be unavailable at any time. --> - -`max-unavailable` 字段指示 Kubernetes 在任何时候,`zk` `StatefulSet` 至多有一个 Pod 是不可用的。 +`max-unavailable` 字段指示 Kubernetes 在任何时候,`zk` `StatefulSet` +至多有一个 Pod 是不可用的。 ``` NAME MIN-AVAILABLE MAX-UNAVAILABLE ALLOWED-DISRUPTIONS AGE @@ -1326,8 +1381,7 @@ zk-pdb N/A 1 1 - -在一个终端观察 `zk` `StatefulSet` 中的 Pods。 +在一个终端中,使用下面的命令观察 `zk` `StatefulSet` 中的 Pods。 ```shell kubectl get pods -w -l app=zk @@ -1337,7 +1391,7 @@ kubectl get pods -w -l app=zk In another terminal, use this command to get the nodes that the Pods are currently scheduled on. --> -在另一个终端获取 Pods 当前调度的节点。 +在另一个终端中,使用下面的命令获取 Pods 当前调度的节点。 ```shell for i in 0 1 2; do kubectl get pod zk-$i --template {{.spec.nodeName}}; echo ""; done @@ -1354,7 +1408,8 @@ Use [`kubectl drain`](/docs/reference/generated/kubectl/kubectl-commands/#drain) drain the node on which the `zk-0` Pod is scheduled. --> -使用 [`kubectl drain`](/docs/reference/generated/kubectl/kubectl-commands/#drain) 来 cordon 和 drain `zk-0` Pod 调度的节点。 +使用 [`kubectl drain`](/docs/reference/generated/kubectl/kubectl-commands/#drain) +来隔离和腾空 `zk-0` Pod 调度所在的节点。 ```shell kubectl drain $(kubectl get pod zk-0 --template {{.spec.nodeName}}) --ignore-daemonsets --force --delete-local-data @@ -1372,8 +1427,7 @@ node "kubernetes-node-pb41" drained As there are four nodes in your cluster, `kubectl drain`, succeeds and the `zk-0` is rescheduled to another node. --> - -由于你的集群中有4个节点, `kubectl drain` 执行成功,`zk-0 被调度到其它节点。 +由于你的集群中有 4 个节点, `kubectl drain` 执行成功,`zk-0` 被调度到其它节点。 ``` NAME READY STATUS RESTARTS AGE @@ -1396,8 +1450,7 @@ zk-0 1/1 Running 0 1m Keep watching the `StatefulSet`'s Pods in the first terminal and drain the node on which `zk-1` is scheduled. --> - -在第一个终端持续观察 StatefulSet 的 Pods并 drain `zk-1` 调度的节点。 +在第一个终端中持续观察 StatefulSet 的 Pods 并腾空 `zk-1` 调度所在的节点。 ```shell kubectl drain $(kubectl get pod zk-1 --template {{.spec.nodeName}}) --ignore-daemonsets --force --delete-local-data "kubernetes-node-ixsl" cordoned @@ -1413,42 +1466,42 @@ node "kubernetes-node-ixsl" drained The `zk-1` Pod cannot be scheduled because the `zk` `StatefulSet` contains a `PodAntiAffinity` rule preventing co-location of the Pods, and as only two nodes are schedulable, the Pod will remain in a Pending state. --> - -`zk-1` Pod 不能被调度。由于 `zk` StatefulSet 包含了一个防止 Pods 共存的 PodAntiAffinity 规则,而且只有两个节点可用于调度,这个 Pod 将保持在 Pending 状态。 +`zk-1` Pod 不能被调度,这是因为 `zk` `StatefulSet` 包含了一个防止 Pods +共存的 PodAntiAffinity 规则,而且只有两个节点可用于调度, +这个 Pod 将保持在 Pending 状态。 ```shell kubectl get pods -w -l app=zk ``` ``` -NAME READY STATUS RESTARTS AGE -zk-0 1/1 Running 2 1h -zk-1 1/1 Running 0 1h -zk-2 1/1 Running 0 1h -NAME READY STATUS RESTARTS AGE -zk-0 1/1 Terminating 2 2h -zk-0 0/1 Terminating 2 2h -zk-0 0/1 Terminating 2 2h -zk-0 0/1 Terminating 2 2h -zk-0 0/1 Pending 0 0s -zk-0 0/1 Pending 0 0s -zk-0 0/1 ContainerCreating 0 0s -zk-0 0/1 Running 0 51s -zk-0 1/1 Running 0 1m -zk-1 1/1 Terminating 0 2h -zk-1 0/1 Terminating 0 2h -zk-1 0/1 Terminating 0 2h -zk-1 0/1 Terminating 0 2h -zk-1 0/1 Pending 0 0s -zk-1 0/1 Pending 0 0s +NAME READY STATUS RESTARTS AGE +zk-0 1/1 Running 2 1h +zk-1 1/1 Running 0 1h +zk-2 1/1 Running 0 1h +NAME READY STATUS RESTARTS AGE +zk-0 1/1 Terminating 2 2h +zk-0 0/1 Terminating 2 2h +zk-0 0/1 Terminating 2 2h +zk-0 0/1 Terminating 2 2h +zk-0 0/1 Pending 0 0s +zk-0 0/1 Pending 0 0s +zk-0 0/1 ContainerCreating 0 0s +zk-0 0/1 Running 0 51s +zk-0 1/1 Running 0 1m +zk-1 1/1 Terminating 0 2h +zk-1 0/1 Terminating 0 2h +zk-1 0/1 Terminating 0 2h +zk-1 0/1 Terminating 0 2h +zk-1 0/1 Pending 0 0s +zk-1 0/1 Pending 0 0s ``` - -继续观察 stateful set 的 Pods 并 drain `zk-2` 调度的节点。 +继续观察 StatefulSet 中的 Pods 并腾空 `zk-2` 调度所在的节点。 ```shell kubectl drain $(kubectl get pod zk-2 --template {{.spec.nodeName}}) --ignore-daemonsets --force --delete-local-data @@ -1469,11 +1522,10 @@ You cannot drain the third node because evicting `zk-2` would violate `zk-budget Use `zkCli.sh` to retrieve the value you entered during the sanity test from `zk-0`. --> - 使用 `CRTL-C` 终止 kubectl。 -你不能 drain 第三个节点,因为删除 `zk-2` 将和 `zk-budget` 冲突。然而这个节点仍然保持 cordoned。 - +你不能腾空第三个节点,因为驱逐 `zk-2` 将和 `zk-budget` 冲突。 +然而这个节点仍然处于隔离状态(Cordoned)。 使用 `zkCli.sh` 从 `zk-0` 取回你的健康检查中输入的数值。 @@ -1484,7 +1536,6 @@ kubectl exec zk-0 zkCli.sh get /hello - 由于遵守了 PodDisruptionBudget,服务仍然可用。 ``` @@ -1506,12 +1557,13 @@ numChildren = 0 - -使用 [`kubectl uncordon`](/docs/reference/generated/kubectl/kubectl-commands/#uncordon) 来取消对第一个节点的隔离。 +使用 [`kubectl uncordon`](/docs/reference/generated/kubectl/kubectl-commands/#uncordon) +来取消对第一个节点的隔离。 ```shell kubectl uncordon kubernetes-node-pb41 ``` + ``` node "kubernetes-node-pb41" uncordoned ``` @@ -1519,44 +1571,43 @@ node "kubernetes-node-pb41" uncordoned - `zk-1` 被重新调度到了这个节点。等待 `zk-1` 变为 Running 和 Ready 状态。 ```shell kubectl get pods -w -l app=zk ``` + ``` -NAME READY STATUS RESTARTS AGE -zk-0 1/1 Running 2 1h -zk-1 1/1 Running 0 1h -zk-2 1/1 Running 0 1h -NAME READY STATUS RESTARTS AGE -zk-0 1/1 Terminating 2 2h -zk-0 0/1 Terminating 2 2h -zk-0 0/1 Terminating 2 2h -zk-0 0/1 Terminating 2 2h -zk-0 0/1 Pending 0 0s -zk-0 0/1 Pending 0 0s -zk-0 0/1 ContainerCreating 0 0s -zk-0 0/1 Running 0 51s -zk-0 1/1 Running 0 1m -zk-1 1/1 Terminating 0 2h -zk-1 0/1 Terminating 0 2h -zk-1 0/1 Terminating 0 2h -zk-1 0/1 Terminating 0 2h -zk-1 0/1 Pending 0 0s -zk-1 0/1 Pending 0 0s -zk-1 0/1 Pending 0 12m -zk-1 0/1 ContainerCreating 0 12m -zk-1 0/1 Running 0 13m -zk-1 1/1 Running 0 13m +NAME READY STATUS RESTARTS AGE +zk-0 1/1 Running 2 1h +zk-1 1/1 Running 0 1h +zk-2 1/1 Running 0 1h +NAME READY STATUS RESTARTS AGE +zk-0 1/1 Terminating 2 2h +zk-0 0/1 Terminating 2 2h +zk-0 0/1 Terminating 2 2h +zk-0 0/1 Terminating 2 2h +zk-0 0/1 Pending 0 0s +zk-0 0/1 Pending 0 0s +zk-0 0/1 ContainerCreating 0 0s +zk-0 0/1 Running 0 51s +zk-0 1/1 Running 0 1m +zk-1 1/1 Terminating 0 2h +zk-1 0/1 Terminating 0 2h +zk-1 0/1 Terminating 0 2h +zk-1 0/1 Terminating 0 2h +zk-1 0/1 Pending 0 0s +zk-1 0/1 Pending 0 0s +zk-1 0/1 Pending 0 12m +zk-1 0/1 ContainerCreating 0 12m +zk-1 0/1 Running 0 13m +zk-1 1/1 Running 0 13m ``` - -尝试 drain `zk-2` 调度的节点。 +尝试腾空 `zk-2` 调度所在的节点。 ```shell kubectl drain $(kubectl get pod zk-2 --template {{.spec.nodeName}}) --ignore-daemonsets --force --delete-local-data @@ -1565,7 +1616,6 @@ kubectl drain $(kubectl get pod zk-2 --template {{.spec.nodeName}}) --ignore-dae - 输出: ``` @@ -1581,10 +1631,9 @@ This time `kubectl drain` succeeds. Uncordon the second node to allow `zk-2` to be rescheduled. --> - 这次 `kubectl drain` 执行成功。 -Uncordon 第二个节点以允许 `zk-2` 被重新调度。 +取消第二个节点的隔离,以允许 `zk-2` 被重新调度。 ```shell kubectl uncordon kubernetes-node-ixsl @@ -1600,19 +1649,20 @@ If drain is used to cordon nodes and evict pods prior to taking the node offline services that express a disruption budget will have that budget respected. You should always allocate additional capacity for critical services so that their Pods can be immediately rescheduled. --> - -你可以同时使用 `kubectl drain` 和 `PodDisruptionBudgets` 来保证你的服务在维护过程中仍然可用。如果使用了 drain 来隔离节点并在节点离线之前排出了 pods,那么表达了 disruption budget 的服务将会遵守该 budget。你应该总是为关键服务分配额外容量,这样它们的 Pods 就能够迅速的重新调度。 +你可以同时使用 `kubectl drain` 和 `PodDisruptionBudgets` 来保证你的服务 +在维护过程中仍然可用。如果使用了腾空操作来隔离节点并在节点离线之前驱逐了 pods, +那么设置了干扰预算的服务将会遵守该预算。 +你应该总是为关键服务分配额外容量,这样它们的 Pods 就能够迅速的重新调度。 ## {{% heading "cleanup" %}} - - * 使用 `kubectl uncordon` 解除你集群中所有节点的隔离。 -* 你需要删除在本教程中使用的 PersistentVolumes 的持久存储媒介。请遵循必须的步骤,基于你的环境、存储配置和准备方法,保证回收所有的存储。 +* 你需要删除在本教程中使用的 PersistentVolumes 的持久存储媒介。 + 请遵循必须的步骤,基于你的环境、存储配置和制备方法,保证回收所有的存储。 +