KCNA 4:kubernetes实践

1. 简介

在本章中,我们将学习不同的Kubernetes对象,它们的用途以及如何与它们交互。 在设置集群或使用现有集群之后,我们可以开始部署一些工作负载。Kubernetes中最小的计算单元不是一个容器,而是一个Pod对象。也就是说,Pod不是我们用于工作负载的唯一抽象。Kubernetes有各种各样的工作负载对象来控制如何部署、扩展和管理pod。 部署工作负载并不是开发人员或管理员必须执行的唯一任务。Kubernetes为容器和编配的一些固有问题提供了解决方案,比如配置管理、跨节点网络、外部流量路由、负载平衡或pod的扩展。

2. 学习目标

在本章结束时,你应该能够:

  • 解释什么是Kubernetes对象以及如何描述它。

  • 讨论Pod的概念及其解决的问题。

  • 了解如何使用工作负载资源来扩展和安排pod。

  • 了解如何用服务抽象Pods,以及如何公开它们。

Kubernetes的核心概念之一是提供大量抽象资源(也称为对象),您可以使用这些资源来描述应该如何处理工作负载。其中一些用于处理容器编排的问题,如调度和自愈,另一些用于解决容器的一些固有问题。 Kubernetes对象可以区分为面向工作负载的对象(用于处理容器工作负载)和面向基础设施的对象(例如处理配置、网络和安全)。其中一些对象可以放在一个名称空间中,而其他对象可以跨整个集群使用。 作为用户,我们可以用流行的数据序列化语言YAML描述这些对象,并将它们发送到api服务器,在创建它们之前要对它们进行验证。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec: 
  selector:
    matchLabels:
      app: nginx
  replicas: 2 # tells deployment to run 2 pods matching the template
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.19
        ports:
        - containerPort: 80

红色突出显示的字段是必填字段。它们包括:

  • apiVersion:每个对象都可以进行版本控制。这意味着对象的数据结构可以在不同的版本之间变化。

  • kind:应该创建的对象类型。 metadata:可以用来识别它的数据。每个对象都需要一个名称,并且必须是唯一的。如果需要多个具有相同名称的对象,可以使用名称空间。

  • spec:对象的说明。在这里你可以描述你想要的状态。要小心,因为对象的结构可能会随着它的版本而改变!

创建,修改或删除一个对象只是一个意图记录,在那里你描述你的对象应该处于的状态,你不像你在本地机器上做的那样主动启动pods或甚至容器,并获得直接反馈,如果它工作与否。

4. 与Kubernetes交互

要访问API,用户可以使用名为kubectl的官方命令行接口客户端。让我们看看Kubernetes日常使用的一些基本命令。 注意:您可以在官方文档中了解如何安装kubectl。 你可以用下面的命令列出集群中可用的对象:

注意对象是如何使用短名称的。这对于名称较长的对象(如configmapspersistentvolumeclaims)非常有用。该表还显示了哪些对象具有名称空间以及它们的可用版本。 如果你想了解更多关于对象的信息,kubectl有一个内置的explain函数! 让我们进一步了解pod:

要了解更多关于pod规范的信息,您可以深入了解对象定义。使用如下格式:<type>.<fieldName>[.<fieldName>]

让我们看看基本的kubectl命令。你可以使用——help标志来查看它们:

要在Kubernetes中从YAML文件创建一个对象,你可以使用以下命令:

Kubernetes有许多图形用户界面和仪表板,它们允许与集群进行可视化交互。

在这里插入图片描述 Kubernetes官方仪表盘的截图 与Kubernetes交互的其他工具:

尽管有许多CLI工具和gui,但还有一些高级工具允许创建模板和打包Kubernetes对象。也许今天Kubernetes最常用的工具是Helm。 Helm是一个Kubernetes的包管理器,它允许更简单的更新和与对象的交互。Helm将Kubernetes对象封装在所谓的Charts中,可以通过注册表与他人共享。要开始使用Kubernetes,您可以搜索ArtifactHub,找到您最喜欢的软件包,准备部署。

