kubernetes学习总结(33)——kubernetes是如何重塑虚拟机的(代码片段)

科技D人生 科技D人生     2022-10-22     603

关键词:

前言

Kubernetes 大规模使用过的都说简单,没有用过清一色的都是使用复杂、概念晦涩难懂,因此即使是那些具有一定服务器端知识的人也可能会感到困惑。让我在这里尝试一些不同的东西。与其解释一个不熟悉的问题(如何在 Kubernetes 中运行 Web 服务?)和另一个(你只需要一个清单,三个 sidecar 和一堆 gobbledygook),我将尝试揭示 Kubernetes 技术发展趋势。如果您已经知道如何使用虚拟机运行服务,希望您会发现最终并没有太大区别。如果您对大规模运营服务完全不熟悉,那么跟随技术的发展可能会帮助您了解当代方法。像往常一样,这篇文章并不全面。相反,它试图总结我的个人经历以及计算机多年来虚拟化是如何形成的。

如何使用虚拟机部署服务

早在 2010 年,当我刚刚开始我的软件工程师职业生涯时,使用虚拟机(或有时是裸机)部署应用程序非常普遍。你需要一个临时的 Linux 虚拟机,将 Nginx 或 Apache 反向代理放在它前面,然后在它旁边运行一堆守护进程和 cronjobs。这样的机器将代表服务的单个实例,打个比方,就类似于一个盒子,而服务本身将只是分布在网络上的一组命名的相同机器。根据您的业务规模,您可能只有几个、几十个、几百个甚至几千个盒子分布在为生产流量提供服务的多个盒子中。

服务的抽象将应用程序的复杂性隐藏在单个入口点之后。

使用虚拟机部署服务带来的挑战

通常,机器群的大小将定义配置(安装操作系统和软件包)、扩展(产生相同的盒子)、服务发现(将一组盒子隐藏在一个名称后面)和部署(运送新版本的代码)的方式到盒子)完成了。如果你是一个只有几个类似宠物的盒子的公司,您可能会发现自己很少半手动地配置新盒子。这通常意味着总线系数低(由于缺乏自动化)、安全状况差(由于缺乏定期补丁更新)以及可能更长的灾难恢复。从好的方面来说,管理成本会非常低,因为不需要扩展,您的部署会很简单(只需几个盒子来交付代码),而且服务发现会很简单(由于相当静态地址池)。

对于拥有大量盒子的公司来说,现实情况会有所不同。大量机器通常会导致更频繁地需要配置新盒子(更多的盒子意味着更多的破损)。你会投资自动化(投资回报率会很高),最终得到许多牛一样的盒子。作为不断重新创建盒子的副产品,您将增加总线因素并改善安全状况(将自动更新和安装补丁)。在它的反面,会存在低效的扩展(由于每日/每年的流量分布不均匀),过于复杂的部署(很难将代码快速交付到许多机器上),以及脆弱的服务发现(您是否尝试过大规模运行consul或zookeeper?)会导致更高的运营成本。

Amazon Elastic Compute Cloud (EC2) 等早期云产品允许更快地启动(和关闭)机器;使用packer制作并使用cloud-init自定义的机器镜像,使配置稍微容易一些;puppet和ansible等自动化工具支持应用基础架构更改并大规模交付新版本的软件。但是,仍有很大的改进空间。

Docker 容器解决了什么问题

在过去,拥有不同的生产和开发环境是很常见的。这将导致应用程序可能在您安装的 Debian 机器上本地运行,但由于缺少依赖项而无法在生产中的 vanilla CentOS 上启动。相反,在本地安装应用程序的依赖项可能会遇到一些麻烦,但由于资源需求高,为每个服务运行预配置的虚拟机进行开发将是不可行的。即使在生产中,虚拟机的庞大也是一个问题。每个服务拥有一个虚拟机可能会导致低于最佳资源利用率和/或相当大的存储和计算开销,但是将多个服务放在一个盒子中可能会使它们发生资源抢占冲突。世界显然需要一个更轻量级的盒子。

