From d4e8e8fb9ee07193e812620df83a703107b4e133 Mon Sep 17 00:00:00 2001 From: Kevin Frommelt Date: Mon, 25 Sep 2017 12:57:11 -0500 Subject: [PATCH 1/2] Fix typo in pod preset conflict example Move container port definition to the correct line. --- docs/tasks/inject-data-application/podpreset.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tasks/inject-data-application/podpreset.md b/docs/tasks/inject-data-application/podpreset.md index 6e1026a5df707..ff4485156cbb5 100644 --- a/docs/tasks/inject-data-application/podpreset.md +++ b/docs/tasks/inject-data-application/podpreset.md @@ -526,10 +526,10 @@ spec: - mountPath: /cache name: cache-volume ports: + - containerPort: 80 volumes: - name: cache-volume emptyDir: {} - - containerPort: 80 ``` **If we run `kubectl describe...` we can see the event:** From 88d3ed821dd69dd687bf9e7934f64de6758869b0 Mon Sep 17 00:00:00 2001 From: Dean Shi Date: Thu, 28 Sep 2017 23:50:35 +0800 Subject: [PATCH 2/2] k8smeetup-shirdrn-pr-2017-8-15 (#5376) * Chinese translation: 7.24-9.3, by shirdrn. * Translate and review again, and fix any translation problems for these 14 docs. * Delete network-policies.md temporarily, and then re-submit it for this PR. --- .../kubernetes-objects.md | 98 +++ .../nginx-deployment.yaml | 16 + .../concepts/policy/pod-security-policy.md | 221 ++++++ cn/docs/concepts/policy/psp.yaml | 18 + ...ries-to-pod-etc-hosts-with-host-aliases.md | 76 +++ .../connect-applications-service.md | 381 +++++++++++ .../services-networking/dns-pod-service.md | 419 ++++++++++++ .../concepts/services-networking/service.md | 631 ++++++++++++++++++ .../workloads/controllers/cron-jobs.md | 213 ++++++ .../workloads/controllers/daemonset.md | 164 +++++ .../controllers/garbage-collection.md | 180 +++++ .../workloads/pods/init-containers.md | 330 +++++++++ .../administer-cluster/cpu-memory-limit.md | 259 +++++++ .../dns-custom-nameservers.md | 161 +++++ ...aranteed-scheduling-critical-addon-pods.md | 55 ++ 15 files changed, 3222 insertions(+) create mode 100644 cn/docs/concepts/overview/working-with-objects/kubernetes-objects.md create mode 100644 cn/docs/concepts/overview/working-with-objects/nginx-deployment.yaml create mode 100644 cn/docs/concepts/policy/pod-security-policy.md create mode 100644 cn/docs/concepts/policy/psp.yaml create mode 100644 cn/docs/concepts/services-networking/add-entries-to-pod-etc-hosts-with-host-aliases.md create mode 100644 cn/docs/concepts/services-networking/connect-applications-service.md create mode 100644 cn/docs/concepts/services-networking/dns-pod-service.md create mode 100644 cn/docs/concepts/services-networking/service.md create mode 100644 cn/docs/concepts/workloads/controllers/cron-jobs.md create mode 100644 cn/docs/concepts/workloads/controllers/daemonset.md create mode 100644 cn/docs/concepts/workloads/controllers/garbage-collection.md create mode 100644 cn/docs/concepts/workloads/pods/init-containers.md create mode 100644 cn/docs/tasks/administer-cluster/cpu-memory-limit.md create mode 100644 cn/docs/tasks/administer-cluster/dns-custom-nameservers.md create mode 100644 cn/docs/tasks/administer-cluster/guaranteed-scheduling-critical-addon-pods.md diff --git a/cn/docs/concepts/overview/working-with-objects/kubernetes-objects.md b/cn/docs/concepts/overview/working-with-objects/kubernetes-objects.md new file mode 100644 index 0000000000000..dc7a1701dd0fe --- /dev/null +++ b/cn/docs/concepts/overview/working-with-objects/kubernetes-objects.md @@ -0,0 +1,98 @@ +--- +title: 理解 Kubernetes 对象 + +redirect_from: +- "/docs/concepts/abstractions/overview/" +- "/docs/concepts/abstractions/overview.html" +--- + +{% capture overview %} + +本页说明了 Kubernetes 对象在 Kubernetes API 中是如何表示的,以及如何在 `.yaml` 格式的文件中表示。 +{% endcapture %} + +{% capture body %} + + + + +## 理解 Kubernetes 对象 + +在 Kubernetes 系统中,*Kubernetes 对象* 是持久化的实体。Kubernetes 使用这些实体去表示整个集群的状态。特别地,它们描述了如下信息: + +* 哪些容器化应用在运行(以及在哪个 Node 上) +* 可以被应用使用的资源 +* 关于应用运行时表现的策略,比如重启策略、升级策略,以及容错策略 + + + +Kubernetes 对象是 “目标性记录” —— 一旦创建对象,Kubernetes 系统将持续工作以确保对象存在。通过创建对象,本质上是在告知 Kubernetes 系统,所需要的集群工作负载看起来是什么样子的,这就是 Kubernetes 集群的 **期望状态(Desired State)**。 + +操作 Kubernetes 对象 —— 是否创建、修改,或者删除 —— 需要使用 [Kubernetes API](https://git.k8s.io/community/contributors/devel/api-conventions.md)。比如,当使用 `kubectl` 命令行接口时,CLI 会执行必要的 Kubernetes API 调用,也可以在程序中直接调用 Kubernetes API。为了实现该目标,Kubernetes 当前提供了一个 `golang` [客户端库](https://github.com/kubernetes/client-go) +,其它语言库(例如[Python](https://github.com/kubernetes-incubator/client-python))也正在开发中。 + + + +### 对象规约(Spec)与状态(Status) + +每个 Kubernetes 对象包含两个嵌套的对象字段,它们负责管理对象的配置:对象 *spec* 和 对象 *status*。 +*spec* 是必需的,它描述了对象的 *期望状态(Desired State)* —— 希望对象所具有的特征。 +*status* 描述了对象的 *实际状态(Actual State)*,它是由 Kubernetes 系统提供和更新的。在任何时刻,Kubernetes 控制面一直努力地管理着对象的实际状态以与期望状态相匹配。 + + + +例如,Kubernetes Deployment 对象能够表示运行在集群中的应用。 +当创建 Deployment 时,可能需要设置 Deployment 的规约,以指定该应用需要有 3 个副本在运行。 +Kubernetes 系统读取 Deployment 规约,并启动我们所期望的该应用的 3 个实例 —— 更新状态以与规约相匹配。 +如果那些实例中有失败的(一种状态变更),Kubernetes 系统通过修正来响应规约和状态之间的不一致 —— 这种情况,会启动一个新的实例来替换。 + +关于对象 spec、status 和 metadata 的更多信息,查看 [Kubernetes API 约定](https://git.k8s.io/community/contributors/devel/api-conventions.md)。 + + + +### 描述 Kubernetes 对象 + +当创建 KUbernetes 对象时,必须提供对象的规约,用来描述该对象的期望状态,以及关于对象的一些基本信息(例如名称)。 +当使用 KUbernetes API 创建对象时(或者直接创建,或者基于`kubectl`),API 请求必须在请求体中包含 JSON 格式的信息。 +**大多数情况下,需要在 .yaml 文件中为 `kubectl` 提供这些信息**。 +`kubectl` 在发起 API 请求时,将这些信息转换成 JSON 格式。 + +这里有一个 `.yaml` 示例文件,展示了 KUbernetes Deployment 的必需字段和对象规约: + +{% include code.html language="yaml" file="nginx-deployment.yaml" ghlink="/docs/concepts/overview/working-with-objects/nginx-deployment.yaml" %} + +使用类似于上面的 `.yaml` 文件来创建 Deployment,一种方式是使用 `kubectl` 命令行接口(CLI)中的 [`kubectl create`](/docs/user-guide/kubectl/v1.7/#create) 命令,将 `.yaml` 文件作为参数。下面是一个示例: + +```shell +$ kubectl create -f docs/user-guide/nginx-deployment.yaml --record +``` + + +输出类似如下这样: + +```shell +deployment "nginx-deployment" created +``` + + + +### 必需字段 + +在想要创建的 KUbernetes 对象对应的 `.yaml` 文件中,需要配置如下的字段: + +* `apiVersion` - 创建该对象所使用的 Kubernetes API 的版本 +* `kind` - 想要创建的对象的类型 +* `metadata` - 帮助识别对象唯一性的数据,包括一个 `name` 字符串、UID 和可选的 `namespace` + + + +也需要提供对象的 `spec` 字段。对象 `spec` 的精确格式对每个 Kubernetes 对象来说是不同的,包含了特定于该对象的嵌套字段。[Kubernetes API 参考](/docs/api/)能够帮助我们找到任何我们想创建的对象的 spec 格式。 + +{% endcapture %} + +{% capture whatsnext %} + +* 了解最重要的基本 Kubernetes 对象,例如 [Pod](/docs/concepts/abstractions/pod/)。 +{% endcapture %} + +{% include templates/concept.md %} diff --git a/cn/docs/concepts/overview/working-with-objects/nginx-deployment.yaml b/cn/docs/concepts/overview/working-with-objects/nginx-deployment.yaml new file mode 100644 index 0000000000000..2aabf5e7b100c --- /dev/null +++ b/cn/docs/concepts/overview/working-with-objects/nginx-deployment.yaml @@ -0,0 +1,16 @@ +apiVersion: apps/v1beta1 +kind: Deployment +metadata: + name: nginx-deployment +spec: + replicas: 3 + template: + metadata: + labels: + app: nginx + spec: + containers: + - name: nginx + image: nginx:1.7.9 + ports: + - containerPort: 80 diff --git a/cn/docs/concepts/policy/pod-security-policy.md b/cn/docs/concepts/policy/pod-security-policy.md new file mode 100644 index 0000000000000..6d269c89b2678 --- /dev/null +++ b/cn/docs/concepts/policy/pod-security-policy.md @@ -0,0 +1,221 @@ +--- +assignees: +- pweil- +title: Pod 安全策略 +redirect_from: +- "/docs/user-guide/pod-security-policy/" +- "/docs/user-guide/pod-security-policy/index.html" +--- + + + +`PodSecurityPolicy` 类型的对象能够控制,是否可以向 Pod 发送请求,该 Pod 能够影响被应用到 Pod 和容器的 `SecurityContext`。 +查看 [Pod 安全策略建议](https://git.k8s.io/community/contributors/design-proposals/security-context-constraints.md) 获取更多信息。 + +* TOC +{:toc} + + + +## 什么是 Pod 安全策略? + +_Pod 安全策略_ 是集群级别的资源,它能够控制 Pod 运行的行为,以及它具有访问什么的能力。 +`PodSecurityPolicy` 对象定义了一组条件,指示 Pod 必须按系统所能接受的顺序运行。 +它们允许管理员控制如下方面: + + + +| 控制面 | 字段名称 | +| ------------------------------------------------------------- | --------------------------------- | +| 已授权容器的运行 | `privileged` | +| 为容器添加默认的一组能力 | `defaultAddCapabilities` | +| 为容器去掉某些能力 | `requiredDropCapabilities` | +| 容器能够请求添加某些能力 | `allowedCapabilities` | +| 控制卷类型的使用 | [`volumes`](#controlling-volumes) | +| 主机网络的使用 | [`hostNetwork`](#host-network) | +| 主机端口的使用 | `hostPorts` | +| 主机 PID namespace 的使用 | `hostPID` | +| 主机 IPC namespace 的使用 | `hostIPC` | +| 主机路径的使用 | [`allowedHostPaths`](#allowed-host-paths) | +| 容器的 SELinux 上下文 | [`seLinux`](#selinux) | +| 用户 ID | [`runAsUser`](#runasuser) | +| 配置允许的补充组 | [`supplementalGroups`](#supplementalgroups) | +| 分配拥有 Pod 数据卷的 FSGroup | [`fsGroup`](#fsgroup) | +| 必须使用一个只读的 root 文件系统 | `readOnlyRootFilesystem` | + + + +_Pod 安全策略_ 由设置和策略组成,它们能够控制 Pod 访问的安全特征。这些设置分为如下三类: + +- *基于布尔值控制*:这种类型的字段默认为最严格限制的值。 +- *基于被允许的值集合控制*:这种类型的字段会与这组值进行对比,以确认值被允许。 +- *基于策略控制*:设置项通过一种策略提供的机制来生成该值,这种机制能够确保指定的值落在被允许的这组值中。 + + + +### RunAsUser + + + +- *MustRunAs* - 必须配置一个 `range`。使用该范围内的第一个值作为默认值。验证是否不在配置的该范围内。 +- *MustRunAsNonRoot* - 要求提交的 Pod 具有非零 `runAsUser` 值,或在镜像中定义了 `USER` 环境变量。不提供默认值。 +- *RunAsAny* - 没有提供默认值。允许指定任何 `runAsUser` 。 + + + +### SELinux + +- *MustRunAs* - 如果没有使用预分配的值,必须配置 `seLinuxOptions`。默认使用 `seLinuxOptions`。验证 `seLinuxOptions`。 +- *RunAsAny* - 没有提供默认值。允许任意指定的 `seLinuxOptions` ID。 + + + +### SupplementalGroups + +- *MustRunAs* - 至少需要指定一个范围。默认使用第一个范围的最小值。验证所有范围的值。 +- *RunAsAny* - 没有提供默认值。允许任意指定的 `supplementalGroups` ID。 + + + +### FSGroup + +- *MustRunAs* - 至少需要指定一个范围。默认使用第一个范围的最小值。验证在第一个范围内的第一个 ID。 +- *RunAsAny* - 没有提供默认值。允许任意指定的 `fsGroup` ID。 + + + +### 控制卷 + +通过设置 PSP 卷字段,能够控制具体卷类型的使用。当创建一个卷的时候,与该字段相关的已定义卷可以允许设置如下值: + +1. azureFile +1. azureDisk +1. flocker +1. flexVolume +1. hostPath +1. emptyDir +1. gcePersistentDisk +1. awsElasticBlockStore +1. gitRepo +1. secret +1. nfs +1. iscsi +1. glusterfs +1. persistentVolumeClaim +1. rbd +1. cinder +1. cephFS +1. downwardAPI +1. fc +1. configMap +1. vsphereVolume +1. quobyte +1. photonPersistentDisk +1. projected +1. portworxVolume +1. scaleIO +1. storageos +1. \* (allow all volumes) + + + +对新的 PSP,推荐允许的卷的最小集合包括:configMap、downwardAPI、emptyDir、persistentVolumeClaim、secret 和 projected。 + + + +### 主机网络 + - *HostPorts*, 默认为 `empty`。`HostPortRange` 列表通过 `min`(包含) and `max`(包含) 来定义,指定了被允许的主机端口。 + +### 允许的主机路径 + - *AllowedHostPaths* 是一个被允许的主机路径前缀的白名单。空值表示所有的主机路径都可以使用。 + + + +## 许可 + +包含 `PodSecurityPolicy` 的 _许可控制_,允许控制集群资源的创建和修改,基于这些资源在集群范围内被许可的能力。 + +许可使用如下的方式为 Pod 创建最终的安全上下文: +1. 检索所有可用的 PSP。 +1. 生成在请求中没有指定的安全上下文设置的字段值。 +1. 基于可用的策略,验证最终的设置。 + +如果某个策略能够匹配上,该 Pod 就被接受。如果请求与 PSP 不匹配,则 Pod 被拒绝。 + +Pod 必须基于 PSP 验证每个字段。 + + + +## 创建 Pod 安全策略 + +下面是一个 Pod 安全策略的例子,所有字段的设置都被允许: + +{% include code.html language="yaml" file="psp.yaml" ghlink="/docs/concepts/policy/psp.yaml" %} + + + +下载示例文件可以创建该策略,然后执行如下命令: + +```shell +$ kubectl create -f ./psp.yaml +podsecuritypolicy "permissive" created +``` + + + +## 获取 Pod 安全策略列表 + +获取已存在策略列表,使用 `kubectl get`: + +```shell +$ kubectl get psp +NAME PRIV CAPS SELINUX RUNASUSER FSGROUP SUPGROUP READONLYROOTFS VOLUMES +permissive false [] RunAsAny RunAsAny RunAsAny RunAsAny false [*] +privileged true [] RunAsAny RunAsAny RunAsAny RunAsAny false [*] +restricted false [] RunAsAny MustRunAsNonRoot RunAsAny RunAsAny false [emptyDir secret downwardAPI configMap persistentVolumeClaim projected] +``` + + + +## 修改 Pod 安全策略 + +通过交互方式修改策略,使用 `kubectl edit`: + +```shell +$ kubectl edit psp permissive +``` + + + +该命令将打开一个默认文本编辑器,在这里能够修改策略。 + + + +## 删除 Pod 安全策略 + +一旦不再需要一个策略,很容易通过 `kubectl` 删除它: + +```shell +$ kubectl delete psp permissive +podsecuritypolicy "permissive" deleted +``` + + + +## 启用 Pod 安全策略 + +为了能够在集群中使用 Pod 安全策略,必须确保满足如下条件: + + + +1. 已经启用 API 类型 `extensions/v1beta1/podsecuritypolicy`(仅对 1.6 之前的版本) +1. 已经启用许可控制器 `PodSecurityPolicy` +1. 已经定义了自己的策略 + + + +## 使用 RBAC + +在 Kubernetes 1.5 或更新版本,可以使用 PodSecurityPolicy 来控制,对基于用户角色和组的已授权容器的访问。访问不同的 PodSecurityPolicy 对象,可以基于认证来控制。基于 Deployment、ReplicaSet 等创建的 Pod,限制访问 PodSecurityPolicy 对象,[Controller Manager](/docs/admin/kube-controller-manager/) 必须基于安全 API 端口运行,并且不能够具有超级用户权限。 + +PodSecurityPolicy 认证使用所有可用的策略,包括创建 Pod 的用户,Pod 上指定的服务账户(Service Acount)。当 Pod 基于 Deployment、ReplicaSet 创建时,它是创建 Pod 的 Controller Manager,所以如果基于非安全 API 端口运行,允许所有的 PodSecurityPolicy 对象,并且不能够有效地实现细分权限。用户访问给定的 PSP 策略有效,仅当是直接部署 Pod 的情况。更多详情,查看 [PodSecurityPolicy RBAC 示例](https://git.k8s.io/kubernetes/examples/podsecuritypolicy/rbac/README.md),当直接部署 Pod 时,应用 PodSecurityPolicy 控制基于角色和组的已授权容器的访问 。 diff --git a/cn/docs/concepts/policy/psp.yaml b/cn/docs/concepts/policy/psp.yaml new file mode 100644 index 0000000000000..9f037f67d0d4d --- /dev/null +++ b/cn/docs/concepts/policy/psp.yaml @@ -0,0 +1,18 @@ +apiVersion: extensions/v1beta1 +kind: PodSecurityPolicy +metadata: + name: permissive +spec: + seLinux: + rule: RunAsAny + supplementalGroups: + rule: RunAsAny + runAsUser: + rule: RunAsAny + fsGroup: + rule: RunAsAny + hostPorts: + - min: 8000 + max: 8080 + volumes: + - '*' diff --git a/cn/docs/concepts/services-networking/add-entries-to-pod-etc-hosts-with-host-aliases.md b/cn/docs/concepts/services-networking/add-entries-to-pod-etc-hosts-with-host-aliases.md new file mode 100644 index 0000000000000..dc0fec24cdff1 --- /dev/null +++ b/cn/docs/concepts/services-networking/add-entries-to-pod-etc-hosts-with-host-aliases.md @@ -0,0 +1,76 @@ +--- +assignees: +- rickypai +- thockin +title: 使用 HostAliases 向 Pod /etc/hosts 文件添加条目 +redirect_from: +- "/docs/user-guide/add-entries-to-pod-etc-hosts-with-host-aliases/" +- "/docs/user-guide/add-entries-to-pod-etc-hosts-with-host-aliases.md" +--- + +* TOC +{:toc} + + + +当 DNS 配置以及其它选项不合理的时候,通过向 Pod 的 /etc/hosts 文件中添加条目,可以在 Pod 级别覆盖对主机名的解析。在 1.7 版本,用户可以通过 PodSpec 的 HostAliases 字段来添加这些自定义的条目。 + +建议通过使用 HostAliases 来进行修改,因为该文件由 Kubelet 管理,并且可以在 Pod 创建/重启过程中被重写。 + + + +## 默认 hosts 文件内容 + +让我们从一个 Nginx Pod 开始,给该 Pod 分配一个 IP: + +``` +$ kubectl get pods --output=wide +NAME READY STATUS RESTARTS AGE IP NODE +nginx 1/1 Running 0 13s 10.200.0.4 worker0 +``` + + + +默认,hosts 文件只包含 ipv4 和 ipv6 的样板内容,像 `localhost` 和主机名称。 + +## 通过 HostAliases 增加额外的条目 + + + +除了默认的样板内容,我们可以向 hosts 文件添加额外的条目,将 `foo.local`、 `bar.local` 解析为`127.0.0.1`,将 `foo.remote`、 `bar.remote` 解析为 `10.1.2.3`,我们可以在 `.spec.hostAliases` 下为 Pod 添加 HostAliases。 + +{% include code.html language="yaml" file="hostaliases-pod.yaml" ghlink="/docs/concepts/services-networking/hostaliases-pod.yaml" %} + +hosts 文件的内容看起来类似如下这样: + +``` +$ kubectl logs hostaliases-pod +# Kubernetes-managed hosts file. +127.0.0.1 localhost +::1 localhost ip6-localhost ip6-loopback +fe00::0 ip6-localnet +fe00::0 ip6-mcastprefix +fe00::1 ip6-allnodes +fe00::2 ip6-allrouters +10.200.0.4 hostaliases-pod +127.0.0.1 foo.local +127.0.0.1 bar.local +10.1.2.3 foo.remote +10.1.2.3 bar.remote +``` + + + +在最下面额外添加了一些条目。 + +## 限制 + +在 1.7 版本,如果 Pod 启用 hostNetwork,那么将不能使用这个特性,因为 kubelet 只管理非 hostNetwork 类型 Pod 的 hosts 文件。目前正在讨论要改变这个情况。 + + + +## 为什么 Kubelet 管理 hosts文件? + +kubelet [管理](https://github.com/kubernetes/kubernetes/issues/14633) Pod 中每个容器的 hosts 文件,避免 Docker 在容器已经启动之后去 [修改](https://github.com/moby/moby/issues/17190) 该文件。 + +因为该文件是托管性质的文件,无论容器重启或 Pod 重新调度,用户修改该 hosts 文件的任何内容,都会在 Kubelet 重新安装后被覆盖。因此,不建议修改该文件的内容。 diff --git a/cn/docs/concepts/services-networking/connect-applications-service.md b/cn/docs/concepts/services-networking/connect-applications-service.md new file mode 100644 index 0000000000000..869d0bcea697f --- /dev/null +++ b/cn/docs/concepts/services-networking/connect-applications-service.md @@ -0,0 +1,381 @@ +--- +approvers: +- caesarxuchao +- lavalamp +- thockin +title: 应用连接到 Service +--- + +* TOC +{:toc} + + + +## Kubernetes 连接容器模型 + +既然有了一个持续运行、可复制的应用,我们就能够将它暴露到网络上。 +在讨论 Kubernetes 网络连接的方式之前,非常值得与 Docker 中 “正常” 方式的网络进行对比。 + + + +默认情况下,Docker 使用私有主机网络连接,只能与同在一台机器上的容器进行通信。 +为了实现容器的跨节点通信,必须在机器自己的 IP 上为这些容器分配端口,为容器进行端口转发或者代理。 + +多个开发人员之间协调端口的使用很难做到规模化,那些难以控制的集群级别的问题,都会交由用户自己去处理。 +Kubernetes 假设 Pod 可与其它 Pod 通信,不管它们在哪个主机上。 +我们给 Pod 分配属于自己的集群私有 IP 地址,所以没必要在 Pod 或映射到的容器的端口和主机端口之间显式地创建连接。 +这表明了在 Pod 内的容器都能够连接到本地的每个端口,集群中的所有 Pod 不需要通过 NAT 转换就能够互相看到。 +文档的剩余部分将详述如何在一个网络模型之上运行可靠的服务。 + +该指南使用一个简单的 Nginx server 来演示并证明谈到的概念。同样的原则也体现在一个更加完整的 [Jenkins CI 应用](http://blog.kubernetes.io/2015/07/strong-simple-ssl-for-kubernetes.html) 中。 + + + +## 在集群中暴露 Pod + +我们在之前的示例中已经做过,然而再让我重试一次,这次聚焦在网络连接的视角。 +创建一个 Nginx Pod,指示它具有一个容器端口的说明: + +{% include code.html language="yaml" file="run-my-nginx.yaml" ghlink="/docs/concepts/services-networking/run-my-nginx.yaml" %} + + + +这使得可以从集群中任何一个节点来访问它。检查节点,该 Pod 正在运行: + +```shell +$ kubectl create -f ./run-my-nginx.yaml +$ kubectl get pods -l run=my-nginx -o wide +NAME READY STATUS RESTARTS AGE IP NODE +my-nginx-3800858182-jr4a2 1/1 Running 0 13s 10.244.3.4 kubernetes-minion-905m +my-nginx-3800858182-kna2y 1/1 Running 0 13s 10.244.2.5 kubernetes-minion-ljyd +``` + + + +检查 Pod 的 IP 地址: + +```shell +$ kubectl get pods -l run=my-nginx -o yaml | grep podIP + podIP: 10.244.3.4 + podIP: 10.244.2.5 +``` + + + +应该能够通过 ssh 登录到集群中的任何一个节点上,使用 curl 也能调通所有 IP 地址。 +需要注意的是,容器不会使用该节点上的 80 端口,也不会使用任何特定的 NAT 规则去路由流量到 Pod 上。 +这意味着可以在同一个节点上运行多个 Pod,使用相同的容器端口,并且可以从集群中任何其他的 Pod 或节点上使用 IP 的方式访问到它们。 +像 Docker 一样,端口能够被发布到主机节点的接口上,但是出于网络模型的原因应该从根本上减少这种用法。 + +如果对此好奇,可以获取更多关于 [如何实现网络模型](/docs/concepts/cluster-administration/networking/#how-to-achieve-this) 的内容。 + + + +## 创建 Service + +我们有 Pod 在一个扁平的、集群范围的地址空间中运行 Nginx 服务,可以直接连接到这些 Pod,但如果某个节点死掉了会发生什么呢? +Pod 会终止,Deployment 将创建新的 Pod,且使用不同的 IP。这正是 Service 要解决的问题。 + +Kubernetes Service 从逻辑上定义了运行在集群中的一组 Pod,这些 Pod 提供了相同的功能。 +当每个 Service 创建时,会被分配一个唯一的 IP 地址(也称为 clusterIP)。 +这个 IP 地址与一个 Service 的生命周期绑定在一起,当 Service 存在的时候它也不会改变。 +可以配置 Pod 使它与 Service 进行通信,Pod 知道与 Service 通信将被自动地负载均衡到该 Service 中的某些 Pod 上。 + +可以使用 `kubectl expose` 命令为 2个 Nginx 副本创建一个 Service: + +```shell +$ kubectl expose deployment/my-nginx +service "my-nginx" exposed +``` + + + +这等价于使用 `kubectl create -f` 命令创建,对应如下的 yaml 文件: + +{% include code.html language="yaml" file="nginx-svc.yaml" ghlink="/docs/concepts/services-networking/nginx-svc.yaml" %} + + + +上述规约将创建一个 Service,对应具有标签 `run: my-nginx` 的 Pod,目标 TCP 端口 80,并且在一个抽象的 Service 端口(`targetPort`:容器接收流量的端口;`port`:抽象的 Service 端口,可以使任何其它 Pod 访问该 Service 的端口)上暴露。 +查看 [Service API 对象](/docs/api-reference/{{page.version}}/#service-v1-core) 了解 Service 定义支持的字段列表。 + +```shell +$ kubectl get svc my-nginx +NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE +my-nginx 10.0.162.149 80/TCP 21s +``` + + + +正如前面所提到的,一个 Service 由一组 backend Pod 组成。这些 Pod 通过 `endpoints` 暴露出来。 +Service Selector 将持续评估,结果被 POST 到一个名称为 `my-nginx` 的 Endpoint 对象上。 +当 Pod 终止后,它会自动从 Endpoint 中移除,新的能够匹配上 Service Selector 的 Pod 将自动地被添加到 Endpoint 中。 +检查该 Endpoint,注意到 IP 地址与在第一步创建的 Pod 是相同的。 + +```shell +$ kubectl describe svc my-nginx +Name: my-nginx +Namespace: default +Labels: run=my-nginx +Selector: run=my-nginx +Type: ClusterIP +IP: 10.0.162.149 +Port: 80/TCP +Endpoints: 10.244.2.5:80,10.244.3.4:80 +Session Affinity: None +No events. + +$ kubectl get ep my-nginx +NAME ENDPOINTS AGE +my-nginx 10.244.2.5:80,10.244.3.4:80 1m +``` + + + +现在,能够从集群中任意节点上使用 curl 命令请求 Nginx Service `:` 。 +注意 Service IP 完全是虚拟的,它从来没有走过网络,如果对它如何工作的原理感到好奇,可以阅读更多关于 [服务代理](/docs/user-guide/services/#virtual-ips-and-service-proxies) 的内容。 + + + +## 访问 Service + +Kubernetes 支持两种主要的服务发现模式 —— 环境变量和 DNS。前者在单个节点上可用使用,然而后者必须使用 [kube-dns 集群插件](http://releases.k8s.io/{{page.githubbranch}}/cluster/addons/dns/README.md)。 + + + +### 环境变量 + +当 Pod 在 Node 上运行时,kubelet 会为每个活跃的 Service 添加一组环境变量。这会有一个顺序的问题。想了解为何,检查正在运行的 Nginx Pod 的环境变量(Pod 名称将不会相同): + +```shell +$ kubectl exec my-nginx-3800858182-jr4a2 -- printenv | grep SERVICE +KUBERNETES_SERVICE_HOST=10.0.0.1 +KUBERNETES_SERVICE_PORT=443 +KUBERNETES_SERVICE_PORT_HTTPS=443 +``` + + + +注意,还没有谈及到 Service。这是因为创建副本先于 Service。 +这样做的另一个缺点是,调度器可能在同一个机器上放置所有 Pod,如果该机器宕机则所有的 Service 都会挂掉。 +正确的做法是,我们杀掉 2 个 Pod,等待 Deployment 去创建它们。 +这次 Service 会 *先于* 副本存在。这将实现调度器级别的 Service,能够使 Pod 分散创建(假定所有的 Node 都具有同样的容量),以及正确的环境变量: + +```shell +$ kubectl scale deployment my-nginx --replicas=0; kubectl scale deployment my-nginx --replicas=2; + +$ kubectl get pods -l run=my-nginx -o wide +NAME READY STATUS RESTARTS AGE IP NODE +my-nginx-3800858182-e9ihh 1/1 Running 0 5s 10.244.2.7 kubernetes-minion-ljyd +my-nginx-3800858182-j4rm4 1/1 Running 0 5s 10.244.3.8 kubernetes-minion-905m +``` + + + +可能注意到,Pod 具有不同的名称,因为它们被杀掉后并被重新创建。 + +```shell +$ kubectl exec my-nginx-3800858182-e9ihh -- printenv | grep SERVICE +KUBERNETES_SERVICE_PORT=443 +MY_NGINX_SERVICE_HOST=10.0.162.149 +KUBERNETES_SERVICE_HOST=10.0.0.1 +MY_NGINX_SERVICE_PORT=80 +KUBERNETES_SERVICE_PORT_HTTPS=443 +``` + +### DNS + + + +Kubernetes 提供了一个 DNS 插件 Service,它使用 skydns 自动为其它 Service 指派 DNS 名字。 +如果它在集群中处于运行状态,可以通过如下命令来检查: + +```shell +$ kubectl get services kube-dns --namespace=kube-system +NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE +kube-dns 10.0.0.10 53/UDP,53/TCP 8m +``` + + + +如果没有在运行,可以 [启用它](http://releases.k8s.io/{{page.githubbranch}}/cluster/addons/dns/README.md#how-do-i-configure-it)。 +本段剩余的内容,将假设已经有一个 Service,它具有一个长久存在的 IP(my-nginx),一个为该 IP 指派名称的 DNS 服务器(kube-dns 集群插件),所以可以通过标准做法,使在集群中的任何 Pod 都能与该 Service 通信(例如:gethostbyname)。 +让我们运行另一个 curl 应用来进行测试: + +```shell +$ kubectl run curl --image=radial/busyboxplus:curl -i --tty +Waiting for pod default/curl-131556218-9fnch to be running, status is Pending, pod ready: false +Hit enter for command prompt +``` + + +然后,按回车并执行命令 `nslookup my-nginx`: + +```shell +[ root@curl-131556218-9fnch:/ ]$ nslookup my-nginx +Server: 10.0.0.10 +Address 1: 10.0.0.10 + +Name: my-nginx +Address 1: 10.0.162.149 +``` + + + +## Service 安全 + +到现在为止,我们只在集群内部访问了 Nginx server。在将 Service 暴露到 Internet 之前,我们希望确保通信信道是安全的。对于这可能需要: + + + +* https 自签名证书(除非已经有了一个识别身份的证书) +* 使用证书配置的 Nginx server +* 使证书可以访问 Pod 的[秘钥](/docs/user-guide/secrets) + +可以从 [Nginx https 示例](https://github.com/kubernetes/kubernetes/tree/{{page.githubbranch}}/examples/https-nginx/) 获取所有上述内容,简明示例如下: + +```shell +$ make keys secret KEY=/tmp/nginx.key CERT=/tmp/nginx.crt SECRET=/tmp/secret.json +$ kubectl create -f /tmp/secret.json +secret "nginxsecret" created +$ kubectl get secrets +NAME TYPE DATA AGE +default-token-il9rc kubernetes.io/service-account-token 1 1d +nginxsecret Opaque 2 1m +``` + + + +现在修改 Nginx 副本,启动一个使用在秘钥中的证书的 https 服务器和 Servcie,都暴露端口(80 和 443): + +{% include code.html language="yaml" file="nginx-secure-app.yaml" ghlink="/docs/concepts/services-networking/nginx-secure-app.yaml" %} + + + +关于 nginx-secure-app manifest 值得注意的点如下: + + + +- 它在相同的文件中包含了 Deployment 和 Service 的规格 +- [Nginx server](https://github.com/kubernetes/kubernetes/tree/{{page.githubbranch}}/examples/https-nginx/default.conf) 处理 80 端口上的 http 流量,以及 443 端口上的 https 流量,Nginx Service 暴露了这两个端口。 +- 每个容器访问挂载在 /etc/nginx/ssl 卷上的秘钥。这需要在 Nginx server 启动之前安装好。 + +```shell +$ kubectl delete deployments,svc my-nginx; kubectl create -f ./nginx-secure-app.yaml +``` + + + +这时可以从任何节点访问到 Nginx server。 + +```shell +$ kubectl get pods -o yaml | grep -i podip + podIP: 10.244.3.5 +node $ curl -k https://10.244.3.5 +... +