4.1 Demo: kubectl

5. Pod 概念

Kubernetes中最重要的对象是Pod。pod描述一个或多个容器的单元,这些容器共享一个名称空间和cgroups隔离层。它是Kubernetes中最小的可部署单元,这也意味着Kubernetes不会直接与容器交互。pod概念的引入是为了允许运行相互依赖的多个进程的组合。pod中的所有容器共享一个IP地址,并且可以通过文件系统共享。

在这里插入图片描述 多个容器共享名称空间形成一个pod 下面是一个带有两个容器的简单Pod对象的例子:

您可以向主应用程序添加任意数量的容器。但是要小心,因为您失去了单独缩放它们的能力!使用支持主应用程序的第二个容器称为sidecar容器。 所有定义的容器都是在同一时间启动的,没有顺序,但是您也可以使用initContainers在主应用程序启动之前启动容器。在这个例子中,init容器init-myservice试图到达另一个服务。一旦完成,主容器就会启动。

请务必浏览有关pod的文档,因为还有更多设置有待发现。对于Pod中的每个容器,可以设置的一些重要设置示例如下:

  • resources: 设置一个资源请求和CPU和内存的最大限制

  • livenessProbe: 配置定期检查应用程序是否仍处于活动状态的运行状况检查。如果检查失败,可以重新启动容器。

  • securityContext: 设置用户和组设置,以及内核功能。

5.1 Demo: Pods

6. 负载均衡

在容器编排平台中,仅仅使用Pods是不够灵活的。例如,如果一个Pod因为一个节点失败而丢失,那么它就永远消失了。为了确保始终运行已定义数量的Pod副本,我们可以使用控制器对象来为我们管理Pod。

  • ReplicaSet 确保在任何给定时间运行所需数量的pod的控制器对象。replicaset可以用于向外扩展应用程序并提高其可用性。它们通过启动一个pod定义的多个副本来实现这一点。

  • Deployment Kubernetes中功能最丰富的对象。部署可以用来描述完整的应用程序生命周期,它们通过管理多个replicaset来实现这一点,当应用程序被更改时,这些replicaset会被更新,例如,提供一个新的容器映像。部署非常适合在Kubernetes中运行无状态应用程序。

  • StatefulSet StatefulSets可以用于在Kubernetes上运行像数据库这样的有状态应用程序,这在很长一段时间内都被认为是一个不好的实践。有状态应用程序有特殊的需求,这些需求不适合pod和容器的短暂性。与部署不同,StatefulSets尝试保留pod的IP地址,并给它们一个稳定的名称、持久的存储和更优雅的伸缩和更新处理。

  • DaemonSet 确保Pod的副本在集群的所有(或部分)节点上运行。守护进程集非常适合运行与基础设施相关的工作负载,例如监视或日志工具。

  • Job 创建一个或多个执行任务的Pods,然后终止该任务。作业对象非常适合运行数据库迁移或管理任务等一次性脚本。

  • CronJob CronJobs为作业添加基于时间的配置。这允许定期运行Jobs,例如每天晚上4点执行备份作业。

交互式教程-部署一个应用程序并探索它 在Kubernetes文档提供的交互式教程的第2部分中,您可以了解如何在Minikube集群中部署应用程序。 应用您从“与Kubernetes交互”中学到的知识,在交互式教程的第三部分探索您的应用程序

6.2 demo: pod、replicats、deployments

replicas部署

deployment部署

7. 网络

由于大量的Pods需要大量的手工网络配置,我们可以使用ServiceIngress对象来定义和抽象网络

  • ClusterIP 最常见的服务类型。ClusterIPKubernetes内部的一个虚拟IP,可以作为一组pods的单个端点使用。这种服务类型可以用作轮询负载均衡器。

