Fork me on GitHub
k8s的架构and核心概念

1.k8s的前世今生

 

k8s是Kubernetes的缩写,Google 于 2014 年开源了 Kubernetes 项目。

一、k8s的历史演变

 

image

k8s的演变过程:首先从传统的服务-->虚拟机部署-->容器部署-->k8s。

k8s的由来,归根结底是容器的由来,搞清楚容器的来历,k8s是在容器的基础上,方便容器管理、维护,包括声明式配置和自动化。

1、 Docker的由来

Docker 是一个开源的应用容器引擎,是一种资源虚拟化技术,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,虚拟化技术演历路径可分为三个时代:

1)物理机时代

物理机时代,多个应用程序可能跑在一台物理机器上。

image

当多个应用程序跑在一台物理机上的时候,无法为物理机中的应用程序定义资源边界,这会导致资源分配问题。例如,如果多个应用程序在同一台物理服务器上运行,则可能会出现一个应用程序占用大部分资源,而导致其他应用程序的性能会不佳的情况。一个解决方案是在不同的物理服务器上运行每个应用程序。但这并没有扩展,因为资源没有得到充分利用,而且组织维护许多物理服务器的成本很高。

2)虚拟机时代

一台物理机器启动多个虚拟机实例,一个虚拟机跑多个应用程序,每个虚拟机都是完整的独立的系统。

image

为了解决多个应用部署在同一台物理机资源分配不均的问题,引入了虚拟化。它可以在单个物理服务器的 CPU 上运行多个虚拟机 (VM)。应用程序在虚拟机之间可以实现隔离,并提供一定程度的安全性,一个应用程序的信息不能被另一个应用程序自由访问。

虚拟化更好地利用物理服务器中的资源并有更好的可扩展性,可以轻松添加或更新应用程序,降低硬件成本等等。

每个 虚拟机 都是在虚拟化硬件之上运行所有组件的完整机器,包括它自己的操作系统。

虚拟机的性能损耗是非常大的

3)容器化时代

容器化时代,一台物理机上启动多个容器实例,一个容器跑多个应用程序。

image

容器类似于虚拟机,但它们具有松隔离性,可以在应用程序之间共享操作系统(OS)。因此,容器被认为是轻量级的。

与 VM 类似,容器有自己的文件系统、CPU 、内存、进程等。由于它们与底层基础架构分离,因此它们可以跨云和操作系统分布移植。

容器之所以流行,是因为它们提供了额外的好处,例如:

  • 敏捷的应用程序创建和部署:与使用 VM 映像相比,容器映像创建的简便性和效率更高。
  • 持续开发、集成和部署:提供可靠且频繁的容器映像构建和部署以及快速高效的回滚(由于映像不变性)。
  • Dev 和 Ops 的关注点分离:在构建/发布时而不是部署时创建应用程序容器映像,从而将应用程序与基础架构解耦。
  • 可观察性:不仅可以显示操作系统级别的信息和指标,还可以显示应用程序运行状况和其他信号。
  • 开发、测试和生产之间的环境一致性:在笔记本电脑上运行与在云中运行环境相同。
  • 以应用程序为中心的管理:将抽象级别从在虚拟硬件上运行操作系统提高到使用逻辑资源在操作系统上运行应用程序。
  • 松散耦合、分布式、弹性、自由的微服务:应用程序被分解成更小的、独立的部分,并且可以动态部署和管理——而不是在一台大型单一用途机器上运行的单一堆栈。
  • 资源隔离:可预测的应用程序性能。
  • 资源利用:高效率、高密度。

Docker 由镜像、镜像仓库、容器三个部分组成:
镜像: 跨平台、可移植的程序+环境包
镜像仓库: 镜像的存储位置,有云端仓库和本地仓库之分,官方镜像仓库地址
容器: 进行了资源隔离的镜像运行时环境

2 、从Docker到Kubernetes

随着容器的火爆,越来越多的业务系统利用容器来搭建部署,像 Docker 之类的容器引擎,部署少量还可以,但随着业务的增多,服务越来越多,动辄就要使用成百上千的容器,要管理这么多容器,Docker 们就力不从心了。随着容器技术越来越多的使用,出现了很多问题

  • 百上千的容器管理问题
  • 分布式环境下容器如何通信?
  • 如何协调和调度这些容器?
  • 如何在升级应用程序时不会中断服务?
  • 如何监视应用程序的运行状况?
  • 如何批量重新启动容器里的程序?

