基于 Docker 的高可用 Rails 集群方案探索

er74 9年前發布 | 18K 次閱讀 Rails Docker

【編者的話】本文是暴走漫畫團隊關于自動化高可用集群方案的探索。主要目的是減少人工接入,實現自動化。主要是用了consul和registrator兩個工具。

0x0 問題

運維從來都是一個“不起眼”的大問題。運維工作還特別瑣碎,一般要花去工程師大量的時間,同時還有突發性,對工程師機動性要求比較高。所以運維自動化一直在困擾暴走漫畫團隊。

雖說大部分運維工作都交給第三方云服務提供商了,但是平時還是有一些機器的維護之類的,繁瑣的事情。

我做的最多的應該就是加減服務器了。暴漫的流量趨勢非常規律,每年寒暑假是訪問的高峰期,一般會比平時流量高出2倍。所以每年寒暑假之前都要添加 足量的服務器以應對馬上到來的流量高峰,而在寒暑假結束之后,要去掉一部分機器,以節省開支。剛開始完全是人肉運維,我一個人先去開5臺機器,然后一個個 上去改配置文件之類的,感覺好傻。后面就用了ansible作為自動化運維工具,這個要比puppet和chef輕量很多,對付我們這些簡單的運維工作綽 綽有余。 于是現在就變成了開完機器,用ansible同步配置,代碼,啟動app server,然后手動更新nginx配置。

使用ansible之后其實已經輕松很多了,但是還不夠,我想象中的自動化集群,應該可以隨意增加刪除node,node不可用的時候自動刪除,當然這些都不能影響服務的訪問。

下面介紹一種自動化的Rails集群方案。

