使用 Spring Boot 和 Docker 构建微服务架构(四)
【编者的话】 这篇也是我多年前发表在DockOne社区的文章,对微服务架构以及容器化概念作一个概述,利用工具进行设置,深入探讨如何使用Docker工作,然后搭建我们的第一个容器,接着构建一个Employee微服务,并将添加一些额外的服务/容器,并且更新容器,采用Docker Compose以及使用HAProxy容器进行负载均衡。现在回顾这篇处于微服务及容器技术洪荒时代的文章,还是挺有意思的,现在从故纸堆里拿出来给大家作为参考吧,由于文章比较长,所以分为四篇来讲,本篇是第四篇。
第四篇 额外的微服务、更新容器、Docker Compose和负载均衡
现在我们对于微服务和Docker有了扎实的了解,启动了一个MongoDB容器和Spring Boot微服务容器并且借助于容器的link机制(参考我们的Git版本库第四部分开始部分)实现了它们之间的相互通讯。为了完成我们最初的用例,我们需要两个微服务——分别是“missions”和“rewars”。我将开始这个话题并按照与我们之前构建employee微服务相同的方式来构建这两个微服务,可以参考Git版本库第四部分的第一步来获得这两个微服务容器。现在如果我们执行docker ps,我们将看到如下的信息,其中的一些列为了简洁被移掉了:
CONTAINER ID IMAGE PORTS NAMES
86bd9bc19917 microservicedemo/employee 0.0.0.0:32779->8080/tcp employee
1c694e248c0a microservicedemo/reward 0.0.0.0:32775->8080/tcp reward
c3b5c56ff3f9 microservicedemo/mission 0.0.0.0:32774->8080/tcp mission
48647d735188 mongo 0.0.0.0:32771->27017/tcp mongodb
更新镜像
这都是很简单的,但是没有太大的作用,因为此时没有一个微服务可以在简单的数据CRUD功能之外带来任何直接的价值。让我们开始对一些代码的更改进行分层,以提供更多的价值和功能。我们会对某个微服务做出一些改变,然后处理如何更新镜像,并了解对于容器的版本控制。在该服务里员工通过完成任务来获得积分,我们需要追踪他们的任务完成情况、积分总计(获得和活跃的)以及奖励兑换。我们将添加一些额外的类到Employee模型中——这些不是顶层的业务对象,所以它们不会有它们自己的微服务,但是将会在Employee对象中提供上下文内容。一旦这些变化做出了(参见Git版本库第四部分第二步),将会有一些结构性的改变,需要在整个软件栈中同步,更新镜像的步骤如下:
● 重新编译源代码
gradle build
● 重新构建镜像
docker build -t microservicedemo/employee .
最后将会看到一些如下的消息:
Removing intermediate container 5ca297c19885 Successfully build 088558247
● 现在我们需要删除旧容器,替换为新的:
docker stop employee
docker rm employee
docker run -P -d --name employee --link mongodb microservicedemo/employee
需要注意的重要事项就是在运行容器内的代码是不会更新的,容器和微服务的另外一个核心原则是容器内部的代码和配置是不可变的。换句话说,你不用更新容器,只需要替换它。这对于一些容器的使用案例会造成一些问题,比如使用容器来操作数据库或者其它的持久化资源。
使用Docker Compose来编排容器
像我一样,如果你在本系列文章之间有其他的工作要做,如何确保所有的各种命令行参数都可以来连接这些容器,可能有点令人沮丧。编排这一队容器就是Docker Compose(以前被成为Fig)的目的。在Yaml配置文件中定义你的一组容器,并且管理这些容器的运行时配置。在很多方面,可以把Docker Compose想象成为一个编排者,确保“正运行”的容器有着正确的选项和配置。我们将通过命令行参数为我们的应用创建一个这样的编排者来做所有管理想做的事情。
docker-compose.yml:
employee:
build: employee
ports:
- "8080"
links:
- mongodb
reward:
build: reward
ports:
- "8080"
links:
- mongodb
mission:
build: mission
ports:
- "8080"
links:
- mongodb
mongodb:
image: mongo
接着在命令行上敲入:
docker-compose up -d
然后整个一队容器都将会启动,非常方便!许多Docker命令在Docker-Compose上都有类似的命令,如果我们运行“docker-compose ps
”,我们会看到:
Name Command State Ports
-------------------------------------------------------------
git_employee_1 java -Dspring.data.mongodb ... Up 0.0.0.0:32789->8080/tcp
git_mission_1 java -Dspring.data.mongodb ... Up 0.0.0.0:32785->8080/tcp
git_mongodb_1 /entrypoint.sh mongod Up 27017/tcp
git_reward_1 java -Dspring.data.mongodb ... Up 0.0.0.0:32784->8080/tcp
容器的动态伸缩和负载均衡
不过上述这些还不是Docker Compose可以做的所有工作,如果你运行“docker-compose scale [compose container name]=3
”,,将会创建多个容器实例。比如运行“docker-compose scale employee=3
”,接着运行“docker-compose ps
”,将会看到:
Name Command State Ports
---------------------------------------------------------------------------------
git_employee_1 java -Dspring.data.mongodb ... Up 0.0.0.0:32789->8080/tcp
git_employee_2 java -Dspring.data.mongodb ... Up 0.0.0.0:32791->8080/tcp
git_employee_3 java -Dspring.data.mongodb ... Up 0.0.0.0:32790->8080/tcp
git_mission_1 java -Dspring.data.mongodb ... Up 0.0.0.0:32785->8080/tcp
git_mongodb_1 /entrypoint.sh mongod Up 27017/tcp
git_reward_1 java -Dspring.data.mongodb ... Up 0.0.0.0:32784->8080/tcp
我们的employee容器现在有了三个实例!Docker Compse记得你设置的数量,所以下次运行的时候,将会启动三个employee容器实例,就我个人而言,我认为这个应该在docker-compose.yml文件中设置,但是其实不是的。
你希望从一开始就看到一个问题是如何解决的。我们应该如何构建一个针对终端用户的事实上使用微服务的应用呢?因为容器的端口变了,并且在一个Docker集群服务器环境中(比如Docker Swarm),宿主机的IP地址也会改变。有一些先进的解决方案(Kubernetes和AWS的ECS),但是现在我们将寻找一个相对简单的选项,将采用一个非常容易的方法来对容器实例做负载均衡。Tutum是一家构造多重云容器组织能力的公司,已经给Docker社区提交了一个HAProxy的扩展插件,这个扩展插件会基于连接的容器自动配置其自身。让我们为多个employee容器实例添加一个负载均衡器,我们将其添加到docker-compose.yml文件中:
…
ha_employee:
image: tutum/haproxy
links:
- employee
ports:
- "8080:80"
接着我们运行“docker-compose up -d
”,将会下载缺失的镜像并且启动容器。现在我们可以再次在特定的端口(8080)运行测试,这次将会对于所有运行的employee容器进行负载均衡。接着,我们可以在192.168.99.100:8080上敲击employee服务集群,默认情况下将会轮循这三个实例。易如反掌!HAProxy的Docker容器还有许多额外的特性和功能点,我建议可以从https://github.com/tutumcloud/haproxy 获得更多的信息。
HAProxy对于一个特定容器的多个实例的负载均衡问题处理地非常巧妙,是单容器环境的理想选择。然而,我们没有这样的环境,那咋办?我们可以启动多个HAProxy实例来处理容器集群,在宿主机的不同端口上转发每一个HAProxy容器端口,所以我们的employee服务放在了8080端口,mission服务放在8081端口,reward服务放在8082端口(参考Git版本库第四部分第三步)。如果我们来到生产环境,我们可以利用Nginx来创建反向代理,将所有的服务请求转发到一个单独的IP地址和端口上(通过URL路径/employee/和/reward/路由到相应的容器)。或者我们可以使用一个更加健壮的服务发现路由策略,比如利用etcd和一些让人印象深刻的Docker元数据脚本和模板引擎,它们来自于Jason Wilder的Docker-gen系统(https://hub.docker.com/r/jwilder/docker-gen/),以及大量的额外的自我管理的服务发现的解决方案。我们目前将会保留这个简单的HAProxy解决方案,因为它给予了我们一个对于如何管理容器集群的扎实的理解。
这是一个结束本系列的好地方,不过我们还有很多额外的领域没有涉及到,包括:
● 构建一个前端的容器,或者一个移动APP容器
● 包含后端的数据批处理过程
● 动态分级的容器集群来处理消息队列的条目
● 将服务从Java/Spring Boot迁移到Scala/Akka/Play
● 构建持续集成
● 构建自己的镜像Repository或者使用容器Repository服务(Google或者Docker Hub)
● 评估容器管理系统比如AWS的ECS或者Kubernetes
版权声明: 本文为 InfoQ 作者【MaxHu】的原创文章。
原文链接:【http://xie.infoq.cn/article/d5315cd8af1099f263b7d7d49】。文章转载请联系作者。
评论