有需求就有改变,于是乎,市场上就出现了一批容器编排工具,典型的是 Swarm、Mesos 和 K8S。最后,K8S“击败”Swarm 和 Mesos,几乎成了当前容器编排的事实标准。

Kubernetes(简称 K8s,其中8代指中间的8个字符),是一个全新的基于容器技术的分布式架构方案。K8S 最初是由 Google 开发的,后来捐赠给了 CNCF(云原生计算基金会,隶属 Linux 基金会)。K8S是 Google 十几年来大规模应用容器技术的经验积累和升华的重要成果,确切的说是 Google 一个久负盛名的内部使用的大规模集群管理系统——Borg的开源版本其目的是实现资源管理的自动化以及跨数据中心的资源利用率最大化。

Kubernetes具有完备的集群管理能力,包括多层次的安全防护和准入机制、多租户应用支撑能力、透明的服务注册和服务发现机制、内建的智能负载均衡器、强大的故障发现和自我修复能力、服务滚动升级和在线扩容能力、可扩展的资源自动调度机制,以及多力度的资源配额管理能力。

同时,Kubernetes 提供了完善的管理工具,这些工具涵盖了包括开发、部署测试、运维监控在内的各个环节,不仅是一个全新的基于容器技术的分布式架构解决方案,还是一个一站式的完备分布式系统开发和支撑平台。

K8S 是个杂技高手,最擅长的就是“搬箱子”,盘各种容器玩。

image

K8S 的大致架构,就像上面。Master 节点,用来放“脑子”,“腿脚”搭在工作节点上“搬砖”,工作节点就是实际业务容器的存放地。

单个容器或多个关系密切的容器,被编成一组,称为 pod。K8S 就是以 pod 为单位进行编排操作。

同时,K8S 还要和其它相关软件配合,来完成联网、存储、安全等功能。

Kubernetes 由 Master 节点、 Node 节点以及外部的 ETCD 集群组成,集群的状态、资源对象、网络等信息存储在 ETCD 中,Mater 节点管控整个集群,包括通信、调度等,Node 节点为工作真正执行的节点,并向主节点报告。

3、容器和k8s的关系

image

K8S 和 Docker 们不是替代关系,而是配合关系。K8S 仍然会使用 Docker 之类的容器引擎(Docker、Containerd、RKT、CRI-O 等),来对容器进行生命周期管理。

二、K8S的特性

  1. 高可用,不宕机,自动灾难恢复

2.灰度更新,不影响业务正常运转

3.一键回滚到历史版本

4.方便的伸缩管理(包括应用伸缩,机器增减),提供负载均衡

5.有一个完善的生态体系

三、k8s有哪些功能

  • 服务发现和负载平衡

    Kubernetes 可以使用 DNS 名称或使用自己的 IP 地址公开容器。如果容器的流量很高,Kubernetes 能够负载均衡和分配网络流量,从而使部署稳定。

  • 存储编排

    Kubernetes 允许您自动挂载您选择的存储系统,例如本地存储、公共云提供商等。

  • 自动推出和回滚

    您可以使用 Kubernetes 描述已部署容器的所需状态,它可以以受控的速率将实际状态更改为所需状态。例如,您可以自动化 使用Kubernetes 创建新容器、删除现有容器并将其所有资源用于新容器。

  • 自动装箱 你为 Kubernetes 提供了一个节点集群,它可以用来运行容器化的任务。你告诉 Kubernetes 每个容器需要多少 CPU 和内存 (RAM)。Kubernetes 可以将容器安装到您的节点上,以充分利用您的资源。

  • 自我修复 Kubernetes 会重新启动失败的容器、替换容器、杀死不响应用户定义的健康检查的容器,并且在它们准备好服务之前不会将它们通告给客户端。

  • 秘密和配置管理 Kubernetes 允许您存储和管理敏感信息,例如密码、OAuth 令牌和 SSH 密钥。您可以部署和更新机密和应用程序配置,而无需重新构建容器映像,也无需在堆栈配置中公开机密。

  • 水平扩展

  • 滚动更新

