kubernetes(1) 微服务与kubernetes基础概念

Kubernetes与云原生

kubernetes(k8s),是一个容器编排系统。它旨在实现一个新时代企业级的操作系统,即下一代的Linux。

传统API与云原生API

传统意义上,程序员开发程序面临的接口是操作系统的api,比如开发时会调用java接口,并直接部署在单一主机上运行java程序。

由于不同操作系统之间的ABI(应用二进制接口,比如调用的库文件。在Linux上表现为.so,windows上表现为.ddl)的不一致,将导致应用程序的移植面临巨大困难。而当k8s诞生后,应用程序的运行将不会运行于单一主机之上,而是进入这么一个逻辑。

底层是由多台裸金属的主机,或公有云上或私有云上提供的虚拟机,安装Linux并构建为k8s集群。k8s集群对外提供api接口。此时应用程序的开发和部署时,将不再考虑针对系统接口或java接口开发,而是从传统上针对单机的api,开始移向于针对k8s的api接口,这就是所谓的Cloud native apps 云原生应用。

所谓云原生应用,意思是应用程序开发出来天然适应于运行在云平台之上,而非传统的单机应用程序。而云原生应用的诞生,将催生出serverless 无服务器的应用逻辑:knative。

serverless 无服务器

所谓无服务器,是跟传统的系统守护进程区别开来。传统意义上,假如提供web服务,需要在系统后台运行nginx或httpd的守护进程。无论有客户端访问与否,应用程序始终运行并监听套接字,不断消耗系统资源。

而无服务器,平时并不运行应用程序,而是等到有客户端去访问服务时,才运行一个微服务。一旦访问完毕,应用程序的使命结束后将停止运行。直到再次被调用时,才再次运行。此时,应用程序变成了函数,也就是所谓的函数即服务。

FaaS

Function as Service 函数即服务。函数部署在服务器上,当客户端发起请求时,调用函数,并返回结果。此时,每个函数都是可被调用的个体,各自负责一个或多个功能。如果一个软件系统存在成千上百个功能,就有成千上百个函数。如何以用户无感知的方式,去运行这些函数,函数之间的关系调用如何能准确并高效运行,将是面临的难题。

因此,开发应用程序时,必须遵循微服务架构的开发范式。这种开发范式对标过去传统的开发范式:所有功能整合在同一个应用程序实现,通过不同模块和插件之间的相互调用,组织逻辑以实现功能。这种开发在传统意义上,称为单体应用程序或者巨石应用程序。

这种开发方式,经常面临一个问题:任何小问题的改动都可能导致应用程序的重新构建,牵一发而动全身。

第二种开发范式:分层架构。这种方式将同一类型的功能归并为一层,每个层次是一个或一类功能,由不同的团队独立维护和迭代,对外输出标准接口。

第三种开发范式:微服务。每个功能维护一个服务,由不同团队自行维护和迭代。但这种方式,将面临服务注册,服务发现的问题。这俨然已经成为主流的开发范式了。

微服务

面临的问题:动态配置与服务发现

如此一来,传统部署在单机上的35个服务,可能将转变为3050个微服务。而服务之间的调用关系,将会形成一个网状结构。而传统服务的静态配置方式,将不再适用于微服务。因为服务数量众多,如果使用静态配置,服务会随时发生故障,因此必须以动态服务发现的方式去动态配置。

因此必须存在一个服务总线,每个服务都可在总线上注册自身的服务地址和服务名称。客户端访问服务时不再是通过配置文件读取,而是直接到服务总线上查找是否存在满足自己需求的服务。如果有,则从查询结果中获取需要访问的接口,然后再主动联系接口和服务端。

如果服务发生异常,则总线会自动移除服务。当客户端访问不到此前的服务接口时,会再到总线上查找服务,以获得可用的新服务。

因此,服务总线必须高可用。

面临的问题:部署与容器化

微服务的部署也是一个问题,哪个服务应该部署在哪个节点上,如何规划如何部署。如果人工完成此项工作,将繁杂无比。因此,需要服务编排系统。

只需提供一组主机,并且构建为集群,并在集群上运行服务编排系统。当需要运行服务时,直接提交此请求到系统,系统会根据当前资源消耗状态和应用程序的需求,自动探测最适合运行此应用程序的主机,并部署上去运行起来。