在这里插入图片描述
  • NodePort NodePort服务类型通过添加简单的路由规则扩展了ClusterIP。它在集群中的每个节点上打开一个端口(默认在30000-32767之间),并将其映射到ClusterIP。这种服务类型允许将外部流量路由到集群。

  • loadbalance LoadBalancer服务类型通过部署外部LoadBalancer实例来扩展NodePort。只有当你在一个有API来配置LoadBalancer实例的环境中,比如GCP、AWS、Azure甚至OpenStack,这才会起作用。

  • ExternalName 一种没有任何路由的特殊服务类型。ExternalName使用Kubernetes内部DNS服务器创建DNS别名。您可以使用它创建一个简单的别名来解析一个相当复杂的主机名,比如:my-cool-database-az1-uid123.cloud-provider-i-like.com。如果您想从Kubernetes集群获取外部资源,这一点尤其有用。

在这里插入图片描述

ClusterIP、NodePort和LoadBalancer相互扩展

如果需要更大的灵活性来公开应用程序,可以使用Ingress对象。入口提供了一种从集群外部为集群内的服务公开HTTP和HTTPS路由的方法。它通过配置路由规则来实现这一点,用户可以通过入口控制器设置和实现路由规则。

在这里插入图片描述

一个Ingress将所有流量发送到一个Service的例子,从Kubernetes文档中获取

入口控制器的标准特性可能包括:

  • LoadBalancing

  • TLS offloading/termination

  • Name-based virtual hosting

  • Path-based routing

许多入口控制器甚至提供了更多的功能,比如:

  • Redirects

  • Custom errors

  • Authentication

  • Session affinity

  • Monitoring

  • Logging

  • Weighted routing

  • Rate limiting.

Kubernetes还提供了一个具有NetworkPolicy概念的集群内部防火墙。NetworkPolicies是一个简单的IP防火墙(OSI三层或四层),可以基于规则控制流量。您可以为传入(进入)和传出(出口)流量定义规则。NetworkPolicies的一个典型用例是限制两个不同名称空间之间的流量。

交互式教程-展示你的应用程序 现在,您可以在Kubernetes文档提供的交互式教程的第4部分中了解如何使用Service公开应用程序

7.1 demo

8. Volume & Storage

如前所述,在设计容器时并没有考虑到持久存储,特别是当存储跨越多个节点时。Kubernetes介绍了一些解决方案,但请注意,这些解决方案并没有自动消除使用容器管理存储的所有复杂性。 集装箱已经有了安装卷的概念,但由于我们没有直接使用集装箱,Kubernetes将卷作为Pod的一部分,就像集装箱一样。 下面是一个hostPath卷挂载的例子,类似于Docker引入的主机挂载:

在这里插入图片描述 卷允许在同一个Pod中的多个容器之间共享数据。当您想要使用侧车模式时,这个概念允许极大的灵活性。它们的第二个用途是在Pod崩溃并在同一节点上重新启动时防止数据丢失。pod以干净的状态启动,但所有数据会丢失,除非写入卷。 不幸的是,包含多个服务器的集群环境在持久性存储方面需要更多的灵活性。根据环境的不同,我们可以使用像Amazon EBS谷歌Persistent DisksAzure Disk storage这样的云块存储,也可以使用像CephGlusterFS这样的存储系统或更传统的系统,比如NFS。 这些只是Kubernetes中可以使用的存储的几个例子。为了让用户体验更加统一,Kubernetes使用了容器存储接口CSI (Container Storage Interface),它允许存储供应商编写一个可以在Kubernetes中使用的插件(存储驱动程序)。

为了使用这个抽象,我们还有两个可以使用的对象:

  • PersistentVolumes (PV) 存储片的抽象描述。对象配置包含卷的类型、卷大小、访问模式和唯一标识符以及如何挂载它的信息。

  • PersistentVolumeClaims (PVC) 用户对存储的请求。如果集群有多个持久化卷,用户可以创建一个PVC,根据用户的需要预留一个持久化卷。

这个例子展示了一个PersistentVolume,它使用了一个使用CSI驱动程序实现的AWS EBS卷。在配置了PersistentVolume之后,开发人员可以使用PersistentVolumeClaim来预留它。最后一步是在Pod中使用PVC作为卷,就像我们之前看到的hostPath示例一样。 可以直接在Kubernetes中操作存储集群。像Rook这样的项目提供云本地存储业务编排,并与经过实战测试的存储解决方案(如Ceph)集成。 在这里插入图片描述