之前了解了k8s到底是什么,接下来看看k8s的组成。

一、Kubernetes架构

学习k8s,最终目的是为了部署应用,部署一个完整的k8s, 就要知道k8s的组成。k8s主要包含两大部分: 中间包含三个绿色包的是master服务器. 下面是node节点.

img

1、Master节点

master中有哪些东西?

1)api server

api server是所有服务访问的统一入口(所有请求的统一的入口).就是一起访问的入口. 从上图可以看出. Master中scheduler需要和api server交互, rc要和api server交互, kubectl(客户端)也要和api sever交互, web UI也要和api server交互, etcd也要和api server交互. apiserver是非常繁忙的.

api server采用的是无状态http请求,所以,他不会记录任何数据,所有数据都存储在etcd上。

Kubernetes API 服务器的主要实现是 kube-apiserver。 kube-apiserver 设计上考虑了水平伸缩,也就是说,我们可以通过部署多个实例进行伸缩。 你可以运行 kube-apiserver 的多个实例,并在这些实例之间平衡流量。

2)scheduler

scheduler是任务调度器, 负责调度任务, 选择合适的节点来执行任务. 当用户下达资源请求的时候,任务调度器会把任务分配给后端的多个node节点上,要基于一定的原则,公平的,快速的分发。也就是说,保证每个节点都有事做,不要浪费资源,做到资源利用最大化。所以,scheduler调度区非常关键,他是保证整个集群资源利用高不高的核心组件。

举个例子,一个任务来了, 要部署一个应用,到底应该部署在哪个节点上呢? 这个过程就是通过scheduler进行任务调度的。有的机器繁忙,有的机器空闲,scheduler会找一台空闲的机器进行部署。通过scheduler进行任务调度分发至不同的node.

scheduler会将任务交给api server, 由api server将任务写入到etcd, 也就是说scheduler不会直接和etcd交互。

3)controller-manager

controller-manager: 控制器, 处理集群中常规后台任务,一个资源对应一个控制器。

举例来说,有一个订单服务,我们要部署这个服务,首先是交给任务调度器,任务调度器调用api server,将信息保存到etcd,etcd会创建一个controller-manager来专门管理这个订单服务。通常来说,一个资源对应一个控制器。

4)etcd:

etcd是键值对数据库, 存储K8s集群的所有重要信息(持久化). 在Kubernetes集群中起到的了持久化的作用. 对于etcd有两点说明:

  • etcd官方将其定位为一个可信赖分布式****键值存储服务, 它能够为整个分布式集群存储一些关键数据, 协助分布式集群的正常运转.
  • etcd的版本

     img

 etcd现在有两个版本, v2和v3版本, v2版本将数据保存到内存, v3版本将数据保存到数据库. 正常我们都选择使用v3版本, 但Kubernetes v1.11版本之前使用的是v2版本.

  • etcd内部架构图

img

  • http Server: 这里采用的是使用http进行构建的c/s服务, k8s也是采用的http协议进行c/s服务的开发. 为什么要这么做呢? 因为http天生支持一系列的操作. 例如: get ,post, put, delete, 授权认证等. 所以, 没有必要再去采用标准的tcp协议. 开发一系列的认证流程, 所以, 直接采用http协议即可.

  • Raft:共识算法,或者叫最终一致算法。比如:有3台etcd机器在运行的过程中,突然停了,那么3台etcd中的配置可能是不一样的,但是,一旦运行起来,经过一段时间,最终会达到一致。每一个Raft集群都包含多个服务器,在任意时刻,每一台服务器只可能处于Leader(主节点)、Follower(跟随者)、Candidater(竞选者)三种状态中的一种。在处于正常状态(可访问)时,集群中只会存在一个Leader,其余的服务器都是Follower。

    是读写的信息, 所有的读写信息都被存在Raft里面, 而且, 为了防止这些信息出现损坏, 他还有一个WAL预写日志

  • WAL: 预写日志, 吸入到数据库之前,先保存到日志里。如果要对数据进行更改, 那么先写入一条日志, 然后定时的对日志进行完整的备份. 也就是完整+临时. 比如: 我先备份一个大版本, 备份以后, 还会有1个子版本, 两个子版本....., 然后将这些版本再次进行一个完整备份,把它变成一个大版本. 这样做的好处, 我们不能始终进行完整备份, 因为消耗的数据量太大. 为什么还要在一定时间内进行完整的备份呢?防止增量备份太多, 还原的时候太费事. 并且, Raft还会实时的把这些数据和日志存入到本地磁盘进行持久化.

  • Store: 试试把WAL中的日志和数据, 写入磁盘进行持久化.

