over 5 years ago

本文章介紹的架構可以做什麼?

- 當機器無預警關機時服務會自動在另一台機器部署新的服務
- 當服務負載過大時可以動態在其他機器啟動服務並做負載均衡

架構

swarm

docker 原生的 cluster 方案,透過 swarm manager 管理整個 docker cluster 集群

consul

一個 key/value 儲存服務,常被應用分布式環境下讓所有服務共享訊息

nginx

常見的網頁伺服器

confd

一個設定檔管理工具,可以透過查詢 etcd,consul 等服務並動態從樣板產生新的設定檔

monitor script

一些簡單的腳本程式,主要功能有

  • 定期向 swarm manager 確認集群中的 container ,並更新當下所有服務的位置同步到 consul 上
  • 透過 swarm manager 去跑一個新的 container ,並將新啟動的服務位置資訊同步到 consul 上
  • 透過 swarm manager 去刪除某個節點上的 container ,並更新資訊到 consul 上

基本概念

1. swarm manager 負責指派 nodes 啟動服務,並回報目前集群服務狀態
2. consul 記錄所有服務對應到的 node ip 以及 port
3. monitor script 定期查詢 swarm manager 並把服務資訊更新至 consul
4. confd 定期確認 consul 上的變數值,並透過樣板產生新的 nginx 設定檔後呼叫 nginx reload 設定檔

實作

簡單的實作 demo 可以參考

quick demo

較詳細的部署流程

  • 本篇文章利用四台安裝好 docker 的 ubuntu 14.04
  • docker daemon 啟動時需加入下列參數 "-H 0.0.0.0:2375 -H unix:///var/run/docker.sock" 以供 manager 獲取資訊
  • master ip 為 192.168.99.104,上面跑 nginx + confd + consul + swarm manage
  • 三台 node 分別為 docker01(192.168.99.105),docker02(192.168.99.106),docker03(192.168.99.107)

在開始前 master 須先安裝 jq,一個 bash 解析 json 的套件,方便後續作業

$ apt-get install jq

consul

