库伯内斯101

2020-08-31 01:06:07

目前,Kubernetes是DevOps世界中最令人兴奋的技术之一。最近围绕它形成了很多炒作,原因很简单,这个原因就是强大的容器。

曾几何时,也不是很久以前,我们仍然在服务器硬件上运行应用程序。我们有一个在硬件服务器上旋转的网站和一个存储网站状态的数据库。用户访问该网站,一切都很酷,一切都正常。

网站变得成功了,我们的公司也发展壮大了。然后网站变得越来越复杂,演变成一个拥有一系列服务的平台。

然后出现了虚拟机(VM),我们可以在一台机器上运行多个操作系统和应用程序。这使公司能够在一台服务器上运行十次或更多服务器实例。但是,如果我们放弃了VM的大部分操作系统,那么如果我们可以在一台服务器上运行更多的服务器程序,情况会怎样呢?这将给我们带来更大的成本节约和灵活性。然后集装箱就来了。

Docker Containers已经成为事实上的开发标准已经有很长一段时间了,但有趣的是,Docker并不是容器世界的先驱。

Docker基于命名空间和cgroup技术(第一个提供隔离,第二个提供进程分组和资源限制),因此在虚拟化方面,它与历史上领先的LXC/OpenVZ没有太大区别。相同的原生速度,相同的基于Linux内核机制的隔离方法。然而,在更高的层面上,这是一个非常不同的故事。Docker的亮点在于,它允许您部署一个完全虚拟的环境,并在其上运行应用程序,并且可以轻松地对其进行管理。

与虚拟机一样,Docker在其自己的预配置操作系统中运行其进程。但是,所有坞站进程都运行在与主机系统中运行的所有其他进程共享处理器和可用内存的物理主机服务器上。Docker使用的方法介于在物理服务器上运行一切和虚拟机提供的完全虚拟化之间。这种方法被称为集装箱化。

从Linux容器开始,人们的兴奋之情转移到了Docker容器,同时增加了对容器编排的需求。

回到我们的故事。在某种程度上,公司已经意识到目前的服务器是不够的,无论它有多大(垂直伸缩)。历史进入下一个阶段-公司购买更多服务器,并在它们之间分配负载(水平扩展)。

IT团队的任务变得越来越难。他们需要在更新、补丁、监控、安全、备份、耐用性、可靠性、恢复能力等问题上付出更大的努力。也就是说周日早上6点的传呼值班...。

酷,现在我们要担心的就是应用程序逻辑了。但是,高度可用的分布式系统的复杂性并没有发挥作用--它只是转移到了编排和连接基础设施组件的复杂性上。部署、服务发现、网关配置、监控、复制、一致性...。

这是一个短篇小说,就像我们走到今天一样。现在我们有了一个分布式系统,在云中的一堆容器中有一组服务。

别自吹自擂了,即使我一直在和库伯内斯合作,我也只知道冰山一角。因此,这里并不是深入研究,而是对现有的子系统和概念进行一次平稳的飞行。

Kubernetes是一种复杂的机制,旨在使系统具有可伸缩性、弹性和易于部署。它允许我们自动执行容器编排-启动、扩展和管理集装箱化的应用程序,包括集群管理、调度、服务发现、监控、秘密管理等。

Kubernetes允许自动启动和回滚部署,Kubernetes管理资源,并可以根据应用程序需要多少来扩展所需的资源,以避免资源浪费。因此,K8本质上是过程自动化。K8中的应用程序在没有管理员的情况下推出和测试。开发人员编写了一个脚本,然后云魔术就发生了。因此,在Kubernetes的理想世界中,软件的所有运营支持都在程序员的肩上,管理员确保云基础设施层-即Kubernetes本身-稳定运行。这就是为什么公司会走上云端,完全摆脱管理的常规,只做开发工作。

在我看来,Kubernetes最酷的特性之一是它有助于标准化与云服务提供商的工作。无论我们谈论的是哪一个CSP,与库伯内斯的合作看起来总是一样的。开发者以声明的方式告诉Kubernetes他需要什么,Kubernetes使用系统资源,帮助开发者从平台的实现细节中抽象出来。

