玩转docker容器编排调度 docker-compose、docker-swarm

一、前言

公众号首发、欢迎关注

一、前言

二、DockerCompose

2.1、简介

2.2、下载安装

2.3、小实验

2.4、小实验的细节

2.5、Composefile的编写规则

三、DockerSwarm

3.1、简介

3.2、注意点

3.3、环境搭建

3.4、Raft一致性协议

3.5、弹性扩容、缩容

3.6、使用的感受

为什么还学Docker的容器编排?

kubernetes几年前就是容器编排的龙头老大了,感觉上想学容器编排,是不是可以直接去学学k8s了呢?

其实我是学了一阵k8s之后折回头实践使用一下Docker容器编排的,因为在学k8s的过程中难免总是和Docker的容器编排做对比。所以不学学DockerSwarm,怎么知道K8S才是最好用、最强大的容器编排工具呢?

所以整理笔记记录实战DockerCompose和DockerSwarm

二、DockerCompose2.1、简介

DockerCompose是Docker提供的定义、运行多个Docker应用容器的工具,我们通过DockerCompose定义配置好应用服务后,通过一条简单的命令就能根据这个配置创建出配置中描述的容器。

DockerCompose可以运行在生产环境、测试环境和开发环境中。

使用DockerCompose需要有这三个基础的步骤:

使用Dockerfile定义你的应用环境。

编写定义你的服务,目的是为了让Dockerfile定义的容器可以一起运行。version:'2.0'
services:在启动前先启动redis
-redis
redis:
image:redis
volumes:将docker-compose下载安装到/usr/local/bin目录下curl-L`uname-s`-`uname-m`-o/usr/local/bin/docker-compose

一键部署[root@VM-0-6-centosmyServer]docker-compose为我们创建了一叫做:myserver_default的网络Creatingnetwork"myserver_default"withthedefaultdriver开始构建中指定的web模块BuildingwebStep1/5:FROMjava:88:Pullingfromlibrary/java5040bd298390:Pullcompletefce5728aad85:Pullcomplete76610ec20bf5:Pullcomplete60170fec2151:Pullcompletee98f73de8f0d:Pullcomplete11f7af24ed9c:Pullcomplete49e2d6393f32:Pullcompletebb9cdec9c7f3:PullcompleteDigest:sha256:c1ff613e8ba25833d2e1940da0940c3824f03f802c449f3d1815a66b7f8c0e9dStatus:Downloadednewerimageforjava:8---d23bdf5b1b1bStep2/5:COPY*.jar//5:EXPOSE8888---Runningin18f52319a82dRemovingintermediatecontainer18f52319a82d---86cb853b5711Step4/5:CMD["--=8888"]---Runningin147d797d5848Removingintermediatecontainer147d797d5848---323a85aa9c61Step5/5:ENTRYPOINT["java","-jar","/"]---Runningin6988142d65d4Removingintermediatecontainer6988142d65d4---245e7675226dSuccessfullybuilt245e7675226dSuccessfullytaggedmyserver_web:latestWARNING:`docker-composebuild`or`docker-composeup--build`.先启动redisredis_1|1:C21Sep202012:27:54.827Redisversion=6.0.8,bits=64,commit=00000000,modified=0,pid=1,juststartedredis_1|1:C21Sep202012:27:54.827WARNING:TheTCPbacklogsettingof511cannotbeenforcedbecause/proc/sys/net/core/_1|1:M21Sep202012:27:54.828WARNINGovercommit_memoryissetto0!'_memory=1'to/etc/'_memory=1'_1|1:M21Sep202012:27:54.828启动SpringBootweb_1|web_1|._________web_1|/\\/___'_____(_)______\\\\web_1|(()\___|'_|'_||'_\/_`|\\\\web_1|\\/___)||_)|||||||(_||))))web_1|'|____|.__|_||_|_||_\__,|////web_1|=========|_|==============|___/=/_/_/_/web_1|::SpringBoot::()web_1|web_1|2020-09-2112:27:56.777INFO1---[main]:(//)web_1|2020-09-2112:27:56.780INFO1---[main]:Noactiveprofileset,fallingbacktodefault

服务启动后可以验证一下docker-composeup命令是否构建起了我们的服务

