使用 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的職責,不過這些就要你進行一些開發了。你還有什麼更多的想法嗎?歡迎留言。

相關文章
相關標籤/搜索