Welcome to nginx!

+``` + + + +注意最后一步我们是如何提供 `-k` 参数执行 curl命令的,这是因为在证书生成时,我们不知道任何关于运行 Nginx 的 Pod 的信息,所以不得不在执行 curl 命令时忽略 CName 不匹配的情况。 +通过创建 Service,我们连接了在证书中的 CName 与在 Service 查询时被 Pod使用的实际 DNS 名字。 +让我们从一个 Pod 来测试(为了简化使用同一个秘钥,Pod 仅需要使用 nginx.crt 去访问 Service): + +{% include code.html language="yaml" file="curlpod.yaml" ghlink="/docs/concepts/services-networking/curlpod.yaml" %} + +```shell +$ kubectl create -f ./curlpod.yaml +$ kubectl get pods -l app=curlpod +NAME READY STATUS RESTARTS AGE +curl-deployment-1515033274-1410r 1/1 Running 0 1m +$ kubectl exec curl-deployment-1515033274-1410r -- curl https://my-nginx --cacert /etc/nginx/ssl/nginx.crt +... +Welcome to nginx! +... +``` + + +## 暴露 Service + +对我们应用的某些部分,可能希望将 Service 暴露在一个外部 IP 地址上。 +Kubernetes 支持两种实现方式:NodePort 和 LoadBalancer。 +在上一段创建的 Service 使用了 `NodePort`,因此 Nginx https 副本已经就绪,如果使用一个公网 IP,能够处理 Internet 上的流量。 + +```shell +$ kubectl get svc my-nginx -o yaml | grep nodePort -C 5 + uid: 07191fb3-f61a-11e5-8ae5-42010af00002 +spec: + clusterIP: 10.0.162.149 + ports: + - name: http + nodePort: 31704 + port: 8080 + protocol: TCP + targetPort: 80 + - name: https + nodePort: 32453 + port: 443 + protocol: TCP + targetPort: 443 + selector: + run: my-nginx + +$ kubectl get nodes -o yaml | grep ExternalIP -C 1 + - address: 104.197.41.11 + type: ExternalIP + allocatable: +-- + - address: 23.251.152.56 + type: ExternalIP + allocatable: +... + +$ curl https://: -k +... +

