Docker, Kubernetes, DCOS 不谈信仰谈技术

2020-06-01 00:00:00 多个 节点 调度 容器 均衡器

看来容器编排系统的争夺已经白热化了,乱花渐欲迷人眼,近老是有各种文章比较这三个框架,这篇文章不谈信仰,不对比优劣,只谈技术,甚至会谈如果从一个平台转向另一个平台,都要了解些啥。


一、架构


啥都不说,先上三个架构图


Docker Swarm Mode



DCOS

这张图主要显示了一些组件,还是上一张Mesos的图


后是Kubernetes


这三个架构都很复杂,但是还是能够一眼看出是一个老大,多个干活的这种结构,基本上所有的分布式系统都是这样,但是里面的组件名称就纷繁复杂,我们来一一解析。


二、元数据存储与集群维护


作为一个集群系统,总要有一个统一的地方维护整个集群以及任务的元数据。而且作为集群系统的控制节点,为了高可用性,往往存在多个Master,在多个Master中间,总要有一个Leader。


在Docker Swarm Mode里面,多个Manager之间需要选出一个Leader,而且整个集群的状态也需要在一个统一的地方存储,从而任何一个Manager挂了之后,其他的Manager能够马上接替上,Swarm Node通过Raft协议,自己实现了一个内部的统一存储和集群一致性管理系统。在传统的Swarm里面,推荐的使用consul,在Swarm Mode里面则自己实现了。


在docker swarm init的参数里面有--advertise-addr声明swarm manager会通过这个地址和端口让其他组件来连接。


在DCOS里面,Mesos的多个进程也需要选择一个Leader,容器的编排多通过Marathon进行,Marathon需要一个地方存储所有Task的信息,在DCOS里面,多通过Zookeeper来实现。


在Mesos Master的启动有参数--zk=VALUE。


当然也能看到熟悉的--advertise_ip=VALUE和--advertise_port=VALUE。


Marathon启动的时候,也有参数--zk zk://1.2.3.4:2181,2.3.4.5:2181,3.4.5.6:2181/marathon。


在Kubernetes里面,统一的存储使用etcd来保存,Leader的选举也是通过etcd进行,因而有apiserver有参数--etcd-servers,controller和scheduler都有参数--master string指向apiserver,并且有参数--leader-elect选举出Leader,也会有熟悉的--address ip。


三、API层与命令行


作为一个分布式系统,每一层都会有自己的API,但是对外往往需要一个统一的API接口层,一般除了酷酷的界面之外,为了自动化,往往会有一个命令行可以执行操作,其实命令里面封装的也是对API的调用。


对于Docker Swarm Mode来讲,API层是集成在Manager里面的,而命令行其实就是Docker的命令行,只不过调用的时候,原来是Docker Daemon本地把事情做了,现在Manager需要让Work去做事情。


这一点也是Docker的优势所在,也即使用本地的Docker和使用Swarm Mode集群,不需要学习成本,一样的命令,同样的味道。


对于DCOS来讲,API层是有一个单独的组件,叫做Admin Router,后端的很多API都是通过Admin Router经过封装暴露给外面的。


对于命令行,有一个dcos cli,可以调用admin router暴露出来的api进行操作。


dcos命令行可以有一些子命令,例如marathon子命令,就是用来创建容器的,node可以管理节点。


dcos里面很有特色的一点就是可以安装package,这源于mesos是一个双层调度系统,上面可以跑多个framework,例如spark,cassandra等,都可以通过package进行安装,这点会另外一节说明。


对于Kubernetes,API层是一个单独的进程apiserver提供,认证和鉴权也是在这一层实现的,所有对于kubernetes的管理平台的访问都是通过apiserver这一层进行的。


对于命令行,kubernetes是kubectl,通过向apiserver调用执行操作,例如pod,service,deployment等。


kubernetes也有自己的类似package的管理,Kubernetes Helm,但是命令就变成了helm了。


四、调度


