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