over 5 years ago

這般文章是基於 基於 swarm + consul + nginx 達到 HA 和 dynamic scaling 的架構 的延續架構改造

主要修改的架構為

- 用 interlock 取代原本的 bash 腳本和 confd
- 加入 redis 保存用戶 session,每次新的服務啟動時都能存取到原本的 session

demo 示意

HA

開啟頁面

頁面會顯示使用者名稱,瀏覽次數以及提供服務的 container ip 資訊

此時將後台對應的 vm 關機

頁面會出現 503 error

等兩到三分鐘後,頁面自動恢復服務,且保有上次服務的 session (等待時間取決於 swarm manager 對失敗節點訊息的同步速度)

dynamic scaling

直接在 swarm cluster 上重複啟動多個 container,且不重啟任何服務

$ docker -H master_ip:4000 run -d -P -h goweb.example.url --restart=always genchilu/go-web-example -sessiontype=redis -redisinfo=master_ip:6379 -sessionlifetime=3000

此時在瀏覽器 refresh,會看到 container 的 ip 會有變化(代表每次存取頁面不一定是同一台 vm 提供服務),如下圖
refresh 前

refresh 後

架構

swarm / consul / nginx 在前篇文章有介紹,不贅述

intelock

interlock 會將 swarm cluster 上的container 對應的 ip 和 port 更新到 server 設定檔,目前支援的 plugin 有
- haproxy
- nginx
- stats

redis

常用來存 session 資訊的 key/value DB

monitor script

一個簡單的腳本程式,主要功能為確認 swarm cluster 上有無目標 container ,若無則從 image 跑一個新的 container

基本概念

1. swarm manager 負責指派 nodes 啟動服務,並回報目前集群服務狀態
2. monitor script 負責監控 swarm 上的服務,一旦發現目標 image 沒有在 swarm cluster 啟動的服服中,則從目標 image 啟動新的服務 
3. nginx 用固定的網址訪問 interlock 的 nginx
4. interlock 依據 swarm cluster 中的 container 狀態更新 server 設定檔

實作

簡單的實作 demo 可以參考

quick demo

較詳細的部署流程

  • 本篇文章利用四台安裝好 docker 的 ubuntu 14.04
  • docker daemon 啟動時需加入下列參數 "-H 0.0.0.0:2376 -H unix:///var/run/docker.sock" 以供 manager 獲取資訊
  • master ip 為 192.168.99.102,上面跑 nginx + interlock + consul + swarm manage + redis
  • 三台 node 分別為 192.168.99.109,192.168.99.101,192.168.99.103

swarm / consul 安裝在前篇文章有介紹,不贅述
下面指令都是在 master 上執行

redis

在 ubuntu 上可以直接用 apt-get 安裝

$ apt-get install redis-server

或者可用我打包的 image

$ docker run -d --name redis-server -p 6379:6379 --restart=always genchilu/redis

Dockerfile

example web

$ docker -H 192.168.99.102:4000 run -d -P -h goweb.example.url --restart=always genchilu/go-web-example -sessiontype=redis -redisinfo=192.168.99.102:6379 -sessionlifetime=3000

Dockerfile & source code

這邊要注意兩點
1. interlock 是用 hostname 做為轉址的依據,一定要輸入
2. 以 -P 讓 docker 自動找合法的 port 對應到 container 內的服務,interlock 會根據這個更新設定檔

interlock

直接採用作者自己打包的 image

docker run -d --name interlock --restart=always ehazlett/interlock --swarm-url tcp://192.168.99.102:4000 --plugin nginx start

啟動後可以觀察 interlock 產生的設定檔

$ docker exec -ti interlock cat /etc/nginx/nginx.conf

會有一段

upstream goweb.example.url {
    server 192.168.99.109:32771;
    server 192.168.99.109:32770;
}
server {
    listen 80;     
    listen 8080;
    server_name goweb.example.url;
    location / {
        proxy_pass http://goweb.example.url;
    }
}

當 user 透過 goweb.example.url(即為 hostname)著這個網址訪問 nginx 時
連線都會轉到對應的 container
(若用 ip 訪問 nginx 則會導到 503 頁面)
如果這時候啟動或刪除 container 會更新 upstream goweb.example.url 裡的資訊
而若是啟動 container 時沒有對應的 port 則會出現 "server;;"
因此啟動 container 服務時要注意這兩個地方
(此時有 swarm cluster 中有其他 hostname 的 container 時會有新的 server 設定)

nginx

上面提到要透過 hostname 當網址訪問 interlock 的 nginx
所以前面我多設一台 nginx,作用就是用強迫用 hostname 去訪問 interlock
(這裡是我偷懶...用 dns server 或修改 interlock 的 nginx plugin 會比較好)
作法如下
先修改 nginx 所在的 server 的 /etc/hosts,將 goweb.example.url 對應 interlock 的 ip 192.168.99.102
然後修改 nginx 設定檔如下

worker_processes  1;
events {
    worker_connections  1024;
}
http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;
    server {
        listen 80;
        listen 8080;
        server_name localhost;
        location / {
            proxy_pass http://goweb.example.url;
        }
    }
}

以本文範例的話可以用我自己打包好的 nginx image,然後啟動時用 link 指令把 interlock 和 goweb.example.url 對應起來

docker run -d -p 80:80 --link interlock:goweb.example.url genchilu/nginx

Dockerfile

監控腳本

拷貝監控腳本 執行
(執行前確認 bash 有裝 jq 套件,參考上一篇文章)

# 直接輸入 bash checkAlive.sh 顯示參數說明
$ bash checkAlive.sh -a "-sessiontype=redis -redisinfo=192.168.99.102:6379 -sessionlifetime=3000" -i "genchilu/go-web-example" -o "-d -P -h goweb.example.url --restart=always" -s "192.168.99.102:4000" -t 10

監控腳本同樣有包成 docker image

$ docker run -ti --rm genchilu/checkalive -a "-sessiontype=redis -redisinfo=192.168.99.102:6379 -sessionlifetime=3000" -i "genchilu/go-web-example" -o "-d -P -h goweb.example.url --restart=always" -s "192.168.99.102:4000" -t 10

Dockerfile

自此大功告成,可以透過下列指令觀察 container 跑在那一台 server 並關機來測試 HA

$ docker -H 192.168.99.102:4000 ps

或者瘋狂的啟動新服務觀察 interlock 的設定檔更新狀況

個人心得

- redis / swarm manager / interlock 都還是有單點失敗的問題。如果要做到更完善的 HA,還必須要參酌 redis 和 swarm manager 本身的分散式機制,至於 interlock...應該多啟動起台就沒問題了。
- interlock 還有一些小問題,當 VM 關機時並不會觸發 interlock 更新設定檔,如果關機的 VM 上面剛好有跑服務的 container 的話就會出包了...這時候得靠監控腳本在 swarm cluster 啟動服務去觸發 interlock。 
- 搭配監控系統和 alert 機制自動去觸發 dynamic scaling 比較有意義,有空再把這段補上。
← btrfs 壓縮功能實測和一些心得 用 Spring Boot 實作預防暴力登入嘗試的機制 →