容器 - 单个应用程序的盒子

这就是容器的用武之地。就像允许将裸机服务器分割成几台更小(更便宜)的机器的虚拟机一样,容器将一个 Linux 机器分割成数十个甚至数百个独立的环境。在一个容器中,您可能会觉得您拥有自己的虚拟机,以及您最喜欢的 Linux 发行版。好吧,至少乍一看。从外部看,容器只是在主机操作系统上运行并共享其内核的常规进程。打包应用程序及其所有依赖项(包括特定版本的操作系统用户空间和库)的能力,将其作为容器镜像发送,并在安装了 Docker(或类似工具)的任何位置的标准化执行环境中运行,极大地提高了工作负载的可重复性.

由于容器边界的轻量级实现,计算开销显著降低,允许单个生产服务器运行可能属于多个(微)服务的数十个不同容器。当然,这可能以降低安全性为代价。由于不可变和共享的镜像层,镜像存储和分发也变得更加高效。在某种程度上,容器也改变了供应的方式。使用(粗心编写的)Dockerfiles 和ko和Jib之类的(神奇的)工具,责任极大地转移到了开发人员身上,简化了生产 VM 的要求——从开发人员的角度来看,你只需要一个 Docker-(或更高版本的 OCI-)兼容应用程序的运行时,因此您不会再因为要求安装某个版本的 Linux 或系统包而惹恼您的运维朋友。

最重要的是,容器加速了运行服务的替代方式的开发。现在有 17 种方法可以在 AWS 上运行容器https://www.lastweekinaws.com/blog/the-17-ways-to-run-containers-on-aws/,其中大部分是完全无服务器的,在足够简单的情况下,您可以使用 Lambda 或 Fargate 并从牛一样的盒子中受益!

容器不能解决什么问题

容器被证明是一个非常方便的开发工具。构建容器镜像也比构建 VM 更简单、更快捷。再加上如何有效分离团队之间职责的老组织问题,导致典型企业的平均服务数量显著增加,每个服务的盒子数量也有类似的增加。Docker 普及的容器形式实际上具有很强的欺骗性。乍一看,每个服务实例都有一个便宜的专用 VM。但是,如果这样的实例需要sidecar(例如在您的 Web 应用程序前面运行的本地反向代理来终止 TLS 连接或加载秘密和/或预热缓存的守护程序),您会立即感觉到疼痛,这就是容器与虚拟机的本质区别。

Docker 容器被刻意设计为只包含一个应用程序。一个容器——一个 Nginx;一个容器 - 一个 Python Web 服务器;一个容器 - 一个守护进程。容器的生命周期将绑定到该应用程序的生命周期。并且特别不鼓励将像systemd这样的 init 进程作为顶级入口点运行。因此,要从本文开头的图表重新创建一个 VM-box,您需要拥有三个具有共享网络堆栈的协调容器-box(嗯,至少localhost需要相同)。要运行该服务的两个实例,您需要三个三个一组的六个容器!

从扩展的角度来看,这意味着我们需要一起扩展(和缩减)一些容器。部署也需要同步进行。新版本的 Web 应用程序容器可能会开始使用新的端口号,并与旧版本的反向代理容器不兼容。我们显然在这里错过了一个抽象,它与容器一样轻量级,但与原始 VM 盒子一样富有表现力。此外,容器本身也没有提供任何将盒子分组为服务的方法。但他们促成了箱子人数的增加!Docker 竞相用它的 Swarm 产品解决这些问题,但另一个系统赢了……

Kubernetes 解决了这一切……还是没有?

Kubernetes 设计师显然没有发明新的运行容器的方法,而是决定重新创建良好的旧的基于 VM 的服务架构,但使用容器作为构建块。好吧,至少这是我的看法。但对我来说,作为以前有 VM 经验的人,一旦我了解了新术语并弄清楚了类似的概念,许多最初的 Kubernetes 想法就会开始看起来很熟悉。

Kubernetes Pod 是新的虚拟机

