443 lines
32 KiB
Markdown
443 lines
32 KiB
Markdown
# 第二章 Pods & Deployment 在 Kubernetes 中的应用
|
||
|
||
Kubernetes 通过容器来运行应用的工作负载,但是容器并不是在 Kubernetes 场景下你需要工作处理的对象。每个容器都属于某个叫做 Pod 的对象,它用于管理一个或多个容器,然后另外一个方面 Pods 被其它类型的资源所管理。这些高层级的对象抽象了具体的容器,它们提供了自我修复的程序并且提供了目标状态的工作流程:你告诉 Kubernetes 你想要实现的内容,然后 Kubernetes 来决定如何实现。
|
||
|
||
在本章中,我们将从 Kubernetes 基础的构建块开始:Pods 用于运行容器,以及 Deployments 用于管理 Pods。我们将使用一个简单的 Web app 进行练习,让你亲自动手使用 Kubernetes 命令行工具来管理应用并且使用 Kubernetes YAML 配置文件来定义应用程序。
|
||
|
||
## 2.1 Kubernetes 如何运行并管理容器
|
||
|
||
一个容器通常情况下作为虚拟环境来运行单个应用程序的组件。Kubernetes 将容器包装在另一个虚拟环境中: Pod。Pod 是一个计算单元,它在集群中的单个节点上运行。Pod 拥有受 Kubernetes 管理的的虚拟 IP 地址,并且就算Pods 运行在不同节点,它们之间也可以通过 Kubernetes 的虚拟网络进行通信。
|
||
|
||
正常情况下你只会在一个 Pod 中运行一个容器,但是你可以在一个 Pod 中运行多个容器,这将会开启一些有趣的部署选项。一个 Pod 中的所有容器将拥有相同的虚拟环境,所以它们共享相同的网络地址并且可以通过 localhost 通信。图 2.1 显示了容器与 Pods 之间的关系。
|
||
|
||
![图2.1](./images/Figure2.1.png)
|
||
<center>图2.1 容器在 Pod 内运行。你管理 Pods, Pods 管理容器 </center>
|
||
|
||
多容器 Pods 的业务在早期介绍有点多,但如果我掩盖它,只谈论单容器 Pods,你会理所当然地问:为什么 Kubernetes 使用 Pods 而不是容器。让我们运行一个Pod,然后看看使用容器上的这种抽象是什么样子的。
|
||
|
||
<b>现在就试试</b> 你可以通过 Kubernetes 命令行运行一个简单的 Pod(无需编写 YAML 文件)。语法类似于 Docker 运行容器: 你需要说明你要使用的容器的镜像以及任何用于配置 Pod 行为的其他参数。
|
||
|
||
```
|
||
# 运行一个单容器 Pod; restart 参数代表如果退出,不重启:
|
||
kubectl run hello-kiamol --image=kiamol/ch02-hello-kiamol --restart=Never
|
||
# 等待 Pod 就绪:
|
||
kubectl wait --for=condition=Ready pod hello-kiamol
|
||
# 查询所有的 Pod 清单:
|
||
kubectl get pods
|
||
# 查看 Pod 详细信息:
|
||
kubectl describe pod hello-kiamol
|
||
```
|
||
|
||
你可以看到图 2.2 是我执行时的输出,当你自己运行它时,你将在其中看到很多看起来晦涩难懂的信息,比如节点选择符和容错。他们都是 Pod 配置规范,Kubernetes 已将缺省值应用于我们没有在 Run 命令中指定的配置。
|
||
|
||
![图2.2](./images/Figure2.2.png)
|
||
<center>图2.2 运行最简单的 Pods 并且使用 Kubectl 检查它的状态 </center>
|
||
|
||
现在你的集群中拥有一个应用容器,它运行在一个 Pod 内。如果你之前接触过 Docker,这是一个熟悉的工作流程,事实证明 Pods 并不像看起来那么复杂。你的大多数 Pods 都会运行单个容器(直到您开始探索更高级的选项),因此你可以有效地将 Pod 视为 Kubernetes 用来运行容器的一种机制。
|
||
|
||
Kubernetes 并不真正运行容器,它把这个责任交给节点上的容器运行时,可能会是 Docker 或者其他之类的。这就是为什么是抽象的:它是 Kubernetes 管理的资源,而容器则是 Kubernetes 之外的某个东西管理。你可以通过 Kubectl 来获取有关 Pod 的信息。
|
||
|
||
<b>现在就试试</b> Kubectl 从 get pod 命令返回基本的信息,你可以通过指定输出相关的参数来请求返回更多的内容。你可以说出要在输出参数中看到的各个字段,可以使用 JSONPath 查询语言或者 Go 模板来得到复杂的输出。
|
||
|
||
```
|
||
# 获取 Pod 基本信息:
|
||
kubectl get pod hello-kiamol
|
||
# 指定输出的自定义列, 选择了网络相关信息:
|
||
kubectl get pod hello-kiamol --output custom-
|
||
columns=NAME:metadata.name,NODE_IP:status.hostIP,POD_IP:status.podIP
|
||
# 指定查询 JSONPath ,
|
||
# 选择 Pod 中第一个容器的 ID:
|
||
kubectl get pod hello-kiamol -o
|
||
jsonpath='{.status.containerStatuses[0].containerID}'
|
||
```
|
||
|
||
我的输出如图 2.3 所示。我在 windows 上的 Docker Desktop 运行了一个单节点的 Kubernetes 集群,第二条命令中的节点 IP 是我的 Linux 虚机的 Ip 地址,然后 Pod IP 是集群中的 Pod 虚拟地址。第三条命令输出的 container ID 以容器运行时的名字作为前缀,我的是 Docker。
|
||
|
||
![图2.3](./images/Figure2.3.png)
|
||
<center>图2.3 Kubectl 有很多选项可以自定义各种对象包括 Pods 的输出 </center>
|
||
|
||
这可能会让人觉得很枯燥,但它有两个重要的要点。首先,kubectl 是一个非常强大的工具,作为你与 Kubernetes 的主要联系点,你将花费大量时间使用它,非常值得去理解它能做什么。查询命令的输出是查看您关心的信息的有用方法,因为您可以访问所有资源的详细信息,这对自动化也很有用。第二个要点是提醒 Kubernetes不运行容器,Pod 中的容器 ID 是引用自另一个运行容器的系统。
|
||
|
||
Pods 在创建时被分配给一个节点,该节点负责管理 Pod 及其容器,它通过使用容器运行时中的称为容器运行时接口(CRI)的已知API 来实现此操作。CRI 让节点以相同的方式为所有不同的容器运行时管理容器。它使用一个标准API来创建和删除容器并查询它们的状态。当 Pod 运行时,节点与容器运行时一起工作,以确保 Pod 有它需要的所有容器。
|
||
|
||
<b>现在就试试</b> 所有 Kubernetes 环境都使用相同的 CRI 机制来管理容器,但不是所有的容器运行时都允许您访问 Kubernetes 之外的容器。本练习向您展示Kubernetes 节点如何确保其 Pod 容器的运行,这里使用的是 Docker 容器运行时进行演示。
|
||
|
||
```
|
||
# 查询 Pod 中的容器:
|
||
docker container ls -q --filter
|
||
label=io.kubernetes.container.name=hello-kiamol
|
||
# 现在,删除容器:
|
||
docker container rm -f $(docker container ls -q --filter
|
||
label=io.kubernetes.container.name=hello-kiamol)
|
||
# 检查 Pod 状态:
|
||
kubectl get pod hello-kiamol
|
||
# 可以再次看到容器:
|
||
docker container ls -q --filter
|
||
label=io.kubernetes.container.name=hello-kiamol
|
||
```
|
||
|
||
从图 2.4 中可以看到,当我删除 Docker 容器时,Kubernetes做出了反应,一时间 Pod 没有了容器,但 Kubernetes 立即创建了一个替代容器来修复 Pod 并将其恢复到正确的状态。
|
||
|
||
![图2.4](./images/Figure2.4.png)
|
||
<center>图2.4 Kubernetes 确保 Pods 可以始终拥有它所需的容器 </center>
|
||
|
||
从容器到 Pods 的抽象可以让 Kubernetes 修复此类问题。一个失败的容器可能是临时的故障;Pod 仍然存在,并且 Pod 可以用一个新的容器使其符合目标。这只是 Kubernetes 提供的自我修复的一个级别,Kubernetes 在 Pods 之上提供了进一步的抽象,使你的应用程序更具弹性。
|
||
|
||
其中一个抽象是 Deployment,我们将在下一节中讨论。在我们继续之前,让我们看看Pod中到底在运行什么。这是一个 web 应用程序,但你还不能访问它,因为我们还没有配置 Kubernetes 来路由网络到 Pod 的流量。我们可以使用 kubectl 的另一个特性来解决这个问题。
|
||
|
||
<b>现在就试试</b> Kubectl 可以将流量从节点转发到Pod,这是一个从集群外部与 Pod 通信的快速方式。你可以监听在计算机上的特定端口,该端口是集群中的单个节点上的——并将流量转发到 Pod 中运行的应用程序。
|
||
|
||
```
|
||
# 监听你机器的 8080 端口,并将流量发送到 Pod 的 80 端口
|
||
kubectl port-forward pod/hello-kiamol 8080:80
|
||
# 现在访问 http://localhost:8080
|
||
# 当你结束之后,可以通过 ctrl-c 结束端口转发
|
||
```
|
||
|
||
我的输出如图 2.5 所示,你可以发现它是一个非常基本的 web 网站。这个 Web服务器和所有的内容都已打包到 Docker Hub 上的容器镜像中,该镜像是公开可用的。所有CRI-兼容的容器运行时可以提取镜像并从中运行容器,因此当您运行应用程序是,它对你的工作方式与对我的工作方式是相同的。
|
||
|
||
![图2.5](./images/Figure2.5.png)
|
||
<center>图 2.5 这个应用并没有配置接收网络流量,但是 Kubectl 可以转发网络流量 </center>
|
||
|
||
现在我们很好地了解了Pod,它是Kubernetes 中最小的计算单元。你需要了解这一切是如何运作的,但Pod是一个原始的资源,在正常使用中,您永远不会直接运行Pod;您总是创建一个控制器对象来管理 Pod。
|
||
|
||
## 2.2 通过控制器运行 Pods
|
||
|
||
这只是第二章的第二节,我们即将开始讨论另一个 Kubernetes 对象,它是对其他对象的抽象。Kubernetes 确实很快就会变得复杂,但这种复杂性是这种强大且可配置系统的必要组成部分。
|
||
|
||
单独使用 Pods 太简单了;它们是应用程序的隔离实例,每个 Pod 都分配给一个节点。如果该节点离线,Pod 将丢失,Kubernetes 不会替换它。您可以通过运行多个 Pods 来尝试获得高可用性,但不能保证 Kubernetes 不会将它们全部运行在同一个节点上。即使您将 Pods 分散在多个节点上,您仍需要自己管理它们。当您有一个可以为您管理它们的编排器时,为什么要这样做呢?
|
||
|
||
那就是控制器的作用了。控制器是一种 Kubernetes 资源,用于管理其他资源。它与 Kubernetes API 一起工作,以观察系统的当前状态,将其与其资源的期望状态进行比较,并进行任何必要的更改。Kubernetes 有许多控制器,但用于管理 Pods 的主要控制器是 Deployment,它可以解决我刚才描述的问题。如果一个节点离线并且你失去了一个 Pod,Deployment 就会在另一个节点上创建一个替换 Pod;如果你想扩展你的 Deployment,你可以指定你想要多少 Pods,Deployment 控制器会将它们运行在许多节点上。图 2.6 显示了 Deployments、Pods 和容器之间的关系。
|
||
|
||
![图2.6](./images/Figure2.6.png)
|
||
<center>图2.6 Deployment 控制器管理 Pods, Pods 管理 容器</center>
|
||
|
||
您可以使用 kubectl 创建 Deployment 资源,指定要运行的容器镜像和 Pod 的任何其他配置。Kubernetes 创建 Deployment,Deployment 创建 Pod。
|
||
|
||
<b>现在就试试</b> 使用 Deployment 创建另一个 web 应用的实例。唯一需要的参数是 Deployment 的名称和要运行的镜像。
|
||
|
||
```
|
||
# 创建名为 "hello-kiamol-2" 的 deployment, 运行同样的 web 应用:
|
||
kubectl create deployment hello-kiamol-2 --image=kiamol/ch02-hello-kiamol
|
||
# 查询所有的 Pods:
|
||
kubectl get pods
|
||
```
|
||
|
||
您可以在图 2.7 中查看我的输出。现在您的集群中有两个 Pod:使用 kubectl run 命令创建的原始 Pod,以及由 Deployment 创建的新 Pod。由 Deployment 管理的 Pod 具有 Kubernetes 生成的名称,该名称是 Deployment 名称后面跟随一个随机后缀。
|
||
|
||
![图2.7](./images/Figure2.7.png)
|
||
<center>图2.7 创建一个控制器资源, 它负责创建自己的资源—Deployments 创建 Pods</center>
|
||
|
||
从这次练习中需要意识到的一个重要事情:您创建了 Deployment,但您没有直接创建 Pod。Deployment 规范描述了您想要的 Pod,并由 Deployment 创建了该 Pod。Deployment 是一个控制器,它使用 Kubernetes API 检查哪些资源正在运行,意识到应该管理的 Pod 不存在,并使用 Kubernetes API 创建它。确切的机制并不重要;您可以只与 Deployment 一起工作,并依靠它来创建您的 Pod。
|
||
|
||
但是,Deployment 是如何跟踪其资源的确很重要,因为这是 Kubernetes 经常使用的一种模式。任何 Kubernetes 资源都可以应用简单的键值对标签。您可以添加标签来记录您自己的数据。例如,您可以为 Deployment 添加一个名为 release 的标签,并将值设置为 20.04,表示这个 Deployment 来自 20.04 发行周期。Kubernetes 也使用标签来松散耦合资源,映射 Deployment 及其 Pods 之类对象之间的关系。
|
||
|
||
<b>现在就试试</b> Deployment 在它管理的 Pods 上添加了标签,使用 Kubectl 来输出 Deployment 添加的标签,然后查询标签匹配的 Pods:
|
||
```
|
||
# 打印 Deployment 添加到 Pods 上的标签:
|
||
kubectl get deploy hello-kiamol-2 -o
|
||
jsonpath='{.spec.template.metadata.labels}'
|
||
# 列出标签匹配的 Pods:
|
||
kubectl get pods -l app=hello-kiamol-2
|
||
```
|
||
|
||
我的输出如图 2.8 所示,您可以看到资源的配置方式的一些内部细节。Deployments 使用模板来创建 Pods,该模板的一部分是元数据字段,其中包括 Pod 的标签。在这种情况下,Deployment 向 Pod 添加了一个名为 app 的标签,值为 hello-kiamol-2。查询具有匹配标签的 Pod 会返回由 Deployment 管理的单个 Pod。
|
||
|
||
![图2.8](./images/Figure2.8.png)
|
||
<center> 图2.8 当 Deployments 创建 Pods 时添加标签, 然后你可以使用这些标签进行过滤 </center>
|
||
|
||
使用标签来识别资源之间的关系是 Kubernetes 中的核心模式,因此值得显示一个图表来确保它是明确的。资源在创建时可以应用标签,然后在其生命周期内添加、删除或编辑。控制器使用标签选择器来识别其管理的资源。这可以是一个匹配具有特定标签的资源的简单查询,如图 2.9 所示。
|
||
|
||
![图2.9](./images/Figure2.9.png)
|
||
<center>图2.9 控制器通过使用标签和选择器来识别他们管理的资源.</center>
|
||
|
||
这个过程是灵活的,因为这意味着控制器不需要维护它们管理的所有资源的列表;标签选择器是控制器规范的一部分,控制器可以通过查询 Kubernetes API 来随时找到匹配的资源。这也是您需要小心的地方,因为您可以编辑资源的标签,最终导致它与其控制器之间的关系中断。
|
||
|
||
<b>现在就试试</b> Deployment 与它创建的 Pod 之间没有直接关系;它只需要知道一个带有与其标签选择器匹配的标签的 Pod。如果您编辑了 Pod 的标签,Deployment 就不再识别它了。
|
||
|
||
```
|
||
# 查询所有 Pods, 显示 Pod 名称和标签:
|
||
kubectl get pods -o custom-
|
||
columns=NAME:metadata.name,LABELS:metadata.labels
|
||
# 更新 Deployment 的 Pod "app" 标签:
|
||
kubectl label pods -l app=hello-kiamol-2 --overwrite app=hello-kiamol-x
|
||
# 再次查询 Pods:
|
||
kubectl get pods -o custom-
|
||
columns=NAME:metadata.name,LABELS:metadata.labels
|
||
```
|
||
|
||
您期望发生什么?您可以从图2.10中显示的输出中看出,更改 Pod 标签有效地将 Pod 从 Deployment 中删除。在那个时候,Deployment 看到没有匹配其标签选择器的 Pod 存在,因此它创建了一个新的 Pod。Deployment 已经完成了它的工作,但通过直接编辑 Pod,您现在拥有了一个不受管理的 Pod。
|
||
|
||
![图2.10](./images/Figure2.10.png)
|
||
<center>图2.10 如果您干扰了 Pod 上的标签,您可以将其从部署的控制中删除</center>
|
||
|
||
在调试中,这可能是一种有用的技术——将 Pod 从控制器中删除,这样您就可以连接并调查问题,而控制器启动了一个新的 Pod,这样您的应用程序就可以按照所需的规模运行。您也可以做相反的事情:编辑 Pod 的标签,使控制器被骗到把该 Pod 作为其管理的集合的一部分。
|
||
|
||
<b>现在就试试</b> 将原始 Pod 通过将其 app 标签设置为与标签选择器匹配来返回 Deployment 的控制。
|
||
|
||
```
|
||
# 查询包含标签 “app” 的 Pods, 显示 Pod 名称和标签:
|
||
kubectl get pods -l app -o custom-
|
||
columns=NAME:metadata.name,LABELS:metadata.labels
|
||
# 更新不受管理的 Pod 的 “app” 标签:
|
||
kubectl label pods -l app=hello-kiamol-x --overwrite app=hello-kiamol-2
|
||
# 再次查询 Pods:
|
||
kubectl get pods -l app -o custom-
|
||
columns=NAME:metadata.name,LABELS:metadata.labels
|
||
```
|
||
|
||
此练习实际上逆转了前一个练习,将 app 标签重新设置为 Deployment 中原始 Pod 的 hello-kiamol-2。现在,当 Deployment 控制器与 API 检查时,它会看到两个与其标签选择器匹配的 Pod。然而,它应该只管理一个 Pod,因此它删除了其中一个(使用一组删除规则来决定哪一个)。您可以在图2.11中看到,Deployment 删除了第二个 Pod,并保留了原始 Pod。现在您拥有一个不受管理的 Pod。
|
||
|
||
![图2.11](./images/Figure2.11.png)
|
||
<center>图2.11 更多标签干扰—如果标签匹配,您可以强制部署采用 Pod </center>
|
||
|
||
Pod 运行您的应用程序容器,但与容器一样,Pod 的生命周期也很短。您通常会使用更高级别的资源(如 Deployment)来为您管理 Pod。这样做会使 Kubernetes 在容器或节点出现问题时更容易维护您的应用程序,但最终 Pod 运行的容器与您自己运行的容器相同,而您的应用程序的最终用户体验也是相同的。
|
||
|
||
<b>现在就试试</b> Kubectl 的 port-forward 命令将流量发送到 Pod,但您不必为 Deployment 找到随机的 Pod 名称。您可以在 Deployment 资源上配置端口转发,并且 Deployment 会选择其中一个 Pod 作为目标。
|
||
|
||
```
|
||
# 在你的本地机器运行一个 Deployment 的端口转发:
|
||
kubectl port-forward deploy/hello-kiamol-2 8080:80
|
||
# 访问 http://localhost:8080
|
||
# 结束之后, 通过 ctrl-c 退出
|
||
```
|
||
|
||
您可以看到我的输出,如图2.12所示,容器中运行的同一个应用程序来自同一个 Docker 镜像,但这一次,它在由 Deployment 管理的 Pod 中运行。
|
||
|
||
![图2.12](./images/Figure2.12.png)
|
||
<center>图2.12 Pod 和 Deployment 是容器上面的一层,但应用程序仍然在容器中运行</center>
|
||
|
||
Pod 和 Deployment 是本章中唯一要介绍的资源。您可以使用 kubectl run 和 create 命令部署非常简单的应用程序,但更复杂的应用程序需要更多的配置,而这些命令将无法完成。是时候进入 Kubernetes YAML 的世界了。
|
||
|
||
## 2.3 在清单文件中定义 Deployments
|
||
|
||
应用程序清单是 Kubernetes 最具吸引力的方面之一,但也是最令人沮丧的方面之一。当您在几百行 YAML 中挣扎时,试图找到破坏应用程序的小配置错误,可能会认为 API 是故意写来混淆和激怒您的。在这些时候,请记住 Kubernetes 清单是应用程序的完整描述,可以对其进行版本控制并跟踪源控制,并且在任何 Kubernetes 集群上部署相同的应用程序。
|
||
|
||
清单可以用 JSON 或 YAML 编写;JSON 是 Kubernetes API 的原生语言,但 YAML 更适合清单,因为它更容易阅读,允许您在单个文件中定义多个资源,最重要的是,它可以在规范中记录注释。清单 2.1 是您可以编写的最简单的应用程序清单。它定义了一个使用本章已经使用过的相同容器镜像的单个 Pod。
|
||
|
||
> 清单 2.1 pod.yaml, 单个 Pod 运行单个容器
|
||
```
|
||
# 清单同时指定了 Kubernetes API 的版本以及资源类型
|
||
apiVersion: v1
|
||
kind: Pod
|
||
# 资源的元数据包括名称(必填项)和标签(可选)
|
||
metadata:
|
||
name: hello-kiamol-3
|
||
# spec 是资源的实际规格,对于 Pod 最小值是要运行的容器:包括容器名称以及镜像
|
||
spec:
|
||
containers:
|
||
- name: web
|
||
image: kiamol/ch02-hello-kiamol
|
||
```
|
||
|
||
这比 kubectl run 命令所需的信息要多得多,但应用程序清单的大优势是它是声明性的。Kubectl run 和 create 是命令性操作——你告诉 Kubernetes 做些什么。清单是声明性的——你告诉 Kubernetes 最终结果应该是什么,它就会去决定它需要做什么来使这种情况发生。
|
||
|
||
<b>现在就试试</b> 您仍然使用 kubectl 从清单文件部署应用程序,但您使用 apply 命令,告诉 Kubernetes 将文件中的配置应用于集群。使用与清单 2.1 相同的内容的 YAML 文件运行本章示例应用程序的另一个 pod。
|
||
|
||
```
|
||
# 切换到第二章练习源码目录:
|
||
cd ch02
|
||
# 基于清单文件部署应用:
|
||
kubectl apply -f pod.yaml
|
||
# 查询 Pods:
|
||
kubectl get pods
|
||
```
|
||
|
||
新的 Pod 与使用 kubectl run 命令创建的 Pod 工作方式相同:它分配给一个节点,并运行容器。图2.13 的输出显示,当我应用清单时,Kubernetes 决定需要创建一个 Pod 来使集群的当前状态达到我的期望状态。这是因为清单指定了一个名为 hello-kiamol-3 的 Pod,但没有这样的 Pod。
|
||
|
||
![图2.13](./images/Figure2.13.png)
|
||
<center>图2.13 应用清单将YAML文件发送到Kubernetes API,该API应用更改</center>
|
||
|
||
现在 Pod 正在运行,您可以使用 kubectl 以相同的方式进行管理:通过列出 Pod 的详细信息并运行端口转发来向 Pod 发送流量。大的不同在于清单易于共享,基于清单的部署是可重复的。我可以多次运行相同的 kubectl apply 命令和相同的清单,结果总是一样的:一个名为 hello-kiamol-3 的 Pod 运行我的 web 容器。
|
||
|
||
<b>现在就试试</b> Kubectl 并不一定需要本地的清单文件,它可以获取远程的 URL 信息,让我们通过 GitHub 中的文件来部署同样的 Pod。
|
||
|
||
```
|
||
# 从清单文件部署:
|
||
kubectl apply -f https://github.com/yyong-brs/learn-kubernetes/blob/master/kiamol/ch02/pod.yaml
|
||
```
|
||
|
||
图2.14 显示了输出。资源定义与集群中运行的 Pod 匹配,因此 Kubernetes 不需要做任何事情,而 kubectl 显示匹配的资源未更改。
|
||
|
||
![图2.14](./images/Figure2.14.png)
|
||
<center>图2.14 Kubectl 可以通过从远程服务器下载清单文件,并发送到 Kubernetes API 执行</center>
|
||
|
||
应用程序清单在与高级资源一起工作时变得更有趣。当您在 YAML 文件中定义 Deployment 时,其中一个必填字段是 Deployment 应运行的 Pod 的规范。该 Pod 规范是独立定义 Pod 的相同 API,因此 Deployment 定义是一个复合体,包括 Pod spec。清单 2.2 显示了运行同一个 web 应用程序的另一个版本的 Deployment 资源的最小定义。
|
||
|
||
> 清单 2.2 deployment.yaml, Deployment 及 Pod 配置
|
||
|
||
```
|
||
# Deployments 是 apps 版本v1 API 规范的一部分
|
||
apiVersion: apps/v1
|
||
kind: Deployment
|
||
# Deployments 需要一个名称
|
||
metadata:
|
||
name: hello-kiamol-4
|
||
# spec 包括 Deployment 使用的标签选择器来找到它自己的资源 - 我正在使用 app 标签,但这可能是任何组合的键值对.
|
||
spec:
|
||
selector:
|
||
matchLabels:
|
||
app: hello-kiamol-4
|
||
# template 在 Deployment 创建 Pod 模板时使用.
|
||
# PDeployment 中的 Pods 没有名称,但它们需要指定与选择器匹配的标签
|
||
labels:
|
||
app: hello-kiamol-4
|
||
# Pod 规范列出容器名称和镜像规范
|
||
containers:
|
||
- name: web
|
||
image: kiamol/ch02-hello-kiamol
|
||
```
|
||
|
||
这个清单是用于一个完全不同的资源(它只是运行相同应用程序的巧合),但所有 Kubernetes 清单都是使用 kubectl apply 以相同方式部署的。这为您的所有应用程序提供了一个很好的一致性层,无论它们有多复杂,您都可以在一个或多个 YAML 文件中定义它们,并使用相同的 kubectl 命令部署它们。
|
||
|
||
<b>现在就试试</b> 这个指令是要求将 Deployment 清单应用于创建一个新的 Deployment,这样可以创建一个新的 Pod。
|
||
```
|
||
# 使用 deployment 清单运行应用程序:
|
||
kubectl apply -f deployment.yaml
|
||
# 找到新 deployment 管理的 Pods:
|
||
kubectl get pods -l app=hello-kiamol-4
|
||
```
|
||
|
||
图 2.15 输出结果与使用 kubectl create 创建的 Deployment 相同,但整个应用程序规范都定义在单个 YAML 文件中。
|
||
|
||
![图2.15](./images/Figure2.15.png)
|
||
<center>图2.15 通过 deployment 清单可以创建一个 deployment,因为没有匹配的资源存在</center>
|
||
|
||
随着应用程序复杂度的增加,我需要指定我想要的副本数量,应用的 CPU 和内存限制,以及 Kubernetes 如何检查应用程序是否健康,应用程序配置设置来自哪里,数据写入哪里——我可以通过添加 YAML 来实现所有这些。
|
||
|
||
## 2.4 应用在 Pods 中运行
|
||
|
||
Pods 和 Deployments 只是为了让你的应用程序运行,但真正的工作发生在容器里。容器运行时可能不允许你直接使用容器 ——一个托管的Kubernetes集群不会给你 Docker 或者 Containerd 的控制权-但您仍然可以使用kubectl在Pods中使用容器。Kubernetes命令行允许您在容器中运行命令,查看应用程序日志,复制文件。
|
||
|
||
<b>现在就试试</b> 您可以使用 kubectl 在容器内运行命令并连接终端会话,这样您就可以像连接远程计算机一样连接到 Pod 的容器。
|
||
|
||
```
|
||
# 检查我们运行的第一个 Pod 的内部 IP 地址:
|
||
kubectl get pod hello-kiamol -o custom-columns=NAME:metadata.name,POD_IP:status.podIP
|
||
# 在 Pod 中运行交互式 shell 命令:
|
||
kubectl exec -it hello-kiamol -- sh
|
||
# 在 Pod 中, 检查 IP address:
|
||
hostname -i
|
||
# 测试 web app:
|
||
wget -O - http://localhost | head -n 4
|
||
# 退出 shell:
|
||
exit
|
||
```
|
||
|
||
我的输出如图2.16所示,在图中可以看到,容器环境中的 IP 地址是 Kubernetes 设置的,容器中运行的 Web 服务器可以通过 localhost 地址访问。
|
||
|
||
![图2.16](./images/Figure2.16.png)
|
||
<center>图2.16 您可以使用 kubectl 在 Pod 容器内运行命令,包括交互式 shells.</center>
|
||
|
||
在 Pod 容器内部运行一个交互式 shell 是查看 Pod 中的世界的样子最有用的办法。你可以查看文件内容以检查相关配置是否正确,运行 DNS 查询以验证服务是否被正常识别,以及 ping 一些端点以测试网络。如上所述的所有都是很好的故障排除方法,但对于持续的管理,一个更简单的选择是读取应用程序日志,kubectl有一个专门的命令就是这个为了日志的目的。
|
||
|
||
<b>现在就试试</b> Kubernetes从容器运行时获取应用程序日志。您可以使用kubectl阅读日志,如果您有权访问容器运行时,可以验证它们与容器日志是相同的。
|
||
|
||
```
|
||
# 打印 kuberneters 容器最新的日志:
|
||
kubectl logs --tail=2 hello-kiamol
|
||
# 和 Docker 原生的日志对比:
|
||
docker container logs --tail=2 $(docker container ls -q --filter
|
||
label=io.kubernetes.container.name=hello-kiamol)
|
||
```
|
||
|
||
从我的输出中可以看出,如图2.17所示,Kubernetes只是将日志条目完全转发自容器运行时。
|
||
|
||
![图2.17](./images/Figure2.17.png)
|
||
<center>图2.17 kubernetes 从容器读取日志,所以你不需要自己去访问容器运行时</center>
|
||
|
||
所有 Pod 都可以使用相同的功能,无论它们是如何创建的。由控制器管理的 Pod 名称是随机的,因此您不能直接引用它们。相反,您可以通过它们的控制器或标签来访问它们。
|
||
|
||
<b>现在就试试</b> 您可以在由 Deployment 管理的 Pod 中运行命令,而无需知道 Pod 名称,并且可以查看与标签选择器匹配的所有 Pod 的日志。
|
||
|
||
```
|
||
# 调用我们通过 Deployment YAML 创建的 POD 容器中的 web app:
|
||
kubectl exec deploy/hello-kiamol-4 -- sh -c 'wget -O - http://localhost
|
||
> /dev/null'
|
||
# 然后查看 Pod 日志:
|
||
kubectl logs --tail=1 -l app=hello-kiamol-4
|
||
```
|
||
|
||
图 2.18 显示了在 Pod 容器内运行的命令,该命令导致应用程序写入日志条目。我们可以在 Pod 日志中看到信息。
|
||
|
||
![图2.18](./images/Figure2.18.png)
|
||
<center>图2.18 通过 Kubect 命令无需知道 POD 的名称也可以操作 Pod.</center>
|
||
|
||
在生产环境中,您可以收集所有 Pod 的所有日志,并将它们发送到中央存储系统,但在这之前,这是一种读取应用程序日志的有用且简单的方法。您还在那个练习中看到,有不同的方法可以访问由控制器管理的 Pod。Kubectl 可以在大多数命令中提供标签选择器,并且一些命令 - 比如 exec - 可以针对不同的目标运行。
|
||
|
||
您最有可能使用的最后一个 Pod 功能是与文件系统交互。Kubectl 允许您在本地计算机和 Pod 中的容器之间复制文件。
|
||
|
||
<b>现在就试试</b> 创建您的计算机上的临时目录,并从 Pod 容器中复制文件到该目录。
|
||
```
|
||
# 创建本地目录:
|
||
mkdir -p /tmp/kiamol/ch02
|
||
# 从 Pod 中拷贝 web 界面源文件:
|
||
kubectl cp hello-kiamol:/usr/share/nginx/html/index.html
|
||
/tmp/kiamol/ch02/index.html
|
||
# 检查本地文件内容:
|
||
cat /tmp/kiamol/ch02/index.html
|
||
```
|
||
|
||
在图2.19中,您可以看到 kubectl 将文件从 Pod 容器复制到我的本地计算机上。无论您的 Kubernetes 集群是在本地运行还是在远程服务器上运行,它都是双向的,因此您可以使用相同的命令将本地文件复制到 Pod 中。这可能是一种有用的解决应用程序问题的方法。
|
||
|
||
![图2.19](./images/Figure2.19.png)
|
||
<center>图2.19. 在本地文件系统与 POD 之间拷贝文件可能会帮你解决一些问题</center>
|
||
|
||
|
||
在本章中,这就是我们要涵盖的所有内容,但在我们继续之前,我们需要删除我们正在运行的 Pod,这比你想象的要复杂一些。
|
||
|
||
|
||
## 2.5 了解 Kubernetes 资源管理
|
||
|
||
您可以使用 kubectl 轻松删除 Kubernetes 资源,但该资源可能无法被真正删除掉。如果您使用控制器创建了资源,那么管理该资源就是控制器的工作。它拥有资源生命周期,并且不希望有任何外部干涉。如果您删除了受管资源,那么它的控制器将创建替代品。
|
||
|
||
<b>现在就试试</b> 使用 kubectl delete 命令删除所有 Pod 并验证它们是否真的已被删除。
|
||
```
|
||
# 查询所有运行的 Pods:
|
||
kubectl get pods
|
||
# 删除所有的 Pods:
|
||
kubectl delete pods --all
|
||
# 再次检查:
|
||
kubectl get pods
|
||
```
|
||
|
||
你可以看到 图 2.20 是我的输出,是你期望看到的结果吗?
|
||
|
||
![图2.20](./images/Figure2.20.png)
|
||
<center>图2.20 控制器拥有它们的资源,如果其它操作删除了它们,控制器将创建替换对象 </center>
|
||
|
||
这其中的两个 Pod 是通过 run 命令和 YAML Pod 规范直接创建的。它们没有控制器来管理它们,因此当您删除它们时,它们会保持删除状态。另外两个 Pod 是由 Deployment 创建的,当您删除 Pod 时,Deployment 控制器仍然存在。它们会发现没有匹配它们标签选择器的 Pod,因此它们会创建新的 Pod。
|
||
|
||
当您了解它时,这似乎是显而易见的问题,但这是一个很容易让人误解的地方,在您与 Kubernetes 合作的所有日子里都会出现。如果您想删除一个由控制器管理的资源,您需要删除控制器。当删除控制器时,它们会清理它们的资源,因此删除 Deployment 就像是一个级联删除,它也会删除 Deployment 的所有 Pod。
|
||
|
||
<b>现在就试试</b> 检查你运行的 Deployments,然后删除它们,最后确认剩余的 Pod 已经被删除。
|
||
```
|
||
# 查看 Deployments:
|
||
kubectl get deploy
|
||
# 删除所有的 Deployments:
|
||
kubectl delete deploy --all
|
||
# 查看 Pods:
|
||
kubectl get pods
|
||
# 检查所有的 resources:
|
||
kubectl get all
|
||
```
|
||
|
||
图 2.21 显示了我的输出。我的速度足够快,可以看到Pods正在被删除,因此它们处于终止状态。几秒钟后,Pods和Deployment被删除,因此我运行的唯一资源就是Kubernetes API服务器本身。
|
||
|
||
![图2.21](./images/Figure2.21.png)
|
||
<center>图2.21 删除控制器启动级联动作,控制器将删除所有它管理的资源</center>
|
||
|
||
现在,您的Kubernetes集群没有运行任何应用程序,并且已回到原始状态。
|
||
|
||
我们在本章中讲了很多内容。您对Kubernetes如何使用Pods和Deployments管理容器有了很好的理解,同时对 YAML 规范有了介绍,并且使用 kubectl 与 Kubernetes API 进行了大量交互。我们逐步建立了核心概念,但是现在您可能已经大致了解Kubernetes是一个复杂的系统。如果您有时间完成以下实验,这将有助于巩固您所学的内容。
|
||
|
||
## 2.6 实验室
|
||
|
||
这是您的第一个实验,挑战你自己完成它。目标是编写一个 Kubernetes YAML规范,用于在Pod中运行应用程序的Deployment,然后测试应用程序,以确保它按预期运行。以下是一些提示,帮助您开始:
|
||
|
||
- 在 ch02/lab 文件夹中,有一个名为 pod.yaml 的文件,您可以尝试。它运行应用程序,但定义了Pod 而不是 Deployment。
|
||
- 应用容器运行监听端口 80 的网站。
|
||
- 当您将流量转发到端口时,Web 应用程序会以它正在运行的机器的主机名作为响应。
|
||
- 该主机名实际上是 Pod 名称,您可以使用kubectl验证。
|
||
|
||
如果您觉得这有点棘手,我在 GitHub 上提供了以下示例解决方案,您可以用作参考:https://github.com/yyong-brs/learn-kubernetes/tree/master/kiamol/ch02/lab/README.md。 |