当运行一个容器的时候,放在哪台节点上,这个过程是调度。


Swarm Mode 的调度的默认规则是spread,也即尽量让容器平均分配到整个集群当中。


当然也可以设置一些调度策略,例如使用constraint,每个节点可以配置一些label,并在创建容器的时候通过指定constraint,来使得容器运行或者不运行在某些节点上。


docker node update --label-add 'com.acme.server=db' node-03 


docker service create --name redis --constraint 'node.labels.com.acme.server==db' redis


也可以使用placement-pref,使得容器优先调度在某些节点上。


docker service create --name nginx --placement-pref 'spread=node.labels.com.acme.zone' --replicas 12 nginx


对于DCOS来讲,Mesos的调度是双层调度,首先一层是Mesos Master的Allocator将节点资源提供给框架,例如Marathon,第二层是Marathon里面也有一个调度器,真正分配某个容器放在某个节点上。


Mesos的调度策略参考文章号称了解mesos双层调度的你,先来回答下面这五个问题!


Marathon的调度也可以有constraints。


"constraints": [["hostname", "UNIQUE"]]表示每个节点只能跑一个。


"constraints": [["rack_id", "CLUSTER", "rack-1"]]容器跑着有attribute为rack_id并且值为rack-1的节点上。


"constraints": [["rack_id", "GROUP_BY", "3"]]将容器分布在三个rack上以实现高可用。


"constraints": [["rack_id", "LIKE", "rack-[1-3]"]]和"constraints": [["rack_id", "UNLIKE", "rack-[7-9]"]]表示容器要跑在哪些节点上和不能跑在哪些节点上。


"constraints": [["rack_id", "MAX_PER", "2"]]表示每个rack多能跑两个容器。


对于Kubernetes,调度是由一个单独的进程scheduler负责的。


Kubernetes也支持通过对Node设置Label,从而将pod放在某些节点上。


kubectl label nodes <your-node-name> disktype=ssd 


apiVersion: v1kind: Podmetadata:

  name: nginx

  labels:

    env: testspec:

  containers:

  - name: nginx

    image: nginx

    imagePullPolicy: IfNotPresent

  nodeSelector:

    disktype: ssd


另外kubernetes还有NodeAffinity:

RequiredDuringSchedulingRequiredDuringExecution:在调度的时候必须部署到某些节点,运行期如果条件不满足则重新调度

RequiredDuringSchedulingIgnoredDuringExecution :在调度的时候必须部署到某些节点,运行期就算了。

PreferredDuringSchedulingIgnoredDuringExecution :在调度的时候好部署到某些节点,运行期就算了。


五、副本与弹性伸缩


容器如果部署无状态服务,一个好处就是可以多副本,并且可以弹性伸缩。


在Swarm Mode里面,可以通过scale数字指定副本数目。


docker service scale frontend=50


在DCOS里面,通过instances指定副本的数目。


{

    "id": "nginx",

    "container": {

    "type": "DOCKER",

    "docker": {

          "image": "mesosphere/simple-docker",

          "network": "BRIDGE",

          "portMappings": [

            { "hostPort": 80, "containerPort": 80, "protocol": "tcp"}

          ]

        }

    },

    "acceptedResourceRoles": ["slave_public"],

    "instances": 1,

    "cpus": 0.1,

    "mem": 64

}


修改数目的时候使用以下的命令

dcos marathon app update basic-0 instances=6


在新版本的DCOS中,已经支持的pod的概念了。


