使用 Etcd 和 Haproxy 做 Docker 服務發現

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

使用 Etcd 和 Haproxy 做 Docker 服務發現

標籤(空格分隔): Etcd Haproxy Docker 服務發現 architecture discovery docker-gen golang service

本文作者是 jwilder,本文的原文是 Docker Service Discovery Using Etcd and Haproxy

在前一篇文章中,我們展示了一種為 Docker 容器在同一台主機上建立一個自動化 Nginx 反向 Proxy的方式。那個設定對於前端 web app 來說工作的很好,但是對於後端服務來說它不是一個好的點子,因為通常它們跨越多個主機。

這篇文章描述了一個為後端服務的 Docker 容器提供服務發現的解決方案。

我們將構建的架構體系是模仿 SmartStack,但是使用 etcd 代替 Zookeeper,和兩個 docker 容器運行 docker-gen 和 haproxy 代替 nerve 和 synapse。

它怎樣工作的

類似於 SmartStack,我們的元件服務作為一個註冊(etcd),一個註冊夥伴進程(docker-register),發現夥伴進程(docker-discover),一些後端服務(whoami)以及最後一個消費者(ubuntu/curl)。

註冊和發現組件作為裝置與應用程式容器工作,因此在後端或消費者容器的註冊或發現代碼不是被嵌入的。它們僅僅監聽連接埠或串連其他本地連接埠。

服務註冊 - Etcd

在任何事情被註冊之前,我們需要一些地方跟蹤註冊條目(比如,服務的 IP 和連接埠)。我們使用 etcd,因為它由服務註冊的簡單程式模型和支援鍵的 TTLs 以及目錄。

通常,你將運行 3到5個 etcd 節點,但是我們僅僅使用一個來保持事情簡化。

沒有理由為什麼我們不能使用 Consul 或任何其他儲存選項支援 TTL 到期。

開始 etcd:

docker run -d --name etcd -p 4001:4001 -p 7001:7001 coreos/etcd

服務註冊 - docker-register

註冊服務容器被 jwilder/docker-register 容器處理。這個容器註冊其他運行在同一台主機上的容器到 etcd 中。我們想註冊的容器必須暴露一個連接埠。容器在不同的主機上運行相同的鏡像是在 etcd 中被分組並將構成一個負載平衡叢集。容器怎樣分組是有點亂的,為這個演練我已經選擇了容器鏡像名字。在一個真實的部署中,你可能想通過環境變數,服務版本或其他的中繼資料分組。

(當前的實現僅僅支援每個容器一個連接埠並假設它當前是 TCP,沒有理由為什麼不能支援多個連接埠和類型以及不同的分組屬性)

docker-register 使用 docker-gen連同一個 Python 指令碼作為一個模板。當啟動並執行時候,動態產生一個指令碼,將在 /backends 目錄註冊每個容器的 IP 和連接埠。

docker-gen 關注監控 docker events和調用在一個間隔調用產生指令碼來確保 TTLs 始終在最近的日期,如果 docker-register 停止了,註冊到期。

為了啟動 docker-register,我們需要傳遞主機的外部 IP,其他的主機能訪問它的容器以及你的 etcd 主機的地址。 為了調用它的 API,docker-gen 要求訪問 docker daemon,因此我們也綁定掛載 docker 的 unix socket 到容器中。

HOST_IP=$(hostname --all-ip-addresses | awk '{print $1}')ETCD_HOST=w.x.y.z:4001docker run --name docker-register -d -e HOST_IP=$HOST_IP -e ETCD_HOST=$ETCD_HOST -v /var/run/docker.sock:/var/run/docker.sock -t jwilder/docker-register

服務發現 - docker-discover

服務發現被 jwilder/docker-discover 容器處理。 docker-discover 周期性的投票 etcd 並通過監聽每個服務類型來產生一個 haproxy 設定檔。

比如,容器運行 jwilder/whoami 被註冊在 /backends/whoami/<id>以及被暴露在主機上的連接埠是 8000。

