上一节里我们谈到了小型的独立项目如何使用 Docker Compose 来搭建程序的运行环境,对于由多人或多部门参与的中大型服务化架构的项目,仅由一个 Docker Compose 项目来管理它们的运行环境显然是不切实际的。在这一小节里,我们就谈谈如何在服务化开发中合理利用 Docker 来搭建环境。
在开始之前,我们依然来设定一个场景。在这里,假定我们处于一个 Dubbo 治下的微服务系统,而工作是开发系统中某一项微服务。
微服务开发与上一节里我们提到的小型项目开发在环境搭建上有一定的区别,我们要合理地调整 Docker 的使用方法和策略,就必须先了解这些区别。
在微服务开发中,我们所开发的功能都不是完整的系统,很多功能需要与其他服务之间配合才能正常运转,而我们开发所使用的机器时常无法满足我们在一台机器上将这些相关服务同时运行起来。
我们仅仅是开发某一部分服务的内容,既对其他服务的运转机制不太了解,又完全没有必要在自己的机器上运行其他的服务。所以我们最佳的实践自然就是让参与系统中服务开发的同事,各自维护自己开发服务的环境,而直接提供给我们对应的连接地址使用服务即可。
更确切地说,我们在开发中,只需要在本地搭建起自己所开发服务的运行环境,再与其他开发者搭建的环境互联即可。
在我们的开发机器上,我们只需要运行我们正在开发的服务,这个过程依然可以使用 Docker Compose 来完成。这里我给出了一个简单的例子,表示一个简单的小服务运行环境。
version: "3"
networks:
backend:
mesh:
services:
mysql:
image: mysql:5.7
networks:
- backend
volumes:
- ../mysql/my.cnf:/etc/mysql/my.cnf:ro
- ../mysql/data:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: my-secret-pw
ports:
- "3306:3306"
app:
build: ./spring
networks:
- mesh
- backend
volumes:
- ../app:/app
depends_on:
- mysql
关于这里 Spring 镜像的使用和改造方法,我就不展开了,大家可以通过 Docker Hub 以及 Spring 官方所提供的镜像,练习如何改造它,使它适配自己的服务。
搭建好本地的环境,我们就需要考虑如何与朋友们所搭建的环境进行互联了。
这时候大家也许会想到,可以将服务涉及的相关端口通过映射的方式暴露到我们机器的端口上,接着我们只需要通过各服务机器的 IP 与对应的端口就可以连接了。
然而这种方法还不算特别方便,一来除了处理映射外,我们还需要配置防火墙等才能使其他的机器正确访问到容器,二来是这种方式我们依然要记录各个服务的网络地址等配置,而开发中切换它们是个烦琐的过程。
在介绍 Docker Compose 的小节里,我们知道了可以通过设置网络别名 ( alias ) 的方式来更轻松地连接其他容器,如果我们在服务化开发里也能这么做就能减少很多烦琐操作了。
要实现设置网络别名的目的,自然要先确保所有涉及的容器位于同一个网络中,这时候就需要引出我们之前在网络小节里说到的 Overlay 网络了。
Overlay Network 能够跨越物理主机的限制,让多个处于不同 Docker daemon 实例中的容器连接到同一个网络,并且让这些容器感觉这个网络与其他类型的网络没有区别。
要搭建 Overlay Network 网络,我们就要用到 Docker Swarm 这个工具了。Docker Swarm 是 Docker 内置的集群工具,它能够帮助我们更轻松地将服务部署到 Docker daemon 的集群之中。
在真实的服务部署里,我们通常是使用 Docker Compose 来定义集群,而通过 Docker Swarm 来部署集群。
如果熟悉 Docker 周边知识的朋友,相信这时候已经想到了另外一个工具,即 Kubernetes ( K8s )。没错,Kubernetes 与这两者的组合相比,功能要丰富强大很多,也正因此,与它相关的内容完全足以另辟一本小册来说。而在开发里,我们几乎使用不到 Kubernetes,所以我们这里就不做介绍了。如果大家有想要了解的 Kubernetes 知识点,可以通过小册的微信群向我提出,我会挑选大家关注的内容补充到小册的后面。
Docker Swarm 最初是独立的项目,不过目前已经集成到了 Docker 之中,我们通过 docker CLI 的命令就能够直接操控它。
对于 Docker Swarm 来说,每一个 Docker daemon 的实例都可以成为集群中的一个节点,而在 Docker daemon 加入到集群成为其中的一员后,集群的管理节点就能对它进行控制。我们要搭建的 Overlay 网络正是基于这样的集群实现的。
既然要将 Docker 加入到集群,我们就必须先有一个集群,我们在任意一个 Docker 实例上都可以通过 docker swarm init
来初始化集群。
$ sudo docker swarm init
Swarm initialized: current node (t4ydh2o5mwp5io2netepcauyl) is now a manager.
To add a worker to this swarm, run the following command:
docker swarm join --token SWMTKN-1-4dvxvx4n7magy5zh0g0de0xoues9azekw308jlv6hlvqwpriwy-cb43z26n5jbadk024tx0cqz5r 192.168.1.5:2377
在集群初始化后,这个 Docker 实例就自动成为了集群的管理节点,而其他 Docker 实例可以通过运行这里所打印的 docker swarm join
命令来加入集群。
加入到集群的节点默认为普通节点,如果要以管理节点的身份加入到集群中,我们可以通过 docker swarm join-token
命令来获得管理节点的加入命令。
$ sudo docker swarm join-token manager
To add a manager to this swarm, run the following command:
docker swarm join --token SWMTKN-1-60am9y6axwot0angn1e5inxrpzrj5d6aa91gx72f8et94wztm1-7lz0dth35wywekjd1qn30jtes 192.168.1.5:2377
我们通过这些命令来建立用于我们服务开发的 Docker 集群,并将相关开发同事的 Docker 加入到这个集群里,就完成了搭建跨主机网络的第一步。
接下来,我们就通过 docker network create
命令来建立 Overlay 网络。
$ sudo docker network create --driver overlay --attachable mesh
在创建 Overlay 网络时,我们要加入 --attachable
选项以便不同机器上的 Docker 容器能够正常使用到它。
在创建了这个网络之后,我们可以在任何一个加入到集群的 Docker 实例上使用 docker network ls
查看一下其下的网络列表。我们会发现这个网络定义已经同步到了所有集群中的节点上。
$ sudo docker network ls
NETWORK ID NAME DRIVER SCOPE
## ......
y89bt74ld9l8 mesh overlay swarm
## ......
接下来我们要修改 Docker Compose 的定义,让它使用这个我们已经定义好的网络,而不是再重新创建网络。
我们只需要在 Docker Compose 配置文件的网络定义部分,将网络的 external 属性设置为 true,就可以让 Docker Compose 将其建立的容器都连接到这个不属于 Docker Compose 的项目上了。
networks:
mesh:
external: true
通过这个实现,我们在开发中就使整个服务都处于一个可以使用别名映射网络中,避免了要对不同功能联调时切换服务 IP 的烦琐流程。在这种结构下,我们只需要让我们开发的 Docker 退出和加入不同的集群,就能马上做到切换不同联调项目。