{

    "containers": [

        {

            "artifacts": [],

            "endpoints": [],

            "environment": {},

            "exec": {

                "command": {

                    "shell": "sleep 1000"

                }

            },

            "healthCheck": null,

            "image": null,

            "labels": {},

            "lifecycle": null,

            "name": "sleep1",

            "resources": {

                "cpus": 0.1,

                "disk": 0,

                "gpus": 0,

                "mem": 32

            },

            "user": null,

            "volumeMounts": []

        }

    ],

    "environment": {},

    "id": "/simplepod2",

    "labels": {},

    "networks": [

        {

            "labels": {},

            "mode": "host",

            "name": null

        }

    ],

    "scaling": {

        "instances": 2,

        "kind": "fixed",

        "maxInstances": null

    },

    "scheduling": {

        "backoff": {

            "backoff": 1,

            "backoffFactor": 1.15,

            "maxLaunchDelay": 3600

        },

        "placement": {

            "acceptedResourceRoles": [],

            "constraints": []

        },

        "upgrade": {

            "maximumOverCapacity": 1,

            "minimumHealthCapacity": 1

        }

    },

    "secrets": {},

    "user": null,

    "volumes": []

}


在DCOS里面可以实现自动弹性伸缩,通过使用marathon-lb-autoscale,通过监控marathon-lb的情况进行弹性伸缩。


在Kubernetes里面,副本数目是以pod为单位的,由controller进程控制,可以通过创建一个Deployment来控制副本数。


apiVersion: apps/v1beta1 # for versions before 1.6.0 use extensions/v1beta1

kind: Deployment

metadata:

  name: nginx-deployment

spec:

  replicas: 3

  template:

    metadata:

      labels:

        app: nginx

    spec:

      containers:

      - name: nginx

        image: nginx:1.7.9

        ports:

        - containerPort: 80


Kubernetes也可以实现autoscaling。


有一个组件Horizontal Pod Autoscaling,可以通过监控CPU的使用情况动态调整Pod的数量。


六、编排


为了能够通过编排文件一键创建整个应用,需要有编排功能。


Swarm Mode的编排


docker stack deploy --compose-file docker-compose.yml vossibility


version: "2"

services:

  foo:

    image: foo

    volumes_from: ["bar"]

    network_mode: "service:baz"

    environment:

      - "constraint:node==node-1"

  bar:

    image: bar

    environment:

      - "constraint:node==node-1"

  baz:

    image: baz

    environment:

      - "constraint:node==node-1"


Marathon的编排是基于json文件


{

  "id": "/product",

  "groups": [

    {

      "id": "/product/database",

      "apps": [

         { "id": "/product/database/mongo", ... },

         { "id": "/product/database/mysql", ... }

       ]

    },{

      "id": "/product/service",

      "dependencies": ["/product/database"],

      "apps": [

         { "id": "/product/service/rails-app", ... },

         { "id": "/product/service/play-app", ... }

      ]

    }

  ]

}


Kubernetes的编排是基于yml文件


为redis-master服务新建一个名为redis-master-controller.yaml的RC定义文件


apiVersion: v1

kind: ReplicationController

metadata:

  name: redis-master

  labels:

    name: redis-master

spec:

  replicas: 1

  selector:

    name: redis-master

  template:

    metadata:

     labels:

       name: redis-master

    spec:

      containers:

      - name: master

        image: kubeguide/redis-master

        ports:

        - containerPort: 6379


创建一个Service


apiVersion: v1

kind: Service

metadata:

  name: redis-master

  labels:

    name: redis-master

spec:

  ports:

  - port: 6379

    targetPort: 6379

  selector:

    name: redis-master


七、服务发现与DNS


容器平台的一个重要的功能是服务发现,也即当容器的地址改变的时候,可以自动进行服务之间的关联。


一般的服务发现首先要通过DNS将服务名和应用关联起来,可以基于DNS对一个服务的多个应用进行内部负载均衡,也有直接加一个内部负载均衡器来做这件事情。


Swarm Mode有一个内置的DNS组件,并且负载均衡也是根据DNS名来做的。


DCOS的DNS组件是通过Mesos-DNS实现的,负载均衡有两种方式,一种是直接通过Mesos-DNS根据域名进行负载均衡。另一种方式是将DNS转化为VIP,然后有个内置的负载均衡器,DCOS有个组件minuteman是做这个事情的。