其他的容器需要調用 jwilder/whoami 服務,可以發送請求到 docker bridge IP:8000 或主機 IP:8000。

如果任何的後端服務宕了,haproxy 健全狀態檢查從池子中移除它並將在一台健康的主機上嘗試請求。這確保後端服務可以隨著需求被啟動和停止,以及處理註冊資訊的不一致同時確保最小化的用戶端影響。

最後,stats 可以通過在 docker-discover 容器上訪問連接埠 1936 來查看。

運行 docker-discover:

ETCD_HOST=w.x.y.z:4001docker run -d --net host --name docker-discover -e ETCD_HOST=$ETCD_HOST -p 127.0.0.1:1936:1936 -t jwilder/docker-discover

我們正在使用 --net host 以至於容器使用主機網路棧。當這個容器綁定 8000 連接埠,它實際是綁定在主機的網路上。這個簡化了代理的設定。

AWS Demo

我們將在 4 台 AWS 主機上運行整套服務:一台 etcd 主機, 一台 client 主機 和 兩台後端主機。後端服務 是一個簡單的返回主機名稱的 golang HTTP 服務。

Etcd 主機

首先,我們啟動 etcd 註冊:

$ hostname --all-ip-addresses | awk '{print $1}'10.170.71.226$ docker run -d --name etcd -p 4001:4001 -p 7001:7001 coreos/etcd

我們的 etcd 地址是 10.170.71.226。我們將在其他主機上使用它。如果我們正在啟動並執行是一個線上環境,我們可以分配一個 EIP 和 DNS 地址給它使得它更容易配置。

後端主機

下一步,我們在每台主機上啟動這個服務和 docker-register。該服務被配置成監聽容器中的 8000 連接埠並且我們讓 docker 把它發布在一台主機上的隨機連接埠。

後端主機 1:

$ docker run -d -p 8000:8000 --name whoami -t jwilder/whoami736ab83847bb12dddd8b09969433f3a02d64d5b0be48f7a5c59a594e3a6a3541$ docker run --name docker-register -d -e HOST_IP=$(hostname --all-ip-addresses | awk '{print $1}') -e ETCD_HOST=10.170.71.226:4001 -v /var/run/docker.sock:/var/run/docker.sock -t jwilder/docker-register77a49f732797333ca0c7695c6b590a64a7d75c14b5ffa0f89f8e0e21ae47ae3e$ docker psCONTAINER ID        IMAGE                            COMMAND                CREATED             STATUS              PORTS                     NAMES736ab83847bb        jwilder/whoami:latest            /app/http              48 seconds ago      Up 47 seconds       0.0.0.0:49153->8000/tcp   whoami77a49f732797        jwilder/docker-register:latest   "/bin/sh -c 'docker-   28 minutes ago      Up 28 minutes                                 docker-register

後端主機 2:

$ docker run -d -p 8000:8000 --name whoami -t jwilder/whoami4eb0498e52076275ee0702d80c0d8297813e89d492cdecbd6df9b263a3df1c28$ docker run --name docker-register -d -e HOST_IP=$(hostname --all-ip-addresses | awk '{print $1}') -e ETCD_HOST=10.170.71.226:4001 -v /var/run/docker.sock:/var/run/docker.sock -t jwilder/docker-register832e77c83591cb33bba53859153eb91d897f5a278a74d4ec1f66bc9b97deb221$ docker psCONTAINER ID        IMAGE                            COMMAND                CREATED             STATUS              PORTS                     NAMES4eb0498e5207        jwilder/whoami:latest            /app/http              59 seconds ago      Up 58 seconds       0.0.0.0:49154->8000/tcp   whoami832e77c83591        jwilder/docker-register:latest   "/bin/sh -c 'docker-   34 minutes ago      Up 34 minutes                                 docker-register

用戶端主機

在用戶端主機,我們需要啟動 docker-discover 和一個用戶端服務。對於這個用戶端容器,我使用 Ubuntu Trusty 並將做一些 curl 請求。