最初,Kubernetes是一个谷歌项目,它考虑到了Kubernetes的祖先Borg的缺点。谷歌使用Kubernetes来管理其由数百万个容器组成的庞大基础设施。在某种程度上,谷歌向全世界赠送了库伯内斯夫妇,即云原生计算基金会(Cloud Native Computing Foundation)。到目前为止,Docker已经将Kubernetes添加为乐团之一-Kubernetes现在是Docker社区和Docker企业版的一部分。

Kubernetes有很多名字,其中有Kube或K8(我喜欢带有数字的很酷的缩写,并将进一步使用它)。

库伯内斯家族本身就是一件巨大的抽象作品。这些抽象映射到虚拟或物理基础设施。要了解它是如何工作的,我们必须首先了解它的基本组件。

Pod是群集节点上可以启动的最小单元。这是一组容器,出于某种原因,它们应该一起工作。Pod中的容器共享端口号、Linux内核命名空间和网络堆栈设置。因此,当您在K8中扩展应用程序时,应该增加Pod的数量,而不是增加某个特定Pod中的容器数量。默认情况下,吊舱内的容器会自动重启,为您修复间歇性问题。因此,即使在这些基本级别上,K8也能让您的容器保持运行,并且只需稍加努力,您就可以对其进行调优,以获得更高的可靠性。

通常(但不总是)吊舱下面只有一个容器。但Pod抽象提供了额外的灵活性,例如,当两个容器需要共享同一数据仓库时,或者如果它们之间存在使用进程间通信的连接,或者如果它们由于某种其他原因紧密连接,则所有这些都可以通过在同一Pod中运行来实现。

因此,您可以很容易地将其与现有的代码原语和K8中的分布式原语进行比较。类是容器镜像,Object是容器实例,Pod是部署单元,可以通过SideCar模式实现组合等。

Pod的另一个灵活性是它们不需要使用Docker容器。如果需要,您还可以使用其他应用程序容器技术,例如RKT。

期望状态是K8的基本概念之一。您可以指定运行Pod所需的状态,而不是编写如何实现该状态。例如,如果Pod因某种原因停止工作,K8S将根据指定的所需状态重新创建Pod。

K8总是检查集群中容器的状态,这是通过所谓的Kubernetes Master上的控制循环来完成的,Kubernetes Master是控制平面的一部分。我们稍后再谈这件事。

K8S中的对象是意图的记录-所需的群集状态。并且在对象创建之后,K8会不断地检查该对象状态。此外,这些对象还充当容器接口上方的附加抽象层。您可以与对象实体交互,而不是直接与容器交互。

几乎每个K8对象都包括两个嵌套的对象字段,它们控制对象的配置:对象规范和对象状态。

豆荚是凡人的-它们出生和死亡,但为了能够与豆荚通信,并使它们能够相互通信,引入了服务的抽象。服务充当提供与底层Pod相同功能的Pod集的接入点。有不同类型的服务,ClusterIP、NodePort、LoadBalancer、ExternalName。默认情况下,K8使用ClusterIP-它将服务暴露在集群内部IP上,因此您只能使用Kubernetes代理进行访问。

此外,为了公开服务,您可以使用Inress Object。入口不是一种服务类型,但它充当您的群集的入口点。入口是对通信量应如何从群集外部流向您的服务的描述,它充当智能路由器或ALB。它允许您将路由规则整合到单个资源中,因为它可以在单个IP地址下聚合多个服务。

例如,Web应用程序可以在https://example.com上有主页,在https://example.com/cart上有购物车,在https://example.com/api.上有api。我们可以在一个Pod中实现所有这些组件,但是为了让所有这些组件独立扩展,我们可以将它们解耦到不同的Pod中并连接到Inress。

K8S还有大量的控制器,例如ReplicaSet-它检查一定数量的Pod副本是否正在运行,StatefulSet用于有状态应用程序和分布式系统,DemonSet用于将Pod复制到群集中的所有节点或仅复制到指定节点,等等。它们都实现了控制环路-监控其子系统状态的非终止环路,然后在必要时进行或请求更改。每个控制器都尝试将群集的当前状态移动到更接近所需状态的位置。

