zshuangyan / blog

我的个人博客
2 stars 0 forks source link

Kubernetes和Docker简介 #5

Open zshuangyan opened 6 years ago

zshuangyan commented 6 years ago

Docker

Docker提出了一个新的理念,即软件是容器的组合,就拿一个web应用来说,可以把它拆分为业务逻辑容器,nginx容器,数据库容器...这些容器之间可以像搭积木一样组合起来,使得软件更容易扩展和移植,这也正是微服务的思想。在容器出现之前,部署一个软件通常需要下面的几个步骤:

  1. 安装软件所依赖的语言环境,数据库
  2. 修改配置文件,创建日志目录,检查端口是否被占用
  3. 启动服务 虽然对于一个有经验的程序员来说不是什么难事,但是毕竟还是比较浪费时间的。但是在Docker中,我们可以按照Docker指定的命令格式把安装,配置,启动服务的过程定义在Dockerfile中,然后使用docker build命令打包构建镜像后,就可以多次利用镜像文件来部署应用了,并且构建出的镜像还可以作为基础镜像被其他镜像引用。

镜像系统

为何docker镜像可以重复利用,并且可以作为基础镜像被其他镜像引用呢?在docker文档中讲到,镜像是从一系列的层构建成的,每一层代表了Dockerfile中的一条指令,最上面一层是容器层,它是可读可写的,其他都是镜像层,只能读。其结构如下图: image 发生在容器层的文件的修改不会影响到镜像层中,因此多个容器可以共享镜像层的文件。Docker支持copy-on-write(COW)策略,上面的层要读下面层的目录和文件时,它直接访问文件本身,上面的层要对下面的层的目录或文件进行修改时,文件才被拷贝到上面的层以执行修改,这样做的目的当然也是节省空间。

Kubernetes

Pod

Kubernetes是一个容器编排系统,Pod(容器组)是Kubernetes中的最小单元,Pod由一个或多个容器组成,拥有全局唯一的虚拟IP。集群中的所有Node(节点)以及其他Pod都可以通过这个虚拟IP来和这个Pod进行通信。同一个Pod中的容器共享一个IP地址,它们可以通过localhost来相互访问,类似虚拟机,同个Pod上的容器不能占用相同的端口号。

Replication Controller

Kubernetes上有各种控制器,我们在部署应用的时候最常用到的就是Replication Controller(复制控制器),简称RC,它通过Selector(选择器)关联到拥有特定Label(标签)的Pod,并负责确保指定数量的Pod副本在运行,当它发现有Pod出现故障后,会调度新的Pod并删除故障的Pod,使Kubernetes系统具有自愈功能。另一方面,通过调整RC的replicas数目就可以对Pod进行扩容缩容,通过调整RC中Pod模板中的镜像版本来达到滚动升级的目的,不过Kubernetes在滚动升级的时候偶尔会出现错误。

Service

虽然Kubernetes中的Pod拥有虚拟IP,支持集群内部Pod-Pod,Pod-Node的通信,避免了原生的docker需要把端口映射到主机的端口来实现不同主机上容器的通信的方式,但是Pod的IP地址是不可靠的,例如当Pod所在的Node发生故障,Pod被重新调度到另一台Pod进行启动,Pod的IP地址将发生变化。更重要的是,如果容器应用本身是分布式的部署方式,通过多个实例提供服务,就需要在这些实例的前端设置一个负载均衡器来实现请求的分发。

基于上面的需求,Kubernetes抽象出了Service(服务)这个概念来暴露一个可靠的IP地址,Service本身不负责实际业务,而是通过Selector关联到相应的Pod上,由Pod执行实际的业务,它只负责把流量反向代理到后端的Pod上,具体的实现的机制是:每个Service创建以后,都会分配一个全局唯一虚拟的IP地址,在每个Node节点都运行着kube-proxy程序,它负责把把所有目的IP为Service的虚拟IP的流量以RoundRobin(默认)或SessionAffinity(基于客户端的IP进行会话保持)的方式分发请求到Service关联的其中一个Pod上。

资源管理

在Kubernetes的Node上运行着多个容器,这些容器使用的内存,CPU和网络等资源都来自于一个Node,无论这个Node节点配置再大,它都是有限的。为了尽可能充分利用Node的资源,提高Node上可运行的Pod的数量,Kubernetes为每个Pod上的容器提供Resource Requests(资源请求)和Resource Limits(资源限制)两种选项,目前版本的Kubernetes只支持对内存和CPU资源进行管理。分别通过设置pod的spec.container[].resources.requests.cpu,spec.container[].resources.limits.cpu,spec.container[].resources.requests.memory,spec.container[].resources.requests.memory。

举一个复杂点的例子说明Request和Limit的作用,主要说明Request和Limit都为0的Pod对提高资源使用率的作用。测试集群仍然包含有一个4U4G的Pod。集群中已经部署了四个Pod(1~4),每个Pod的资源设置为(CPU Requst,CPU Limit,Memory Requst, Memory Limit)= (1U, 2U, 512M,512M)。

此时节点上CPU和内存的资源使用情况如下图所示: image

此时按照Request的需求,已经没有可以供分配的CPU资源。但由于Pod1~4业务负载比较低,造成节点上CPU使用率较低,造成了资源的浪费。这的时候可以通过将Request设置为0来实现对资源使用率的进一步提高。在此节点上部署4个资源限制为(CPU Requst,CPU Limit,Memory Requst, Memory Limit)= (0U, 0U, 512M,512M)。资源的使用情况如下图所示: image

Pod(5-8)能够在Pod(1-4)空闲时,使用节点上剩余的CPU资源,从而进一步提高资源的使用率。

使用Kubernetes部署应用的例子

下面我以openmind项目的为例来说明怎样在kubernetes上部署一个web系统。openmind是一个NLP工具箱,支持分词,关键字提取,关键词提取,情感识别等功能,实现为基于tornado的web API,使用Redis数据库。为了提高服务的并发数,我们需要同时启动3个服务进行负载分担。

部署图

image

openmind-svc: 在七层负载分担规则中把域名和路径绑定到openmind-svc上,使得外网的用户可以访问到这个应用,openmind-svc负责把请求分发给它关联的的5个Pod openmind-pod: 负责处理用户的API请求,需要把redis服务名作为环境变量配置到Pod的模板定义中,以便pod访问redis redis-svc: 负责接收来自openmind-pod的读写请求,转发给redis-pod进行处理 redis-pod: 接收openmind-pod的请求,进行CURD操作

参考资料

Kubernetes官方文档 Kubernetes权威指南(第二版) http://cizixs.com/2017/03/30/kubernetes-introduction-service-and-kube-proxy https://cloud.tencent.com/developer/article/1004976