前言
DOCKER 技术在推出后掀起了一阵容器化技术的热潮,容器化使得服务的部署变得极其简易,这为微服务和分布式计算提供了很大的便利.
为了把容器化技术的优点发挥到极致,docker 公司先后推出了三大技术:
,,,可以说是几乎实现了容器化
技术中所有可能需要的底层技术手段.
在使用 go 语言实现了判题引擎并打包好 docker 镜像后,就需要进行分布式判题的编写,这次就让我们手动实践,尝试使用 docker 的三大杀器来部署一个多机器构成的判题服务集群.
三剑客简介
docker-machine
docker 技术是基于 Linux 内核的技术实现的,那么问题来了,在非 Linux 平台上是否就不能使用 docker 技术了呢?答案是可以的,不过显然需要借助虚拟机去模拟出 Linux 环境来.
docker-machine 就是 docker 公司官方提出的,用于在各种平台上快速创建具有 docker 服务的虚拟机的技术,甚至可以通过指定 driver 来定制虚拟机的实现原理(一般是 virtualbox).
docker-compose
docker 镜像在创建之后,往往需要自己手动 pull 来获取镜像,然后执行 run 命令来运行.当服务需要用到多种容器,容器之间又产生了各种依赖和连接的时候,部署一个服务的手动操作是令人感到十分厌烦的.
dcoker-compose 技术,就是通过一个配置文件,将所有的容器的部署方法,文件映射,容器连接等等一系列的配置写在一个配置文件里,最后只需要执行
命令就会像执行脚本一样的去一个个安
装容器并自动部署他们,极大的便利了复杂服务的部署.
docker-swarm
swarm 是基于 docker 平台实现的集群技术,他可以通过几条简单的指令快速的创建一个 docker 集群,接着在集群的共享网络上部署应用,最终实现分布式的服务.
相比起 zookeeper 等集群管理框架来说,swarm 显得十分轻量,作为一个工具,它把节点的加入,管理,发现等复杂的操作都浓缩为几句简单的命令,并且具有自动发现节点和调度的算法,还支持自定制.虽然 swarm 技术现在还不是非常成熟,但其威力已经可见一般.
浅谈 docker 服务架构和远程 API
在正式使用 docker 技术部署集群应用时,我们应该先来了解一下 docker 工作的一些底层原理,和 docker 远程调用的 API,这样才能大体了解集群究竟是如何运作的.
daemon
之前的 docker 入门文章中讲过,docker 的基础服务,比如容器的创建,查看,停止,镜像的管理,其实都是由 docker 的守护进程 (daemon) 来实现的.
每次执行的 docker 指令其实都是通过向 daemon 发送请求来实现的.
daemon 的运作(通信模式)主要有两种,一种是通过 unix 套接字(默认,但只能在本地访问到,比较安全),一种是通过监听 tcp 协议地址和端口来实现(这个可以实现在远程调用到 docker 服务).
远程 API
除了通过远程 tcp 协议访问远程主机上的 docker 服务外,docker 还提供了一套基于 HTTP 的 API,可以使用 curl 来实现操作远程主机上的 docker 服务,这为开发基于 web 的 docker 服务提供了便利.
远程 docker 使用示例
最终实现集群的时候实际是使用 docker 的远程调用来将不同的 docker 主机连接成一个整体的(通过 tcp 协议).
我们不妨先来手动模拟尝试一下 docker 服务的远程调用吧.
首先需要在提供服务的主机上将 docker 的运行方式改为 tcp,具体方法为修改
中的为如下内容
-H tcp://127.0.0.1:4243 -H unix:///var/run/docker.sock
-H 后的参数是自己定义的要绑定的 tcp 地址和端口,成功绑定后重启 docker 服务就可以在该端口访问到 docker 的 daemon 服务.
不幸的是:
在 OSX 平台上,并没有找到 docker 的 daemon 配置文件
在 OSX 平台上,使用
这样的命令来尝试以tcp的方式启动docker daemon也是失败的,并没有任何作用
目前推测除了 Linux 平台上,其他平台上的配置方法都不太一样,但是在网络上暂时没有找到解决方案,所以后面的操作我只能通过在本地创建多个 docker-machine 的方式来模拟实现远程调用.
假设我们在
这台主机上开启了docke
r 服务,监听了端口,那么我们就可以在同一网段的其他主机上(比如
)通过的方式调用到该主机上的docker服务.
比如
docker -H tcp://192.168.1.123:2345 ps
docker -H tcp://192.168.1.123:2345 images
docker -H tcp://192.168.1.123:2345 run ...
最终 swarm 构建集群的时候,就是通过这样的远程服务调用来调度各个节点的.
集群和分布式运算
在正式开始实践集群之前,我们有必要了解究竟什么是集群,什么是分布式计算.
首先,这两者有一个共同点,就是他们都是使用了多个服务节点的,通俗的说,就是要用到多台服务器协同工作(不一定是实体,也可能是虚拟机).
而两者的区别在于:
集群是多台机器执行同一个业务,每次根据调度算法寻找最合适的节点来执行该业务
分布式计算是将一个业务拆分成多个独立的部分,由多台机器共同协作完成
集群的优点在于,当业务的需要的资源比较大时,可以避免由一个服务器去独自承担压力,而且即便有一个节点宕机了,业务仍然可以继续正常运行.这有点类似于负载均衡.
分布式的优点则是在计算上,可以协同多台机器发挥计算的威力,进行需要超高性能的运算.
构建集群
说现在我们正式开始构建集群.
使用 docker-machine 创建节点
由于实体机器的缺乏以及在 osx 上无法正常开启 tcp 的 docker 服务,我们基于 docker-machine 来创建多个虚拟机,作为集群中的节点.
执行下面的命令就可以创建一个新的 docker-machine 虚拟机
docker-machine create --driver virtualbox manager1
在创建了虚拟机后,可以使用
来查看虚拟机的相关信息,包括IP地址等
现在我们继续执行命令创建
和两个节点,使用命令可以查看到所有正在工作的虚拟机
:
docker-machine ls
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
manager1 - virtualbox Running tcp://192.168.99.100:2376 v17.06.1-ce
worker1 - virtualbox Running tcp://192.168.99.101:2376 v17.06.1-ce
worker2 - virtualbox Running tcp://192.168.99.102:2376 v17.06.1-ce
创建 docker machine 后,可以通过
的方式来访问虚拟机,执行指令.
创建 swarm 集群
初始化一个 swarm 集群的命令为:
docker swarm init --listen-addr <MANAGER-IP>:<PORT> --advertise-addr <IP>
参数是管理者节点的dock
er 服务所在的 IP:PORT,也就是说,可以通过这个组合访问到该节点的 docker 服务.
是广播地址,也就是其他节点加入该
swarm 集群时,需要访问的 IP
现在我们在节点里创建 swarm 网络,执行
docker-machine ssh manager1 docker swarm init --listen-addr 192.168.99.100:2377 --advertise-addr 192.168.99.100
返回响应:
Swarm initialized: current node (23lkbq7uovqsg550qfzup59t6) is now a manager.
To add a worker to this swarm, run the following command:
docker swarm join \
--token SWMTKN-1-3z5rzoey0u6onkvvm58f7vgkser5d7z8sfshlu7s4oz2gztlvj-c036gwrakjejql06klrfc585r \
192.168.99.100:2377
To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
这样便创建了一个 swarm 集群,并且
节点目前是以管理
者的身份加入在节点中的.
现在我们把
和两个节点加入到
swarm 集群中去,分别在两个节点的虚拟机中执行即可:
docker-machine ssh worker1 docker swarm join --token \
SWMTKN-1-3z5rzoey0u6onkvvm58f7vgkser5d7z8sfshlu7s4oz2gztlvj-c036gwrakjejql06klrfc585r \
192.168.99.100:2377
This node joined a swarm as a worker.
docker-machine ssh worker2 docker swarm join --token \
SWMTKN-1-3z5rzoey0u6onkvvm58f7vgkser5d7z8sfshlu7s4oz2gztlvj-c036gwrakjejql06klrfc585r \
192.168.99.100:2377
This node joined a swarm as a worker.
在任何一个节点上执行
都可以查看到当前整个集群中的
所有节点:
docker-machine ssh manager1 docker node ls
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
manager1 - virtualbox Running tcp://192.168.99.100:2376 v1.12.3
worker1 - virtualbox Running tcp://192.168.99.101:2376 v1.12.3
worker2 - virtualbox Running tcp://192.168.99.102:2376 v1.12.3
创建跨主机网络
集群建立完毕之后我们要做的就是在集群上部署我们的服务.但是首先应该让所有的节点处在一个共享的网络中,这样当我们把服务部署在这个共享网络中,就相当于部署在整个集群中了.
使用
可以查看到当前主机所参与的所有网络
:
docker-machine ssh manager1 docker network ls
NETWORK ID NAME DRIVER SCOPE
764ff31881e5 bridge bridge local
fbd9a977aa03 host host local
6p6xlousvsy2 ingress overlay swarm
e81af24d643d none null local
其中 SCOPE 为 swarm,DRIVER 为 overlay 的即为集群节点中的共享网络.集群建立后会有一个默认的 ingress 共享网络,现在我们来再创建一个:
docker-machine ssh manager1 docker network create --driver overlay swarm_test
在跨主机网络上部署服务
在集群上部署应用,就是在共享网络上部署服务 (service).
但首先要保证每个节点上都已经有所需的镜像和环境了,这点便可以通过将同一份 docker-compose 配置文件共享到每个主机上,使用 docker-compose 在每个节点上下载镜像和搭建环境的工作.
由于 judge_server 的服务架构很简单,就一个镜像,所以我在这里直接在每台主机上把它 pull 下来就好了:
docker-machine ssh manager1 docker pull registry.cn-qingdao.aliyuncs.com/marklux/judge_server:1.0
docker-machine ssh worker1 docker pull registry.cn-qingdao.aliyuncs.com/marklux/judge_server:1.0
docker-machine ssh worker2 docker pull registry.cn-qingdao.aliyuncs.com/marklux/judge_server:1.0
接下来便是重头戏,我们使用
节点,在共享网络
上启动我们的服务
docker service create --replicas 3 --name judge_swarm -p 8090:8090 --network=swarm_test registry.cn-qingdao.aliyuncs.com/marklux/judge_server:1.0
这个命令看起来是不是很像
?没错,swarm最
终的目的就是把操作集群变得像操作单一的 docker 服务端一样简单!
--replicas 用于指定服务需要的节点数量,也就是集群的规模,这个值是弹性的,你可以在后续动态的更改它.
当服务中某个节点挂掉时,swarm 将会搜寻集群中剩余的可用节点,顶替上去.也就是说,swarm 会动态的调度,总是保持服务是由 3 个节点运行着的.
-p 用于暴露端口到宿主机,这样我们就能访问到了.
--network 用于指定部署 service 的网络是哪一个
现在在
节点中使用来查看集群中的服务:
docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
kofcno637cmq judge_swarm replicated 3/3 registry.cn-qingdao.aliyuncs.com/marklux/judge_server:1.0 *:8090->8090/tcp
现在我们尝试在本地访问 192.168.99.100:8090/ping,就可以得到响应了, 事实上,现在无论将 ip 换为
或者的,响应的结果
都是一样,因为此时所有节点已经处在一个共同的集群网络下了
经过大量的访问测试,可以看到
是在变化着的,这
说明每次请求,都由 swarm 动态的调度,选择了不同的节点来进行处理.
遗留问题
至此集群的部署已经完成,但是我们还遗留了几个问题没有解决:
集群节点的动态添加删除不是很方便,这导致在 web 端管理判题服务机有一定的难度,当然可以通过 docker 的 REMOTE API 来实现,不过复杂度比较高
集群节点间的文件同步不太好实现,可能需要自己写脚本同步或是使用之类的服务来实现
swarm 非常适合快速构建大量集群来实现业务的处理,不过对于只有几台机器的情况而言,有些 "杀鸡用牛刀" 的感觉
来源: https://cloud.tencent.com/developer/article/1027473