docker hub 上有包好的的 consul image 可以直接用(建議如果是正式服務的話還是參考官網教學
在 master 啟動 consul 服務

$ docker run -d -p 8400:8400 -p 8500:8500 -p 8600:53/udp -h node1 --name consul progrium/consul -server -bootstrap

測試 consul

$ curl -X PUT -d 'bar' http://192.168.99.104:8500/v1/kv/foo
$ culr -s http://192.168.99.104:8500/v1/kv/foo | jq .
[
  {
    "Value": "YmFy",
    "Flags": 0,
    "Key": "foo",
    "LockIndex": 0,
    "ModifyIndex": 12289,
    "CreateIndex": 12286
  }
]

可以看到 foo 這個 key 已經有儲存上傳的值 (用 base64 加密)

swarm

swarm manager 可以透過下列方式得知集群中所有 node 的位置資訊

- token
必須要連上 internet 才能使用,若處於 proxy 環境還要克服 proxy 障礙,相當麻煩...
- etcd
跟 consul 一樣是個 key/value 儲存服務,架設的過程一直遇到奇怪的雷...後來放棄
- consul
本文用的 key/value 儲存服務
- zookeeper
還沒實驗
- 將集群資訊寫在檔案中或直接在 manager 啟動時指定 nodes ip
好處是 nodes 不用裝 swarm ,壞處是 cluster 不能動態加節點,比較適合小型集群

由於 docker 官方有將 swarm 包成 image,當然義無反顧直接用
在 nodes 上啟動 swarm agent

$ ssh 192.168.99.105
$ docker run -d --name swarm --restart=always swarm join --advertise=192.168.99.105:2375 consul://192.168.99.104:8500/v1/kv/swarm

$ ssh 192.168.99.106
$ docker run -d --name swarm --restart=always swarm join --advertise=192.168.99.106:2375 consul://192.168.99.104:8500/v1/kv/swarm

$ ssh 192.168.99.107
$ docker run -d --name swarm --restart=always swarm join --advertise=192.168.99.107:2375 consul://192.168.99.104:8500/v1/kv/swarm

在 master 上啟動 swarm manager

$ docker run -d -p 2376:2375 --name swarm swarm manage consul://192.168.99.104:8500/v1/kv/swarm

利用 info 指令測試 swarm manager 能正確獲取三個 nodes 的資訊

$ docker -H 192.168.99.104:2376 info
Containers: 3
Images: 6
Role: primary
Strategy: spread
Filters: health, port, dependency, affinity, constraint
Nodes: 3
 docker01: 192.168.99.105:2375
  └ Containers: 1
  └ Reserved CPUs: 0 / 1
  └ Reserved Memory: 0 B / 778.3 MiB
  └ Labels: executiondriver=native-0.2, kernelversion=3.19.0-28-generic, operatingsystem=Ubuntu 14.04.3 LTS, storagedriver=aufs
 docker02: 192.168.99.106:2375
  └ Containers: 1
  └ Reserved CPUs: 0 / 1
  └ Reserved Memory: 0 B / 778.3 MiB
  └ Labels: executiondriver=native-0.2, kernelversion=3.19.0-28-generic, operatingsystem=Ubuntu 14.04.3 LTS, storagedriver=aufs
 docker03: 192.168.99.107:2375
  └ Containers: 1
  └ Reserved CPUs: 0 / 1
  └ Reserved Memory: 0 B / 778.3 MiB
  └ Labels: executiondriver=native-0.2, kernelversion=3.19.0-28-generic, operatingsystem=Ubuntu 14.04.3 LTS, storagedriver=aufs
CPUs: 3
Total Memory: 2.28 GiB
Name: ebb77eea736a

nginx & confd

在 master 上安裝 nginx

$ apt-get install nginx

在 master 下載 confd 並加入 PATH

$ wget https://github.com/kelseyhightower/confd/releases/download/v0.10.0/confd-0.10.0-linux-amd64 -O confd
$ chmod +x confd
$ mv confd /usr/local/bin/

編輯 confd 設定檔

$ vi /etc/confd/conf.d/nginx.toml 
# 加入以下內容

[template]
# template 名稱

src = "nginx.tmpl"
#動態產生的設定檔置放位置

dest = "/etc/nginx/sites-enabled/default"
# 將會監控 http://192.168.99.104:8500/v1/kv/helloweb 值的變化

keys = [ "/helloweb" ]

# 使用者和權限

owner = "root"
mode = "0644"

# 每當 consul 上監控的 key/value 有變化,動態產生設定檔後執行下列指令

reload_cmd = "/usr/sbin/service nginx reload"

編輯 nginx 設定檔的 template

$ vi /etc/confd/templates/nginx.tmpl
# 加入以下內容

upstream helloweb {
        #helloweb 的 value 會被帶入  {{.}}

        {{range getvs "/helloweb/*"}}
                server {{.}};
        {{end}}
}

server {
        listen 83 default_server;
        listen [::]:83 default_server ipv6only=on;

        root /usr/share/nginx/html;
        index index.html index.htm;

        server_name localhost;

        location / {
                proxy_pass        http://helloweb;
                proxy_redirect    off;
                try_files $uri $uri/ =404;
        }

}

啟動 confd 每 5 秒去向 consul 確認監控的值有無變化

$ confd -interval 5 -backend consul -node 192.168.99.104:8500

此時觀察 /etc/nginx/site-enable/default 可看到 upstream helloweb 為空

upstream helloweb {

}

測試 High availability 和動態發佈服務

在任一台機器下載 demo 用的 monitor script

$ git clone https://github.com/genchilu/haWithSwarm.git
$ cd haWithSwarm/haScripts/

動態發佈服務

先測試動態發佈一個服務

# 輸入 bash scaleup.sh -h 輸出參數說明

$ bash scaleup.sh -s "192.168.99.104:2376" -c "http://192.168.99.104:8500" -p "helloweb" -o "-d -p 3000:3000 --restart=always --entrypoint=/usr/bin/node" -i "genchilu/helloweb" -a "/opt/helloweb/app.js"

這時候在 swarm manager 查詢,會看到 demo 的服務在 192.168.99.106:3000 啟動

$ docker -H 192.168.99.104:2376 ps
CONTAINER ID        IMAGE               COMMAND                  CREATED              STATUS              PORTS                           NAMES
98b1168f2e19        genchilu/helloweb   "/usr/bin/node /opt/h"   About a minute ago   Up About a minute   192.168.99.106:3000->3000/tcp   docker02/distracted_ptolemy

去看 /etc/nginx/site-enable/default,upstream helloweb 指向 192.168.99.106:3000

upstream helloweb {

                server 192.168.99.106:3000;

}

curl master的 83 port 會回傳一個 express 的 example 內容

$ curl http://192.168.99.104:83
<!DOCTYPE html><html><head><title>Express</title><link rel="stylesheet" href="/stylesheets/style.css"></head><body><h1>Express</h1><p>Welcome to Express</p></body></html>

每次執行 scaleup.sh 會動態在 nodes 中選一台機器部署服務,scaledown.sh 則相反
可搭配系統 loading 動態做新增服務做 load balance 或減少服務釋放資源

High availability

執行 script

# 輸入 checkEvery10s.sh  -h 輸出參數說明

$ bash checkEvery10s.sh -a "/opt/helloweb/app.js" -c "http://192.168.99.104:8500" -i "genchilu/helloweb" -o "-d -p 3000:3000 --restart=always --entrypoint=/usr/bin/node" -p "helloweb" -s "192.168.99.104:2376"

連線到 192.168.99.106 後執行關機

$ ssh 192.168.99.106
$ poweroff

此時 curl master 的 83 port 會發現 502 錯誤

$ curl http://192.168.99.104:83                                                                  <html>
<head><title>502 Bad Gateway</title></head>
<body bgcolor="white">
<center><h1>502 Bad Gateway</h1></center>
<hr><center>nginx/1.4.6 (Ubuntu)</center>
</body>
</html>

等約一分鐘後,查詢 swarm manager,發現服務自動在 192.168.99.105 啟動

$ docker -H 192.168.99.104:2376 ps
CONTAINER ID        IMAGE               COMMAND                  CREATED                  STATUS                  PORTS                           NAMES
40ff012aad58        genchilu/helloweb   "/usr/bin/node /opt/h"   Less than a second ago   Up Less than a second   192.168.99.105:3000->3000/tcp   docker01/high_shockley

觀看 /etc/nginx/site-enable/default 可看到 upstream helloweb 被指向 192.168.99.105:3000

upstream helloweb {

                server 192.168.99.105:3000;

}

curl master的 83 port 發現服務依舊正常運作

$ curl http://192.168.99.104:83
<!DOCTYPE html><html><head><title>Express</title><link rel="stylesheet" href="/stylesheets/style.css"></head><body><h1>Express</h1><p>Welcome to Express</p></body></html>

重新將 192.168.99.106 開機,待 swarm agent 啟動後會發現 nginx 上的設定檔會變成這樣

upstream helloweb {

                server 192.168.99.105:3000;

                server 192.168.99.106:3000;

}

此時服務流量會被分散到 105,106 兩台機器上
執行 scaledown.sh 可以動態卸除一個服務

← Hello World btrfs 壓縮功能實測和一些心得 →