使用 caddy 做为微服务的 API gateway

背景

你们都知道,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 化

咱们须要把上面的应用作成一个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

为了让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 啊。咱们前边只是解决了容器项目中 API Gateway 和DNS式服务发现配合的一个难题,
接下来就简单了啊,咱们写n个app,每一个app是一个微服务,在gateway中把不一样的url路由到不一样的app就行了啊。

进阶

caddy还能够轻松的顺便把认证中心作了,微服务建议用jwt作认证,将权限携带在token中,caddy稍微配置下就能够。
我后续也会给出教程和demo 。auth2.0我认为并不适合微服务架构,但依然是有个复杂的架构方案的,这个主题改天再说。

caddy还能够作API状态监控,缓存,限流等API gateway的职责,不过这些就要你进行一些开发了。你还有什么更多的想法吗?欢迎留言。

相关文章
相关标签/搜索