用户希望应用程序始终可用,并且每天会部署几次新版本的应用程序。Deployment对象是一个示例,展示了K8S如何将乏味的手动更新应用程序的过程转变为可以重复和自动化的声明性活动。如果没有部署,我们将不得不手动创建、更新和删除大量Pod。部署对象允许我们自动从应用程序的一个版本转换到另一个版本,并且表示副本集之上的一层,并且实际管理副本集和Pod对象。这是在不中断系统操作的情况下完成的。如果在该过程中出现错误,它将能够快速返回到应用程序的前一个工作版本。此外,使用部署,我们可以非常轻松地扩展应用程序。我们稍后再试一下。

现成的部署策略(滚动部署和固定部署)控制用新容器替换旧容器,而发布策略(蓝绿色和金丝雀)控制新版本如何可供客户服务使用。后两种发布策略基于人类对迁移的决策,因此不是完全自动化的,可能需要人工交互。

Kubernetes控制平面是一组控制集群状态的进程。通常,所有这些进程都由集群中的单个节点运行,此节点也称为主节点。还可以复制主节点以实现冗余和容错。

Kubectl命令行工具是通过API与集群中的主服务器通信的接口。

运行在Master节点上的服务称为Kubernetes Control Plane(etcd除外),Master本身只用于管理任务,而包含您的服务的真正容器将在Worker节点上运行。

在每个主节点(可以有多个用于容错的节点)上,有以下基本组件可确保所有系统组件的运行:

Etcd是K8用于配置管理和服务发现的高度一致的分布式键值存储。有点像动物园管理员和领事。它存储系统的当前状态和所需的状态。如果K8S发现当前状态和所需状态之间的etcd存在差异,它会执行必要的调整。

Kube-apiserver是群集的主要控制端点。来自kubectl的任何命令都作为API请求发送到主节点上的kubectl。API服务器处理所有REST请求、验证它们、对客户端进行身份验证和授权,并更新etcd上的信息。API服务器是唯一使用etcd的服务器-集群的所有其他组件向API服务器发出请求,并更新etcd上的信息。

Kube-Controller-manager是一个守护进程,它嵌入了K8附带的基本控制循环。它包括我们前面提到的复制控制器、端点控制器和命名空间控制器。现在,Kubernetes没有一个单独的控制循环,但许多循环都同时运行,并试图将当前系统状态转换到所需的状态。

Kube-Scheduler在群集中的所有可用节点上调度任务-根据所需的资源和节点工作负载确定在哪个工作节点上创建新Pod。

Worker Node是具有启动Pod的Kubernetes组件的虚拟机或物理机。工作节点上运行着两个组件:

每个群集节点上的主要K8S组件。它检查Kube-apiserver以获得要部署在给定节点上的新Pod的描述,并通过其容器管理API处理Docker(或其他容器系统)。在对节点上Pod的状态进行更改后,它将状态信息传递回Kube-apiserver(后者将其添加到etcd),并监视容器状态。

Kube-Proxy相当于反向代理服务器,负责将请求转发和代理到K8S集群内网对应的服务或应用。默认情况下,它使用iptables。

当然,这远远不是所有的K8实体,也绝不是所有的细节。还有更多,更多。

要试用K8,您需要为自己配置一个K8集群。有不同的工具可以实现这一点。最受欢迎的是Minikube,K3,Kind(Kubernetes-in-Docker),Kubeadm,MicroK8,Kops,Kubernetes-Ansible。这些工具中的每一个都在考虑不同目标的情况下实现其预期目的,并且都有自己的一组权衡。

Minikube是最接近本地测试和开发的官方迷你发行版,它由与K8相同的基金会运行,并且可以由kubectl管理。它完全是跨平台的,但它严重依赖中间VM(这是一个很大的开销),并且可以在实际主机上运行(仅在Linux上)。

为此,我使用Ubuntu Server 18.04LTS启动了一台t2.Medium AWS EC2机器,因为Minikube至少需要2vCPU和2 GB的空闲内存(t3.Micro也可以)。

在新创建的计算实例上要做的第一件事是下载并安装Docker Community Edition 17.12.+,然后安装Minikube和kubectl。

使用Minikube时,请记住创建了一个本地虚拟机,并启动了具有单个节点的集群。切勿将其用于生产部署-Minikube仅用于测试和开发。

要启动单节点群集,只需运行Minikube start命令。通过这样做,您可以同时启动虚拟机、集群和K8本身。

$Minikube状态 迷你立方 类型:控制平面 主机:运行 库贝莱特:跑步 Apiserver:运行 Kubeconfig:已配置。