Rook架构,从Rook文档中检索

9. 配置对象

12因素应用程序建议将配置存储在环境中。但这到底是什么意思呢?运行应用程序需要的不仅仅是应用程序代码和一些库。应用程序有配置文件,连接到其他服务、数据库、存储系统或缓存,这需要像连接字符串这样的配置。 将配置直接合并到容器构建中被认为是不好的做法。任何配置更改都需要重新构建整个映像,并重新部署整个容器或吊舱。当使用多个环境(开发、登台、生产)并为每个环境构建映像时,这个问题只会变得更糟。12因素应用程序更详细地解释了这个问题:Dev/prod奇偶性。 在Kubernetes中,这个问题是通过使用ConfigMap将配置从Pods中解耦来解决的。ConfigMaps可用于将整个配置文件或变量存储为键-值对。有两种可能的方式使用ConfigMap:

  • 将ConfigMap挂载为Pod中的卷

  • 将ConfigMap中的变量映射到Pod中的环境变量。

下面是一个包含nginx配置的ConfigMap示例:

一旦ConfigMap被创建,你就可以在Pod中使用它:

从一开始,Kubernetes也提供了一个对象来存储敏感信息,如密码、密钥或其他凭证。这些对象被称为Secrets。秘密与ConfigMaps非常相关,基本上它们唯一的区别是秘密是base64编码的。 关于使用“秘密”的风险,人们一直在争论不休,因为“秘密”(与名称相反)并不被认为是安全的。在原生云环境中,已经出现了专门创建的秘密管理工具,它们可以很好地与Kubernetes集成。HashiCorp Vault就是一个例子。

10. Autoscaling

自动伸缩机制

  • Horizontal Pod Autoscaler (HPA) Horizontal Pod Autoscaler (HPA)是Kubernetes中最常用的自动定标器。HPA可以监视deployments或ReplicaSets,并在达到某个阈值时增加副本的数量。成像Pod可以使用500MiB的内存,并且您配置了80%的阈值。如果利用率超过400MiB(80%),将调度第二个Pod。现在您的容量为1000MiB。如果使用了800MiB,将调度第三个Pod,以此类推。

  • Cluster Autoscaler 当然,如果集群容量是固定的,那么启动越来越多的pod副本是没有意义的。如果需求增加,Cluster Autoscaler可以向集群添加新的工作节点。集群自动伸缩器与水平自动伸缩器协同工作。

  • Vertical Pod Autoscaler Vertical Pod Autoscaler 相对较新,允许吊舱动态增加资源请求和限制。如前所述,垂直扩展受到节点容量的限制。

不幸的是,Kubernetes的(水平)自动伸缩是无法开箱即用的,需要安装一个名为metrics-server的附加组件。

但是,用Kubernetes Metrics api的Prometheus Adapter替换度量服务器是可能的。prometheus-adapter允许您在Kubernetes中使用自定义指标,并根据系统上的请求或用户数量等因素进行放大或缩小。 像KEDA这样的项目可以根据外部系统触发的事件来扩展Kubernetes工作负载,而不是仅仅依赖于指标。KEDA是基于kubernetes的事件驱动自动scaler的缩写,于2019年作为微软和红帽公司的合作伙伴启动。与HPA类似,KEDA可以扩展部署、复制集、pod等,还可以扩展Kubernetes作业等其他对象。通过大量现成的扩展器的选择,KEDA可以扩展到特殊的触发器,比如数据库查询,甚至Kubernetes集群中pod的数量。

交互式教程-缩放您的应用程序 在交互式教程的第五部分:运行应用程序的多个实例中,你可以学习如何手动扩展应用程序

11. Additional Resources

Learn more about…

Differences between Containers and Pods

kubectl tips & tricks

Storage and CSI in Kubernetes

Autoscaling in Kubernetes


最后更新于