5)kubectl

命令行管理工具。这个工具我们可以安装在任何节点上。通常,我们将其安装在master节点上。可以安装在安卓手机上,苹果手机上,windows电脑上,只要能够通过网络连接到api server,就能下发请求。

2、Node节点

从图中可以看出, Node节点包含三个组件 ,kubelet, kube proxy, 以及container. 也就是说我们在node节点需要安装三个软件: kebelet, kebu proxy, docker

    img

1)kubelet的作用:

pod中安装的是docker容器,外层环境是k8s,docker和k8s能直接交互么?不能,一个是外国人,只会说英语,一个是中国人,只会说汉语。那如何让外国人和中国人交流呢?翻译呗。kubelet的作用就是连接k8s和docker的。kubelet监听api server,api server下发命令以后,kubelet要去调用docker,去执行指令,比如容器的创建。

直接跟容器交互, 实现容器的生命周期管理.他会和CRI, C是容器, R是runtime, I是interface. CRI就是docker的操作形式. kubelet会和docker交互, 创建需要的容器. kubelet会维持Pod的生命周期.

2)kube proxy的作用:

kube proxy监听者api server,api server发出请求以后,会调用linux的内核接口,叫做net link接口,这个接口允许我们通过接口调用的方式实现IPVS的创建,实现netfire的管控,就是IPVS和防火墙的管控。负载均衡和数据的转发都是基于kube proxy组件实现的。

负责写入规则至IPTABLES, IPVS实现服务映射访问. 之前说过svc, 可以进行负载操作, 负责的操作就是通过kube proxy完成的. 怎么实现Pod与Pod之间的访问, 以及负载均衡. 默认操作是操作防火墙, 去实现Pod的映射. 新版本还支持IPVS.

3、其他重要的插件

1)Web UI

Dashboard是 Kubernetes 集群的通用的、基于 Web 的用户界面。 它使用户可以管理集群中运行的应用程序以及集群本身并进行故障排除。

2) COREDNS

可以为集群中的SVC创建一个域名IP对应的关系解析. 也就是说,我们在集群中访问其他Pod的时候, 完全不需要通过Pod的ip地址, 通过CoreDns给他生成的域名去实现访问. 他是集群中的重要重要组件, 也是实现负载均衡的其中一项功能.

3)DASHBOARD

给K8S集群提供一个 B/S结构访问体系.

4)Ingress Controller

官方只为我们实现了四层代理. Ingress可以实现七层代理, 也就是可以根据组件名和域名进行负载均衡.

5)Federation

提供一个可以跨集群中心多K8s统一集群管理功能.

6)Prometheus(普罗米修斯)

提供K8S集群的监控能力.

7)ELK

提供k8s集群日志统一接入平台

二、K8S和docker的关系

为什么会说k8s和docker的关系呢?这还要源于k8s发布的一则消息,在后续版本将不再增加垫片这个组件。导致很多人觉得docker不行了,很可能会被k8s遗弃,为什么这个垫片会有这么大的影响呢?这就要从CRI和O-CRI说起了。

image

先来看看容器是如何创建的?

kubelet监听了server api,有任何的变化都会下发命令给docker,然后docker操作容器。那么,kubelet调用docker的时候,是使用命令还是调用接口呢?

肯定是直接调用接口。因为调用命令最终也是去执行接口,中间还转一步,效率太低了。

但是,kubelet能直接调用docker接口么?

我们知道docker采用的是CRI容器运行时接口,

而k8s是google的产品,现在是CNCF云容器基金会的产品,这是一个开源镜像,k8s会直接对接到CRI这样一个私有协议么?我是公共使用的,所以肯定不会对接到私有协议接口。那么,我会对接到O-CRI接口,这时一个共有协议接口。问题来了,docker是CRI私有协议接口,k8s是O-CRI共有协议接口,对接不过去啊。所以,怎么办?再加一层转换,这层转换的作用是承上启下,上面承的是O-CRI,下面承的是CRI。这个转换是在kubectl实现的。这一层被叫做垫片。承上启下用的。