如果系统是异构的,或者应用程序有特殊的环境依赖,该如何解决?比如app部署到三个节点,而三个节点环境都不同,只有两个节点满足库依赖。此时部署app时可以指定运行的节点。假设三个节点都无法满足依赖,那么部署app时将遇到挑战。

如果可以无需考虑底层系统环境,app自身自带所依赖的环境,就可以解决上述挑战。这就是容器。

因此这种调度逻辑,面临的问题就是底层系统是否满足系统运行依赖,而此时有标准化的打包方式—镜像 和运行方式----容器,而主机只需要容器引擎即可。

此时,把app镜像下载到节点上,并启动为容器即可。这就是一次打包,到处运行。而无需考虑底层系统环境异构和依赖满足与否。

此时,系统上不再运行程序,而是运行容器。服务编排系统 -> 容器编排系统。

容器编排系统

众所周知,单一容器没有太大的生产价值,只有自动将容器编排成集群后才能发挥其生产价值。

Docker通过镜像机制,解决打包的难题,解决环境依赖的问题,容器化如此快速普及和生产落地。但需要把容器之间的调用依赖解决,才是真正产生价值所在。这就是容器编排系统。

Docker通过镜像机制极富创造性解决了应用程序打包的根本性难度,它推动了容器技术的快速普及和生产落地。

容器编排的特点和功能:容器生命周期管理工具

  • 提供和部署容器
  • 冗余和可用性:提供高可用逻辑
  • 按需完成应用规模的自动增缩:通过动态配置的方式,提高资源的利用率
    • 客户的请求量大,自动扩容
    • 客户端请求量小,自动缩容
  • 底层节点故障或资源紧缺,自动迁移容器到其他节点,用户无感知
  • 容器之间,实现资源分配
  • 容器提供的服务,暴露到集群外部以提供外部客户端访问(只有少数的服务才需要暴露)
  • 负载均衡和容器服务发现
  • 健康状态检测
  • 应用程序配置

具体任务

  • Service discovery
  • Load balanced
  • Secrets/config/storage
  • Health checks
  • Auto scaling restart healing of container
  • Zero-downtime deploys

容器编排系统类型

  • Kubernetes: 目前最火热 的容器编排系统,由google主导研发,捐赠给CNCF
  • Docker swarm:docker自带的容器编排系统
  • Apache mesos and marathons DC/OS:是数据中心的OS。比如数据中心有1w台服务器,而它可以管理这些服务器。它是管理服务器的,因此需要安装marathons才能在它之上调度和编排容器

kubernetes简介

Kubernetes:docker容器是集装箱,而kubernetes是舵手

K8s运行时的物理结构

k8s把多个底层物理主机,把它们的资源抽象出来,统一进行管理。因此需要物理主机集群,或者虚拟主机集群。

主机分为两类:主节点master和工作/数据节点node。将来运行程序时,都运行于node之上,而master负责管理node,是控制容器应该运行在哪个node 的控制中心,因此称为控制平面。

而node,则是数据平面或工作节点。

如果多个master存在,则必须统一。多个master是为了冗余,而不是负载均衡。可使用类似keepalived高可用应用程序实现高可用。而node是负载均衡的,每个只负责一部分任务。如果某node宕机,则其他node接管这些任务,必要时冗余。

因此,node至少2+,master至少1个。这是实验环境。

而生产环境,至少三个master,node取决于任务数。

k8s将各node整合起来,会形成资源池。Master会得知,每个node拥有多少cpu和内存,以实现容器调度。

K8s架构角色划分

  • 一个master
  • 多个node
  • node的镜像来自registry,比如harbor私有registry或docker hub
  • 前端是API,UI,CLI等客户端工具。客户端通过向master发出指令,master在node上实现增删改查,master控制node完成各项任务。