让我们从 Pod 抽象开始。Pod 是您可以在 Kubernetes 中运行的最小的东西。最简单的 Pod 定义如下所示:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx:1.20.1
    ports:
    - containerPort: 80

乍一看,上面的清单只是说明要运行什么镜像(以及如何命名)。但是请注意containers属性是一个列表!现在,回到那个nginx + web app例子,在 Kubernetes 中,您可以简单地将反向代理和应用程序本身放在一个盒子中,而不是为 Web 应用程序容器运行额外的 Pod:

apiVersion: v1
kind: Pod
metadata:
  name: foo-instance-1
spec:
  containers:
  - name: nginx            # <-- sidecar container
    image: nginx:1.20.1
    ports:
    - containerPort: 80
  - name: app              # <-- main container
    image: app:0.3.2

然而,Pod 不仅仅是一组容器。Pod 中容器之间的隔离边界被削弱。就像在 VM 上运行的常规进程一样,Pod 中的容器可以通过localhost或使用传统的 IPC 方式自由通信。同时,每个容器仍然有一个独立的根文件系统,以保持打包应用程序及其依赖项的好处。对我来说,这看起来像是在尝试同时利用 VM 和容器世界的最佳部分:

扩展和部署 Pod 很简单

现在,当我们得到新的盒子时,我们如何运行多个它们来组成一个服务?换句话说,如何在 Kubernetes 中进行扩展和部署?事实证明,它非常简单,至少在基本场景中是这样。Kubernetes 引入了一个方便的抽象,称为 Deployment。最小的 Deployment 定义由名称和 Pod 模板组成,但指定所需的 Pod 副本数量也很常见:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: foo-deployment-1
  labels:
    app: foo
spec:
  replicas: 10
  selector:
    matchLabels:
      app: foo
  template:
    metadata:
      labels:
        app: foo
    spec:
      <...Pod definition comes here>

Kubernetes 的伟大之处在于作为开发人员,您并不关心服务器(或 Kubernetes 术语中的节点)。您根据 Pod 组进行思考和操作,它们会自动调动(和重新分布)到集群节点:

这使得 Kubernetes 更像是一种无服务器技术。但同时,Pod 的外观和行为与过去熟悉的 VM 非常相似(除了您不需要管理它们),因此您可以在熟悉的抽象中设计和推理您的应用程序:

内置服务发现

Kubernetes Service - 一组命名的 Pod。Kubernetes 设计人员肯定知道,仅仅创建 N 个盒子的副本并将其称为服务是不够的。客户端应该能够使用单个(可能是逻辑的)名称访问服务,并且服务发现系统应该能够将该名称转换为特定的 IP 地址(类似于我们理解的负载均衡器,服务于特定的实例) )。

过去,您需要一个单独的(并且要求非常高的)解决方案。但是,Kubernetes 内置了这个功能,而且默认实现还不错!它还可以使用Linkerd或Istio等服务网格进行扩展,使其更加强大。将一组 Pod 转换为服务唯一需要做的就是创建一个 Service 对象(不是真正的创建服务,只是一个网络层面的抽象)。下面是一个简单的 Kubernetes Service 定义:

apiVersion: v1
kind: Service
metadata:
  name: foo
spec:
  selector:
    app: foo
  ports:
    - protocol: TCP
      port: 80