最开始,Docker的名气要比k8s大的多得多,所以,k8s就承接了垫片的任务。而如今,k8s的名气已经很大了,它不再需要依赖于docker,于是他要去掉垫片。并且发了公告。

那么docker是不是就完蛋了,k8s没有垫片做转换了,就不能调用docker接口了。docker也很机智,随即发布消息,他会增加垫片功能。这样k8s依然可以调用docker容器。但是,我们要知道,docker就重了,k8s减负了,k8s可以兼容任何容器,现在市面上有好几款容器,他不是飞docker不可的了。

k8s的核心概念

一. Pod

pod,中文翻译过来叫豆荚,如下图。我们都知道豆荚,一个豆荚里面有很多豆子。豆荚就可以理解为pod,一个个的豆子就可以理解为容器。pod和容器的关系是一个pod里面可以有一个或者多个容器。Pod是k8s部署的最小单位。

image

那么pod中容器和容器之间有什么关系呢?

当server api将指令下发给kubelet的时候,kubelet会创建第一个容器,不管创建pod的目标是什么,第一个容器总是不变的,他就是pause。pause是第一个被创建的容器,它的作用有两个:

  • 初始化网络栈

    创建对应的veth,网络命名空间,网桥,这些都是初始化网络栈的时候完成的。

  • 挂载网络卷

    当我们需要 pod有一定的存储能力,这时就需要给pod挂载到对应的卷,这个卷是被谁挂载的呢?不是被应用容器挂载的,而是被pause挂载的。

初始化好pause,后续可以在pod中安装一个或多个容器。这些容器跟pause共享网络栈,共享数据卷。

image

比如,我们在pod中添加了3个容器,nginx,mysql,java应用程序。还可以更多,但最少要有一个container。这些容器共享pause的网络和数据卷。所以,在pod中的容器,容器名、端口号都不能重复,否则会报错。

提示:通常容器关联比较大的应用,放在一个pod中,在同一个pod中可以使用localhost进行访问

1.Pod的类型

1) 自主式Pod

  自主式Pod是不被控制器管理的Pod. 这种Pod死亡以后, 不会被重新启动. 这个Pod死了以后, 副本数就达不到期望值了, 也不会有人去创建一个新的Pod来达到副本数的期望值.

 在传统情况下, 我们运行一个容器, 每一个容器都是独立存在的, 每个容器都有自己的ip地址, 每个容器都有自己的挂载卷. 但在k8s移植的时候, 就不太容易了. 我们把一个没有在容器里运行的环境转移到或迁移到k8s的环境里, 就比较难迁移.比如:LAMP, 那么A和php之间有联系,我们把A和php分开了, 他俩个是不同的地址, 还要去配置反向代理, 比较费劲.

  说的是什么意思呢? 有些组件应该在一起, 并且能互相见面, 也就是通过localhost能访问到. 但是, 使用标准的容器, 你没办法这样做, 除非你把两个进程封装在一个容器内部. K8S给我们建立了一个Pod, Pod是怎样实现的呢?

  首先, 要定义一个Pod, 他会先启动第一个容器, 需要注意的是, 只要运行了Pod, 这个容器就会被启动. 这个容器叫pause

image

 然后, 在Pod里定义了两个容器, 这两个容器不是固定的,几个都可以,至少是一个。 然后这两个容器会共用PAUSE的网络栈和存储卷.

  image

 也就是说, 这两个容器没有自己独立的ip地址和存储卷, 或者说, 他们有的是PAUSE或者pod的ip地址. 这两个容器他们尾根隔离, 但是进程不隔离. 也就是说, 如果容器1运行的是php, 容器2运行的是nginx, nginx想要反向代理访问php, 只需要要写localhost:9000即可. 不需要写IP地址+端口映射. 能够直接使用localhost的原因是这两个容器共享的是PAUSE的网络栈.

  这样就说明了, 在同一个Pod里, 容器之间的端口不能冲突. 一个pod里不能有两个容器的端口都是80

image

 下面一个要说明的是: 共享存储. 这里两个容器除了共享网络, 同时也共享存储卷.

image

