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.
pull/5673/head
Dean Shi 2017-09-28 23:50:35 +08:00 committed by Andrew Chen
parent d4e8e8fb9e
commit 88d3ed821d
15 changed files with 3222 additions and 0 deletions

View File

@ -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 %}

View File

@ -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

View File

@ -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 控制基于角色和组的已授权容器的访问 。

View File

@ -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:
- '*'

View File

@ -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 重新安装后被覆盖。因此,不建议修改该文件的内容。

View File

@ -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 <none> 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: <unset> 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 `<CLUSTER-IP>:<PORT>`
注意 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 <none> 53/UDP,53/TCP 8m
```
如果没有在运行,可以 [启用它](http://releases.k8s.io/{{page.githubbranch}}/cluster/addons/dns/README.md#how-do-i-configure-it)。
本段剩余的内容,将假设已经有一个 Service它具有一个长久存在的 IPmy-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
...
<h1>Welcome to nginx!</h1>
```
注意最后一步我们是如何提供 `-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
...
<title>Welcome to nginx!</title>
...
```
## 暴露 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://<EXTERNAL-IP>:<NODE-PORT> -k
...
<h1>Welcome to nginx!</h1>
```
让我们重新创建一个 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://<EXTERNAL-IP> -k
...
<title>Welcome to nginx!</title>
```
`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)

View File

@ -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 的子域名。
最终的域名将是 “<hostname>.<subdomain>.<pod namespace>.svc.<cluster domain>”。
举个例子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`,会以 <hostname>.<service name>.<pod namespace>.svc.<cluster domain> 的格式创建 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 <some-time>
```
### 验证 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 <none> 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=<default local 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/)

View File

@ -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 地址不能是 loopback127.0.0.0/8、 link-local169.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` APIbeta 版),用来表示 “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 SRVService记录。
如果名称为 `"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 IPkube-proxy 不会处理它们,而且平台也不会为它们进行负载均衡和路由。
DNS 如何实现自动配置,依赖于 `Service` 是否定义了 selector。
### 配置 Selector
对定义了 selector 的 Headless ServiceEndpoint 控制器在 API 中创建了 `Endpoints` 记录,并且修改 DNS 配置返回 A 记录(地址),通过这个地址直接到达 `Service` 的后端 `Pod` 上。
### 不配置 Selector
对没有定义 selector 的 Headless ServiceEndpoint 控制器不会创建 `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` 服务会自动创建。通过请求 `<NodeIP>:<NodePort>`,可以从集群的外部访问一个 `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 将能够通过 `<NodeIP>: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 和 SSLELB 将期望该 `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 将简化数据包的传输。
我们打算为 L7HTTP`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并开始接收请求连接。
当一个客户端连接到一个 VIPiptables 规则开始起作用,它会重定向该数据包到 `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。
当一个客户端连接到一个 VIPiptables 规则开始起作用。一个 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/)。

View File

@ -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 <none>
```
如上所示,既没有 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 完成后将不会被保留。

View File

@ -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
如果修改了节点标签LabelDaemonSet 将立刻向新匹配上的节点添加 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。

View File

@ -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 %}

View File

@ -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=$(<POD_NAME>)&ip=$(<POD_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` 设置为 AlwaysInit 容器失败时会使用 `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` 设置为 AlwaysPod 中所有容器会终止,强制重启,由于垃圾收集导致 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 %}

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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。第二个是二次调度器算法必需的。