你们都知道,Docker这些年让IT界产生了深入的变革,
从开发到测试到运维,到处都有它的身影。
它同时也和微服务架构相互促进,并肩前行。node
在最新版的 Docker(CE 17.03) 里,随着 swarm mode
的成熟,
在较简单的场景里已经能够再也不须要专门的基础设施管理
,服务编排
,服务发现
,健康检查
,负载均衡
等等。nginx
可是API gateway
仍是须要一个的。或许再加上一个日志收集
,
你的微服务架构就五脏俱全了。
咱们知道Nginx Plus
是能够很好的胜任 API gateway 的工做的,
但它是商业软件。Nginx
咱们不说认证啊限流啊统计啊之类的功能,
单就请求转发这一点最基本的就出了问题。git
咱们知道Docker是用DNS的方式,均衡同一名称的服务请求到不一样的node,
可是Nginx为了速度,在反向代理的时候会有一个不可取消的 DNS Cache,
这样咱们Docker在根据容器的扩展或收缩动态的更新DNS,可Nginx却不为所动,
坚持把请求往固定的IP上发,不说均衡,这个IP甚至可能已经失效了呢。github
有一个配置文件上的小Hack能够实现Nginx每次去查询DNS,我原本准备写一篇文章来着,
如今看来不用了,咱们找到了更优雅的API gateway
, Caddy 。
我上篇文章也写了一个它的简介。golang
接下来的全部代码,都在这个demo中,
你能够clone下来玩,也能在此基础上作本身的实验。web
咱们先用golang写一个最简单的HTTP API,你能够用你会的任何语言写出来,
它为GET
请求返回 Hello World 加本身的 hostname .docker
package main import ( "io" "log" "net/http" "os" ) // HelloServer the web server func HelloServer(w http.ResponseWriter, req *http.Request) { hostname, _ := os.Hostname() log.Println(hostname) io.WriteString(w, "Hello, world! I am "+hostname+" :)\n") } func main() { http.HandleFunc("/", HelloServer) log.Fatal(http.ListenAndServe(":12345", nil)) }
咱们须要把上面的应用作成一个docker镜像,暴露端口12345
。
接着才有可能使用Docker Swarm
启动成集群。
原本作镜像特别简单,但我为了让你们直接拉镜像测试时快一点,用了两步构建,
先编译出应用,而后添加到比较小的alpine
镜像中。你们能够没必要在乎这些细节。
咱们仍是先来看看最终的docker-compose.yml
编排文件吧。shell
version: '3' services: app: image: muninn/caddy-microservice:app deploy: replicas: 3 gateway: image: muninn/caddy-microservice:gateway ports: - 2015:2015 depends_on: - app deploy: replicas: 1 placement: constraints: [node.role == manager]
这是最新版本的docker-compose
文件,再也不由docker-compose
命令启动,而是要用docker stack deploy
命令。
总之如今这个版本在编排方面尚未彻底整合好,有点晕,不过能用。如今咱们看到编排中有两个镜像:后端
muninn/caddy-microservice:app 这是咱们上一节说的app镜像,咱们将启动3个实例,测试上层的负载均衡。api
muninn/caddy-microservice:gateway 这是咱们接下来要讲的gateway了,它监听2015端口并将请求转发给app。
为了让caddy看成gateway,咱们主要来看一下Caddyfile
:
:2015 { proxy / app:12345 }
好吧,它太简单了。它监听本机的2015端口,将全部的请求都转发到 app:12345 。
这个app,实际上是一个域名,在docker swarm的网络中,它会被解析到这个名字服务随机的一个实例。
未来若是有不少app,将不一样的请求前缀转发到不一样的app就好啦。
因此记得写规范的时候让一个app的endpoint前缀尽可能用同样的。
而后caddy也须要被容器化,感兴趣的能够看看Dockerfile.gateway .
理解了上面的内容,就能够开始运行服务端了。直接用我上传到云端的镜像就能够。本文用到的三个镜像下载时总计26M左右,不大。
clone我背景章节提到的库进入项目目录,或者仅仅复制上文提到的compose文件存成docker-compose.yml
,而后执行以下命令。
docker-compose pull docker stack deploy -c docker-compose.yml caddy
啊,对了,第二个stack命令须要你已经将docker切到了swarm模式,若是没有会自动出来提示,根据提示切换便可。
若是成功了,咱们检查下状态:
docker stack ps caddy
若是没问题,咱们能看到已经启动了3个app和一个gateway。而后咱们来测试这个gateway是否能将请求分配到三个后端。
咱们是能够经过访问http://{your-host-ip}:2015
来测试服务是否是通的,用浏览器或者curl。
而后你会发现,怎么刷新内容都不变啊,并无像想象中的那样会访问到随机的后端。
不要着急,这个现象并不是由于caddy像nginx那样缓存了dns致使均衡失败,而是另外一个缘由。
caddy为了反向代理的速度,会和后端保持一个链接池。当只有一个客户端的时候,用到老是那第一个链接呢。
为了证实这一点,咱们须要并发的访问咱们的服务,再看看是否符合咱们的预期。
一样的,测试我也为你们准备了镜像,能够直接经过docker使用。
docker run --rm -it muninn/caddy-microservice:client
感兴趣的人能够看client文件夹里的代码,它同时发起了30个请求,而且打印出了3个后端被命中的次数。
另外我还作了一个shell版本,只须要sh test.sh
就能够,不过只能看输出拉,没有自动检查结果。
好了,如今咱们能够知道,caddy能够很好的胜任微服务架构中的 API Gateway 了。
什么?你说没看出来这是个 API Gateway 啊。咱们前边只是解决了容器项目中 API Gateway 和DNS式服务发现配合的一个难题,
接下来就简单了啊,咱们写n个app,每一个app是一个微服务,在gateway中把不一样的url路由到不一样的app就行了啊。
caddy
还能够轻松的顺便把认证中心作了,微服务建议用jwt作认证,将权限携带在token中,caddy稍微配置下就能够。
我后续也会给出教程和demo 。auth2.0我认为并不适合微服务架构,但依然是有个复杂的架构方案的,这个主题改天再说。
caddy
还能够作API状态监控
,缓存
,限流
等API gateway的职责,不过这些就要你进行一些开发了。你还有什么更多的想法吗?欢迎留言。