假设PAUSE挂载了一个存储,容器1会共享PAUSE的储存卷,容器2也共享PAUSE的存储卷,也就是说一个pod中的容器共享PAUSE的存储卷

2) 控制器管理的Pod

控制器管理的Pod有三种:ReplicationController & ReplicaSet & Deployment , 这三种控制器有很多相似的地方

i.ReplicationController

ReplicationController简称rc. 它的作用是确保容器应用的副本数始终保持在用户定义的副本数, 这是用户的期望。即如果有容器异常退出, 会自动创建新的Pod来代替; 而如果异常多出来的容器也会自动回收. 在新版本的k8s中, 建议使用ReplicaSet来取代ReplicationController.

ii.ReplicaSet:

ReplicaSet简称rs. 跟ReplicationController没有本质上的区别, 除了名字不同, ReplicaSet支持集合式的selector.

这个集合式的选择器是什么呢?就是在我们创建Pod的时候, 可以给他打标签. 比如: app = http, version = v1版本等等. 我们会打一堆的标签. 当我们想删除容器的时候, 我们可以这样说: 当app=http, version = v1的时候, 执行什么操作. rs支持这种集合方案, 但是rc不支持. 所以在大型项目中, rs比rc会更简单, 更有效率. 所以, 在新版本中, 官方抛弃rc, 全部转用rs.

在小的集群下,有没有标签都没所谓,但当集群越来越大,pod越来越多的时候,标签就很有用了。我们可以通过标签定位某一个pod。

所以,rc适合小集群使用,rs适合多集群,pod量很大的时候使用。rs包含了rc的功能,所以官方都建议使用rs。

iii.Deployment:

虽然replicaSet可以独立使用, 但一般还是建议使用Deployment来自动管理ReplicaSet, 这样就无需担心跟其他机制的不兼容问题(比如ReplicaSet不支持 rolling-update滚动更新, 但Deployment支持) 。Deployment为何要和RS一起使用呢?是因为Deployment本身不能创建Pod。

滚动更新还是很有意义的, 尤其是在生成环境中

比如:我们现在有两个容器, 我们要将现在容器的版本从v1版本升级到v2版本. 这时候, 怎么办呢? 我们可以进行滚动更新.

      img

首先, 先生成一个新的pod. 然删除一个旧的pod, 如下如所示. 先生成一个v2版本的pod, 然后删除一个v1版本的pod .再把v2版本的pod挂载到集群上,然后以此类推,所有服务器以此替换。最后,所有服务器就都是v2版本的,这就是滚动更新.

img

那么, Deployment是如何管理rs并滚动更新的呢?

  • 首次部署的时候,要做那些事呢?

第一步:会创建一个Deployment控制器,在Deployment控制器中定义了pod的模板和副本数量。

第二步,Deployment会主动创建一个RS。也就是说rs不是我们自己定义的, 是Deployment自动生成的. RS会创建多个pod

第三步,RS主动帮我们创建Pod,并维持pod副本数的稳定。

Deployment定义出来以后, 他会定义一个rs, RS会创建多个pod. 如下图

image
  • 当需要更新版本的时候. 怎么做呢?

官方开发出了v2版本,这时我们要进行滚动更新了。如何滚动更新呢?

第一步:Deployment会更改pod模板为V2。

第二步:Deployment控制器再创建一个新的RS。我们的期望是从左边的RS迁移到右边的RS。