首先,啟動 docker-discover:

$ docker run -d --net host --name docker-discover -e ETCD_HOST=10.170.71.226:4001 -p 127.0.0.1:1936:1936 -t jwilder/docker-discover

然後,啟動一個簡單的用戶端容器並傳給它 HOST_IP。我們正在使用 eth0 地址,但也可以使用 docker0 IP。我們正以一個環境變數傳給它因為它是被配置的在兩個部署之間變化的。

$ docker run -e HOST_IP=$(hostname --all-ip-addresses | awk '{print $1}') -i -t ubuntu:14.04 /bin/bash$ root@2af5f52de069:/# apt-get update && apt-get -y install curl

這時,構造一些請求給 whoami 服務連接埠 8000 來看他們的負載。

$ root@2af5f52de069:/# curl $HOST_IP:8000I'm 4eb0498e5207$ root@2af5f52de069:/# curl $HOST_IP:8000I'm 736ab83847bb$ root@2af5f52de069:/# curl $HOST_IP:8000I'm 4eb0498e5207$ root@2af5f52de069:/# curl $HOST_IP:8000I'm 736ab83847bb

我們可以在後端啟動一些執行個體:

$ docker run -d -p :8000 --name whoami-2 -t jwilder/whoami$ docker run -d -p :8000 --name whoami-3 -t jwilder/whoami$ docker psCONTAINER ID        IMAGE                            COMMAND                CREATED             STATUS              PORTS                     NAMES5d5c12c96192        jwilder/whoami:latest            /app/http              3 seconds ago       Up 1 seconds        0.0.0.0:49156->8000/tcp   whoami-2bb2a408b8ec5        jwilder/whoami:latest            /app/http              21 seconds ago      Up 20 seconds       0.0.0.0:49155->8000/tcp   whoami-34eb0498e5207        jwilder/whoami:latest            /app/http              2 minutes ago       Up 2 minutes        0.0.0.0:49154->8000/tcp   whoami832e77c83591        jwilder/docker-register:latest   "/bin/sh -c 'docker-   36 minutes ago      Up 36 minutes                                 docker-register

然後再次在用戶端主機上構造一些請求:

$ root@2af5f52de069:/# curl $HOST_IP:8000I'm 736ab83847bb$ root@2af5f52de069:/# curl $HOST_IP:8000I'm 4eb0498e5207$ root@2af5f52de069:/# curl $HOST_IP:8000I'm bb2a408b8ec5$ root@2af5f52de069:/# curl $HOST_IP:8000I'm 5d5c12c96192$ root@2af5f52de069:/# curl $HOST_IP:8000I'm 736ab83847bb

最後,我們關閉一些容器,路由將被更新。這個殺死在後端 2 的任何東西。

$ docker kill 5d5c12c96192 bb2a408b8ec5 4eb0498e5207$ root@2af5f52de069:/# curl $HOST_IP:8000I'm 736ab83847bb$ root@2af5f52de069:/# curl $HOST_IP:8000I'm 67c3cccbb8ba$ root@2af5f52de069:/# curl $HOST_IP:8000I'm 736ab83847bb$ root@2af5f52de069:/# curl $HOST_IP:8000I'm 67c3cccbb8ba

如果你想看 haproxy 是怎樣負載流量的或監控錯誤,我們可以在 網頁瀏覽器訪問用戶端主機的 1936 連接埠。

總結

雖然有不同的方式來實現服務發現,SmartStack 的夥伴註冊行為和代理保持應用程式代碼簡單以及非常容易的融合進一個分布式環境,真的適合 Docker 容器。

同樣地,Docker 的事件和容器 APIs 減輕了服務註冊和使用註冊服務發現(比如 etcd)的困難。

docker-register 和 docker-discover 的代碼在 github 上。雖然兩個都是有用的,但是有很多地方需要提升。請隨時提交或提出改進意見。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.