0x1 相關技術

  • docker
  • hashicorp/consul
  • hashicorp/consul-template
  • hashicorp/registrator
  • </ul>

    0X2 整體架構

     基于 Docker 的高可用 Rails 集群方案探索

    0x3 實踐

    由于不是科普帖,各種工具的詳細信息就不自己介紹了。

    consul & registrator 

    這兩個工具是集群中每個節點的基礎服務。我在一臺機器上,所以把consul cluster給省掉了,只用了一個consul節點。下面是docker-compose配置。

    consul:
    hostname: node1
    image: progrium/consul
    command: -server -bootstrap -advertise 172.17.42.1
    ports:
     - "8400:8400"
     - "8500:8500"
     - "8600:53/udp"
    registrator:
    image: gliderlabs/registrator
    command: consul://consul:8500
    hostname: registrator1
    volumes:
     - /var/run/docker.sock:/tmp/docker.sock
    links:
     - consul 

    需要注意的是consul的啟動參數里的advertise。應該聲明為host機器的docker0的ip。如果不聲明的話會默認使用容器ip,這樣registrator注冊的設備ip都是不可訪問的。

    以上配置啟動之后一套自動服務器發現功能就算完工了。

    接下來,我們配置一個web應用測試一下。

    web:
        image: nginx
        volumes:
             - ./sites-enabled:/etc/nginx/conf.d
        ports:
             - "80:80"
        links:
             - rails
    rails:
        image: tutum/hello-world
        ports:
             - "80"

    這個配置生成了nginx和rails,并且掛載了本地的sites-enabled文件夾作為nginx的配置來源。注意rails的ports是隨機的。

    在sites-enabled中配置nginx server已rails作為backend。

    upstream backend {
    }
    server {
        server_name "10.0.2.15";
        location / {
                proxy_pass http://backend;
        }
    } 

    對你并沒有看錯, upstream并沒有定義可用的backend node。這個配置將會有consul-template根據服務列表自動填充。

    讓我們啟動這個web應用,訪問80端口是無法訪問的 應為沒有backend node。

    接下來需要安裝consule-template, 在github下載對應的release。

    然后創建一個配置文件ct-nginx.conf

    consul = "127.0.0.1:8500"
    log_level = "warn"

    template {     source = "/home/vagrant/nginx.ctmpl"     destination = "/home/vagrant/sites-enabled/backend.conf"     command = "docker-compose -f /home/vagrant/docker-compose.yml restart web" } </pre>
    我有映射consul的端口,所以直接使用了127.0.0.1。source就是我們配置文件的模板。如下

    upstream backend {
        {{range service "hello-world"}}
        server {{.Address}}:{{.Port}};{{end}}
    }
    server {
        server_name "10.0.2.15";
        location / {
                proxy_pass http://backend;
        }
    } 

    consul-template提供了方便的模板查詢語句,可以直接從consul中查詢并渲染出配置文件。這個模板中就找出了所有名字為“hello-world”的service,并且循環輸出service對應的Address,也就是服務ip,Port。

    接著看ct-nginx.conf,destination代表模板生成完畢之后的位置, 這里直接放在nginx的配置文件夾,最后command是生成配置后的動作,可以重啟各種服務。我們這里在更新nginx之后重啟生效。

    OK,配置已經妥當,consul和registrator也已經啟動, 讓我們看看當前的服務列表。

    $ curl localhost:8500/v1/catalog/service/hello-world?pretty
    $ []

    當前并沒有注冊名為hello_world的服務。

    稍微提一下registrator注冊服務的命名機制,registaor默認是通過提取容器各種參數來生成的的服務參數
    比如

    docker run -d --name nginx.0 -p 4443:443 -p 8000:80 progrium/nginx

    生成的服務信息如下

    [
    {
        "ID": "hostname:nginx.0:443",
        "Name": "nginx-443",
        "Port": 4443,
        "IP": "192.168.1.102",
        "Tags": [],
        "Attrs": {},
    },
    {
        "ID": "hostname:nginx.0:80",
        "Name": "nginx-80",
        "Port": 8000,
        "IP": "192.168.1.102",
        "Tags": [],
        "Attrs": {}
    }
    ]

    當然也可以通過環境變量來指定

    $ docker run -d --name nginx.0 -p 4443:443 -p 8000:80 \
               -e "SERVICE_443_NAME=https" \
               -e "SERVICE_443_ID=https.12345" \
               -e "SERVICE_443_SNI=enabled" \
               -e "SERVICE_80_NAME=http" \
               -e "SERVICE_TAGS=www" progrium/nginx

    [
    {
        "ID": "https.12345",
        "Name": "https",
        "Port": 4443,
        "IP": "192.168.1.102",
        "Tags": ["www"],
        "Attrs": {"sni": "enabled"},
    },
    {
        "ID": "hostname:nginx.0:80",
        "Name": "http",
        "Port": 8000,
        "IP": "192.168.1.102",
        "Tags": ["www"],
        "Attrs": {}
    }
    ]

    接下來我們啟動consul-template

    $ consul-template -config=./ct-nginx.conf

    會直接根據模板生成一個nginx配置 雖然現在并沒有backend node。

    $ cat /home/vagrant/sites-enabled/backend.conf
    upstream backend {
    }
    server {
        server_name "10.0.2.15";
        location / {
                proxy_pass http://backend;
        }
    } 

    然后啟動我們的web應用。
    啟動之后,請求服務列表就可以看到helle-world服務,說明已經實現自動服務發現。

    $ curl localhost:8500/v1/catalog/service/hello-world?pretty
    [
    {
        "Node": "node1",
        "Address": "172.17.42.1",
        "ServiceID": "registrator1:vagrant_rails_1:80",
        "ServiceName": "hello-world",
        "ServiceTags": null,
        "ServiceAddress": "",
        "ServicePort": 32783
    }
    ] 

    瀏覽器中可以正常訪問了,Yeah!

     基于 Docker 的高可用 Rails 集群方案探索

    OK, 讓我們更進一步,scale我們的rails node。

    $ docker-compose scale rails=4

    $ curl localhost:8500/v1/catalog/service/hello-world?pretty [ {     "Node": "node1",     "Address": "172.17.42.1",     "ServiceID": "registrator1:vagrant_rails_1:80",     "ServiceName": "hello-world",     "ServiceTags": null,     "ServiceAddress": "",     "ServicePort": 32783 }, {     "Node": "node1",     "Address": "172.17.42.1",     "ServiceID": "registrator1:vagrant_rails_2:80",     "ServiceName": "hello-world",     "ServiceTags": null,     "ServiceAddress": "",     "ServicePort": 32784 }, {     "Node": "node1",     "Address": "172.17.42.1",     "ServiceID": "registrator1:vagrant_rails_3:80",     "ServiceName": "hello-world",     "ServiceTags": null,     "ServiceAddress": "",     "ServicePort": 32785 }, {     "Node": "node1",     "Address": "172.17.42.1",     "ServiceID": "registrator1:vagrant_rails_4:80",     "ServiceName": "hello-world",     "ServiceTags": null,     "ServiceAddress": "",     "ServicePort": 32786 } ] </pre>
    此時nginx已經重啟,可以看到4個backend都開始處理請求。

     基于 Docker 的高可用 Rails 集群方案探索

    可以看到,rails服務器只要啟動之后就自動加入集群,如果一臺節點掛掉之后,會自動從集群里去掉,基本上沒有任何影響。

    肯定有不足的地方,歡迎討論, 發出來就是為了學習。

    原帖地址

 本文由用戶 er74 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!