`      image

第三步:RS-2再创建一个新的 Pod, 将其升级到v2版本. 然后下掉一个v1版本的Pod

      image

第四步:在创建一个Pod, 将其版本升级到v2, 在下掉一个v1版本的Pod

      image

第五步:直至全部下完.

image

这就是Deployment管理的滚动rolling-update滚动升级。

如果升级的过程中, 发现新版本有一些小bug, 我们还可以回滚. 如何回滚, 执行undo即可. 回滚的逻辑和版本升级的原理一样. 恢复一个v1, 下掉一个v2. 直至全部恢复.

image

为什么RS能够恢复呢?

因为, 下掉的RS没有被删掉. 只是停用了. 当回滚的时候, 老旧的RS就会被启动.   

iv. HPA控制器

Horizontal Pod Autoscaling 简称HPA控制器,仅适用于Deployment和ReplicaSet,在V1版本中仅支持根据Pod的CPU利用率扩缩容,在vlalpha版本中,可以根据内存和用户自定义的metric扩缩容。

举个例子:

首先,我们创建出一个Deployment控制器或者RS控制器,这个控制器会创建pod副本,在Deployment或者RS之上可以增加一个HPA控制器。HPA也是一个对象, 他是基于RS创建的。HPA控制器可以定义一个阈值,比如CPU使用率大于80%的时候,进行扩容;CPU使用率小于20%的时候进行缩容。pod副本数最小2个,最大20个。

image

于是,当cpu使用率超过80%的时候,RS会自动进行扩容,最大扩容到20个副本。

image

如果创建了20个副本以后,CPU依然大于80%,那么就爱莫能助了。

缩容也是一样的,当cpu使用率很小的时候,就缩容,但最小是2个。也即是减到只剩2个pod, 不能再减了.

这样就达到了一个水平扩展的目的. 这也是HPA帮我们实现的.

v. statefulSet

statefulSet主要解决的是有状态服务的问题. docker主要面对的是无状态服务。

服务的分类: 

1. 无状态服务: 踢出去过段时间放回来, 依然能正常工作. 比如LVS调度器, APACHE(http服务) 
    为什么apache是无状态服务呢? 因为apache中的数据可以通过共享服务来完成. 对于组件本身他不需要数据, 也没有数据的更新. 所以, apache被定义到无状态服务里面.
    docker: 对于docker来说, 他更适合运行的是无状态服务. 

2. 有状态服务:  踢出集群后过段时间再放回来, 就不能正常工作了, 这样的服务就是有状态服务. 比如: 数据库DBMS, 因为有很大一部分数据缺失了.
     Kubernetes的一个难点就是必须要攻克有状态服务. 那么, 有状态服务, 有些数据需要持久化, 需要保存起来, 这时,我们就会引入存储的概念.

主要解决的是有状态服务的问题. docker主要面对的是无状态服务, 无状态服务的含义时, 没有对应的存储需要实时的保留. 或者是把他摘出来, 经过一段时间以后, 放回去依然能够工作. 典型的无状态服务有哪些呢? 比如: apache服务, LVS服务(负载均衡调度器) . 典型的有状态服务有哪些呢?mysql, mongodb, 他们需要实时的对数据进行更新和存储. 把他抽离出集群,再放回来就没办法工作了. statefulSet就是为了解决有状态服务而诞生的. Deployment 和 ReplicaSet是无状态服务

statefulset的应用场景包括:

1>稳定的持久化存储.

即Pod重新调度后还是能访问到相同的持久化数据. 基于PVC来实现.

Pod重新调度指的是Pod死亡以后, 我们会在调度回来。也就是创建一个新的Pod,创建的这个新的Pod取代原来的Pod的时候, 他的存储依然是之前的存储, 并不会变, 并且里面的数据也不会丢失.

2> 稳定的网络标识:

即Pod重新调度后,其PodName和HostName是不变的,也就是说之前的Pod叫什么, 现在Pod就叫什么. 之前的主机名是什么,现在的主机名还是哪个。 基于Headless Service(即没有Cluster IP 的Service)来实现。

3> 有序部署.

有序部署分为扩展回收两个阶段.

  • 有序扩展. 即Pod是有顺序的, 再部署或扩展的时候, 要依据定义的顺序一次进行(即从0到n-1, 在下一个pod运行之前, 所有之前的Pod必须都是Running和Ready的标志), 基于init Containars实现

只有当前一个Pod处于running和ready的状态, 第二个才可以被创建. 为什么需要这样部署呢? 原因是, 我们构建一个集群化, 比如集群里有nginx, apache, mysql. 我们的启动顺序是先启mysql, 再启apache, 再启nginx, 因为他们之间是有依赖关系的. nginx依赖apache, apache依赖mysql. 这就是有序部署.

4> 有序收缩, 有序删除

回收也是一样的是有序的, 不同的是, 他是逆序回收. 从n-1开始, 一直到0.

vi. **DaemonSet: **

确保全部(或一些)Node上运行一个Pod的副本. 当有Pod加入集群时, 也会为他们增加一个Pod副本, 当有Pod从集群移除时, Pod副本也会被回收,删除DeamonSet会删除对应的所有的Pod.

  • 这里说的是确保全部或者一些,为什么会是一些呢?这是因为,我们可以在node上打污点,打上污点的node是不被调度的。所以,在DaemonSet运行的时候,打了污点的node是不会被创建的。默认情况下,所有node都会被运行。每个pod只会创建一个副本。

使用DaemonSet的典型用法:

  1. 运行集群存储daemon, 例如在每个Node上运行glusterd, ceph.
  2. 在每个节点上运行日志收集daemon, 例如fluentd, logstash.
  3. 在每个节点上运行监控daemon, 例如Prometheus, Node Exporter.

只要有需求,每个node上都可以运行一个守护进程,去帮我们做一些事情,这个时候就可以使用DaemonSet

vii. Job, CronJob

  • Job是负责批处理的任务. 仅执行一次的任务, 它保证批处理任务的一个或多个Pod成功结束

    比如: 我想备份数据库, 备份代码可以放在Pod里, 我们将其放到Job里去执行,脚本是可以正常执行正常工作。直接在linux操作, 到时间就可以把脚本运行, 执行出来. linux操作系统执行不也是一样的么?

    一方面封装成pod, 我们可以重复利用;

    另一方面,如果脚本执行意外退出,是没办法重复执行的,job如果判断当前脚本不是正常退出,她会重新执行一遍脚本.直到正常退出为止,并且还可以设置政策退出的次数.

  • CronJob管理基于时间的Pob, 在特定的时间可以执行

    》在给定的时间点只运行一次

    》周期性的再交给时间点运行

3. 服务发现

k8s是如何实现服务间的调用的呢?这就是接下来要说的服务发现

  img

客户端想要访问一组pod, 如果这些pod是无相干的话,是不能通过Service统一代理的. pod需要具有相关性, 比如由同一个rs//rc/deployment创建的, 或者拥有同一组标签, 这样的话可以被service收集到. 即: service去搜集Pod是通过标签去选择到的. 这一点很重要.

选择到以后, service会有自己的ip+port, 客户端就可以访问service的ip+端口. 间接访问pod. 并且这里有一个RR的算法存在.

假设我们现在有一个简单的集群环境:

image

有一个myqsl, 三台apache-fpm, 三台缓存服务器SQUID, 有一个负载均衡器LVS. 我们来分析一下, 如果把这个集群放到k8s中应该如何部署.

1> mysql需要运行在一个Pod中
image

在k8s上创建一个pod,里面在创建一个mysql容器

2> apache-fpm, 有三个, 其实他们都是类似, 所以我们可以把它放到Deployment控制器中创建, Deployment可以配置apache-fpm的副本数有3个副本
image
3> SQUID,缓存服务器也有三个, 我们也可以把它放到Deployment控制器中创建.
image
4> LVS, 可以用集群本身的功能, 进行负载调度.

现在这种结构, 我们发现, 如果缓存服务器SQUID想要访问apapche-fpm, 写反向代理的话, 需要写三台服务器. 并且, pod如果退出重新创建, 那么pod的ip地址会变换. 除非采用的是statefulSet, 但是在apache-fpm中使用statefulSet是没有意义, 因为他是一个无状态服务 . 那怎么办呢? 我么可以在前面加一个service, 这个service就是Service-php-fpm的. 他会绑定我们的标签.

image

SQUID去进行反向代理设定的时候, 不需要写php-fpm的三个ip地址了, 而且, pod死亡以后, 控制器会把他维持到三个副本, 会在自动创建一个, 新创建的ip地址和原来的是不一样的. SQUID如果在里面填写的是目标ip, 就有问题. 所以, SQUID里面写的是server-php-fpm的地址. 这样SQUID只要执行到Service-php-fpm上面即可.

image

mysql也是一个pod, 我们要求mysql这个pod如果死了,重启, 他的ip地址和主机名是不能变的, 因此我们把它放到statefulSet中.

Kubernetes内部是一个扁平化的网络, 相互之间可以通过localhost请求访问, 所以, 关联关系如下:

image

SQUID需要被外网访问, 因此, 我们在SQUID上也可以创建一个Service-SQUID

image

这样,我们就可以把这个架构完整的部署在k8s集群中了.

posted on 2022-05-06 20:38  HackerVirus  阅读(326)  评论(0)    收藏  举报