Master的组件

  • API Server

    集群对外输出客户端请求的入口,并判断客户端的指令是否符合规范。如果符合,则把请求保存在etcd中,如此存储了创建请求。而controller会watch API server的资源变动。一旦变动,则controller就会获得变动状态,然后负责执行用户请求的资源变动操作

  • Scheduler

    客户端创建容器时,容器应该分配到哪个node运行?这就是需要评估目标节点的资源情况,并调度容器到某节点。scheduler会监视着 API Server的指令,并实现调度。然后把结果保存在etcd中。

  • Controller

    控制器。k8s的api提供声明式的API,这里和陈述式API区分开来。

    陈述式的前提是,需要使用具体而详细的指令,核心操作方是调用者定义的;而声明式,无需关心如何实现,被调用者有自身的处理逻辑。

    因此,客户端通过ui或cli告诉master,比如运行一个nginx容器。然后容器后续的工作,客户端无需关心,而是由controller完成:哪里获取镜像,调度后如何运行,是否正常运行,是否监听端口。

    因此,它才是真正负责执行和监控用户指定的期望的最核心组件。它会通过controller loop确保客户端创建的资源,始终处于正常状态:它是一个死循环,比如每个1s检查容器是否健康。万一容器故障,则试图重启容器。若重启失败,则重新下载镜像重新启动。

    因此编排功能,实际上通过controller实现

  • Etcd

    不是由k8s提供,而是拿来作为k8s重要组成部分。在Linux下,etc目录用于保存配置文件,因此etcd就是专门用于保存应用程序配置信息的守护进程,类似redis的kv存储系统,并支持其他额外的高级功能。etcd使用go研发,由coreOS公司研发。

    CoreOS公司:google扶持docker的反对派,coreOS抗衡docker。后来没能胜任,但它提供了新的容器引擎rkt。CoreOS自己的操作系统,coreOS也很出名。后来被redhat收购。

Etcd保存用户期望的状态,而controller负责确保容器运行的真正状态。二者可能不一致,因此controller始终对比二者状态,通过控制循环检查用户期望和当前系统运行的状态趋于一致。若不一致则controller负责确保真正状态,通过重启,重建等各种方式,让它们一致为止。

因此,容器存在两种状态:用户期望状态和当前实际状态。

Etcd存储kv数据,因此定义容器时,通过多个kv描述容器多个维度的信息。如果一个容器描述的信息不符合容器的逻辑,API Server会对etcd提供的数据范式进行进一步的包装和抽象,让用户只能以符合k8s规定的数据范式来定义数据。

可以如此理解:etcd是文件系统,而API Server是mysql定义好的表。因此API Server对数据进行了规范,只支持API Server资源属性的数据。

Node的组件

一旦node分配了pod,kubelet会watch API Server的资源变动。如果调度了容器到node执行,它执行创建容器的任务:会调用dokcer,docker下载镜像,然后启动容器。

因此每个node存在两个核心组件:kubelet和docker(k8s支持多种容器引擎,如rkt)。但多个容器引擎的变动速度如果和k8s迭代速度不兼容,会产生问题,因此标准支持docker。其他容器引擎通过cri支持:容器运行时接口。它是插件,外置容器引擎对接到cri接口,和k8s的cri交互即可。

容器的接口包括:

CRI:容器运行时接口

CSI:容器存储接口

CNI:容器网络接口

它们既有标准实现,也可以调用第三方实现。

Pod

虽然k8s是容器编排系统,但k8s不会直接运行容器,而是重新封装为pod。Pod就是容器的外壳。但一个pod可能存在多个容器,多个容器被当作原子单元管理。Pod是k8s运行的基本核心单元。

容器利用了内核的六种名称空间技术,实现程序运行环境的隔离而已:

  • Pid
  • Network
  • Mount
  • Uts
  • user
  • Ipc

而Docker网络模型,包括

  • closed
  • bridge
  • joined
  • host

而pod,可以存在一个或多个容器,因此一个pod就是共享UTS、netowkr和IPC的名称空间,因此也共享同一组网卡。

如此,容器间进程,可通过lo进行通信的。Pod内的容器关系非常紧密。

实际上,pod内还有一个容器:基础架构容器infra container:pause。任何容器加入到pod中,实际上共享了infra的network

Docker支持存储卷,而对于pod而言就是infra的存储卷,任何加入pod的容器,可以共享infra的存储卷。

容器启动时使用存储卷,而另一个容器启动可以复制前一个容器的存储卷,这就是类似pod内的容器,共享pause 的存储卷。当然用不用,是容器决定的。而网络是必然共享的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值