4.0.1 从容器到容器云

一个正在运行的Linux容器,可以被一分为二地看待:

1.一组联合挂载在/var/lib/docker/aufs/mnt上的rootfs,这一部分我们称为容器镜像(Container Image)是容器的静态视图。

2.一个由Namespace+Cgroups构成的隔离环境,这一部分我们称为“容器运行时”(Container Runtime)是容器的动态视图

作为开发者,我们并不关心容器运行时的差异,因为,在整个"开发-测试-发布"的流程中,真正承载着容器信息进行传递的,是容器镜像,而不是容器运行时。

作为一个云服务商或者基础设施提供商,我只要能够将用户提交的Docker镜像以容器的方式运行起来,便可以将整个容器技术栈上的价值,汇聚在自己的节点上。

另外,从这个节点向Docker镜像制作者和使用者方向回溯,整个路径上的各个服务节点: 如CI/CD, 监控,安全,网络,存储,都可以有服务商发挥和盈利的地方。(通过容器镜像,和潜在用户,直接关联)

而容器编排技术,就是当前容器领域最核心的部分。当今的主流就是Google和Redhat公司所主导的Kubernetes项目。

Kubernetes项目的理论基础来自于Google的Borg系统。
Kubernete项目2014年立项,2015年Borg才对外公开发表

相比于Spanner,BigTable等相对上层的项目,Borg要承担的责任,是居于整个基础设施技术栈的最底层

c7ed0043465bccff2efc1a1257e970bd.png

图片来源http://malteschwarzkopf.de/research/assets/google-stack.pdf
这个图来自于Google Omega论文作者的博士论文,描绘了当时Google已经公开发表的整个基础设施栈,

8ee9f2fa987eccb490cfaa91c6484f67.png

Kubernetes项目的架构,跟它的原型项目Borg非常类似,都由Master和Node两种节点组成,而且这两种角色分别对应着控制节点和计算节点。

控制节点,(Master节点) 有三个紧密协作的独立组件组合而成,分别是负责API服务的kube-apiserver,负责调度的kube-scheduler, 负责容器编排的kube-controller-manager。整个集群的持久化数据,则有kube-apiserver处理后保存在Etcd中。

计算节点(Node节点),最核心的部分是一个叫做kubelet的组件。

在Kubernetes项目中,kubelet主要负责同容器运行时,(比如Docker项目)打交道,
这个交互所依赖的是一个称作CRI的远程调用接口(Container Runtime Interface)的远程调用接口,这个接口定义了容器运行时的各项核心操作。
这也是Kubernetes并不关心你部署的是什么容器运行时,使用的什么技术实现,只要你的这个容器运行时能够运行标准的容器镜像,它就可以通过实现CRI接入到Kubernetes项目当中。

具体的容器运行时,一般通过OCI这个容器运行时规范同底层的Linux操作系统进行交互,即把CRI请求翻译成对Linux操作系统的调用(操作Linux Namespace和Cgroups等 )

此外Kubernetes还通过gRPC协议同一个协议叫做Device Plugin的插件进行交互。这个插件,是Kubernetes项目用来管理GPU等宿主机物理设备的主要组件,也是基于Kubernetes项目进行机器学习训练,高性能作业支持等工作必须关注的功能。

而kubelet的另一个重要功能,则是调用网路插件和存储插件为容器配置网络和持久化存储。这两个插件,分别是CNI(Container Networking Interface)和CSI(Container Storage Interface)

尽管像Docker这样的容器镜像在Borg中是不存在的,自然Borg也不需要考虑同Docker交互,容器镜像进行管理,也不需要支持CRI,CNI,CSI等多容器技术。

但对于Kubernetes项目来说,Borg的指导意义在于master节点 : 即如何编排,管理,调度用户提交的作业。

Borg项目完全可以把Docker镜像看做是一种新的应用打包方式。这样,Borg团队过去在大规模作业管理与编排的经验就可以直接套用了。

在Borg项目中,运行在大规模集群中的各种任务之间,实际上存在着各种各样的关系。这些关系的处理,才是作业编排和管理最困难的地方。

举例来说,一个Web应用和数据库之间的访问关系,一个负载均衡器和它的后端服务之间的代理关系,一个门户应用与授权组件之间的调用关系。

在Compose项目中,可以为两个容器定义一个link,而Docker项目会负责维护这个link关系。具体做法是Docker会在Web容器中,将DB容器的IP地址,端口等信息以环境变量的方式注入进去,供应用进程使用。

    DB_NAME=/web/db
    DB_PORT=tcp://172.17.0.5:5432
    DB_PORT_5432_TCP=tcp://172.17.0.5:5432
    DB_PORT_5432_TCP_PROTO=tcp
    DB_PORT_5432_TCP_PORT=5432
    DB_PORT_5432_TCP_ADDR=172.17.0.5

Kubernetes项目最主要的设计思想是,从更宏观的角度,以统一的方式来定义任务之间的各种关系,并且为将来支持各种种类的关系留有余地。

Kubernetes对容器间的访问进行了分类:

第一种是 紧密交互关系:应用之间需要非常频繁的交互和访问,或者双方需要通过本地文件进行信息交互。

常规情况下,这些应用会直接部署在同一台机器上,通过localhost通信,通过本地磁盘交换文件。而在Kubernetes项目中,这些容器会被划分到一个Pod,Pod里的容器共享一个Network Namespace,同一个数据卷,从而达到高效率交换信息的目的。

Pod是Kubernetes项目中最基础的一个对象,源自于Google Borg论文中一个名叫ALLOC的设计

另一种需求,如Web应用与数据库之间的访问关系,Kubernetes项目则提供一种叫做“Service”的服务.

一般像这样两个应用,往往不部署在同一台机器上,这样即使Web应用宕机,数据库也完全不受影响。

Kubernetes的做法就是给Pod绑定一个Service服务,而service服务声明的IP地址信息是不变的。这个service服务的主要作用,就是作为Pod的代理入口(Portal),从而代替Pod对外暴露一个固定的网络地址。

如果两个不同pod之间不仅有访问关系,还要求在发起时加上授权信息。最典型的例子就是Web应用对数据库访问时需要Credential(用户名和密码)信息。Kubernetes项目提供一种叫做Secret的对象,以键值对的形式保存在Etcd里

除了应用与应用之间的关系外,应用运行的形态也是影响“如何容器化这个应用”的重要因素。

为此,Kubernetes定义了新的,基于Pod改进后的对象。比如job,用来描述一次性运行的Pod(比如,大数据任务);再比如DaemonSet,用来描述每个宿主机上必须只能运行一个副本的守护进程服务,有比如CronJob,用于描述定时任务等等。

Kubernetes并没有像其他项目那样,为每一个管理功能创建一个指令,然后在项目中实现其中的逻辑。
而是:

  1. 首先,通过编排对象,比如Pod, Job, CronJob等, 来描述你试图管理的应用;
  2. 然后,在为它定义一些服务对象,比如Service,Secret,Horizontal Pod AutoScaler(自动水平扩展器)等。来负责具体的平台级业务

这种使用方法,就是所谓的“声明式API”。这种API对于的编排对象和服务对象,都是Kubernetes项目中的API对象(API Object)。

例如声明两台Nginx服务器,编写如下代码在YAML文件

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80

在上面的YAML文件中,我们定义了一个Deployment对象,主体是(spec.template包含内容)是一个使用Nginx镜像的Pod,而这个Pod的副本数是2.

然后执行

kubectl create -f nginx-deployment.yaml

这样两个完全相同的Nginx容器就被启动了

你可能感兴趣的:(4.0.1 从容器到容器云)