Kubernetes的DNS组件是通过skyDNS实现的,负载均衡是通过将DNS转化为VIP,有个内置的负载均衡器kube-proxy来完成这件事情。


八、容器


Swarm Mode通过runC运行容器



DCOS对于多种容器的支持Unified Container,可以支持Docker容器和Mesos容器。



Kubernetes也是支持多种容器格式的。



九、网络


容器的网络配置两种:


Docker Libnetwork Container Network Model(CNM)阵营

  ● Docker Swarm overlay

  ● Macvlan & IP network drivers

  ● Calico

  ● Contiv(from Cisco)


Docker Libnetwork的优势就是原生,而且和Docker容器生命周期结合紧密;缺点也可以理解为是原生,被Docker“绑架”。


Container Network Interface(CNI)阵营

  ● Kubernetes

  ● Weave

  ● Macvlan

  ● Flannel

  ● Calico

  ● Contiv

  ● Mesos CNI


CNI的优势是兼容其他容器技术(e.g. rkt)及上层编排系统(Kuberneres & Mesos),而且社区活跃势头迅猛,缺点是非Docker原生。


十、存储


Swarm Mode的存储是通过Volume Plugin,可以在集群中接入外部的统一存储,可以支持Ceph,NFS,GlusterFS等。


DCOS可以创建External Persistent Volumes,dvdi driver其实使用的也是Docker的volume driver,目前支持的是rexray,可以接入EC2,OpenStack等。


Kubernetes可以创建Persistent Volumes,支持GCE,AWS,NFS,GlusterFS,Ceph等。


十一、监控


无论是Swarm Mode,还是Mesos,还是Kubernetes,容器的监控中Prometheus +cadvisor都是主流的方案,而cadvisor来自Kubernetes的一个组件。


十二、大数据与包管理


跑大数据是Mesos的强项,Spark就有一个依赖于Mesos部署,让Mesos作为调度器的方案。



对于Swarm Mode和Kubernetes来讲,不会用自己的Scheduler来做大数据调用。


但是Swarm Mode和Kubernetes是可以部署大数据框架的,但是大数据框架之间的任务的调度和通信,就与Swarm Mode和Kubernetes无关了。


在kubernetes里面部署大数据可以参考https://github.com/kubernetes/examples


十三、负载均衡


这里的负载均衡指的是外部负载均衡。


在Swarm Mode中,swarm manager通过ingress负载均衡,通过published port将服务端口暴露在Node上面,如果有外部云平台的负载均衡器,通过连接Node上的端口,可以对服务进行外部负载均衡。


在DCOS中,外部负载均衡通过marathon-lb来实现。

在kubernetes中,外部负载均衡器是通过一个ingress controller根据请求进行创建,如果在云平台例如GCE,可创建云平台的负载均衡器,云平台的负载均衡器可通过NodePort连接到后端的Service。


https://github.com/kubernetes/ingress


可以通过service连接后端的Pod.


也可以不用service直接连接。



十四、节点


Swarm Mode的Node上面部署的是Swarm的worker,其实还是Docker Daemon,相对保持一致性。


DCOS的Node上面干活的是Mesos-Agent,其实它还不是直接干活的,还有一层Executor真正的干活。


kubernetes的Node上跑的是kubelet。


十五、升级与回滚


所有的容器平台对于容器多副本的升级全部都是要滚动升级。


在Swarm Node里面可以配置rolling update策略

docker service create --name=my_redis \

                        --replicas=5 \

                        --rollback-parallelism=2 \

                        --rollback-monitor=20s \

                        --rollback-max-failure-ratio=.2 \

                        redis:latest


在DCOS里面,rolling update也是默认的行为。


在Kubernetes里面,同样有对rolling update的支持。


kubectl rolling-update foo [foo-v2] --image=myimage:v2



总而言之:三大平台的区别,大概相当于麦当劳和肯德基的区别,看你的口味,掌控能力,社区热度了。





相关文章