Kubernetes就像一座冰山。你学习了基础知识,却发现还有很多东西要学。你学得越多,你看到的就越多。这篇文章解释了Reddit上“Kubernetes冰山”模因中列出的所有概念。
不久前,u/dshurupov published a picture on Reddit上发布了一张我称之为“Kubernetes冰山”的照片。这张照片是由弗兰特的人制作的。
它代表了一座巨大的冰山,在上面你有一些最简单的Kubernetes概念,当你在下面和水下时,你会深入到更先进的Kubernetes主题。这是图片:
CRD
CRD或自定义资源定义是通过添加自己的资源来扩展Kubernetes API的一种方法。
您可以定义它有哪些字段、它们是什么类型、是否需要其中任何一个字段、它们的默认值等。
Kubernetes将为该资源注册CRUD API端点(get、list、watch、delete等),您可以开始将该资源视为Kubernetes的任何内置端点(Pods、Deployments等)。
这包括:
- 使用kubectl获取或描述此资源
- 通过YAML文件和kubectl apply创建此资源的实例
- 直接调用此资源的REST API,例如curl<kube_url>/api/<version>/<crd_name>
如果您希望在您的资源周围有一个额外的逻辑,例如,当有人创建我们的CRD实例时触发一些操作,您可以通过实现一个操作符来实现这一点(在后面的文章中有更多的介绍)。
您可以查看我在ISTA 2021上关于如何使用CRD构建CRUD应用程序后端的演示。
PersistentVolumeClaim
PersistentVolumeClaim是用户/资源对存储的请求。给定的存储是从PersistentVolume中获取的,PersistentVolume是一个抽象的Kubernetes对象,表示一些存储。有不同的存储类,它们代表Kubernetes可以使用的存储的不同实现。storage类在PersistentVolume规范中指定。
一旦创建了PersistentVolumeClaim,Kubernetes将试图通过向请求者提供所请求的数量来利用存储。
PersistentVolumeClaim还指定了对存储的访问模式(例如,ReadWriteOnce、ReadOnlyMany或ReadWriteMany)。
StatefulSet
StatefulSet是代表工作负载的Kubernetes资源。它类似于部署–它根据Pod规范管理一组Pod。StatefulSet的特殊之处在于它管理有状态的应用程序(因此而得名)。
它对于以下情况很有用:
- 稳定、唯一的网络标识符
- 稳定持久存储
- 有序、优雅的部署和伸缩
- 有序的、自动滚动更新
Helm templating
Helm是一个模板引擎,允许您编写可重用的Kubernetes模板。
例如,如果将一个Pod部署到三个环境中,并且希望为每个环境设置不同的标签,则不需要将Pod YAML复制粘贴三次,只需更改标签的值即可。
您可以使用Helm将不同的字段提取到变量中,这些变量随后被模板化,并重用所有在不同环境中没有差异的部分。
例如,这是一个带有硬编码环境标签的Pod模板:
apiVersion: v1
kind: Pod
metadata:
name: busybox-sleep
labels:
environment: production
spec:
containers:
- name: busybox
image: busybox
args:
- sleep
- "1000000"
如果我们想把它转换成一个头盔模板,我们需要做的就是:
apiVersion: v1
kind: Pod
metadata:
name: busybox-sleep
labels:
environment: {{ .Values.environment }}
spec:
containers:
- name: busybox
image: busybox
args:
- sleep
- "1000000"
现在环境标签的值来自。values.environment变量。
这个变量来自一个所谓的值文件。
这是一个指定变量值的YAML。
通过使用不同的值文件,我们可以用不同的值输出不同的资源规格。
例如,我们可以为每个环境设置一个值文件:
# dev.yaml
environment: dev
# staging.yaml
environment: staging
# production.yaml
environment: production
使用上面显示的模板和dev.yaml值文件将输出以下规范:
apiVersion: v1
kind: Pod
metadata:
name: busybox-sleep
labels:
environment: dev
spec:
containers:
- name: busybox
image: busybox
args:
- sleep
- "1000000"
在与production.yaml值文件一起使用时,将输出以下内容:
apiVersion: v1
kind: Pod
metadata:
name: busybox-sleep
labels:
environment: production
spec:
containers:
- name: busybox
image: busybox
args:
- sleep
- "1000000"
Package manager
除了模板引擎,Helm还是Kubernetes的包管理器。您可以使用它来“打包”您的掌舵人图表(掌舵人图表==一堆模板)并将它们发布到存储库中。一旦您这样做了,每个人都可以通过helm install命令将您的图表安装到他们的集群中,并使用helm来管理该图表的生命周期。
Dashboard
仪表板是一种图形用户界面,它提供关于给定系统中某些指标的可视化信息。
Kubernetes仪表板将显示吊舱、部署、服务、节点等的数量。
Kubernetes提供了一个“官方”仪表板,您可以将其安装在集群中。它是一个基于Web的,它向您展示集群中的所有资源和许多其他有用的信息。它看起来是这样的:
要安装它,您需要应用一些Kubernetes资源并将它们公开到集群之外(这样您就可以在浏览器中打开web页面)。
更多关于仪表板的信息,以及如何安装它,您可以在这里找到。
除了这个“官方”仪表板之外,您还可以使用其他开源项目来获取相同的信息。它们被称为Kubernetes IDE,它们提供与Kubernetes仪表板类似的体验。
其中一些是:
K9s
开源的基于CLI的Kubernetes IDE。连接到kubeconfig文件中定义的集群。
对喜欢终端的用户来说很好。支持许多“超级用户”工作流。
Lens
基于Electron的开源桌面应用程序。
由Lens团队建造,现在是Mirantis的一部分。支持添加多个Kubernetes集群并在它们之间切换。
Octant
开源的基于Web的仪表板,由VMware编写。
可以在本地运行,也可以部署到远程集群中,并通过浏览器访问。
HPA
HPA或HorizontalPodAutoscaler是一个Kube API服务器特性,允许您基于度量标准水平地扩展工作负载。
水平伸缩意味着我们正在增加正在运行的工作负载的数量(例如,从3个吊舱增加到5个)。这与垂直伸缩不同,垂直伸缩意味着增加给定工作负载上的资源(例如,增加提供给吊舱的CPU和内存)。
HorizontalPodAutoscaler是作为Kubernetes资源创建的,autoscaling API组的一部分。它定义了workloads selector和度量标准,我们将在此基础上进行伸缩。每隔一段时间(由用户定义一个默认值),Kube API服务器将查询Pods的度量,并根据我们定义的条件决定是否需要扩大(更多的Pods)或缩小(更少的Pods)工作负载。
总有一个最小和最大的豆荚计数,例如,永远不要低于一定数量的豆荚,也不要超过一定数量的豆荚。
Kubernetes定义的一些缺省指标可以与HorizontalPodAutoscaler一起使用,但也可以与您定义的自定义指标一起使用(下一篇文章将详细介绍)。
日志管理
在运行容器时,它们通常被配置为将日志输出到stdout和stderr(至少,这是最佳实践)。如果容器死亡(因为某个问题或因为我们正在升级应用程序),日志将丢失(因为两个输出流将随容器一起消失)。
这意味着我们需要插入其他东西来读取这些日志,并以一种与容器生命周期无关的方式永久存储它们。
幸运的是,Kubernetes对此有一个解决方案。它支持许多日志驱动程序,这些驱动程序可以与应用程序日志集成(在应用程序中无需自定义逻辑)。这些驱动程序将从容器输出流中读取数据,并将日志发送到集中式系统,如logz.io、Splunk或Loki。一旦到达那里,您的日志就安全了,即使容器死亡,它们也不会丢失。此外,许多这些系统提供了一种简单的方法来聚合来自不同服务的日志,并基于关键字或基于字段的搜索在日志中进行搜索(如果您有结构化日志记录)。
Init Containers
Init容器是专门的容器,在pod中运行在app容器之前。
它们可用于执行吊舱中的一些操作,以使吊舱为主容器的执行做好准备。
在一个POD中可以有多个init容器。Kubernetes将在运行主容器之前运行所有这些。
所有init容器都需要成功退出,Kubernetes才能启动主容器。如果init容器失败,Kubernetes将重新启动它
您可以通过Pod规范中的spec.initcontainers字段指定init容器。
apiVersion: v1
kind: Pod
metadata:
name: my-app-pod
labels:
app: my-app
spec:
containers:
- name: my-app-container
image: busybox
command: ["sh", "-c", "echo The app is running! && sleep 3600"]
initContainers:
- name: my-app-first-init-container
image: busybox
# print "Hello from init container 1" 5 times then exit successfully
command:
[
"sh",
"-c",
"for i in {1..5}; do echo 'Hello from init container 1'; done; exit 0",
]
- name: my-app-second-init-container
image: busybox
# print "Hello from init container 1" 10 times then exit successfully
command:
[
"sh",
"-c",
"for i in {1..10}; do echo 'Hello from init container 2'; done; exit 0",
]
Affinity
亲和力是为工作负载指定首选节点的能力,例如,工作负载与节点具有亲和力。
您可以通过在节点上设置标签,然后在工作负载的Pod规范中设置与这些标签的亲和力来实现这一点。
污染和耐受性
Taints and Tolerations是亲和力的对立面。虽然亲和性定义了Pod的首选节点,但污染和公差定义了相反的节点,在那里Pod不应该被调度。
每个节点可以有多个污点。为了使Pod能够在节点上进行调度,Pod必须对该节点上的所有污染具有容忍度。
效果
污染/耐受可以有不同的影响,这意味着不同的事情:
- noSchedule–如果一个Pod没有匹配的容差,Kubernetes将不会在该节点上调度它。豆荚将继续运行,如果它是预定的,在污染被添加之前。
- PrefernoSchedule–如果一个Pod没有匹配的容忍度,Kubernetes将尝试不在该节点上调度它,但如果它没有其他可用节点,它会调度它。豆荚将继续运行,如果它是预定的,在污染被添加之前。
- NoExecute-如果一个Pod没有匹配的容限,Kubernetes将不会在该节点上调度它。如果Pod已经在节点上运行,Kubernetes将驱逐它,并在新的节点上重新调度它。
使用场景
污染的一些用例是公差:
- 专用节点-如果您有不同类型的节点,专用于同一集群中的不同工作负载,您可以使用污点和容忍度来确保每个节点都被调度在正确的节点上。
- 具有特殊硬件的节点-如果您的一组节点具有一些特殊硬件(例如GPU),您可以使用污点和容差来确保只有需要该硬件的节点被调度在这些节点上
- 驱逐-如果满足某些条件,您可以实现在节点上添加NoExecute污点的逻辑。这将从该节点驱逐所有豆荚,并在不同的节点上重新安排它们。Kubernetes使用带有NoExecute效果的Taints来确保没有在被认为没有准备好或有某种硬件问题(磁盘、CPU)或网络问题(不可达节点等)的节点上调度Pods。此外,如果您想删除一个节点,可以在实际删除该节点之前添加NoExecute taint,以确保所有Pods平稳地过渡到其他节点。
ResourceLimits
对于每个Pod,您可以指定容器需要多少资源(请求),以及容器可以获得的最大资源(限制)是多少。
Kube调度器将在将Pod调度到节点上时使用来自请求的信息。
Kubelet将强制要求任何容器都不能获得超过其限制的资源。
在Kubernetes中指定资源限制是可选的,但非常可取。如果您没有为容器设置资源限制,而该容器出现了类似内存泄漏的问题,则它将使用该节点的所有内存,并将该节点上的所有其他工作负载都耗尽内存。