停止服务:

在yml文件所在目录执行:docker-composestop

CTRL+C

docker-composeup运行起所有的容器后,我们得到的是一个project,注意这里的这个project依然是一个单机的应用,相对于拆分前来说,现在的project是一个容器化后的单机应用。(Docker提供的集群化部署方案在下面的章节~)

官方Demo:

2.4、小实验的细节

docker-composeup成功执行后,可以看到他根据我们配置文件描述,为我们自动下载了redis镜像、java镜像、以及根据Dockerfile创建镜像

查看当前正在运行的容器(通过docker-composeup为我们自动运行起来的容器):

正在运行的容器的命名规则:目录名_镜像名_副本数

docker-composeup命令还为我们创建了一个叫做composetest_default的网络,整个项目中的容器都加入到这个网络中,这个网络支持我们使用服务名访问到容器。是实现负载均衡的前提。

查看网络的详情:下面的两个容器在同一个网络下,

在同一个网络下的容器,彼此是可以通过对方的服务名访问到对方,如下:

2.5、Composefile的编写规则

参考:

三、DockerSwarm3.1、简介

诞生了Swarm。Docker的Swarm让我们可以通过一个或者多个DockerEngine组建起一个集群,没错Swarm就是Docker公司推出的集群编排工具。

使用dockerswarm构建起的集群架构如下图:

主要存在两种角色:Manager节点和Worker节点,Manager和Woker本质上也都是Docker容器。

什么是Node?

大家都在说一个集群由多个节点组成,这个Node究竟是什么呢?

其实可以把这个节点理解成下载有Docker软件的服务器,通过DockerSwarm会将我们启动的容器运行在某个Node中的Docker里面。

然后我感觉也可以把Node直接理解成某个服务器上运行的Docker实例。意思是你可以在一个服务器上启动多个Docker软件。当然这其实就和服务容器化以及分布式部署追求的那种容错性有出入~,毕竟都放在一个服务器上,万一服务器挂了,所有Docker实例,所有容器也都挂了。

Manager:

Manager主要掌控集群的管理任务

维持集群的状态

服务的调度(所谓调度就是通过一定的算法,让容器在合适的Node上启动起来)

manager之间彼此通信,我们针对整个集群的操作都要通过manager节点下发。

Worker:

worker节点是swarm集群中普通节点,他们会被DockerSwarm调度Woker节点上面运行起用户指定的容器。

参考:

3.2、注意点

我们使用DockerSwarm做了什么?

不要忽略一件事,使用dockerswarm为我们提供的命令,归根结底是为了搭建起一个swarm集群,往这个集群中添加Node,从这个集群中砍掉Node。

搭建起集群之后下一步才是使用集群,使用集群使用的是另一套命令:dockerservice

3.3、环境搭建

初始环境

查看dockerswarm命令:

初始化一个DockerSwarm集群

通过--advertise-addr指定的ip可以是公网ip,也可以是私网ip。

如何将一个普通Node加入到一个集群中?

然后当我们成功初始化一个集群时,他会提示我们一条dockerswarmjoin--token命令,通过这个命令我们可以让一个Node加入到以当前节点为Manager的去群中。

也可以该命令生成加入令牌:dockerswarmjoin-tokenworker

如何查看当前集群各个节点的状态?

在manager节点执行如下命令:

在普通节点执行dockernodels会报错

如何将一个ManagerNode加入到一个集群中?

使用该命令获取加入令牌:dockerswarmjoin-tokenmanager

创建swarm集群后,swarm为我们自动创建了新的网络~

3.4、Raft一致性协议

manager节点组成的集群使用了Raft算法,要求集群中多数以上的节点存活,集群才可以使用。他的本意是想让集群中存在3台及以上的manager,这样即使有manager挂了,整个集群依然是不影响使用的。

因此:如果你只有一个mananger节点,那集群就是不可用,你也不能通过manager下发任何任务。如果你有三台manager,即使挂了一个manager,因为还有半数以上的manager存在,集群依然可用。如果你说我的集群中就有两个manager,可不可用呢?答案是:可用,这种情况和集群中有三个manager,然后挂了一个manager一样,但是没意义。为啥呢?因为他根本没有任何容错性。再挂一个manager,集群就不可用了。