如果您看到状态为“Running”,那么您现在可以运行kubectl命令。

对于现场实验和测试,可以进行简单的速度提高。在正常的工作流程中,您将在主机上有一个单独的Docker注册表,以将您的映像推送到那里(Minikube之外)。但是使用下面的命令,我们可以重用Minikube的内置Docker守护进程来实现这一点。更多信息请点击此处。

让我们使用K8创建我们的第一个应用程序。它基本上只是一项服务,根据请求使用有关客户端和服务器的信息进行响应。

我们看到创建了一个名字怪异的吊舱,现在它正在运行。创建后,我们可以使用kubectl Describe命令请求有关Pod状态的完整信息:

$kubectl描述Pod hello-world-67999bd854-rgd26 姓名:Hello-world-67999bd854-rgd26 名称空间:默认 优先级:0 节点:Minikube/172.17.0.3 开始时间:2020年08月2日01:12:50+0000 标签:app=hello-world Pod-template-hash=67999bd854 注释:<;无>; 状态:正在运行 IP地址:172.18.0.2 IPS: IP地址:172.18.0.2 控制者:ReplicaSet/hello-world-67999bd854 集装箱: 服务器: 容器ID:docker://7dea4d46d48045e0a190bcaa466de8e7eb55e0945d7eebe174907fd012933aa8 图片地址:k8s.gcr.io/echoserver:1.4 镜像ID:docker-pullable://k8s.gcr.io/echoserver@sha256:5d99aa1120524c801bc8c1a7077e8f5ec122ba16b6dda1a5d3826057f67b9bcb 端口:<;无>; 主机端口:<;无>; 状态:正在运行 开始时间:Sun,02 Aug,2020 01:12:58+0000 READY:TRUE 重新启动计数:0 环境:<;无>; 装载: /var/run/secrets/kubernetes.io/来自default-Token-92zzx(Ro)的serviceaccount 条件: 类型状态 已初始化的True Ready True(准备就绪) ContainersReady True(容器就绪) PodScheduled True 卷: Default-Token-92zzx: 类型:Secret(由Secret填充的卷) SecretName:Default-Token-92zzx 可选:False QoS类别:BestEffort 节点选择器:<;无>; 容差:node.kubernetes.io/Not-Ready:NoExecute for 300s Node.kubernetes.io/Unreacable:NoExecute for 300s 活动: 键入消息中的原因期限 警告失败计划9m33(x3超过9m41s)默认-计划程序0/1节点可用:1个节点有污点{node.kubernetes.io/Not-Ready:},Pod没有';我不能容忍。 正常调度的9m30s默认调度程序已成功将default/hello-world-67999bd854-rgd26分配给Minikube 正常拉取9m29s kubelet,Minikube拉取image";k8s.gcr.io/echoserver:1.4"; 正常拉取9m22s kubelet,Minikube成功拉取image";k8s.gcr.io/echoserver:1.4"; Normal创建了9m22s kubelet,Minikube创建了容器回声服务器 正常启动9m22s kubelet,Minikube启动集装箱服务器。

如您所见,Pod运行在名为hello-world-67999bd854-5ttgz的节点上,其内部IP地址为172.18.0.2。请记住,此IP地址将不可用于Kubernetes集群内外的其他应用程序。只能从Pod本身访问正在运行的Nginx。除了指定的IP地址只能从容器内获得之外,它也不是永久性的。这意味着如果重新创建此Pod,它可以获得不同的IP地址。

要解决这些问题,您可以使用一个名为Service的对象。如前所述,服务允许您为Pod分配持久IP地址,授予它们从外部网络的访问权限,并在Pod之间平衡请求。

创建一个Kubernetes Service对象,该对象通过端口公开外部IP地址,以便我们可以访问它:

[NodePort不起作用,因为NodePort暴露在运行K8的虚拟机中,而不是主机中。您可以使用新的Minikube channel命令,该命令应代理端口以与localhost交互。]。

客户端值: 客户端地址=172.18.0.1 命令=GET 实际路径=/ 查询=空 请求版本=1.1 请求URI=http://172.17.0.3:8080/ 服务器值: 服务器版本=nginx:1.10.0-lua:10001 收到的邮件头: 接受=*/* 主机=172.17.0.3:30504 用户代理=cURL/7.58.0 正文: -没有人被要求-。

您可以使用port-for。

.