上面的清单允许app=foo使用defaultDNS 名称(如foo.default.svc.cluster.local. 而且这一切都没有在集群中安装任何额外的软件!请注意 Service 定义在任何地方都没有提到 Deployment。就像 Deployment 本身一样,它根据 Pod 和标签运行,这使它非常强大!例如,Kubernetes 中良好的蓝/绿或金丝雀部署可以通过让两个 Deployment 对象在单个 Service 选择具有公共标签的 Pod 后运行不同版本的应用程序镜像来实现:

现在,最有趣的部分 - 你注意到 Kubernetes 服务与我们旧的基于 VM 的服务没有什么区别了吗?

Kubernetes 即服务

那么,Kubernetes 是不是就像 VM 一样,但更简单?嗯,是的,但也不是。因为他跟虚拟机存在本质上的差别,套用Kelsey Hightower的话,我们应该区分驾驶汽车的复杂性和修理汽车的复杂性。我们中的许多人会开车,但很少有人擅长修理发动机。幸运的是,有专门的商店!这同样适用于 Kubernetes。使用 EKS 或 GKE 等托管 Kubernetes 产品运行服务确实很相似,但比使用 VM 简单得多。但如果你必须维护 Kubernetes 集群背后的实际服务器,那就完全不同了……,所以仅仅使用 Kubernetes 和维护 Kubernetes 是两码事。

总结

为了改善在 VM 上运行服务的体验,容器改变了我们打包软件的方式,大大降低了对服务器配置的要求,并启用了替代方法来部署我们的工作负载。但就其本身而言,容器并没有成为大规模运行服务的解决方案。顶部仍然需要额外的编排层。Kubernetes 作为容器原生编排系统之一,使用容器作为基本构建块重新创建了过去熟悉的架构模式。Kubernetes 还通过提供用于扩展、部署和服务发现的内置方法来解决传统方案的痛点。

kubernetes学习总结-基本概念

...件  -doctor:负责本机的容器创建和管理工作3.Pod:  Kubernetes最基本概念  -Pause容器作为根容器,代表整个容器的ip和volume存储4.Label(标签):  -实现分组管理功能5.LabelSelector:  使用场景:  -监控Pod的副本数量,kube-Con... 查看详情

kubernetes学习总结synopsis

同类产品dockercompose、dockerswarm、dockermachine、mesos、marathonKubernetes项目地址https://github.com/kubernetes/kubernetes/releasesSchema?核心组件:1)APIserver:接收、分析用户请求,并处理用户请求2)Scheduler:调度资源(通过初选、优选、选定三个阶... 查看详情

kubernetes学习总结(28)——kubernetes常见问题总结(代码片段)

如何删除不一致状态下的rc、deployment、service在某些情况下经常发现kubectl进程挂起现象,然后在get时候发现删了一半而另外的删除不了[root@k8s-master ~]# kubectl get -f fluentd-elasticsearch/NAME DESIRED CURRENT READY AGErc/elasticsearch-logging-... 查看详情

kubernetes学习总结(18)——kubernetes容器网络

前言在Kubernetes 中要保证容器之间网络互通网络至关重要。Kubernetes 本身并没有实现容器网络而是通过插件化的方式自由接入,容器网络接入需要满足如下基本原则:pod无论运行在任何节点都可以互相直接通信,而不... 查看详情

kubernetes学习总结(29)——使用kubeadm部署kubernetes1.24详细步骤总结(代码片段)

前言kubeadm是Kubernetes官方提供的用于快速安部署Kubernetes集群的工具,伴随Kubernetes每个版本的发布都会同步更新,kubeadm会对集群配置方面的一些实践做调整,通过实验kubeadm可以学习到Kubernetes官方在集群配置上一些新... 查看详情

kubernetes学习总结(31)——kubernetes为何需要istio?

一、ServiceMesh是什么?“Aservicemeshisadedicatedinfrastructurelayerforhandlingservice-to-servicecommunication.“——WilliamMorgan。erviceMesh是一个专注于处理服务间通信的基础设施层。云原生应用有着复杂的服务拓扑,而ServiceMesh保证请求可以... 查看详情

kubernetes学习总结(31)——kubernetes为何需要istio?

一、ServiceMesh是什么?“Aservicemeshisadedicatedinfrastructurelayerforhandlingservice-to-servicecommunication.“——WilliamMorgan。erviceMesh是一个专注于处理服务间通信的基础设施层。云原生应用有着复杂的服务拓扑,而ServiceMesh保证请求可以... 查看详情

kubernetes学习总结(32)——kubernetes的架构原理简单总结

一、Master节点与 Node节点通讯原理Master节点启动时,会运行一个 kube-apiserver 进程,它提供了集群管理的API接口,是集群内各个功能模块之间数据交互和通信的中心枢纽,并且它页提供了完备的集群安全机制。在N... 查看详情

kubernetes学习总结(32)——kubernetes的架构原理简单总结

一、Master节点与 Node节点通讯原理Master节点启动时,会运行一个 kube-apiserver 进程,它提供了集群管理的API接口,是集群内各个功能模块之间数据交互和通信的中心枢纽,并且它页提供了完备的集群安全机制。在N... 查看详情

kubernetes学习总结(14)——kubernetes实用命令总结(代码片段)

一、Pods1、Getallpodsinthecurrentnamespacekubectlgetpods2、Getpodsinallnamespaceskubectlgetpods--all-namespaces3、Getpodswithmoredetailskubectlgetpods-owide4、Gettheyamlforapodkubectlgetpod<pod>-oyaml5、Inspectapodkubectldescribepods<pod>6、Getpodssortedbyametrickubectlgetpods--s... 查看详情

kubernetes学习总结(14)——kubernetes实用命令总结(代码片段)

一、Pods1、Getallpodsinthecurrentnamespacekubectlgetpods2、Getpodsinallnamespaceskubectlgetpods--all-namespaces3、Getpodswithmoredetailskubectlgetpods-owide4、Gettheyamlforapodkubectlgetpod<pod>-oyaml5、Inspectapodkubectldescribepods<pod>6、Getpodssortedbyametrickubectlgetpods--s... 查看详情

kubernetes学习总结(18)——kubernetes容器网络(代码片段)

前言在Kubernetes 中要保证容器之间网络互通网络至关重要。Kubernetes 本身并没有实现容器网络而是通过插件化的方式自由接入,容器网络接入需要满足如下基本原则:pod无论运行在任何节点都可以互相直接通信,而不... 查看详情

kubernetes学习总结——kubernetes常见面试题汇总

简述etcd及其特点etcd是CoreOS团队发起的开源项目,是一个管理配置信息和服务发现(servicediscovery)的项目,它的目标是构建一个高可用的分布式键值(key-value)数据库,基于Go语言实现。特点:简单... 查看详情

kubernetes学习总结(26)——kubernetes网络模型基础指南(代码片段)

前言Kubernetes是为运行分布式集群而建立的,分布式系统的本质使得网络成为Kubernetes的核心和必要组成部分,了解Kubernetes网络模型可以使你能够正确运行、监控和排查应用程序故障。网络是非常复杂的,拥有许多概念... 查看详情

kubernetes学习总结(17)——kubernetes快速入门需要掌握的知识点总结(代码片段)

一、DockerDocker是基于操作系统的沙盒技术,使得用户更简单和完整的去打包自己的应用。docker底层是基于Linux的操作系统级别的虚拟化技术LXC实现;LXC是通过CGroup实现了虚拟化资源管理,用来保证应用资源的隔离和应... 查看详情

kubernetes学习总结(36)——kubernetes本地运行的四种方法(代码片段)

前言Kubernetes是一个开源的容器编排平台。它由Google开发,为自动化部署、扩展和管理容器化应用提供了一个开源系统。虽然大多数人在云环境中运行Kubernetes,但在本地运行Kubernetes集群不仅是可能的,它还至少有两个... 查看详情

kubernetes学习总结(36)——kubernetes本地运行的四种方法(代码片段)

前言Kubernetes是一个开源的容器编排平台。它由Google开发,为自动化部署、扩展和管理容器化应用提供了一个开源系统。虽然大多数人在云环境中运行Kubernetes,但在本地运行Kubernetes集群不仅是可能的,它还至少有两个... 查看详情

kubernetes学习总结(36)——kubernetes本地运行的四种方法(代码片段)

前言Kubernetes是一个开源的容器编排平台。它由Google开发,为自动化部署、扩展和管理容器化应用提供了一个开源系统。虽然大多数人在云环境中运行Kubernetes,但在本地运行Kubernetes集群不仅是可能的,它还至少有两个... 查看详情