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
parent
d4e8e8fb9e
commit
88d3ed821d
|
@ -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 %}
|
|
@ -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
|
|
@ -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 控制基于角色和组的已授权容器的访问 。
|
|
@ -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:
|
||||
- '*'
|
|
@ -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 重新安装后被覆盖。因此,不建议修改该文件的内容。
|
|
@ -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,它具有一个长久存在的 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
|
||||
...
|
||||
<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)
|
||||
|
|
@ -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/)
|
|
@ -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` 服务会自动创建。通过请求 `<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 和 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/)。
|
|
@ -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 完成后将不会被保留。
|
|
@ -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。
|
||||
|
|
@ -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 %}
|
|
@ -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` 设置为 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 %}
|
|
@ -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 %}
|
|
@ -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 %}
|
|
@ -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。第二个是二次调度器算法必需的。
|
||||
|
Loading…
Reference in New Issue