Welcome to nginx!

+``` + + + +让我们重新创建一个 Service,使用一个云负载均衡器,只需要将 `my-nginx` Service 的 `Type` 由 `NodePort` 改成 `LoadBalancer`。 + +```shell +$ kubectl edit svc my-nginx +$ kubectl get svc my-nginx +NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE +my-nginx 10.0.162.149 162.222.184.144 80/TCP,81/TCP,82/TCP 21s + +$ curl https:// -k +... +Welcome to nginx! +``` + + + +在 `EXTERNAL-IP` 列指定的 IP 地址是在公网上可用的。`CLUSTER-IP` 只在集群/私有云网络中可用。 + +注意,在 AWS 上类型 `LoadBalancer` 创建一个 ELB,它使用主机名(比较长),而不是 IP。 +它太长以至于不能适配标准 `kubectl get svc` 的输出,事实上需要通过执行 `kubectl describe service my-nginx` 命令来查看它。 +可以看到类似如下内容: + +```shell +$ kubectl describe service my-nginx +... +LoadBalancer Ingress: a320587ffd19711e5a37606cf4a74574-1142138393.us-east-1.elb.amazonaws.com +... +``` + + + +## 进一步阅读 + +Kubernetes 也支持联合 Service,能够跨多个集群和云提供商,为 Service 提供逐步增强的可用性、更优的容错、更好的可伸缩性。 +查看 [联合 Service 用户指南](/docs/concepts/cluster-administration/federation-service-discovery/) 获取更进一步信息。 + + + +## 下一步 + +[了解更多关于 Kubernetes 的特性,有助于在生产环境中可靠地运行容器](/docs/user-guide/production-pods) + diff --git a/cn/docs/concepts/services-networking/dns-pod-service.md b/cn/docs/concepts/services-networking/dns-pod-service.md new file mode 100644 index 0000000000000..e1792c4657670 --- /dev/null +++ b/cn/docs/concepts/services-networking/dns-pod-service.md @@ -0,0 +1,419 @@ +--- +assignees: +- davidopp +- thockin +title: DNS Pod 与 Service +redirect_from: +- "/docs/admin/dns/" +- "/docs/admin/dns.html" +--- + + + +## 介绍 + +Kubernetes 从 1.3 版本起, DNS 是内置的服务,通过插件管理器 [集群插件](http://releases.k8s.io/{{page.githubbranch}}/cluster/addons/README.md) 自动被启动。 + +Kubernetes DNS 在集群中调度 DNS Pod 和 Service ,配置 kubelet 以通知个别容器使用 DNS Service 的 IP 解析 DNS 名字。 + + + + +## 怎样获取 DNS 名字? + +在集群中定义的每个 Service(包括 DNS 服务器自身)都会被指派一个 DNS 名称。 +默认,一个客户端 Pod 的 DNS 搜索列表将包含该 Pod 自己的 Namespace 和集群默认域。 +通过如下示例可以很好地说明: + +假设在 Kubernetes 集群的 Namespace `bar` 中,定义了一个Service `foo`。 +运行在Namespace `bar` 中的一个 Pod,可以简单地通过 DNS 查询 `foo` 来找到该 Service。 +运行在 Namespace `quux` 中的一个 Pod 可以通过 DNS 查询 `foo.bar` 找到该 Service。 + + + +## 支持的 DNS 模式 + +下面各段详细说明支持的记录类型和布局。 +如果任何其它的布局、名称或查询,碰巧也能够使用,这就需要研究下它们的实现细节,以免后续修改它们又不能使用了。 + + + +### Service + +#### A 记录 + +“正常” Service(除了 Headless Service)会以 `my-svc.my-namespace.svc.cluster.local` 这种名字的形式被指派一个 DNS A 记录。这会解析成该 Service 的 Cluster IP。 + +“Headless” Service(没有Cluster IP)也会以 `my-svc.my-namespace.svc.cluster.local` 这种名字的形式被指派一个 DNS A 记录。 +不像正常 Service,它会解析成该 Service 选择的一组 Pod 的 IP。 +希望客户端能够使用这一组 IP,否则就使用标准的 round-robin 策略从这一组 IP 中进行选择。 + + + +#### SRV 记录 + +命名端口需要创建 SRV 记录,这些端口是正常 Service或 [Headless +Services](/docs/concepts/services-networking/service/#headless-services) 的一部分。 +对每个命名端口,SRV 记录具有 `_my-port-name._my-port-protocol.my-svc.my-namespace.svc.cluster.local` 这种形式。 +对普通 Service,这会被解析成端口号和 CNAME:`my-svc.my-namespace.svc.cluster.local`。 +对 Headless Service,这会被解析成多个结果,Service 对应的每个 backend Pod 各一个,包含 `auto-generated-name.my-svc.my-namespace.svc.cluster.local` 这种形式 Pod 的端口号和 CNAME。 + +#### 后向兼容性 + +上一版本的 kube-dns 使用 `my-svc.my-namespace.cluster.local` 这种形式的名字(后续会增加 'svc' 这一级),以后这将不再被支持。 + + + +### Pod + +#### A 记录 + +如果启用,Pod 会以 `pod-ip-address.my-namespace.pod.cluster.local` 这种形式被指派一个 DNS A 记录。 + +例如,`default` Namespace 具有 DNS 名字 `cluster.local`,在该 Namespace 中一个 IP 为 `1.2.3.4` 的 Pod 将具有一个条目:`1-2-3-4.default.pod.cluster.local`。 + + + +#### 基于 Pod hostname、subdomain 字段的 A 记录和主机名 + +当前,创建 Pod 后,它的主机名是该 Pod 的 `metadata.name` 值。 + +在 v1.2 版本中,用户可以配置 Pod annotation, 通过 `pod.beta.kubernetes.io/hostname` 来设置 Pod 的主机名。 +如果为 Pod 配置了 annotation,会优先使用 Pod 的名称作为主机名。 +例如,给定一个 Pod,它具有 annotation `pod.beta.kubernetes.io/hostname: my-pod-name`,该 Pod 的主机名被设置为 “my-pod-name”。 + + + +在 v1.3 版本中,PodSpec 具有 `hostname` 字段,可以用来指定 Pod 的主机名。这个字段的值优先于 annotation `pod.beta.kubernetes.io/hostname`。 +在 v1.2 版本中引入了 beta 特性,用户可以为 Pod 指定 annotation,其中 `pod.beta.kubernetes.io/subdomain` 指定了 Pod 的子域名。 +最终的域名将是 “...svc.”。 +举个例子,Pod 的主机名 annotation 设置为 “foo”,子域名 annotation 设置为 “bar”,在 Namespace “my-namespace” 中对应的 FQDN 为 “foo.bar.my-namespace.svc.cluster.local”。 + + + +在 v1.3 版本中,PodSpec 具有 `subdomain` 字段,可以用来指定 Pod 的子域名。 +这个字段的值优先于 annotation `pod.beta.kubernetes.io/subdomain` 的值。 + +```yaml +apiVersion: v1 +kind: Service +metadata: + name: default-subdomain +spec: + selector: + name: busybox + clusterIP: None + ports: + - name: foo # Actually, no port is needed. + port: 1234 + targetPort: 1234 +--- +apiVersion: v1 +kind: Pod +metadata: + name: busybox1 + labels: + name: busybox +spec: + hostname: busybox-1 + subdomain: default-subdomain + containers: + - image: busybox + command: + - sleep + - "3600" + name: busybox +--- +apiVersion: v1 +kind: Pod +metadata: + name: busybox2 + labels: + name: busybox +spec: + hostname: busybox-2 + subdomain: default-subdomain + containers: + - image: busybox + command: + - sleep + - "3600" + name: busybox +``` + + + +如果 Headless Service 与 Pod 在同一个 Namespace 中,它们具有相同的子域名,集群的 KubeDNS 服务器也会为该 Pod 的完整合法主机名返回 A 记录。 +在同一个 Namespace 中,给定一个主机名为 “busybox-1” 的 Pod,子域名设置为 “default-subdomain”,名称为 “default-subdomain” 的 Headless Service ,Pod 将看到自己的 FQDN 为 “busybox-1.default-subdomain.my-namespace.svc.cluster.local”。 +DNS 会为那个名字提供一个 A 记录,指向该 Pod 的 IP。 +“busybox1” 和 “busybox2” 这两个 Pod 分别具有它们自己的 A 记录。 + + + +在Kubernetes v1.2 版本中,`Endpoints` 对象也具有 annotation `endpoints.beta.kubernetes.io/hostnames-map`。 +它的值是 map[string(IP)][endpoints.HostRecord] 的 JSON 格式,例如: '{"10.245.1.6":{HostName: "my-webserver"}}'。 + +如果是 Headless Service 的 `Endpoints`,会以 ...svc. 的格式创建 A 记录。 +对示例中的 JSON 字符串,如果 `Endpoints` 是为名称为 “bar” 的 Headless Service 而创建的,其中一个 `Endpoints` 的 IP 是 “10.245.1.6”,则会创建一个名称为 “my-webserver.bar.my-namespace.svc.cluster.local” 的 A 记录,该 A 记录查询将返回 “10.245.1.6”。 + +`Endpoints` annotation 通常没必要由最终用户指定,但可以被内部的 Service Controller 用来提供上述功能。 + + + +在 v1.3 版本中,`Endpoints` 对象可以为任何 endpoint 指定 `hostname` 和 IP。 +`hostname` 字段优先于通过 `endpoints.beta.kubernetes.io/hostnames-map` annotation 指定的主机名。 + +在 v1.3 版本中,下面的 annotation 是过时的:`pod.beta.kubernetes.io/hostname`、`pod.beta.kubernetes.io/subdomain`、`endpoints.beta.kubernetes.io/hostnames-map`。 + + + +## 如何测试它是否可以使用? + +### 创建一个简单的 Pod 作为测试环境 + +创建 `busybox.yaml` 文件,内容如下: + +```yaml +apiVersion: v1 +kind: Pod +metadata: + name: busybox + namespace: default +spec: + containers: + - image: busybox + command: + - sleep + - "3600" + imagePullPolicy: IfNotPresent + name: busybox + restartPolicy: Always +``` + + + +然后,用该文件创建一个 Pod: + +``` +kubectl create -f busybox.yaml +``` + + + +### 等待这个 Pod 变成运行状态 + +获取它的状态,执行如下命令: + +``` +kubectl get pods busybox +``` + + + +可以看到如下内容: + +``` +NAME READY STATUS RESTARTS AGE +busybox 1/1 Running 0 +``` + + + +### 验证 DNS 已经生效 + +一旦 Pod 处于运行中状态,可以在测试环境中执行如下 nslookup 查询: + +``` +kubectl exec -ti busybox -- nslookup kubernetes.default +``` + + + +可以看到类似如下的内容: + +``` +Server: 10.0.0.10 +Address 1: 10.0.0.10 + +Name: kubernetes.default +Address 1: 10.0.0.1 +``` + + + +如果看到了,说明 DNS 已经可以正确工作了。 + +### 问题排查技巧 + +如果执行 nslookup 命令失败,检查如下内容: + +#### 先检查本地 DNS 配置 + +查看配置文件 resolv.conf。(关于更多信息,参考下面的 “从 Node 继承 DNS” 和 “已知问题”。) + +``` +kubectl exec busybox cat /etc/resolv.conf +``` + + + +按照如下方法(注意搜索路径可能会因为云提供商不同而变化)验证搜索路径和 Name Server 的建立: + +``` +search default.svc.cluster.local svc.cluster.local cluster.local google.internal c.gce_project_id.internal +nameserver 10.0.0.10 +options ndots:5 +``` + + + +#### 快速诊断 + +出现类似如下指示的错误,说明 kube-dns 插件或相关 Service 存在问题: + +``` +$ kubectl exec -ti busybox -- nslookup kubernetes.default +Server: 10.0.0.10 +Address 1: 10.0.0.10 + +nslookup: can't resolve 'kubernetes.default' +``` + + + +或者 + +``` +$ kubectl exec -ti busybox -- nslookup kubernetes.default +Server: 10.0.0.10 +Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local + +nslookup: can't resolve 'kubernetes.default' +``` + + + +#### 检查是否 DNS Pod 正在运行 + +使用 `kubectl get pods` 命令验证 DNS Pod 正在运行: + +``` +kubectl get pods --namespace=kube-system -l k8s-app=kube-dns +``` + + + +应该能够看到类似如下信息: + +``` +NAME READY STATUS RESTARTS AGE +... +kube-dns-v19-ezo1y 3/3 Running 0 1h +... +``` + + + +如果看到没有 Pod 运行,或 Pod 失败/结束,DNS 插件不能默认部署到当前的环境,必须手动部署。 + +#### 检查 DNS Pod 中的错误信息 + +使用 `kubectl logs` 命令查看 DNS 后台进程的日志: + +``` +kubectl logs --namespace=kube-system $(kubectl get pods --namespace=kube-system -l k8s-app=kube-dns -o name) -c kubedns +kubectl logs --namespace=kube-system $(kubectl get pods --namespace=kube-system -l k8s-app=kube-dns -o name) -c dnsmasq +kubectl logs --namespace=kube-system $(kubectl get pods --namespace=kube-system -l k8s-app=kube-dns -o name) -c healthz +``` + + + +查看是否有任何可疑的日志。在行开头的字母 W、E、F 分别表示 警告、错误、失败。请搜索具有这些日志级别的日志行,通过 [Kubernetes 问题](https://github.com/kubernetes/kubernetes/issues) 报告意外的错误。 + +#### DNS 服务是否运行? + +通过使用 `kubectl get service` 命令,验证 DNS 服务是否运行: + +``` +kubectl get svc --namespace=kube-system +``` + + + +应该能够看到: + +``` +NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE +... +kube-dns 10.0.0.10 53/UDP,53/TCP 1h +... +``` + + + +如果服务已经创建,或在这个例子中默认被创建,但是并没有看到,可以查看 [调试 Service 页面](/docs/tasks/debug-application-cluster/debug-service/) 获取更多信息。 + +``` +kubectl get ep kube-dns --namespace=kube-system +``` + + + +应该能够看到类似如下信息: + +``` +NAME ENDPOINTS AGE +kube-dns 10.180.3.17:53,10.180.3.17:53 1h +``` + + + +如果没有看到 Endpoint,查看 [调试 Service 文档](/docs/tasks/debug-application-cluster/debug-service/) 中的 Endpoint 段内容。 + +关于更多 Kubernetes DNS 的示例,参考 Kubernetes GitHub 仓库中 [集群 DNS 示例](https://git.k8s.io/kubernetes/examples/cluster-dns)。 + +## Kubernetes Federation(多 Zone 支持) + +在1.3 发行版本中,为多站点 Kubernetes 安装引入了集群 Federation 支持。这需要对 Kubernetes 集群 DNS 服务器处理 DNS 查询的方式,做出一些微小(后向兼容)改变,从而便利了对联合 Service 的查询(跨多个 Kubernetes 集群)。参考 [集群 Federation 管理员指南](/docs/concepts/cluster-administration/federation/) 获取更多关于集群 Federation 和多站点支持的细节。 + + + +## 工作原理 + +运行的 Kubernetes DNS Pod 包含 3 个容器 —— kubedns、dnsmasq 和负责健康检查的 healthz。 +kubedns 进程监视 Kubernetes master 对 Service 和 Endpoint 操作的变更,并维护一个内存查询结构去处理 DNS 请求。dnsmasq 容器增加了一个 DNS 缓存来改善性能。为执行对 dnsmasq 和 kubedns 的健康检查,healthz 容器提供了一个单独的健康检查 Endpoint。 + +DNS Pod 通过一个静态 IP 暴露为一个 Service。一旦 IP 被分配,kubelet 会通过 `--cluster-dns=10.0.0.10` 标志将配置的 DNS 传递给每一个容器。 + +DNS 名字也需要域名,本地域名是可配置的,在 kubelet 中使用 `--cluster-domain=` 标志。 + +Kubernetes 集群 DNS 服务器(根据 [SkyDNS](https://github.com/skynetservices/skydns) 库)支持正向查询(A 记录),Service 查询(SRV 记录)和反向 IP 地址查询(PTR 记录)。 + + + +## 从 Node 继承 DNS +当运行 Pod 时,kubelet 将集群 DNS 服务器和搜索路径追加到 Node 自己的 DNS 设置中。如果 Node 能够在大型环境中解析 DNS 名字,Pod 也应该没问题。参考下面 "已知问题” 中给出的更多说明。 + +如果不想这样,或者希望 Pod 有一个不同的 DNS 配置,可以使用 kubelet 的 `--resolv-conf` 标志。设置为 "" 表示 Pod 将不继承自 DNS。设置为一个合法的文件路径,表示 kubelet 将使用这个文件而不是 `/etc/resolv.conf` 。 + + + +## 已知问题 + +Kubernetes 安装但并不配置 Node 的 resolv.conf 文件,而是默认使用集群 DNS的,因为那个过程本质上就是和特定的发行版本相关的。最终应该会被实现。 + +Linux libc 在限制为3个 DNS `nameserver` 记录和3个 DNS `search` 记录是不可能卡住的([查看 2005 年的一个 Bug](https://bugzilla.redhat.com/show_bug.cgi?id=168253))。Kubernetes 需要使用1个 `nameserver` 记录和3个 `search` 记录。这意味着如果本地安装已经使用了3个 `nameserver` 或使用了3个以上 `search`,那些设置将会丢失。作为部分解决方法, Node 可以运行 `dnsmasq` ,它能提供更多 `nameserver` 条目,但不能运行更多 `search` 条目。可以使用 kubelet 的 `--resolv-conf` 标志。 + +如果使用 3.3 版本的 Alpine 或更早版本作为 base 镜像,由于 Alpine 的一个已知问题,DNS 可能不会正确工作。查看 [这里](https://github.com/kubernetes/kubernetes/issues/30215) 获取更多信息。 + + + +## 参考 + +- [DNS 集群插件文档](http://releases.k8s.io/{{page.githubbranch}}/cluster/addons/dns/README.md) + + + +## 下一步 + +- [集群中 DNS Service 自动伸缩](/docs/tasks/administer-cluster/dns-horizontal-autoscaling/) diff --git a/cn/docs/concepts/services-networking/service.md b/cn/docs/concepts/services-networking/service.md new file mode 100644 index 0000000000000..c6f2581062dd9 --- /dev/null +++ b/cn/docs/concepts/services-networking/service.md @@ -0,0 +1,631 @@ +--- +assignees: +- bprashanth +title: Service +redirect_from: +- "/docs/user-guide/services/" +- "/docs/user-guide/services/index.html" +--- + + + +Kubernetes [`Pod`](/docs/user-guide/pods) 是有生命周期的,它们可以被创建,也可以被销毁,然而一旦被销毁生命就永远结束。 +通过 [`ReplicationController`](/docs/user-guide/replication-controller) 能够动态地创建和销毁 `Pod`(例如,需要进行扩缩容,或者执行 [滚动升级](/docs/user-guide/kubectl/v1.7/#rolling-update))。 +每个 `Pod` 都会获取它自己的 IP 地址,即使这些 IP 地址不总是稳定可依赖的。 +这会导致一个问题:在 Kubernetes 集群中,如果一组 `Pod`(称为 backend)为其它 `Pod` (称为 frontend)提供服务,那么那些 frontend 该如何发现,并连接到这组 `Pod` 中的哪些 backend 呢? + + + +关于 `Service` + + + +Kubernetes `Service` 定义了这样一种抽象:一个 `Pod` 的逻辑分组,一种可以访问它们的策略 —— 通常称为微服务。 +这一组 `Pod` 能够被 `Service` 访问到,通常是通过 [`Label Selector`](/docs/concepts/overview/working-with-objects/labels/#label-selectors)(查看下面了解,为什么可能需要没有 selector 的 `Service`)实现的。 + + + +举个例子,考虑一个图片处理 backend,它运行了3个副本。这些副本是可互换的 —— frontend 不需要关心它们调用了哪个 backend 副本。 +然而组成这一组 backend 程序的 `Pod` 实际上可能会发生变化,frontend 客户端不应该也没必要知道,而且也不需要跟踪这一组 backend 的状态。 +`Service` 定义的抽象能够解耦这种关联。 + + + +对 Kubernetes 集群中的应用,Kubernetes 提供了简单的 `Endpoints` API,只要 `Service` 中的一组 `Pod` 发生变更,应用程序就会被更新。 +对非 Kubernetes 集群中的应用,Kubernetes 提供了基于 VIP 的网桥的方式访问 `Service`,再由 `Service` 重定向到 backend `Pod`。 + +* TOC +{:toc} + + + +## 定义 Service + + + +一个 `Service` 在 Kubernetes 中是一个 REST 对象,和 `Pod` 类似。 +像所有的 REST 对象一样, `Service` 定义可以基于 POST 方式,请求 apiserver 创建新的实例。 +例如,假定有一组 `Pod`,它们对外暴露了 9376 端口,同时还被打上 `"app=MyApp"` 标签。 + +```yaml +kind: Service +apiVersion: v1 +metadata: + name: my-service +spec: + selector: + app: MyApp + ports: + - protocol: TCP + port: 80 + targetPort: 9376 +``` + + + +上述配置将创建一个名称为 “my-service” 的 `Service` 对象,它会将请求代理到使用 TCP 端口 9376,并且具有标签 `"app=MyApp"` 的 `Pod` 上。 +这个 `Service` 将被指派一个 IP 地址(通常称为 “Cluster IP”),它会被服务的代理使用(见下面)。 +该 `Service` 的 selector 将会持续评估,处理结果将被 POST 到一个名称为 “my-service” 的 `Endpoints` 对象上。 + + + +需要注意的是, `Service` 能够将一个接收端口映射到任意的 `targetPort`。 +默认情况下,`targetPort` 将被设置为与 `port` 字段相同的值。 +可能更有趣的是,`targetPort` 可以是一个字符串,引用了 backend `Pod` 的一个端口的名称。 +但是,实际指派给该端口名称的端口号,在每个 backend `Pod` 中可能并不相同。 +对于部署和设计 `Service` ,这种方式会提供更大的灵活性。 +例如,可以在 backend 软件下一个版本中,修改 Pod 暴露的端口,并不会中断客户端的调用。 + + + +Kubernetes `Service` 能够支持 `TCP` 和 `UDP` 协议,默认 `TCP` 协议。 + + + +### 没有 selector 的 Service + + + +Servcie 抽象了该如何访问 Kubernetes `Pod`,但也能够抽象其它类型的 backend,例如: + +* 希望在生产环境中使用外部的数据库集群,但测试环境使用自己的数据库。 +* 希望服务指向另一个 [`Namespace`](/docs/user-guide/namespaces) 中或其它集群中的服务。 +* 正在将工作负载转移到 Kubernetes 集群,和运行在 Kubernetes 集群之外的 backend。 + +在任何这些场景中,都能够定义没有 selector 的 `Service` : + +```yaml +kind: Service +apiVersion: v1 +metadata: + name: my-service +spec: + ports: + - protocol: TCP + port: 80 + targetPort: 9376 +``` + + +由于这个 `Service` 没有 selector,就不会创建相关的 `Endpoints` 对象。可以手动将 `Service` 映射到指定的 `Endpoints`: + +```yaml +kind: Endpoints +apiVersion: v1 +metadata: + name: my-service +subsets: + - addresses: + - ip: 1.2.3.4 + ports: + - port: 9376 +``` + + + +注意:Endpoint IP 地址不能是 loopback(127.0.0.0/8)、 link-local(169.254.0.0/16)、或者 link-local 多播(224.0.0.0/24)。 + + + +访问没有 selector 的 `Service`,与有 selector 的 `Service` 的原理相同。请求将被路由到用户定义的 Endpoint(该示例中为 `1.2.3.4:9376`)。 + + + +ExternalName `Service` 是 `Service` 的特例,它没有 selector,也没有定义任何的端口和 Endpoint。 +相反地,对于运行在集群外部的服务,它通过返回该外部服务的别名这种方式来提供服务。 + +```yaml +kind: Service +apiVersion: v1 +metadata: + name: my-service + namespace: prod +spec: + type: ExternalName + externalName: my.database.example.com +``` + + + +当查询主机 `my-service.prod.svc.CLUSTER`时,集群的 DNS 服务将返回一个值为 `my.database.example.com` 的 `CNAME` 记录。 +访问这个服务的工作方式与其它的相同,唯一不同的是重定向发生在 DNS 层,而且不会进行代理或转发。 +如果后续决定要将数据库迁移到 Kubernetes 集群中,可以启动对应的 Pod,增加合适的 Selector 或 Endpoint,修改 `Service` 的 `type`。 + + + +## VIP 和 Service 代理 + + + +在 Kubernetes 集群中,每个 Node 运行一个 `kube-proxy` 进程。`kube-proxy` 负责为 `Service` 实现了一种 VIP(虚拟 IP)的形式,而不是 `ExternalName` 的形式。 +在 Kubernetes v1.0 版本,代理完全在 userspace。在 Kubernetes v1.1 版本,新增了 iptables 代理,但并不是默认的运行模式。 +从 Kubernetes v1.2 起,默认就是 iptables 代理。 + + + +在 Kubernetes v1.0 版本,`Service` 是 “4层”(TCP/UDP over IP)概念。 +在 Kubernetes v1.1 版本,新增了 `Ingress` API(beta 版),用来表示 “7层”(HTTP)服务。 + + + +### userspace 代理模式 + + + +这种模式,kube-proxy 会监视 Kubernetes master 对 `Service` 对象和 `Endpoints` 对象的添加和移除。 +对每个 `Service`,它会在本地 Node 上打开一个端口(随机选择)。 +任何连接到“代理端口”的请求,都会被代理到 `Service` 的backend `Pods` 中的某个上面(如 `Endpoints` 所报告的一样)。 +使用哪个 backend `Pod`,是基于 `Service` 的 `SessionAffinity` 来确定的。 +最后,它安装 iptables 规则,捕获到达该 `Service` 的 `clusterIP`(是虚拟 IP)和 `Port` 的请求,并重定向到代理端口,代理端口再代理请求到 backend `Pod`。 + + + +网络返回的结果是,任何到达 `Service` 的 IP:Port 的请求,都会被代理到一个合适的 backend,不需要客户端知道关于 Kubernetes、`Service`、或 `Pod` 的任何信息。 + + + +默认的策略是,通过 round-robin 算法来选择 backend `Pod`。 +实现基于客户端 IP 的会话亲和性,可以通过设置 `service.spec.sessionAffinity` 的值为 `"ClientIP"` (默认值为 `"None"`)。 + + + +![userspace代理模式下Service概览图](/images/docs/services-userspace-overview.svg) + + + +### iptables 代理模式 + + + +这种模式,kube-proxy 会监视 Kubernetes master 对 `Service` 对象和 `Endpoints` 对象的添加和移除。 +对每个 `Service`,它会安装 iptables 规则,从而捕获到达该 `Service` 的 `clusterIP`(虚拟 IP)和端口的请求,进而将请求重定向到 `Service` 的一组 backend 中的某个上面。 +对于每个 `Endpoints` 对象,它也会安装 iptables 规则,这个规则会选择一个 backend `Pod`。 + + + +默认的策略是,随机选择一个 backend。 +实现基于客户端 IP 的会话亲和性,可以将 `service.spec.sessionAffinity` 的值设置为 `"ClientIP"` (默认值为 `"None"`)。 + + + +和 userspace 代理类似,网络返回的结果是,任何到达 `Service` 的 IP:Port 的请求,都会被代理到一个合适的 backend,不需要客户端知道关于 Kubernetes、`Service`、或 `Pod` 的任何信息。 +这应该比 userspace 代理更快、更可靠。然而,不像 userspace 代理,如果初始选择的 `Pod` 没有响应,iptables 代理能够自动地重试另一个 `Pod`,所以它需要依赖 [readiness probes](/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#defining-readiness-probes)。 + + + +![iptables代理模式下Service概览图](/images/docs/services-iptables-overview.svg) + + + +## 多端口 Service + + + +很多 `Service` 需要暴露多个端口。对于这种情况,Kubernetes 支持在 `Service` 对象中定义多个端口。 +当使用多个端口时,必须给出所有的端口的名称,这样 Endpoint 就不会产生歧义,例如: + +```yaml +kind: Service +apiVersion: v1 +metadata: + name: my-service +spec: + selector: + app: MyApp + ports: + - name: http + protocol: TCP + port: 80 + targetPort: 9376 + - name: https + protocol: TCP + port: 443 + targetPort: 9377 +``` + + + +## 选择自己的 IP 地址 + + + +在 `Service` 创建的请求中,可以通过设置 `spec.clusterIP` 字段来指定自己的集群 IP 地址。 +比如,希望替换一个已经已存在的 DNS 条目,或者遗留系统已经配置了一个固定的 IP 且很难重新配置。 +用户选择的 IP 地址必须合法,并且这个 IP 地址在 `service-cluster-ip-range` CIDR 范围内,这对 API Server 来说是通过一个标识来指定的。 +如果 IP 地址不合法,API Server 会返回 HTTP 状态码 422,表示值不合法。 + + + +### 为何不使用 round-robin DNS? + + + +一个不时出现的问题是,为什么我们都使用 VIP 的方式,而不使用标准的 round-robin DNS,有如下几个原因: + +* 长久以来,DNS 库都没能认真对待 DNS TTL、缓存域名查询结果 +* 很多应用只查询一次 DNS 并缓存了结果 + * 就算应用和库能够正确查询解析,每个客户端反复重解析造成的负载也是非常难以管理的 + +我们尽力阻止用户做那些对他们没有好处的事情,如果很多人都来问这个问题,我们可能会选择实现它。 + + + +## 服务发现 + + + +Kubernetes 支持2种基本的服务发现模式 —— 环境变量和 DNS。 + + + +### 环境变量 + + + +当 `Pod` 运行在 `Node` 上,kubelet 会为每个活跃的 `Service` 添加一组环境变量。 +它同时支持 [Docker links兼容](https://docs.docker.com/userguide/dockerlinks/) 变量(查看 [makeLinkVariables](http://releases.k8s.io/{{page.githubbranch}}/pkg/kubelet/envvars/envvars.go#L49))、简单的 `{SVCNAME}_SERVICE_HOST` 和 `{SVCNAME}_SERVICE_PORT` 变量,这里 `Service` 的名称需大写,横线被转换成下划线。 + + + +举个例子,一个名称为 `"redis-master"` 的 Service 暴露了 TCP 端口 6379,同时给它分配了 Cluster IP 地址 10.0.0.11,这个 Service 生成了如下环境变量: + +```shell +REDIS_MASTER_SERVICE_HOST=10.0.0.11 +REDIS_MASTER_SERVICE_PORT=6379 +REDIS_MASTER_PORT=tcp://10.0.0.11:6379 +REDIS_MASTER_PORT_6379_TCP=tcp://10.0.0.11:6379 +REDIS_MASTER_PORT_6379_TCP_PROTO=tcp +REDIS_MASTER_PORT_6379_TCP_PORT=6379 +REDIS_MASTER_PORT_6379_TCP_ADDR=10.0.0.11 +``` + + +*这意味着需要有顺序的要求* —— `Pod` 想要访问的任何 `Service` 必须在 `Pod` 自己之前被创建,否则这些环境变量就不会被赋值。DNS 并没有这个限制。 + +### DNS + + + +一个可选(尽管强烈推荐)[集群插件](http://releases.k8s.io/{{page.githubbranch}}/cluster/addons/README.md) 是 DNS 服务器。 +DNS 服务器监视着创建新 `Service` 的 Kubernetes API,从而为每一个 `Service` 创建一组 DNS 记录。 +如果整个集群的 DNS 一直被启用,那么所有的 `Pod` 应该能够自动对 `Service` 进行名称解析。 + + + +例如,有一个名称为 `"my-service"` 的 `Service`,它在 Kubernetes 集群中名为 `"my-ns"` 的 `Namespace` 中,为 `"my-service.my-ns"` 创建了一条 DNS 记录。 +在名称为 `"my-ns"` 的 `Namespace` 中的 `Pod` 应该能够简单地通过名称查询找到 `"my-service"`。 +在另一个 `Namespace` 中的 `Pod` 必须限定名称为 `"my-service.my-ns"`。 +这些名称查询的结果是 Cluster IP。 + + + +Kubernetes 也支持对端口名称的 DNS SRV(Service)记录。 +如果名称为 `"my-service.my-ns"` 的 `Service` 有一个名为 `"http"` 的 `TCP` 端口,可以对 `"_http._tcp.my-service.my-ns"` 执行 DNS SRV 查询,得到 `"http"` 的端口号。 + + + +Kubernetes DNS 服务器是唯一的一种能够访问 `ExternalName` 类型的 Service 的方式。 +更多信息可以查看[DNS Pod 和 Service](/docs/concepts/services-networking/dns-pod-service/)。 + + + +## Headless Service + + + +有时不需要或不想要负载均衡,以及单独的 Service IP。 +遇到这种情况,可以通过指定 Cluster IP(`spec.clusterIP`)的值为 `"None"` 来创建 `Headless` Service。 + + + +这个选项允许开发人员自由寻找他们自己的方式,从而降低与 Kubernetes 系统的耦合性。 +应用仍然可以使用一种自注册的模式和适配器,对其它需要发现机制的系统能够很容易地基于这个 API 来构建。 + + + +对这类 `Service` 并不会分配 Cluster IP,kube-proxy 不会处理它们,而且平台也不会为它们进行负载均衡和路由。 +DNS 如何实现自动配置,依赖于 `Service` 是否定义了 selector。 + + + +### 配置 Selector + + + +对定义了 selector 的 Headless Service,Endpoint 控制器在 API 中创建了 `Endpoints` 记录,并且修改 DNS 配置返回 A 记录(地址),通过这个地址直接到达 `Service` 的后端 `Pod` 上。 + + + +### 不配置 Selector + + + +对没有定义 selector 的 Headless Service,Endpoint 控制器不会创建 `Endpoints` 记录。 +然而 DNS 系统会查找和配置,无论是: + +* `ExternalName` 类型 Service 的 CNAME 记录 + * 记录:与 Service 共享一个名称的任何 `Endpoints`,以及所有其它类型 + + + +## 发布服务 —— 服务类型 + +对一些应用(如 Frontend)的某些部分,可能希望通过外部(Kubernetes 集群外部)IP 地址暴露 Service。 + + + +Kubernetes `ServiceTypes` 允许指定一个需要的类型的 Service,默认是 `ClusterIP` 类型。 + +`Type` 的取值以及行为如下: + +* `ClusterIP`:通过集群的内部 IP 暴露服务,选择该值,服务只能够在集群内部可以访问,这也是默认的 `ServiceType`。 +* `NodePort`:通过每个 Node 上的 IP 和静态端口(`NodePort`)暴露服务。`NodePort` 服务会路由到 `ClusterIP` 服务,这个 `ClusterIP` 服务会自动创建。通过请求 `:`,可以从集群的外部访问一个 `NodePort` 服务。 +* `LoadBalancer`:使用云提供商的负载局衡器,可以向外部暴露服务。外部的负载均衡器可以路由到 `NodePort` 服务和 `ClusterIP` 服务。 +* `ExternalName`:通过返回 `CNAME` 和它的值,可以将服务映射到 `externalName` 字段的内容(例如, `foo.bar.example.com`)。 + 没有任何类型代理被创建,这只有 Kubernetes 1.7 或更高版本的 `kube-dns` 才支持。 + + + +### NodePort 类型 + +如果设置 `type` 的值为 `"NodePort"`,Kubernetes master 将从给定的配置范围内(默认:30000-32767)分配端口,每个 Node 将从该端口(每个 Node 上的同一端口)代理到 `Service`。该端口将通过 `Service` 的 `spec.ports[*].nodePort` 字段被指定。 + + + + +如果需要指定的端口号,可以配置 `nodePort` 的值,系统将分配这个端口,否则调用 API 将会失败(比如,需要关心端口冲突的可能性)。 + + + +这可以让开发人员自由地安装他们自己的负载均衡器,并配置 Kubernetes 不能完全支持的环境参数,或者直接暴露一个或多个 Node 的 IP 地址。 + + + +需要注意的是,Service 将能够通过 `:spec.ports[*].nodePort` 和 `spec.clusterIp:spec.ports[*].port` 而对外可见。 + + + +### LoadBalancer 类型 + + + +使用支持外部负载均衡器的云提供商的服务,设置 `type` 的值为 `"LoadBalancer"`,将为 `Service` 提供负载均衡器。 +负载均衡器是异步创建的,关于被提供的负载均衡器的信息将会通过 `Service` 的 `status.loadBalancer` 字段被发布出去。 + +```yaml +kind: Service +apiVersion: v1 +metadata: + name: my-service +spec: + selector: + app: MyApp + ports: + - protocol: TCP + port: 80 + targetPort: 9376 + nodePort: 30061 + clusterIP: 10.0.171.239 + loadBalancerIP: 78.11.24.19 + type: LoadBalancer +status: + loadBalancer: + ingress: + - ip: 146.148.47.155 +``` + + +来自外部负载均衡器的流量将直接打到 backend `Pod` 上,不过实际它们是如何工作的,这要依赖于云提供商。 +在这些情况下,将根据用户设置的 `loadBalancerIP` 来创建负载均衡器。 +某些云提供商允许设置 `loadBalancerIP`。如果没有设置 `loadBalancerIP`,将会给负载均衡器指派一个临时 IP。 +如果设置了 `loadBalancerIP`,但云提供商并不支持这种特性,那么设置的 `loadBalancerIP` 值将会被忽略掉。 + + + +### AWS 内部负载均衡器 +在混合云环境中,有时从虚拟私有云(VPC)环境中的服务路由流量是非常有必要的。 +可以通过在 `Service` 中增加 `annotation` 来实现,如下所示: + +```yaml +[...] +metadata: + name: my-service + annotations: + service.beta.kubernetes.io/aws-load-balancer-internal: 0.0.0.0/0 +[...] +``` + + +在水平分割的 DNS 环境中,需要两个 `Service` 来将外部和内部的流量路由到 Endpoint 上。 + + + +### AWS SSL 支持 +对运行在 AWS 上部分支持 SSL 的集群,从 1.3 版本开始,可以为 `LoadBalancer` 类型的 `Service` 增加两个 annotation: + +``` + metadata: + name: my-service + annotations: + service.beta.kubernetes.io/aws-load-balancer-ssl-cert: arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012 +``` + + + +第一个 annotation 指定了使用的证书。它可以是第三方发行商发行的证书,这个证书或者被上传到 IAM,或者由 AWS 的证书管理器创建。 + +```yaml + metadata: + name: my-service + annotations: + service.beta.kubernetes.io/aws-load-balancer-backend-protocol: (https|http|ssl|tcp) +``` + + +第二个 annotation 指定了 `Pod` 使用的协议。 +对于 HTTPS 和 SSL,ELB 将期望该 `Pod` 基于加密的连接来认证自身。 + + + +HTTP 和 HTTPS 将选择7层代理:ELB 将中断与用户的连接,当转发请求时,会解析 Header 信息并添加上用户的 IP 地址(`Pod` 将只能在连接的另一端看到该 IP 地址)。 + +TCP 和 SSL 将选择4层代理:ELB 将转发流量,并不修改 Header 信息。 + + + +### 外部 IP + +如果外部的 IP 路由到集群中一个或多个 Node 上,Kubernetes `Service` 会被暴露给这些 `externalIPs`。 +通过外部 IP(作为目的 IP 地址)进入到集群,打到 `Service` 的端口上的流量,将会被路由到 `Service` 的 Endpoint 上。 +`externalIPs` 不会被 Kubernetes 管理,它属于集群管理员的职责范畴。 + +根据 `Service` 的规定,`externalIPs` 可以同任意的 `ServiceType` 来一起指定。 +在上面的例子中,`my-service` 可以在 80.11.12.10:80(外部 IP:端口)上被客户端访问。 + +```yaml +kind: Service +apiVersion: v1 +metadata: + name: my-service +spec: + selector: + app: MyApp + ports: + - name: http + protocol: TCP + port: 80 + targetPort: 9376 + externalIPs: + - 80.11.12.10 +``` + + + +## 不足之处 + +为 VIP 使用 userspace 代理,将只适合小型到中型规模的集群,不能够扩展到上千 `Service` 的大型集群。 +查看 [最初设计方案](http://issue.k8s.io/1107) 获取更多细节。 + + + +使用 userspace 代理,隐藏了访问 `Service` 的数据包的源 IP 地址。 +这使得一些类型的防火墙无法起作用。 +iptables 代理不会隐藏 Kubernetes 集群内部的 IP 地址,但却要求客户端请求必须通过一个负载均衡器或 Node 端口。 + + + +`Type` 字段支持嵌套功能 —— 每一层需要添加到上一层里面。 +不会严格要求所有云提供商(例如,GCE 就没必要为了使一个 `LoadBalancer` 能工作而分配一个 `NodePort`,但是 AWS 需要 ),但当前 API 是强制要求的。 + + + +## 未来工作 + +未来我们能预见到,代理策略可能会变得比简单的 round-robin 均衡策略有更多细微的差别,比如 master 选举或分片。 +我们也能想到,某些 `Service` 将具有 “真正” 的负载均衡器,这种情况下 VIP 将简化数据包的传输。 + + + +我们打算为 L7(HTTP)`Service` 改进我们对它的支持。 + +我们打算为 `Service` 实现更加灵活的请求进入模式,这些 `Service` 包含当前 `ClusterIP`、`NodePort` 和 `LoadBalancer` 模式,或者更多。 + + + +## VIP 的那些骇人听闻的细节 + +对很多想使用 `Service` 的人来说,前面的信息应该足够了。 +然而,有很多内部原理性的内容,还是值去理解的。 + + + +### 避免冲突 + +Kubernetes 最主要的哲学之一,是用户不应该暴露那些能够导致他们操作失败、但又不是他们的过错的场景。 +这种场景下,让我们来看一下网络端口 —— 用户不应该必须选择一个端口号,而且该端口还有可能与其他用户的冲突。 +这就是说,在彼此隔离状态下仍然会出现失败。 + + + +为了使用户能够为他们的 `Service` 选择一个端口号,我们必须确保不能有2个 `Service` 发生冲突。 +我们可以通过为每个 `Service` 分配它们自己的 IP 地址来实现。 + + + +为了保证每个 `Service` 被分配到一个唯一的 IP,需要一个内部的分配器能够原子地更新 etcd 中的一个全局分配映射表,这个更新操作要先于创建每一个 `Service`。 +为了使 `Service` 能够获取到 IP,这个映射表对象必须在注册中心存在,否则创建 `Service` 将会失败,指示一个 IP 不能被分配。 +一个后台 Controller 的职责是创建映射表(从 Kubernetes 的旧版本迁移过来,旧版本中是通过在内存中加锁的方式实现),并检查由于管理员干预和清除任意 IP 造成的不合理分配,这些 IP 被分配了但当前没有 `Service` 使用它们。 + + + +### IP 和 VIP + +不像 `Pod` 的 IP 地址,它实际路由到一个固定的目的地,`Service` 的 IP 实际上不能通过单个主机来进行应答。 +相反,我们使用 `iptables`(Linux 中的数据包处理逻辑)来定义一个虚拟IP地址(VIP),它可以根据需要透明地进行重定向。 +当客户端连接到 VIP 时,它们的流量会自动地传输到一个合适的 Endpoint。 +环境变量和 DNS,实际上会根据 `Service` 的 VIP 和端口来进行填充。 + + + +#### Userspace + +作为一个例子,考虑前面提到的图片处理应用程序。 +当创建 backend `Service` 时,Kubernetes master 会给它指派一个虚拟 IP 地址,比如 10.0.0.1。 +假设 `Service` 的端口是 1234,该 `Service` 会被集群中所有的 `kube-proxy` 实例观察到。 +当代理看到一个新的 `Service`, 它会打开一个新的端口,建立一个从该 VIP 重定向到新端口的 iptables,并开始接收请求连接。 + + + +当一个客户端连接到一个 VIP,iptables 规则开始起作用,它会重定向该数据包到 `Service代理` 的端口。 +`Service代理` 选择一个 backend,并将客户端的流量代理到 backend 上。 + +这意味着 `Service` 的所有者能够选择任何他们想使用的端口,而不存在冲突的风险。 +客户端可以简单地连接到一个 IP 和端口,而不需要知道实际访问了哪些 `Pod`。 + + + +#### Iptables + +再次考虑前面提到的图片处理应用程序。 +当创建 backend `Service` 时,Kubernetes master 会给它指派一个虚拟 IP 地址,比如 10.0.0.1。 +假设 `Service` 的端口是 1234,该 `Service` 会被集群中所有的 `kube-proxy` 实例观察到。 +当代理看到一个新的 `Service`, 它会安装一系列的 iptables 规则,从 VIP 重定向到 per-`Service` 规则。 +该 per-`Service` 规则连接到 per-`Endpoint` 规则,该 per-`Endpoint` 规则会重定向(目标 NAT)到 backend。 + + + +当一个客户端连接到一个 VIP,iptables 规则开始起作用。一个 backend 会被选择(或者根据会话亲和性,或者随机),数据包被重定向到这个 backend。 +不像 userspace 代理,数据包从来不拷贝到用户空间,kube-proxy 不是必须为该 VIP 工作而运行,并且客户端 IP 是不可更改的。 +当流量打到 Node 的端口上,或通过负载均衡器,会执行相同的基本流程,但是在那些案例中客户端 IP 是可以更改的。 + + + +## API 对象 + +在 Kubernetes REST API 中,Service 是 top-level 资源。关于 API 对象的更多细节可以查看:[Service API 对象](/docs/api-reference/{{page.version}}/#service-v1-core)。 + + + +## 更多信息 + +阅读 [使用 Service 连接 Frontend 到 Backend](/docs/tutorials/connecting-apps/connecting-frontend-backend/)。 diff --git a/cn/docs/concepts/workloads/controllers/cron-jobs.md b/cn/docs/concepts/workloads/controllers/cron-jobs.md new file mode 100644 index 0000000000000..ba64ea4032bf2 --- /dev/null +++ b/cn/docs/concepts/workloads/controllers/cron-jobs.md @@ -0,0 +1,213 @@ +--- +assignees: +- erictune +- soltysh +- janetkuo +title: Cron Job +redirect_from: +- "/docs/concepts/jobs/cron-jobs/" +- "/docs/concepts/jobs/cron-jobs.html" +- "/docs/user-guide/cron-jobs/" +- "/docs/user-guide/cron-jobs.html" +--- + +* TOC +{:toc} + + + +## Cron Job 是什么? + +_Cron Job_ 管理基于时间的 [Job](/docs/concepts/jobs/run-to-completion-finite-workloads/),即: + +* 在给定时间点只运行一次 +* 在给定时间点周期性地运行 + +一个 CronJob 对象类似于 _crontab_ (cron table)文件中的一行。它根据指定的预定计划周期性地运行一个 Job,格式可以参考 [Cron](https://en.wikipedia.org/wiki/Cron) 。 + + + +**注意:** 在预定计划中,问号(`?`)和星号(`*`)的意义是相同的,表示给定字段的取值是任意可用值。 + +**注意:** 在 Kubernetes 1.4 版本引入了 ScheduledJob 资源,但从 1.5 版本开始改成了 CronJob。 + +典型的用法如下所示: + + + +* 在给定的时间点调度 Job 运行 +* 创建周期性运行的 Job,例如:数据库备份、发送邮件。 + +### 前提条件 + + + +当使用的 Kubernetes 集群,版本 >= 1.4(对 ScheduledJob),>= 1.5(对 CronJob),当启动 API Server(参考 [为集群开启或关闭 API 版本](/docs/admin/cluster-management/#turn-on-or-off-an-api-version-for-your-cluster) 获取更多信息)时,通过传递选项 `--runtime-config=batch/v2alpha1=true` 可以开启 batch/v2alpha1 API。 + +## 创建 Cron Job + +下面是一个 Cron Job 的例子。它会每分钟运行一个 Job,打印出当前时间并输出问候语 hello。 + +% include code.html language="yaml" file="cronjob.yaml" ghlink="/docs/concepts/workloads/controllers/cronjob.yaml" %} + +下载并运行该示例 Cron Job,然后执行如下命令: + +```shell +$ kubectl create -f ./cronjob.yaml +cronjob "hello" created +``` + + + +可选地,使用 `kubectl run` 创建一个 Cron Job,不需要写完整的配置: + +```shell +$ kubectl run hello --schedule="*/1 * * * *" --restart=OnFailure --image=busybox -- /bin/sh -c "date; echo Hello from the Kubernetes cluster" +cronjob "hello" created +``` + + + +创建该 Cron Job 之后,通过如下命令获取它的状态信息: + +```shell +$ kubectl get cronjob hello +NAME SCHEDULE SUSPEND ACTIVE LAST-SCHEDULE +hello */1 * * * * False 0 +``` + + + +如上所示,既没有 active 的 Job,也没有被调度的 Job。 + +等待并观察创建的 Job,大约一分钟时间: + +```shell +$ kubectl get jobs --watch +NAME DESIRED SUCCESSFUL AGE +hello-4111706356 1 1 2s +``` + + + +现在能看到一个名称为 hello 的 Job 在运行。我们可以停止观察,并再次获取该 Job 的状态信息: + +```shell +$ kubectl get cronjob hello +NAME SCHEDULE SUSPEND ACTIVE LAST-SCHEDULE +hello */1 * * * * False 0 Mon, 29 Aug 2016 14:34:00 -0700 +``` + + +应该能够看到名称为 “hello” 的 Job 在 `LAST-SCHEDULE` 指定的时间点被调度了。当前存在 0 个活跃(Active)的 Job,说明该 Job 已经被调度运行完成或失败。 + +现在,找到最近一次被调度的 Job 创建的 Pod,能够看到其中一个 Pod 的标准输出。注意,Job 名称和 Pod 名称是不一样的。 + +```shell +# Replace "hello-4111706356" with the job name in your system +$ pods=$(kubectl get pods --selector=job-name=hello-4111706356 --output=jsonpath={.items..metadata.name}) + +$ echo $pods +hello-4111706356-o9qcm + +$ kubectl logs $pods +Mon Aug 29 21:34:09 UTC 2016 +Hello from the Kubernetes cluster +``` + + +## 删除 Cron Job + +一旦不再需要 Cron Job,简单地可以使用 `kubectl` 命令删除它: + +```shell +$ kubectl delete cronjob hello +cronjob "hello" deleted +``` + + + +这将会终止正在创建的 Job。然而,运行中的 Job 将不会被终止,不会删除 Job 或 它们的 Pod。为了清理那些 Job 和 Pod,需要列出该 Cron Job 创建的全部 Job,然后删除它们: + +```shell +$ kubectl get jobs +NAME DESIRED SUCCESSFUL AGE +hello-1201907962 1 1 11m +hello-1202039034 1 1 8m +... + +$ kubectl delete jobs hello-1201907962 hello-1202039034 ... +job "hello-1201907962" deleted +job "hello-1202039034" deleted +... +``` + + + +一旦 Job 被删除,由 Job 创建的 Pod 也会被删除。注意,所有由名称为 “hello” 的 Cron Job 创建的 Job 会以前缀字符串 “hello-” 进行命名。如果想要删除当前 Namespace 中的所有 Job,可以通过命令 `kubectl delete jobs --all` 立刻删除它们。 + + + +## Cron Job 限制 + +Cron Job 在每次调度运行时间内 _大概_ 会创建一个 Job 对象。我们之所以说 _大概_ ,是因为在特定的环境下可能会创建两个 Job,或者一个 Job 都没创建。我们尝试少发生这种情况,但却不能完全避免。因此,创建 Job 操作应该是 _幂等的_。 + +Job 根据它所创建的 Pod 的并行度,负责重试创建 Pod,并就决定这一组 Pod 的成功或失败。Cron Job 根本不会去检查 Pod。 + + + +## 编写 Cron Job 规约 + +和其它 Kubernetes 配置一样,Cron Job 需要 `apiVersion`、 `kind`、和 `metadata` 这三个字段。 +关于如何实现一个配置文件的更新信息,参考文档 [部署应用](/docs/user-guide/deploying-applications)、 +[配置容器](/docs/user-guide/configuring-containers) 和 +[使用 kubectl 管理资源](/docs/user-guide/working-with-resources)。 + +Cron Job 也需要 [`.spec` 段](https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status)。 + +**注意:** 对一个 Cron Job 的所有修改,尤其是对其 `.spec` 的修改,仅会在下一次运行的时候生效。 + + +### 调度 + + `.spec.schedule` 是 `.spec` 中必需的字段,它的值是 [Cron](https://en.wikipedia.org/wiki/Cron) 格式字的符串,例如:`0 * * * *`,或者 `@hourly`,根据指定的调度时间 Job 会被创建和执行。 + + + +### Job 模板 + +`.spec.jobTemplate` 是另一个 `.spec` 中必需的字段。它是 Job 的模板。 +除了它可以是嵌套的,并且不具有 `apiVersion` 或 `kind` 字段之外,它和 [Job](/docs/concepts/jobs/run-to-completion-finite-workloads/) 一样具有完全相同的模式(schema)。 +参考 [编写 Job 规格](/docs/concepts/jobs/run-to-completion-finite-workloads/#writing-a-job-spec)。 + + + +### 启动 Job 的期限(秒级别) + +`.spec.startingDeadlineSeconds` 字段是可选的。它表示启动 Job 的期限(秒级别),如果因为任何原因而错过了被调度的时间,那么错过执行时间的 Job 将被认为是失败的。如果没有指定,则没有期限。 + + + +### 并发策略 + +`.spec.concurrencyPolicy` 字段也是可选的。它指定了如何处理被 Cron Job 创建的 Job 的并发执行。只允许指定下面策略中的一种: + +* `Allow`(默认):允许并发运行 Job +* `Forbid`:禁止并发运行,如果前一个还没有完成,则直接跳过下一个 +* `Replace`:取消当前正在运行的 Job,用一个新的来替换 + +注意,当前策略只能应用于同一个 Cron Job 创建的 Job。如果存在多个 Cron Job,它们创建的 Job 之间总是允许并发运行。 + + + +### 挂起 + +`.spec.suspend` 字段也是可选的。如果设置为 `true`,后续所有执行都将被挂起。它对已经开始执行的 Job 不起作用。默认值为 `false`。 + + + +### Job 历史限制 + +`.spec.successfulJobsHistoryLimit` 和 `.spec.failedJobsHistoryLimit` 这两个字段是可选的。它们指定了可以保留完成和失败 Job 数量的限制。 + +默认没有限制,所有成功和失败的 Job 都会被保留。然而,当运行一个 Cron Job 时,很快就会堆积很多 Job,推荐设置这两个字段的值。设置限制值为 `0`,相关类型的 Job 完成后将不会被保留。 diff --git a/cn/docs/concepts/workloads/controllers/daemonset.md b/cn/docs/concepts/workloads/controllers/daemonset.md new file mode 100644 index 0000000000000..bad610955876b --- /dev/null +++ b/cn/docs/concepts/workloads/controllers/daemonset.md @@ -0,0 +1,164 @@ +--- +assignees: +- erictune +title: DaemonSet +redirect_from: +- "/docs/admin/daemons/" +- "/docs/admin/daemons.html" +--- + +* TOC +{:toc} + + + +## 什么是 DaemonSet? + + _DaemonSet_ 确保全部(或者某些)节点上运行一个 Pod 的副本。当有节点加入集群时,也会为他们新增一个 Pod 。 + 当有节点从集群移除时,这些 Pod 也会被回收。删除 DaemonSet 将会删除它创建的所有 Pod。 + + + +使用 DaemonSet 的一些典型用法: + +- 运行集群存储 daemon,例如在每个节点上运行 `glusterd`、`ceph`。 +- 在每个节点上运行日志收集 daemon,例如`fluentd`、`logstash`。 +- 在每个节点上运行监控 daemon,例如 [Prometheus Node Exporter](https://github.com/prometheus/node_exporter)、`collectd`、Datadog 代理、New Relic 代理,或 Ganglia `gmond`。 + +一个简单的用法是在所有的节点上都启动一个 DaemonSet,将被作为每种类型的 daemon 使用。 +一个稍微复杂的用法是单独对每种 daemon 类型使用多个 DaemonSet,但具有不同的标志,和/或对不同硬件类型具有不同的内存、CPU要求。 + + + +## 编写 DaemonSet 规约 + +### 必需字段 + + + +和其它所有 Kubernetes 配置一样,DaemonSet 需要 `apiVersion`、`kind` 和 `metadata` 字段。 +有关配置文件的基本信息,详见文档 [deploying applications](/docs/user-guide/deploying-applications/)、[配置容器](/docs/user-guide/configuring-containers/) 和 [资源管理](/docs/concepts/tools/kubectl/object-management-overview/) 。 + +DaemonSet 也需要一个 [`.spec`](https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status) 配置段。 + + + +### Pod 模板 + +`.spec` 唯一必需的字段是 `.spec.template`。 + + + +`.spec.template` 是一个 [Pod 模板](/docs/user-guide/replication-controller/#pod-template)。 +它与 [Pod](/docs/user-guide/pods) 具有相同的 schema,除了它是嵌套的,而且不具有 `apiVersion` 或 `kind` 字段。 + +除了 Pod 必需字段外,在 DaemonSet 中的 Pod 模板必须指定合理的标签(查看 [Pod Selector](#pod-selector))。 + +在 DaemonSet 中的 Pod 模板必须具有一个值为 `Always` 的 [`RestartPolicy`](/docs/user-guide/pod-states),或者未指定它的值,默认是 `Always`。 + + + +### Pod Selector + +`.spec.selector` 字段表示 Pod Selector,它与 [Job](/docs/concepts/jobs/run-to-completion-finite-workloads/) 或其它资源的 `.spec.selector` 的作用是相同的。 + +`spec.selector` 表示一个对象,它由如下两个字段组成: + +* `matchLabels` - 与 [ReplicationController](/docs/concepts/workloads/controllers/replicationcontroller/) 的 `.spec.selector` 的作用相同。 +* `matchExpressions` - 允许构建更加复杂的 Selector,可以通过指定 key、value 列表,以及与 key 和 value 列表相关的操作符。 + + + +当上述两个字段都指定时,结果表示的是 AND 关系。 + +如果指定了 `.spec.selector`,必须与 `.spec.template.metadata.labels` 相匹配。如果没有指定,它们默认是等价的。如果与它们配置的不匹配,则会被 API 拒绝。 + +如果 Pod 的 label 与 selector 匹配,或者直接基于其它的 DaemonSet、或者 Controller(例如 ReplicationController),也不可以创建任何 Pod。 +否则 DaemonSet Controller 将认为那些 Pod 是它创建的。Kubernetes 不会阻止这样做。一个场景是,可能希望在一个具有不同值的、用来测试用的节点上手动创建 Pod。 + + + +### 仅在某些节点上运行 Pod + +如果指定了 `.spec.template.spec.nodeSelector`,DaemonSet Controller 将在能够与 [Node Selector](/docs/concepts/configuration/assign-pod-node/) 匹配的节点上创建 Pod。 +类似这种情况,可以指定 `.spec.template.spec.affinity`,然后 DaemonSet Controller 将在能够与 [Node Affinity](/docs/concepts/configuration/assign-pod-node/) 匹配的节点上创建 Pod。 +如果根本就没有指定,则 DaemonSet Controller 将在所有节点上创建 Pod。 + + + +## 如何调度 Daemon Pod + +正常情况下,Pod 运行在哪个机器上是由 Kubernetes 调度器来选择的。然而,由 Daemon Controller 创建的 Pod 已经确定了在哪个机器上(Pod 创建时指定了 `.spec.nodeName`),因此: + +- DaemonSet Controller 并不关心一个节点的 [`unschedulable`](/docs/admin/node/#manual-node-administration) 字段。 +- DaemonSet Controller 可以创建 Pod,即使调度器还没有启动,这对集群启动是非常有帮助的。 + + + +Daemon Pod 关心 [Taint 和 Toleration](/docs/concepts/configuration/assign-pod-node/#taints-and-tolerations-beta-feature),它们会为没有指定 `tolerationSeconds` 的 `node.alpha.kubernetes.io/notReady` 和 `node.alpha.kubernetes.io/unreachable` 的 Taint,创建具有 `NoExecute` 的 Toleration。这确保了当 alpha 特性的 `TaintBasedEvictions` 被启用时,发生节点故障,比如网络分区,这时它们将不会被清除掉(当 `TaintBasedEvictions` 特性没有启用,在这些场景下也不会被清除,但会因为 NodeController 的硬编码行为而被清除,而不会因为 Toleration 导致被清除)。 + + + +## 与 Daemon Pod 通信 + +与 DaemonSet 中的 Pod 进行通信,几种可能的模式如下: + +- **Push**:配置 DaemonSet 中的 Pod 向其它 Service 发送更新,例如统计数据库。它们没有客户端。 +- **NodeIP 和已知端口**:DaemonSet 中的 Pod 可以使用 `hostPort`,从而可以通过节点 IP 访问到 Pod。客户端能通过某种方法知道节点 IP 列表,并且基于此也可以知道端口。 +- **DNS**:创建具有相同 Pod Selector 的 [Headless Service](/docs/user-guide/services/#headless-services),然后通过使用 `endpoints` 资源或从 DNS 检索到多个 A 记录来发现 DaemonSet。 +- **Service**:创建具有相同 Pod Selector 的 Service,并使用该 Service 随机访问到某个节点上的 daemon(没有办法访问到特定节点)。 + + + +## 更新 DaemonSet + +如果修改了节点标签(Label),DaemonSet 将立刻向新匹配上的节点添加 Pod,同时删除新近不能够匹配的节点上的 Pod。 + +我们可以修改 DaemonSet 创建的 Pod。然而,不允许对 Pod 的所有字段进行更新。当下次节点(即使具有相同的名称)被创建时,DaemonSet Controller 还会使用最初的模板。 + + + +可以删除一个 DaemonSet。如果使用 `kubectl` 并指定 `--cascade=false` 选项,则 Pod 将被保留在节点上。然后可以创建具有不同模板的新 DaemonSet。具有不同模板的新 DaemonSet 将能够通过标签匹配并识别所有已经存在的 Pod。它不会修改或删除它们,即使是错误匹配了 Pod 模板。通过删除 Pod 或者删除节点,可以强制创建新的 Pod。 + +在 Kubernetes 1.6 或以后版本,可以在 DaemonSet 上 [执行滚动升级](/docs/tasks/manage-daemon/update-daemon-set/)。 + +未来的 Kubernetes 版本将支持节点的可控更新。 + + + +## DaemonSet 的可替代选择 + +### init 脚本 + +我们很可能希望直接在一个节点上启动 daemon 进程(例如,使用 `init`、`upstartd`、或 `systemd`)。这非常好,但基于 DaemonSet 来运行这些进程有如下一些好处: + + + +- 像对待应用程序一样,具备为 daemon 提供监控和管理日志的能力。 +- 为 daemon 和应用程序使用相同的配置语言和工具(如 Pod 模板、`kubectl`)。 +- Kubernetes 未来版本可能会支持对 DaemonSet 创建 Pod 与节点升级工作流进行集成。 +- 在资源受限的容器中运行 daemon,能够增加 daemon 和应用容器的隔离性。然而,这也实现了在容器中运行 daemon,但却不能在 Pod 中运行(例如,直接基于 Docker 启动)。 + + + +### 裸 Pod + +可能要直接创建 Pod,同时指定其运行在特定的节点上。 +然而,DaemonSet 替换了由于任何原因被删除或终止的 Pod,例如节点失败、例行节点维护、内核升级。由于这个原因,我们应该使用 DaemonSet 而不是单独创建 Pod。 + + + +### 静态 Pod + +可能需要通过在一个指定目录下编写文件来创建 Pod,该目录受 Kubelet 所监视。这些 Pod 被称为 [静态 Pod](/docs/concepts/cluster-administration/static-pod/)。 +不像 DaemonSet,静态 Pod 不受 kubectl 和其它 Kubernetes API 客户端管理。静态 Pod 不依赖于 apiserver,这使得它们在集群启动的情况下非常有用。 +而且,未来静态 Pod 可能会被废弃掉。 + + + +### Replication Controller + +DaemonSet 与 [Replication Controller](/docs/user-guide/replication-controller) 非常类似,它们都能创建 Pod,这些 Pod 对应的进程都不希望被终止掉(例如,Web 服务器、存储服务器)。 +为无状态的 Service 使用 Replication Controller,比如前端(Frontend)服务,实现对副本的数量进行扩缩容、平滑升级,比之于精确控制 Pod 运行在某个主机上要重要得多。 +需要 Pod 副本总是运行在全部或特定主机上,并需要先于其他 Pod 启动,当这被认为非常重要时,应该使用 Daemon Controller。 + diff --git a/cn/docs/concepts/workloads/controllers/garbage-collection.md b/cn/docs/concepts/workloads/controllers/garbage-collection.md new file mode 100644 index 0000000000000..1d8a921490ff1 --- /dev/null +++ b/cn/docs/concepts/workloads/controllers/garbage-collection.md @@ -0,0 +1,180 @@ +--- +title: 垃圾收集 +redirect_from: +- "/docs/concepts/abstractions/controllers/garbage-collection/" +- "/docs/concepts/abstractions/controllers/garbage-collection.html" +- "/docs/user-guide/garbage-collection/" +- "/docs/user-guide/garbage-collection.html" + +--- + +{% capture overview %} + + + +Kubernetes 垃圾收集器的角色是删除指定的对象,这些对象曾经有但以后不再拥有 Owner 了。 + +**注意**:垃圾收集是 beta 特性,在 Kubernetes 1.4 及以上版本默认启用。 + +{% endcapture %} + + +{% capture body %} + + +## Owner 和 Dependent + +某些 Kubernetes 对象是其它一些对象的 Owner。例如,一个 ReplicaSet 是一组 Pod 的 Owner。 +具有 Owner 的对象被称为是 Owner 的 *Dependent*。 +每个 Dependent 对象具有一个指向其所属对象的 `metadata.ownerReferences` 字段。 + +有时,Kubernetes 会自动设置 `ownerReference` 的值。 +例如,当创建一个 ReplicaSet 时,Kubernetes 自动设置 ReplicaSet 中每个 Pod 的 `ownerReference` 字段值。 +在 1.6 版本,Kubernetes 会自动为某些对象设置 `ownerReference` 的值,这些对象是由 ReplicationController、ReplicaSet、StatefulSet、DaemonSet 和 Deployment 所创建或管理。 + + + +也可以通过手动设置 `ownerReference` 的值,来指定 Owner 和 Dependent 之间的关系。 + +这里有一个配置文件,表示一个具有 3 个 Pod 的 ReplicaSet: + +{% include code.html language="yaml" file="my-repset.yaml" ghlink="/docs/concepts/workloads/controllers/my-repset.yaml" %} + + + +如果创建该 ReplicaSet,然后查看 Pod 的 metadata 字段,能够看到 OwnerReferences 字段: + +```shell +kubectl create -f https://k8s.io/docs/concepts/abstractions/controllers/my-repset.yaml +kubectl get pods --output=yaml +``` + + + +输出显示了 Pod 的 Owner 是名为 my-repset 的 ReplicaSet: + +```shell +apiVersion: v1 +kind: Pod +metadata: + ... + ownerReferences: + - apiVersion: extensions/v1beta1 + controller: true + blockOwnerDeletion: true + kind: ReplicaSet + name: my-repset + uid: d9607e19-f88f-11e6-a518-42010a800195 + ... +``` + + +## 控制垃圾收集器删除 Dependent + +当删除对象时,可以指定是否该对象的 Dependent 也自动删除掉。 +自动删除 Dependent 也称为 *级联删除*。 +Kubernetes 中有两种 *级联删除* 的模式:*background* 模式和 *foreground* 模式。 + +如果删除对象时,不自动删除它的 Dependent,这些 Dependent 被称作是原对象的 *孤儿*。 + + + +### Background 级联删除 + +在 *background 级联删除* 模式下,Kubernetes 会立即删除 Owner 对象,然后垃圾收集器会在后台删除这些 Dependent。 + + + +### Foreground 级联删除 + +在 *foreground 级联删除* 模式下,根对象首先进入 “删除中” 状态。在 “删除中” 状态会有如下的情况: + + * 对象仍然可以通过 REST API 可见。 + * 会设置对象的 `deletionTimestamp` 字段。 + * 对象的 `metadata.finalizers` 字段包含了值 "foregroundDeletion"。 + + 一旦对象被设置为 “删除中” 状态,垃圾收集器会删除对象的所有 Dependent。 + 垃圾收集器在删除了所有 “Blocking” 状态的 Dependent(对象的 `ownerReference.blockOwnerDeletion=true`)之后,它会删除 Owner 对象。 + + + +注意,在 “foreground 删除” 模式下,只有设置了 `ownerReference.blockOwnerDeletion` 值得 Dependent 才能阻止删除 Owner 对象。 +在 Kubernetes 1.7 版本中将增加许可控制器(Admission Controller),基于 Owner 对象上的删除权限来控制用户去设置 `blockOwnerDeletion` 的值为 true,所以未授权的 Dependent 不能够延迟 Owner 对象的删除。 + +如果一个对象的 `ownerReferences` 字段被一个 Controller(例如 Deployment 或 ReplicaSet)设置,`blockOwnerDeletion` 会被自动设置,不需要手动修改这个字段。 + + + +### 设置级联删除策略 + +通过为 Owner 对象设置 `deleteOptions.propagationPolicy` 字段,可以控制级联删除策略。 +可能的取值包括:“orphan”、“Foreground” 或 “Background”。 + +对很多 Controller 资源,包括 ReplicationController、ReplicaSet、StatefulSet、DaemonSet 和 Deployment,默认的垃圾收集策略是 `orphan`。 +因此,除非指定其它的垃圾收集策略,否则所有 Dependent 对象使用的都是 `orphan` 策略。 + +下面是一个在后台删除 Dependent 对象的例子: + +```shell +kubectl proxy --port=8080 +curl -X DELETE localhost:8080/apis/extensions/v1beta1/namespaces/default/replicasets/my-repset \ +-d '{"kind":"DeleteOptions","apiVersion":"v1","propagationPolicy":"Background"}' \ +-H "Content-Type: application/json" +``` + + + +下面是一个在前台删除 Dependent 对象的例子: + +```shell +kubectl proxy --port=8080 +curl -X DELETE localhost:8080/apis/extensions/v1beta1/namespaces/default/replicasets/my-repset \ +-d '{"kind":"DeleteOptions","apiVersion":"v1","propagationPolicy":"Foreground"}' \ +-H "Content-Type: application/json" +``` + + +下面是一个孤儿 Dependent 的例子: + +```shell +kubectl proxy --port=8080 +curl -X DELETE localhost:8080/apis/extensions/v1beta1/namespaces/default/replicasets/my-repset \ +-d '{"kind":"DeleteOptions","apiVersion":"v1","propagationPolicy":"Orphan"}' \ +-H "Content-Type: application/json" +``` + + + +kubectl 也支持级联删除。 +通过设置 `--cascade` 为 true,可以使用 kubectl 自动删除 Dependent 对象。 +设置 `--cascade` 为 false,会使 Dependent 对象成为孤儿 Dependent 对象。 +`--cascade` 的默认值是 true。 + +下面是一个例子,使一个 ReplicaSet 的 Dependent 对象成为孤儿 Dependent: + + +```shell +kubectl delete replicaset my-repset --cascade=false +``` + + + +## 已知的问题 +* 1.7 版本,垃圾收集不支持 [自定义资源](/docs/concepts/api-extension/custom-resources/),比如那些通过 CustomResourceDefinition 新增,或者通过 API server 聚集而成的资源对象。 + +[其它已知的问题](https://github.com/kubernetes/kubernetes/issues/26120) + +{% endcapture %} + + +{% capture whatsnext %} + + + +[设计文档 1](https://git.k8s.io/community/contributors/design-proposals/garbage-collection.md) +[设计文档 2](https://git.k8s.io/community/contributors/design-proposals/synchronous-garbage-collection.md) + +{% endcapture %} + + +{% include templates/concept.md %} diff --git a/cn/docs/concepts/workloads/pods/init-containers.md b/cn/docs/concepts/workloads/pods/init-containers.md new file mode 100644 index 0000000000000..47c31535c07ad --- /dev/null +++ b/cn/docs/concepts/workloads/pods/init-containers.md @@ -0,0 +1,330 @@ +--- +assignees: +- erictune +title: Init 容器 +redirect_from: +- "/docs/concepts/abstractions/init-containers/" +- "/docs/concepts/abstractions/init-containers.html" +- "/docs/user-guide/pods/init-container/" +- "/docs/user-guide/pods/init-container.html" +--- + +{% capture overview %} + +本页提供了 Init 容器的概览,它是一种专用的容器,在应用容器启动之前运行,并包括一些应用镜像中不存在的实用工具和安装脚本。 +{% endcapture %} + +{:toc} + + +这个特性在 1.6 版本已经退出 beta 版本。Init 容器可以在 PodSpec 中同应用的 `containers` 数组一起来指定。 +beta 注解的值将仍然需要保留,并覆盖 PodSpec 字段值。 + +{% capture body %} + + +## 理解 Init 容器 + + + +[Pod](/docs/concepts/abstractions/pod/) 能够具有多个容器,应用运行在容器里面,但是它也可能有一个或多个先于应用容器启动的 Init 容器。 + +Init 容器与普通的容器非常像,除了如下两点: + + + +* 它们总是运行到完成。 +* 每个都必须在下一个启动之前成功完成。 + + + +如果 Pod 的 Init 容器失败,Kubernetes 会不断地重启该 Pod,直到 Init 容器成功为止。然而,如果 Pod 对应的 `restartPolicy` 值为 Never,它不会重新启动。 + +指定容器为 Init 容器,需要在 PodSpec 中添加 `initContainers` 字段,以 [v1.Container](/docs/api-reference/v1.6/#container-v1-core) 类型对象的 JSON 数组的形式,还有 app 的 `containers` 数组。 +Init 容器的状态在 `status.initContainerStatuses` 字段中以容器状态数组的格式返回(类似 `status.containerStatuses` 字段)。 + + + +### 与普通容器的不同之处 + +Init 容器支持应用容器的全部字段和特性,包括资源限制、数据卷和安全设置。 +然而,Init 容器对资源请求和限制的处理稍有不同,在下面 [资源](#resources) 处有说明。 +而且 Init 容器不支持 Readiness Probe,因为它们必须在 Pod 就绪之前运行完成。 + +如果为一个 Pod 指定了多个 Init 容器,那些容器会按顺序一次运行一个。 +每个 Init 容器必须运行成功,下一个才能够运行。 +当所有的 Init 容器运行完成时,Kubernetes 初始化 Pod 并像平常一样运行应用容器。 + + + +## Init 容器能做什么? + +因为 Init 容器具有与应用容器分离的单独镜像,它们的启动相关代码具有如下优势: + +* 它们可以包含并运行实用工具,处于安全考虑,是不建议在应用容器镜像中包含这些实用工具的。 +* 它们可以包含使用工具和定制化代码来安装,但是不能出现在应用镜像中。例如,创建镜像没必要 `FROM` 另一个镜像,只需要在安装过程中使用类似 `sed`、 `awk`、 `python` 或 `dig` 这样的工具。 +* 应用镜像可以分离出创建和部署的角色,而没有必要联合它们构建一个单独的镜像。 +* 它们使用 Linux Namespace,所以对应用容器具有不同的文件系统视图。因此,它们能够具有访问 Secret 的权限,而应用容器不能够访问。 +* 它们在应用容器启动之前运行完成,然而应用容器并行运行,所以 Init 容器提供了一种简单的方式来阻塞或延迟应用容器的启动,直到满足了一组先决条件。 + + + +### 示例 + +下面是一些如何使用 Init 容器的想法: + +* 等待一个 Service 完成创建,通过类似如下 shell 命令: + + for i in {1..100}; do sleep 1; if dig myservice; then exit 0; fi; exit 1 + +* 注册这个 Pod 到远程服务器,通过在命令中调用 API,类似如下: + + curl -X POST http://$MANAGEMENT_SERVICE_HOST:$MANAGEMENT_SERVICE_PORT/register -d 'instance=$()&ip=$()' + +* 在启动应用容器之前等一段时间,使用类似 `sleep 60` 的命令。 +* 克隆 Git 仓库到数据卷。 +* 将配置值放到配置文件中,运行模板工具为主应用容器动态地生成配置文件。例如,在配置文件中存放 POD_IP 值,并使用 Jinja 生成主应用配置文件。 + +更多详细用法示例,可以在 [StatefulSet 文档](/docs/concepts/abstractions/controllers/statefulsets/) 和 [生产环境 Pod 指南](/docs/user-guide/production-pods.md#handling-initialization) 中找到。 + + + +### 使用 Init 容器 + +下面是 Kubernetes 1.5 版本 yaml 文件,展示了一个具有 2 个 Init 容器的简单 Pod。 +第一个等待 `myservice` 启动,第二个等待 `mydb` 启动。 +一旦这两个 Service 都启动完成,Pod 将开始启动。 + +```yaml +apiVersion: v1 +kind: Pod +metadata: + name: myapp-pod + labels: + app: myapp + annotations: + pod.beta.kubernetes.io/init-containers: '[ + { + "name": "init-myservice", + "image": "busybox", + "command": ["sh", "-c", "until nslookup myservice; do echo waiting for myservice; sleep 2; done;"] + }, + { + "name": "init-mydb", + "image": "busybox", + "command": ["sh", "-c", "until nslookup mydb; do echo waiting for mydb; sleep 2; done;"] + } + ]' +spec: + containers: + - name: myapp-container + image: busybox + command: ['sh', '-c', 'echo The app is running! && sleep 3600'] +``` + + + +这是 Kubernetes 1.6 版本的新语法,尽管老的 annotation 语法仍然可以使用。我们已经把 Init 容器的声明移到 `spec` 中: + +```yaml +apiVersion: v1 +kind: Pod +metadata: + name: myapp-pod + labels: + app: myapp +spec: + containers: + - name: myapp-container + image: busybox + command: ['sh', '-c', 'echo The app is running! && sleep 3600'] + initContainers: + - name: init-myservice + image: busybox + command: ['sh', '-c', 'until nslookup myservice; do echo waiting for myservice; sleep 2; done;'] + - name: init-mydb + image: busybox + command: ['sh', '-c', 'until nslookup mydb; do echo waiting for mydb; sleep 2; done;'] +``` + + + +1.5 版本的语法在 1.6 版本仍然可以使用,但是我们推荐使用 1.6 版本的新语法。 +在 Kubernetes 1.6 版本中,Init 容器在 API 中新建了一个字段。 +虽然期望使用 beta 版本的 annotation,但在未来发行版将会被废弃掉。 + +下面的 yaml 文件展示了 `mydb` 和 `myservice` 两个 Service: + +``` +kind: Service +apiVersion: v1 +metadata: + name: myservice +spec: + ports: + - protocol: TCP + port: 80 + targetPort: 9376 +--- +kind: Service +apiVersion: v1 +metadata: + name: mydb +spec: + ports: + - protocol: TCP + port: 80 + targetPort: 9377 +``` + + + +这个 Pod 可以使用下面的命令进行启动和调试: + +``` +$ kubectl create -f myapp.yaml +pod "myapp-pod" created +$ kubectl get -f myapp.yaml +NAME READY STATUS RESTARTS AGE +myapp-pod 0/1 Init:0/2 0 6m +$ kubectl describe -f myapp.yaml +Name: myapp-pod +Namespace: default +[...] +Labels: app=myapp +Status: Pending +[...] +Init Containers: + init-myservice: +[...] + State: Running +[...] + init-mydb: +[...] + State: Waiting + Reason: PodInitializing + Ready: False +[...] +Containers: + myapp-container: +[...] + State: Waiting + Reason: PodInitializing + Ready: False +[...] +Events: + FirstSeen LastSeen Count From SubObjectPath Type Reason Message + --------- -------- ----- ---- ------------- -------- ------ ------- + 16s 16s 1 {default-scheduler } Normal Scheduled Successfully assigned myapp-pod to 172.17.4.201 + 16s 16s 1 {kubelet 172.17.4.201} spec.initContainers{init-myservice} Normal Pulling pulling image "busybox" + 13s 13s 1 {kubelet 172.17.4.201} spec.initContainers{init-myservice} Normal Pulled Successfully pulled image "busybox" + 13s 13s 1 {kubelet 172.17.4.201} spec.initContainers{init-myservice} Normal Created Created container with docker id 5ced34a04634; Security:[seccomp=unconfined] + 13s 13s 1 {kubelet 172.17.4.201} spec.initContainers{init-myservice} Normal Started Started container with docker id 5ced34a04634 +$ kubectl logs myapp-pod -c init-myservice # Inspect the first init container +$ kubectl logs myapp-pod -c init-mydb # Inspect the second init container +``` + + + +一旦我们启动了 `mydb` 和 `myservice` 这两个 Service,我们能够看到 Init 容器完成,并且 `myapp-pod` 被创建: + +``` +$ kubectl create -f services.yaml +service "myservice" created +service "mydb" created +$ kubectl get -f myapp.yaml +NAME READY STATUS RESTARTS AGE +myapp-pod 1/1 Running 0 9m +``` + + + +这个例子非常简单,但是应该能够为创建自己的 Init 容器提供一些启发。 + + + +## 具体行为 + +在 Pod 启动过程中,Init 容器会按顺序在网络和数据卷初始化之后启动。 +每个容器必须在下一个容器启动之前成功退出。 +如果由于运行时或失败退出,导致容器启动失败,它会根据 Pod 的 `restartPolicy` 指定的策略进行重试。 +然而,如果 Pod 的 `restartPolicy` 设置为 Always,Init 容器失败时会使用 `RestartPolicy` 策略。 + + + +在所有的 Init 容器没有成功之前,Pod 将不会变成 `Ready` 状态。 +Init 容器的端口将不会在 Service 中进行聚集。 +正在初始化中的 Pod 处于 `Pending` 状态,但应该会将条件 `Initializing` 设置为 true。 + +如果 Pod [重启](#pod-restart-reasons),所有 Init 容器必须重新执行。 + +对 Init 容器 spec 的修改,被限制在容器 image 字段中。 +更改 Init 容器的 image 字段,等价于重启该 Pod。 + + + +因为 Init 容器可能会被重启、重试或者重新执行,所以 Init 容器的代码应该是幂等的。 +特别地,被写到 `EmptyDirs` 中文件的代码,应该对输出文件可能已经存在做好准备。 + +Init 容器具有应用容器的所有字段。 +然而 Kubernetes 禁止使用 `readinessProbe`,因为 Init 容器不能够定义不同于完成(completion)的就绪(readiness)。 +这会在验证过程中强制执行。 + + + +在 Pod 上使用 `activeDeadlineSeconds`,在容器上使用 `livenessProbe`,这样能够避免 Init 容器一直失败。 +这就为 Init 容器活跃设置了一个期限。 + +在 Pod 中的每个 app 和 Init 容器的名称必须唯一;与任何其它容器共享同一个名称,会在验证时抛出错误。 + + + +### 资源 + +为 Init 容器指定顺序和执行逻辑,下面对资源使用的规则将被应用: + + + +* 在所有 Init 容器上定义的,任何特殊资源请求或限制的最大值,是 *有效初始请求/限制* +* Pod 对资源的 *有效请求/限制* 要高于: + * 所有应用容器对某个资源的请求/限制之和 + * 对某个资源的有效初始请求/限制 +* 基于有效请求/限制完成调度,这意味着 Init 容器能够为初始化预留资源,这些资源在 Pod 生命周期过程中并没有被使用。 +* Pod 的 *有效 QoS 层*,是 Init 容器和应用容器相同的 QoS 层。 + + + +基于有效 Pod 请求和限制来应用配额和限制。 +Pod 级别的 cgroups 是基于有效 Pod 请求和限制,和调度器相同。 + + + +### Pod 重启的原因 + +Pod 能够重启,会导致 Init 容器重新执行,主要有如下几个原因: + +* 用户更新 PodSpec 导致 Init 容器镜像发生改变。应用容器镜像的变更只会重启应用容器。 +* Pod 基础设施容器被重启。这不多见,但某些具有 root 权限可访问 Node 的人可能会这样做。 +* 当 `restartPolicy` 设置为 Always,Pod 中所有容器会终止,强制重启,由于垃圾收集导致 Init 容器完成的记录丢失。 + + + +## 支持与兼容性 + +Apiserver 版本为 1.6 或更高版本的集群,通过使用 `spec.initContainers` 字段来支持 Init 容器。 +之前的版本可以使用 alpha 和 beta 注解支持 Init 容器。 +`spec.initContainers` 字段也被加入到 alpha 和 beta 注解中,所以 Kubernetes 1.3.0 版本或更高版本可以执行 Init 容器,并且 1.6 版本的 apiserver 能够安全的回退到 1.5.x 版本,而不会使存在的已创建 Pod 失去 Init 容器的功能。 + +{% endcapture %} + + +{% capture whatsnext %} + + + +* [创建具有 Init 容器的 Pod](/docs/tasks/configure-pod-container/configure-pod-initialization/#creating-a-pod-that-has-an-init-container) + +{% endcapture %} + + +{% include templates/concept.md %} diff --git a/cn/docs/tasks/administer-cluster/cpu-memory-limit.md b/cn/docs/tasks/administer-cluster/cpu-memory-limit.md new file mode 100644 index 0000000000000..46808eeb71ad1 --- /dev/null +++ b/cn/docs/tasks/administer-cluster/cpu-memory-limit.md @@ -0,0 +1,259 @@ +--- +assignees: +- derekwaynecarr +- janetkuo +title: 设置 Pod CPU 和内存限制 +redirect_from: +- "/docs/admin/limitrange/" +- "/docs/admin/limitrange/index.html" +- "/docs/tasks/configure-pod-container/limit-range/" +- "/docs/tasks/configure-pod-container/limit-range.html" +--- + +{% capture overview %} + + + +默认情况下,Pod 运行没有限制 CPU 和内存。 +这意味着系统中的任何 Pod 将能够像执行该 Pod 所在的节点一样,使用足够多 CPU 和内存。 + + + +这个例子演示了如何限制 Kubernetes [Namespace](/docs/tasks/administer-cluster/namespaces-walkthrough/),以此来控制每个 Pod 的最小/最大资源限额。 +另外,这个例子演示了当终端用户没有为 Pod 设置资源限额时,如何使用默认的资源限额。 + +{% endcapture %} + +{% capture prerequisites %} + +* {% include task-tutorial-prereqs.md %} + +{% endcapture %} + +{% capture steps %} + + + +## 创建 Namespace + +这个例子将使用一个自定义的 Namespace 来演示相关的概念。 + +让我们创建一个名称为 limit-example 的 Namespace: + +```shell +$ kubectl create namespace limit-example +namespace "limit-example" created +``` + + + +注意到 `kubectl` 命令将打印出被创建或修改的资源的类型和名称,也会在后面的命令中使用到: + +```shell +$ kubectl get namespaces +NAME STATUS AGE +default Active 51s +limit-example Active 45s +``` + + + +## 对 Namespace 应用限制 + +在我们的 Namespace 中创建一个简单的限制: + +```shell +$ kubectl create -f https://k8s.io/docs/tasks/configure-pod-container/limits.yaml --namespace=limit-example +limitrange "mylimits" created +``` + + + +让我们描述一下在该 Namespace 中被强加的限制: + +```shell +$ kubectl describe limits mylimits --namespace=limit-example +Name: mylimits +Namespace: limit-example +Type Resource Min Max Default Request Default Limit Max Limit/Request Ratio +---- -------- --- --- --------------- ------------- ----------------------- +Pod cpu 200m 2 - - - +Pod memory 6Mi 1Gi - - - +Container cpu 100m 2 200m 300m - +Container memory 3Mi 1Gi 100Mi 200Mi - +``` + + + +在这个场景下,指定了如下限制: + +1. 如果一个资源被指定了最大约束(在该例子中为 2 CPU 和 1Gi 内存),则必须为跨所有容器的该资源指定限制(limits)。 +当尝试创建该 Pod 时,指定限额失败将导致一个验证错误。 +注意,一个默认的限额通过在 `limits.yaml` 文件中的 *default* 来设置(300m CPU 和 200Mi 内存)。 +2. 如果一个资源被指定了最小约束(在该例子中为 100m CPU 和 3Mi 内存),则必须跨所有容器的该资源指定请求(requests)。 +当尝试创建该 Pod 时,指定的请求失败将导致一个验证错误。 +注意,一个默认的请求的值通过在 `limits.yaml` 文件中的 *defaultRequest* 来设置(200m CPU 和 100Mi 内存)。 +3. 对任意 Pod,所有容器内存 requests 值之和必须 >= 6Mi,所有容器内存 limits 值之和必须 <= 1Gi; +所有容器 CPU requests 值之和必须 >= 200m,所有容器 CPU limits 值之和必须 <= 2。 + + + +## 创建时强制设置限制 + +当集群中的 Pod 创建和更新时,在一个 Namespace 中列出的限额是强制设置的。 +如果将该限额修改成一个不同的值范围,它不会影响先前在该 Namespace 中创建的 Pod。 + + + +如果资源(CPU 或内存)被设置限额,用户将在创建时得到一个错误,并指出了错误的原因。 + +首先让我们启动一个 [Deployment](/docs/concepts/workloads/controllers/deployment/),它创建一个单容器 Pod,演示了如何将默认值应用到每个 Pod 上: + +```shell +$ kubectl run nginx --image=nginx --replicas=1 --namespace=limit-example +deployment "nginx" created +``` + + +注意,在 >= v1.2 版本的 Kubernetes 集群中,`kubectl run` 创建了名称为 “nginx” 的 Deployment。如果在老版本的集群上运行,相反它会创建 ReplicationController。 +如果想要获取老版本的行为,使用 `--generator=run/v1` 选项来创建 ReplicationController。查看 [`kubectl run`](/docs/user-guide/kubectl/v1.6/#run) 获取更多详细信息。 +Deployment 管理单容器 Pod 的 1 个副本。让我们看一下它是如何管理 Pod 的。首先,查找到 Pod 的名称: + +```shell +$ kubectl get pods --namespace=limit-example +NAME READY STATUS RESTARTS AGE +nginx-2040093540-s8vzu 1/1 Running 0 11s +``` + + + +以 yaml 输出格式来打印这个 Pod,然后 `grep` 其中的 `resources` 字段。注意,您自己的 Pod 的名称将不同于上面输出的: + +```shell +$ kubectl get pods nginx-2040093540-s8vzu --namespace=limit-example -o yaml | grep resources -C 8 + resourceVersion: "57" + selfLink: /api/v1/namespaces/limit-example/pods/nginx-2040093540-ivimu + uid: 67b20741-f53b-11e5-b066-64510658e388 +spec: + containers: + - image: nginx + imagePullPolicy: Always + name: nginx + resources: + limits: + cpu: 300m + memory: 200Mi + requests: + cpu: 200m + memory: 100Mi + terminationMessagePath: /dev/termination-log + volumeMounts: +``` + + + +注意,我们的 Nginx 容器已经使用了 Namespace 的默认 CPU 和内存资源的 *limits* 和 *requests*。 + +让我们创建一个 Pod,它具有一个请求 3 CPU 核心的容器,这超过了被允许的限额: + +```shell +$ kubectl create -f https://k8s.io/docs/tasks/configure-pod-container/invalid-pod.yaml --namespace=limit-example +Error from server: error when creating "http://k8s.io/docs/tasks/configure-pod-container/invalid-pod.yaml": Pod "invalid-pod" is forbidden: [Maximum cpu usage per Pod is 2, but limit is 3., Maximum cpu usage per Container is 2, but limit is 3.] +``` + + + +让我们创建一个 Pod,使它在允许的最大限额范围之内: + +```shell +$ kubectl create -f https://k8s.io/docs/tasks/configure-pod-container/valid-pod.yaml --namespace=limit-example +pod "valid-pod" created +``` + + + +现在查看该 Pod 的 resources 字段: + +```shell +$ kubectl get pods valid-pod --namespace=limit-example -o yaml | grep -C 6 resources + uid: 3b1bfd7a-f53c-11e5-b066-64510658e388 +spec: + containers: + - image: gcr.io/google_containers/serve_hostname + imagePullPolicy: Always + name: kubernetes-serve-hostname + resources: + limits: + cpu: "1" + memory: 512Mi + requests: + cpu: "1" + memory: 512Mi +``` + + + +注意到这个 Pod 显式地指定了资源 *limits* 和 *requests*,所以它不会使用该 Namespace 的默认值。 + +注意:在物理节点上默认安装的 Kubernetes 集群中,CPU 资源的 *limits* 是被强制使用的,该 Kubernetes 集群运行容器,除非管理员在部署 kublet 时使用了如下标志: + +```shell +$ kubelet --help +Usage of kubelet +.... + --cpu-cfs-quota[=true]: Enable CPU CFS quota enforcement for containers that specify CPU limits +$ kubelet --cpu-cfs-quota=false ... +``` + + + +## 清理 + +基于使用的该示例来清理资源,可以通过如下命令删除名称为 limit-example 的 Namespace: + +```shell +$ kubectl delete namespace limit-example +namespace "limit-example" deleted +$ kubectl get namespaces +NAME STATUS AGE +default Active 12m +``` +{% endcapture %} + +{% capture discussion %} + + +## 设置资限额制的动机 + +可能由于对资源使用的各种原因,用户希望对单个 Pod 的资源总量进行强制限制。 + +例如: + + + +1. 集群中每个节点有 2GB 内存。集群操作员不想接受内存需求大于 2GB 的 Pod,因为集群中没有节点能支持这个要求。 +为了避免 Pod 永远无法被调度到集群中的节点上,操作员会选择去拒绝超过 2GB 内存作为许可控制的 Pod。 +2. 一个集群被一个组织内部的 2 个团体共享,分别作为生产和开发工作负载来运行。 +生产工作负载可能消耗多达 8GB 内存,而开发工作负载可能消耗 512MB 内存。 +集群操作员为每个工作负载创建了一个单独的 Namespace,为每个 Namespace 设置了限额。 +3. 用户会可能创建一个资源消耗低于机器容量的 Pod。剩余的空间可能太小但很有用,然而对于整个集群来说浪费的代价是足够大的。 +结果集群操作员会希望设置限额:为了统一调度和限制浪费,Pod 必须至少消耗它们平均节点大小 20% 的内存和 CPU。 + + + +## 总结 + +想要限制单个容器或 Pod 消耗资源总量的集群操作员,能够为每个 Kubernetes Namespace 定义可允许的范围。 +在没有任何明确指派的情况下,Kubernetes 系统能够使用默认的资源 *limits* 和 *requests*,如果需要的话,限制一个节点上的 Pod 的资源总量。 + +{% endcapture %} + +{% capture whatsnext %} + + +* 查看 [LimitRange 设计文档](https://git.k8s.io/community/contributors/design-proposals/admission_control_limit_range.md) 获取更多信息。 +* 查看 [资源](/docs/concepts/configuration/manage-compute-resources-container/) 获取关于 Kubernetes 资源模型的详细描述。 + +{% endcapture %} + +{% include templates/task.md %} diff --git a/cn/docs/tasks/administer-cluster/dns-custom-nameservers.md b/cn/docs/tasks/administer-cluster/dns-custom-nameservers.md new file mode 100644 index 0000000000000..bd84ce0b88607 --- /dev/null +++ b/cn/docs/tasks/administer-cluster/dns-custom-nameservers.md @@ -0,0 +1,161 @@ +--- +approvers: +- bowei +- zihongz +title: 在 Kubernetes 中配置私有 DNS 和上游域名服务器 +--- + +{% capture overview %} + +本页展示了如何添加自定义私有 DNS 域(存根域)和上游域名服务器。 + +{% endcapture %} + +{% capture prerequisites %} +* {% include task-tutorial-prereqs.md %} + + + +* Kubernetes 1.6 及其以上版本。 +* 集群必须配置使用 `kube-dns` 插件。 + +{% endcapture %} + +{% capture steps %} + + + +## 配置存根域和上游 DNS 服务器 + +通过为 kube-dns (`kube-system:kube-dns`)提供 ConfigMap,集群管理员能够指定自定义存根域和上游域名服务器。 + +例如,下面的 ConfigMap 建立了一个 DNS 配置,它具有一个单独的存根域和两个上游域名服务器: + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: kube-dns + namespace: kube-system +data: + stubDomains: | + {“acme.local”: [“1.2.3.4”]} + upstreamNameservers: | + [“8.8.8.8”, “8.8.4.4”] +``` + + + +按如上说明,具有 “.acme.local” 后缀的 DNS 请求被转发到 DNS 1.2.3.4。Google 公共 DNS服务器 为上游查询提供服务。 +下表描述了具有特定域名的查询如何映射到它们的目标 DNS 服务器: + + +| 域名 | 响应查询的服务器 | +| ----------- | -------------------------- | +| kubernetes.default.svc.cluster.local| kube-dns | +| foo.acme.local| 自定义 DNS (1.2.3.4) | +| widget.com | 上游 DNS (8.8.8.8, 8.8.4.4,其中之一) | + + +查看 [ConfigMap 选项](#configmap-options) 获取更多关于配置选项格式的详细信息。 + +{% endcapture %} + +{% capture discussion %} + + + +## 理解 Kubernetes 中名字解析 + +可以为每个 Pod 设置 DNS 策略。 +当前 Kubernetes 支持两种 Pod 特定的 DNS 策略:“Default” 和 “ClusterFirst”。 +可以通过 `dnsPolicy` 标志来指定这些策略。 +*注意:“Default” 不是默认的 DNS 策略。如果没有显式地指定 `dnsPolicy`,将会使用 “ClusterFirst”。* + + + +### "Default" DNS 策略 + +如果 `dnsPolicy` 被设置为 “Default”,则名字解析配置会继承自 Pod 运行所在的节点。 +自定义上游域名服务器和存根域不能够与这个策略一起使用。 + + + +### "ClusterFirst" DNS 策略 + +如果 `dnsPolicy` 被设置为 "ClusterFirst",处理名字解析有所不同,*依赖于是否配置了存根域和上游 DNS 服务器*。 + +**未进行自定义配置**:没有匹配上配置的集群域名后缀的任何请求,例如 “www.kubernetes.io”,将会被转发到继承自节点的上游域名服务器。 + +**进行自定义配置**:如果配置了存根域和上游 DNS 服务器(类似于 [前面示例](#configuring-stub-domain-and-upstream-dns-servers) 配置的内容),DNS 查询将基于下面的流程对请求进行路由: + + + +1. 查询首先被发送到 kube-dns 中的 DNS 缓存层。 + +1. 从缓存层,检查请求的后缀,并根据下面的情况转发到对应的 DNS 上: + + * *具有集群后缀的名字*(例如 ".cluster.local"):请求被发送到 kube-dns。 + + * *具有存根域后缀的名字*(例如 ".acme.local"):请求被发送到配置的自定义 DNS 解析器(例如:监听在 1.2.3.4)。 + + * *未能匹配上后缀的名字*(例如 "widget.com"):请求被转发到上游 DNS(例如:Google 公共 DNS 服务器,8.8.8.8 和 8.8.4.4)。 + +![DNS 查询流程](/docs/tasks/administer-cluster/dns-custom-nameservers/dns.png) + + + +## ConfigMap 选项 + +kube-dns `kube-system:kube-dns` 对应的 ConfigMap 选项如下所示: + +| 字段 | 格式 | 描述 | +| ----- | ------ | ----------- | +| `stubDomains`(可选)| 使用 DNS 后缀作为键(例如 “acme.local”)的 JSON map,以及由 DNS IP 的 JSON 数组组成的值。 | 目标域名服务器可能是一个 Kubernetes Service。例如,可以运行自己的 dnsmasq 副本,将 DNS 名字暴露到 ClusterDNS namespace 中。| +| `upstreamNameservers`(可选)| DNS IP 的 JSON 数组。 | 注意:如果指定,则指定的值会替换掉被默认从节点的 `/etc/resolv.conf` 中获取到的域名服务器。限制:最多可以指定三个上游域名服务器。| + + + +## 附加示例 + +### 示例:存根域 + +在这个例子中,用户有一个 Consul DNS 服务发现系统,他们希望能够与 kube-dns 集成起来。 +Consul 域名服务器地址为 10.150.0.1,所有的 Consul 名字具有后缀 “.consul.local”。 +要配置 Kubernetes,集群管理员只需要简单地创建一个 ConfigMap 对象,如下所示: + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: kube-dns + namespace: kube-system + data: + stubDomains: | + {“consul.local”: [“10.150.0.1”]} +``` + + +注意,集群管理员不希望覆盖节点的上游域名服务器,所以他们不会指定可选的 `upstreamNameservers` 字段。 + + + +### 示例:上游域名服务器 + +在这个示例中,集群管理员不希望显式地强制所有非集群 DNS 查询进入到他们自己的域名服务器 172.16.0.1。 +而且这很容易实现:他们只需要创建一个 ConfigMap,`upstreamNameservers` 字段指定期望的域名服务器即可: + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: kube-dns + namespace: kube-system + data: + upstreamNameservers: | + [“172.16.0.1”] +``` + +{% endcapture %} + +{% include templates/task.md %} diff --git a/cn/docs/tasks/administer-cluster/guaranteed-scheduling-critical-addon-pods.md b/cn/docs/tasks/administer-cluster/guaranteed-scheduling-critical-addon-pods.md new file mode 100644 index 0000000000000..0ea2bee5b9175 --- /dev/null +++ b/cn/docs/tasks/administer-cluster/guaranteed-scheduling-critical-addon-pods.md @@ -0,0 +1,55 @@ +--- +approvers: +- davidopp +- filipg +- piosz +title: 关键插件 Pod 的调度保证 +--- + +* TOC +{:toc} + + + +## 概览 + +除了 Kubernetes 核心组件,像运行在 master 机器上的 api-server、scheduler、controller-manager,由于各种原因,还有很多插件必须运行在一个普通的集群节点上(而不是 Kubernetes master)。 +这些插件中的一些对于一个功能完备的集群来说是非常关键的,例如 Heapster、DNS 以及 UI。 +如果一个关键的插件被移除(或者手动,或是类似升级这样具有副作用的其它操作),或者变成挂起状态(例如,当集群利用率过高,以及或者其它被调度到该空间中的挂起 Pod 被清理关键插件 Pod 给移除,或者由于其它原因导致节点上可用资源的总量发生变化),集群可能会停止正常工作。 + + + +## 二次调度器:关键插件的调度保证 + +二次调度器确保关键的插件总是能被调度(假定普通的 Pod 不存在时,集群具有足够的资源去运行关键插件 Pod)。 +如果调度器确定没有节点有足够资源去运行关键插件 Pod,假使有 Pod 已经运行在集群中(通过将关键插件 Pod 的条件 PodScheduled 设置为 false,原因设置为 Unschedulable 来指示),这时二次调度器通过清理一些 Pod 尽量去释放空间,然后调度器将调度该插件 Pod。 + + + +为了避免这种情况,当另一个 Pod 被调度到该空间,为了该关键插件,被选择的节点导致临时变成 "CriticalAddonsOnly" 的 taint(查看 [更多详情](https://git.k8s.io/community/contributors/design-proposals/taint-toleration-dedicated.md))。 +每个关键插件不得不容忍它,然而其它一些 Pod 不可能容忍该 taint。一旦该插件被成功调度,该 taint 会被移除。 + +*警告:* 当前对选中哪个节点,以及为了调度该关键 Pod 杀掉哪个 Pod 并没有任何保证,所以如果启用了二次调度器,任何 Pod 都可能被偶然的杀掉以达到目的。 + + + +## 配置 + +二次调度器应该被 [作为 static Pod 默认启用](https://git.k8s.io/kubernetes/cluster/saltbase/salt/rescheduler/rescheduler.manifest)。 +它没有任何面向用户的配置(组件配置),或者 API,可以通过如下方式来禁用: + + + +* 在集群安装过程中通过设置 `ENABLE_RESCHEDULER` 标志为 `false` +* 在运行中的集群中从 master 节点删除它的 manifest(默认路径 `/etc/kubernetes/manifests/rescheduler.manifest`) + + + +### 标记关键插件 + +想变成关键插件,必须运行在 `kube-system` Namespace 中(是基于标志可配置的),并且: +* 将 `scheduler.alpha.kubernetes.io/critical-pod` annotation 设置为空字符串,并且 +* 将 PodSpec 的 `tolerations` 字段设置为 `[{"key":"CriticalAddonsOnly", "operator":"Exists"}]` + +第一个表示是一个关键 Pod。第二个是二次调度器算法必需的。 +