3.5、弹性扩容、缩容

创建服务的命令:

通过dockerservice启动一个服务

只有在manager节点上才能使用这个命令。

dockerrun和这个dockerservice挺相似的,但是通过dockerservice启动容器具有扩容、缩容的能力

查看启动的容器

思考:

我们在集群中的manager节点通过上面的命令启动一个服务。这其实就是Docker中容器的调度,或者也能说它是简单的编排。dockerservicecreate命令本意上是在集群中启动一个nginx服务。这个nginx服务也就是一个docker容器,这个docker容器,会被manager随机在四个Node中的某一个Node上启动起来!

具体是哪个Node?可以登陆上这几个服务器,通过dockerps查

动态扩容:

如下,增加我们的nginx的容器的副本数

对于WebServer来无论它访问哪台服务器的ip都能访问到nginx,而且无论这台服务器上的Docker中是否运行中nginx服务。dockerswarm对内部的容器运行做了一层屏障,让很多服务以一个整体的形式存在。

理解:服务的概念

这里只是在表象上理解一下服务的概念~不涉及底层实现,但是有助于捋清思路。

我们在上面通过dockerservice命令启动一个服务,什么服务呢,通过--name指定一个叫做mynginx的服务,服务中运行的镜像为nginx,如果本地没有这个镜像,他会去远程拉取。服务启动后,dockerswarm会在集群中随机找一个合适的Node运行redis容器,这个Redis容器属于mynginx服务。

后来我们又用dockerserviceupdate增加服务的副本数,就比如我们上面将服务的副本数增加到3,dockerswarm就会让整个集群中再找合适的Node,然后启动新的service。

如下图,WebServer去连接Redis服务,这个Redis服务实际上就是存在于由DockerSwarm搭建起的集群中的Redis服务,并且它可能有多个副本。

对于WebServer来说,他不知道DockerSwarm集群中有多少服务的副本,对他来说,它只是在连接一个Redis实例,而不知道它连接的实际上是一个多分片集群。

DockerSwarm集群为WebServer提供Redis服务使用,WebServer可以认为Swarm集群是一个实例,然后WebServer只需要使用Swarm集群为他提供的Redis服务,而不关心Swarm集群中有多少个服务副本。

动态缩容:

动态缩容,就是通过--replicas指定一个比原来副本数小的数量即可

动态扩缩容的另一个命令:

移除服务:

服务被移除后,所以的服务副本都会消失,所有的相关容器也会被关闭

3.6、使用的感受

感觉上DockerSwarm的调度设计是容器为核心的。

为啥这样说呢?

从上面的使用上来看,首先是拆解App,于是我们将应用拆分成不同容器。那不同容器之间是需要通过网络通信的,相应的DockerSwarm的实现是:DockerSwarm集群会构建一个igress网络(它是一个拥有负载均衡能力的overlay网络)所有的Node都加入到这个扁平化网络中,实现了网络中的各个Node直接通过对方的服务名就能ping通互联。

有了这样的基础后,我们就能使用DockerSwarm提供的扩容、所容的调度能力

当我们需要DockerSwarm为我们扩容时,它会根据自己的调度算法去找一个合适的Node然后去一个一个的运行起我的容器,也就是说它的思路是:为容器找一个合适的节点运行起来

spread:默认策略,尽量均匀分布,找容器数少的结点调度

binpack:和spread相反,尽量把一个结点占满再用其他结点

random:随机

这种调度思想和k8s的编排思想是不一样的。

当然说起k8s难免会冒出海量的新概念,这里就不展开了。

简单来说:kubernates的编排思想是这样的,它根本不关系底层的容器是Docker还是其他的容器技术,它站在更高的角度上,允许让用户以yaml的方式去描述自己的应用,去描述哪几个镜像启动后应在绑定在一起(在一个Pod里,而不是像DockerSwarm那样,由DockerSwarm去为单个容器找一个合适的Node),允许用户指定Pod的数量,还为用户提供了网关、监控、备份、水平扩展、滚动更新、保持指定数量的副本数、负载均衡等功能。

免责声明:本文章如果文章侵权,请联系我们处理,本站仅提供信息存储空间服务如因作品内容、版权和其他问题请于本站联系