背景
大家都知道,Docker這些年讓IT界產生了深刻的變革,
從開發到測試到運維,處處都有它的身影。
它同時也和微服務架構相互促進,並肩前行。
在最新版的 Docker(CE 17.03) 裡,隨著 swarm mode
的成熟,
在較簡單的場景裡已經可以不再需要專門的基礎設施管理
,服務編排
,服務發現
,健康檢查
,負載均衡
等等。
但是API gateway
還是需要一個的。或許再加上一個日誌收集
,
你的微服務架構就五臟俱全了。
我們知道Nginx Plus
是可以很好的勝任 API gateway 的工作的,
但它是商業軟體。Nginx
我們不說認證啊限流啊統計啊之類的功能,
單就請求轉發這一點最基本的就出了問題。
我們知道Docker是用DNS的方式,均衡同一名稱的服務請求到不同的node,
但是Nginx為了速度,在反向代理的時候會有一個不可取消的 DNS Cache,
這樣我們Docker在根據容器的擴充套件或收縮動態的更新DNS,可Nginx卻不為所動,
堅持把請求往固定的IP上發,不說均衡,這個IP甚至可能已經失效了呢。
有一個配置檔案上的小Hack可以實現Nginx每次去查詢DNS,我本來準備寫一篇文章來著,
現在看來不用了,我們找到了更優雅的API gateway
, Caddy 。
我上篇文章也寫了一個它的簡介。
接下來的所有程式碼,都在這個demo中,
你可以clone下來玩,也能在此基礎上做自己的實驗。
應用
我們先用golang寫一個最簡單的HTTP API,你可以用你會的任何語言寫出來,
它為GET
請求返回 Hello World 加自己的 hostname .
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+" :)
")
}
func main() {
http.HandleFunc("/", HelloServer)
log.Fatal(http.ListenAndServe(":12345", nil))
}
Docker 化
我們需要把上面的應用做成一個docker映象,暴露埠12345
。
接著才有可能使用Docker Swarm
啟動成叢集。
本來做映象特別簡單,但我為了讓大家直接拉映象測試時快一點,用了兩步構建,
先編譯出應用,然後新增到比較小的alpine
映象中。大家可以不必在意這些細節。
我們還是先來看看最終的docker-compose.yml
編排檔案吧。
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個例項,測試上層的負載均衡。
-
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的職責,不過這些就要你進行一些開發了。
你還有什麼更多的想法嗎?歡迎留言。