天天看點

最佳實踐系列丨Docker EE 服務發現參考架構(三)

最佳實踐系列丨Docker EE 服務發現參考架構(三)

出品丨Docker公司(ID:docker-cn)

編譯丨小東

每周一、三、五晚6點10分 與您不見不散

服務發現對服務進行注冊并釋出其連接配接資訊,以使其他服務了解如何連接配接到服務。随着應用向微服務和面向服務的架構轉變,服務發現已經成為所有分布式系統的必要組成部分,增加了這些環境的運維複雜性。點選以下标題,回顧第一部分内容:

HRM 用法

現在您已經了解了 HRM 的工作原理和與它相關的要求,本部分将介紹用于 HTTP 路由、日志記錄、監控和從節點的 HRM 文法。

HTTP 路由

服務必須包含一個标記,其标記鍵以 com.docker.ucp.mesh.http 開頭。如果服務需要公開多個端口,那麼可以使用多個标記,例如,com.docker.ucp.mesh.http.80 和 com.docker.ucp.mesh.http.443。80 和 443 用來通過端口号差別 HRM 标記。您可以使用任何值,隻要確定它們彼此不同并且您可以跟蹤它們即可。

連接配接到 HRM 使用的服務的标記鍵必須以 com.docker.ucp.mesh.http 開頭,例如,com.docker.ucp.mesh.http.80 和 com.docker.ucp.mesh.http.443。

标記的值是以逗号分隔的鍵/值對(以等号分隔)清單。可以使用以下對:

  • external_route (必需) — 要路由到該服務的外部 URL。示例: http://myapp.example.com sni://myapp.example.com
  • internal_port — 用于服務的内部端口。如果服務釋出多個端口,此為必需。示例:80 和 8443
  • sticky_sessions — 如果存在,使用該命名的 cookie 來将使用者路由到此服務的相同後端任務。将會在本文檔後面的 Sticky Sessions 部分對它進行詳細說明。
  • redirect — 如果存在,執行到指定 URL 的重定向。請參閱本文檔後面的重定向部分。

日志記錄

可通過執行以下步驟來記錄經過 HRM 的流量:

  1. 在 UCP UI 中,轉至管理者設定 -> 日志。
  2. 将日志嚴重級别設定為調試。
    最佳實踐系列丨Docker EE 服務發現參考架構(三)
  3. 更新 HRM 伺服器來使用任何可用的 Docker 日志記錄驅動程式。以下是使用系統日志驅動程式的示例:
docker service update --log-driver=syslog --log-opt syslog-address=udp://:514 ucp-hrm
           

監 控

要從前端負載均衡器監控 HRM,使用 TCP 運作狀況檢查将負載均衡器設定為監控叢集上的已公開的 HRM 端口。如果将 HRM 配置為偵聽預設端口 80 和 8443,那麼前端負載均衡器僅需要對其池中的所有節點執行 TCP 運作狀況檢查即可。

HRM HA 注意事項

本部分将讨論 HRM 的幾個使用注意事項。

如果使用粘性會話功能,無法在所有從節點間共享被 HRM 用于實作持久化的 stick table — 是以,隻能運作 HRM 的一個從節點。換句話說,如果使用基于 cookie 的持久化,HRM 隻能作為一個從節點運作。

如果僅使用 HTTP 路由(無粘性會話)和 HTTPS 路由,那麼出于 HA 目的,可将 HRM 擴充到多個從節點。

如果無需使用基于 cookie 的持久化,可将 HRM 服務擴充到多個從節點。例如,要使用 3 個從節點:

docker service update --replicas 3 ucp-hrm
           

HRM 用法示例

本部分将介紹使用所有可用的 HRM 網絡模型的以下類型的應用:

  • 粘性會話
  • HTTPS
  • 多個端口
  • 重定向

要練習這些展示服務發現和負載均衡的示例,必須滿足以下條件:

  1. 安裝了具有 UCP 用戶端證書包的 Docker 用戶端,而且其可以與 UCP 叢集通信。
  2. 指向負載均衡器的 DNS 位于 UCP 叢集前端。如果無負載均衡器可供使用,那麼請将本地主機檔案中的條目指向 UCP 叢集中的主機。如果要直接連接配接到 UCP 叢集中的主機,請使用已釋出的 HRM 端口(預設 80 和 8443)連接配接它們。

注:可在 GitHub上找到示例應用的鏡像倉庫。

HTTP 路由示例

考慮展示 Docker EE 中的服務發現和負載均衡的示例應用。

要部署應用應用棧,在加載了 UCP 用戶端證書包的情況下運作以下指令:

$ wget https://raw.githubusercontent.com/ahromis/spring-session-docker-demo/master/ucp-demo/docker-compose.hrm.http.yml

$ DOMAIN=docker stack deploy -c docker-compose.hrm.http.yml hrm-http-example
           

然後通路示例應用,網址為

http://

,并使用使用者名:user 和密碼:password 登入。

如果您僅想複制/粘貼到 UCP UI,此為 Compose 檔案的内容:

version: “3.1"

services:

   redis:

     image: redis:3.0.7

     hostname: redis

     networks:

       - back-end

     deploy:

       mode: replicated

       replicas:1

   session-example:

     image: ahromis/session-example:0.1

     ports:

       - 8080

     networks:

       - back-end

       - ucp-hrm

     deploy:

       mode: replicated

       replicas:5

       labels:

         - com.docker.ucp.mesh.http.8080=external_route=http://${DOMAIN},internal_port=8080

networks:

 back-end:

   driver: overlay

 ucp-hrm:

   external:

     name: ucp-hrm
           

可通過 UCP UI 轉至資源 -> 應用棧和應用 -> 部署進行部署。對應用棧進行命名,然後複制上面的 compose 檔案并将其粘貼到開放文本字段。通過 UI 部署時,請務必替換 ${DOMAIN} 變量。

最佳實踐系列丨Docker EE 服務發現參考架構(三)

HRM 一旦發現建立服務,就會在 UCP 中将其列出。轉至管理者設定 -> 網格路由。新應用應在已配置的主機下列出。

最佳實踐系列丨Docker EE 服務發現參考架構(三)

HRM 服務部署詳解

HRM 每 30 秒進行一次輪詢,是以應用一啟動,HRM 就會對新服務進行輪詢并在

發現它。

部署 compose 檔案時,會出現以下情況:

  1. 将建立兩個服務,前端 Spring Boot 應用和存儲會話資料的 Redis 服務。
  2. 将建立特定于此特定應用應用棧的新 overlay 網絡,名稱為_backend。
  3. 該 Redis 任務将建立 redis 的 DNS 記錄(在 backend 網絡上)。該 DNS 記錄指向 Redis 容器的 IP 位址。

    -前端應用的配置無需在通路 Redis 時為每個應用棧更改。對于每個環境,在應用配置中,它都保持為 spring.redis.host=redis。

  4. 然後前端服務名稱的 DNS 條目 session-example 将在 ucp-hrm 網絡上注冊。
  5. 前端服務将建立并将連接配接到 ucp-hrm 服務。
  6. 已聲明的服務的良好運作狀态為 5 個從節點,是以将會建立 5 個從節點任務。
  7. HRM 每 30 秒對 Docker 事件進行輪詢,并且它将拾取建立服務上的 com.docker.ucp.mesh.http.8080 标記。
  8. HRM 将建立一個條目,使所有的 5 個前端從節點根據為應用棧部署而傳遞的 $DOMAIN 環境變量進行負載均衡。
  9. 通過在 Web 浏覽器中重新整理 [ $DOMAIN](http://$DOMAIN "http://$DOMAIN"),每次請求發送後都會顯示新的主機名。它會在所有前端服務從節點間進行負載均衡。
  10. 單擊連結登入。憑據是使用者名:user、密碼:password 登入。

HRM 粘性會話示例

HTTP 網格路由能夠基于命名的 cookie 路由到特定的後端服務。例如,如果應用使用名稱為 JSESSIONID 的 cookie 作為會話 cookie,您可以通過将 sticky_sessions=JSESSIONID 傳遞到 HRM 标記來持久存儲到特定服務從節點任務的連接配接。在 HRM 中,粘性連接配接通過使用 stick table 來完成,其中,HRM 将發現并使用應用會話 cookie 來持久存儲與特定後端從節點的連接配接。

為何需要使用基于 cookie 的持久化?它可以減少負載均衡器上的負載。負載均衡器拾取後端池中的特定執行個體并保持連接配接,而非必須在有新請求時重新路由。另外一個應用場景可以針對滾動部署。當将新的應用伺服器引入負載均衡器池中時,新執行個體不會産生驚群效應。相反,随着會話到期它會謹慎地将到新執行個體的連接配接轉變為負載均衡。

總體而言,粘性會話更适合于提高緩存性能和減少系統特定方面的負載。如果因為應用未使用分布式存儲,需要每次都命中同一後端,那麼當 Swarm Mode 重新排程任務時,您将在未來碰到更多問題。請務必在使用應用基于 cookie 的持久化時牢記這一點。

注:使用 HTTPS 時粘性會話将不可用,因為 cookie 的值将在 HTTP 标頭中加密。

要為粘性會話部署示例應用應用棧,在加載 UCP 用戶端證書包的情況下運作以下指令:

wget https://raw.githubusercontent.com/ahromis/spring-session-docker-demo/master/ucp-demo/docker-compose.hrm.sticky.yml

DOMAIN=docker stack deploy -c docker-compose.hrm.http.yml hrm-sticky-example
           

通路示例應用,網址為

,并使用使用者名:user、密碼:password 登入。

如果您想複制/粘貼到 UCP UI 中,此為 compose 檔案的内容:

version: "3.1"

services:

   redis:

     image: redis:3.0.7

     hostname: redis

     networks:

       - back-end

     deploy:

       mode: replicated

       replicas:1

   session-example:

     image: ahromis/session-example:0.1

     ports:

       - 8080

     networks:

       - back-end

       - ucp-hrm

     deploy:

       mode: replicated

       replicas:5

       labels:

         - com.docker.ucp.mesh.http.8080=external_route=http://${DOMAIN},internal_port=8080,sticky_sessions=SESSION

networks:

 back-end:

   driver: overlay

 ucp-hrm:

   external:

     name: ucp-hrm
           

通過 UI 部署時,請務必替換 ${DOMAIN} 變量。

HRM 粘性會話詳解

通路和登入應用時,您應看到與下圖類似的頁面。

最佳實踐系列丨Docker EE 服務發現參考架構(三)

會話示例螢幕截圖

這與 HTTP 路由示例相同,但是具有附加的 sticky_sessions=SESSION 鍵值條目。

将 sticky_sessions 添加到 com.docker.ucp.mesh.http 具有什麼作用?

  1. HRM 将建立一個條目,使所有 5 個前端從節點的 IP 都添加到配置中。除該配置之外,還将添加作為持久化的基礎的會話 cookie 的名稱。
  2. 在 Web 浏覽器中加載 [ $DOMAIN](http://$DOMAIN "http://$DOMAIN"),并使用使用者名:user、密碼:password 登入。對于粘性會話,重新整理頁面應顯示同一後端伺服器。到後端執行個體的連接配接将基于 SESSION cookie 的值持久存儲。

該示範應用使用 Redis 作為會話資料的分布式存儲。可以通過在 UCP UI 中打開到 Redis 容器的控制台來檢視存儲在 Redis 中的 SESSION cookie。

  1. 登入 UCP UI。
  2. 在左側導航窗格中單擊應用棧和應用。
  3. 從清單中選擇應用棧。
  4. 從清單中選擇 redis 服務。
  5. 單擊頂部的任務。
  6. 選擇 Redis 容器。
  7. 單擊頂部的控制台。
  8. 使用 sh 來連接配接到控制台應用棧。
  9. 在控制台上運作 redis-cli keys "*"。
最佳實踐系列丨Docker EE 服務發現參考架構(三)

HRM HTTPS 示例

HTTP 網格路由支援使用 HTTPS 的路由。使用稱作伺服器名稱訓示的 HTTPS 功能,HRM 無需終止 HTTPS 連接配接就能夠将連接配接路由到服務後端。SNI 是 TLS 協定的一個擴充,其中,用戶端在握手過程開始的時候就訓示它嘗試連接配接到的主機名。

要使用 HTTPS 支援,無需向 HTTP 網格路由提供服務的證書。相反,後端服務必須直接處理 HTTPS 連接配接。滿足該條件的服務才可使用 SNI 協定來訓示以這種方式進行 HTTPS 處理。由于到應用伺服器才終止,加密流量可以一直流向應用。但是,這也意味着應用必須管理 TLS 證書。通過利用 Docker 涉密資訊,可以輕松安全地管理應用伺服器的證書。

将 HRM 和 HTTPS 搭配使用時,會重複使用連接配接以降低重新協商新的 TLS 連接配接的開銷。通過使用 SSL 會話 ID 可重複使用 HTTPS 連接配接,它們将持久存儲到服務任務,直至需要重建立立連接配接(即,應用伺服器連接配接逾時)。

處理加密通信時幾乎一定會用到涉密資訊和證書。在部署示例應用前,請先建立 Java 密鑰存儲。

提示:下面是一些有用的指令,可建立可用于 Java 應用的密鑰存儲。

# create PKCS#12 file format

$ openssl pkcs12 -export -out keystore.pkcs12 -in fullchain.pem -inkey privkey.pem

# convert PKCS file into Java keystore format

$ docker run --rm -it -v $(pwd):/tmp -w /tmp java:8 \

   keytool -importkeystore -srckeystore keystore.pkcs12 -srcstoretype PKCS12 -destkeystore keystore.jks
           

現在已經建立好了 Java 密鑰存儲,接下來就該将其轉換為 Docker 涉密資訊,這樣該應用就可以安全地使用它了。

$ docker secret create session-example_keystore.jks_v1 keystore.jks

$ echo "" | docker secret create session-example_keystore-password.txt_v1 -

$ echo "" | docker secret create session-example_key-password.txt_v1 -
           

就是這樣!現在涉密資訊就在叢集範圍内的鍵值存儲中加密了。涉密資訊采用靜态加密方法加密,将在移動到需要涉密資訊的節點的過程中使用 TLS。隻有需要使用涉密資訊的應用才可檢視涉密資訊。

提示:有關使用 Docker 涉密資訊的更多詳細資訊,請參閱涵蓋 DDC 安全和最佳實踐的參考架構。

要添加更多上下文,這是示例應用使用的應用配置:

spring.redis.host=redis

spring.redis.port=6379

server.port=8443

server.ssl.key-store=file:/run/secrets/keystore.jks

server.ssl.key-store-password=${KEY_STORE_PASSWORD}

server.ssl.key-password=${KEY_PASSWORD}
           

環境變量來自 ENTRYPOINT 腳本,它将讀取涉密資訊,然後将其公開給 Spring Boot。可在該應用的 GitHub 鏡像倉庫中找到更多資訊。

現在證書已安全建立并存儲在 Docker KV 存儲中,接下來該建立能夠使用它們的服務了:

wget https://raw.githubusercontent.com/ahromis/spring-session-docker-demo/master/ucp-demo/docker-compose.hrm.ssl.yml

DOMAIN=docker stack deploy -c docker-compose.hrm.http.yml hrm-sticky-example
           
https://

。如果未使用置于 UCP 叢集前端的負載均衡器,那麼請使用

https://:8443

。然後使用使用者名:user、密碼:password 登入。

如果想從 UI 進行複制/粘貼,下面是完整的 compose 檔案(請記得替換 ${DOMAIN} 變量):

version: "3.1"

services:

   redis:

     image: redis:3.0.7

     hostname: redis

     networks:

       - back-end

     deploy:

       mode: replicated

       replicas:1

   session-example:

     image: ahromis/session-example:0.1

     ports:

       - 8443

     environment:

       - SPRING_PROFILES_ACTIVE=https

     networks:

       - back-end

       - ucp-hrm

     deploy:

       mode: replicated

       replicas:5

       labels:

         - com.docker.ucp.mesh.http.1=external_route=http://${DOMAIN},redirect=https://${DOMAIN}

         - com.docker.ucp.mesh.http.8443=external_route=sni://${DOMAIN},internal_port=8443

     secrets:

       - source: session-example_keystore.jks_v1

         target: keystore.jks

         mode:0400

       - source: session-example_keystore-password.txt_v1

         target: keystore-password.txt

         mode:0400

       - source: session-example_key-password.txt_v1

         target: key-password.txt

         mode:0400

 

networks:

 back-end:

   driver: overlay

 ucp-hrm:

   external:

     name: ucp-hrm

 

secrets:

   session-example_keystore.jks_v1:

       external: true

   session-example_keystore-password.txt_v1:

       external: true

   session-example_key-password.txt_v1:

       external: true
           

上面的示例也展示了如何通過使用 redirect 密鑰對處理從 HTTP 到 HTTPS 的重定向。

值得一提的一點是,它非常易于重複使用,即使對現有應用也是如此。隻需幾個簡單步驟就可執行 Modernize Traditional Applications (MTA),而且隻需進行少量配置更改就可多次部署該應用應用棧。

HRM 多個端口示例

有時它可偵聽服務的多個端口。借助 HRM,可獨立路由到每個偵聽端口。

下面是具有多個偵聽端口的服務的示例:

$ docker service create \

 -l com.docker.ucp.mesh.http.8000=external_route=http://site1.example.com,internal_port=8000 \

 -l com.docker.ucp.mesh.http.8001=external_route=http://site2.example.com,internal_port=8001 \

 -p 8000 \

 -p 8001 \

 --network ucp-hrm \

 --replicas 3 \

 --name twosite ahromis/nginx-twosite:latest
           

在本示例中,建立了一個 Nginx 服務,它有兩個偵聽不同端口的 Web 根目錄。HRM 基于它收到的 HTTP Host: 标頭獨立地将流量路由到每個站點。

HRM 重定向示例

當要強制使所有連接配接都成為安全連接配接時,可從 HTTP 重定向到 HTTPS。該重定向選項訓示對該路由的所有請求都應使用 HTTP 重定向重定向到其他域名。

此功能的一個用途是用于僅使用 HTTPS 進行偵聽的服務(HTTP 流量将重定向到 HTTPS)。如果該服務位于 example.com 上,那麼可以使用兩個标記來完成:

com.docker.ucp.mesh.http.1=external_route=http://example.com,redirect=https://example.com

com.docker.ucp.mesh.http.2=external_route=sni://example.com
           

另外一個用途是用于隻接收單個域上的流量,但是有其他域會重定向到它的服務。例如,一個重命名後的網站可能會使用該功能。下面的标記可為 new.example.com 和 old.example.com 實作此效果。

com.docker.ucp.mesh.http.1=external_route=http://old.example.com,redirect=http://new.example.com

com.docker.ucp.mesh.http.2=external_route=http://new.example.com
           

下面是使用不同的示例應用将網站重定向到不同的域的示例。

$ docker service create \

 -l com.docker.ucp.mesh.http.1=external_route=http://oldsite.example.com,redirect=http://foo.example.com \

 -l com.docker.ucp.mesh.http.8080=external_route=http://foo.example.com,internal_port=8080 \

 --replicas 3 \

 --name lbinfo \

  ahromis/lbinfo:latest
           

非 Swarm Mode 容器

HRM 和 swarm mode 網格路由僅支援使用“服務”部署的應用。對于非 swarm mode 容器,例如,在 1.12 版本之前的 Docker 引擎上運作的容器和未使用服務部署的應用(例如,使用 docker run),必須使用 interlock 和 NGINX。

Interlock 是容器化的、事件驅動的工具,它連接配接到 UCP 控制器并監視事件。在這種情況下,事件指的是容器激增或驟減。Interlock 還會查找這些容器具有的特定中繼資料,例如,主機名或為容器配置的标記。然後,它使用中繼資料将這些容器注冊到負載均衡後端 (NGINX) 或取消注冊。負載均衡器使用更新後的後端配置将傳入請求導向運作狀況良好的容器。Interlock 和負載均衡器容器都是無狀态的,是以,可跨多個節點對它們進行橫向擴充,以為所有已部署的應用提供高度可用的負載均衡服務。

要使用 Interlock 和 NGINX,容器需要滿足三個要求:

I. 需要在一個或多個 UCP 工作節點上部署 Interlock 和 NGINX。

部署 Interlock 和 NGINX 最簡單的方法是在 UCP 門戶中使用 Docker Compose:

1.登入 UCP Web 控制台。

2.在頂部,轉至資源頁籤。

3.在左側窗格中,轉至應用,并單擊部署 compose.yml 按鈕。

4.輸入 interlock 作為應用名稱。

5.至于 compose.yml 檔案,請輸入下面的示例 compose.yml 檔案。可按照需要更改 Interlock 或 NGINX config。可在 GitHub 上找到完整文檔。

interlock:

         image: ehazlett/interlock:1.3.0

         command:-D run

         tty: true

         ports:

             - 8080

         environment:

             constraint:node==${UCP_NODE_NAME}:

             INTERLOCK_CONFIG:|

                 ListenAddr = ":8080"

                 DockerURL = "tcp://${UCP_CONTROLLER_IP}:2376"

                 TLSCACert = "/certs/ca.pem"

                 TLSCert = "/certs/cert.pem"

                 TLSKey = "/certs/key.pem"

                 PollInterval = "10s"

                 [[Extensions]]

                 Name = "nginx"

                 ConfigPath = "/etc/nginx/nginx.conf"

                 PidPath = "/etc/nginx/nginx.pid"

                 MaxConn = 1024

                 Port = 80

         volumes:

             - ucp-node-certs:/certs

         restart: always

      nginx:

         image: nginx:latest

         entrypoint: nginx

         command:-g "daemon off;" -c /etc/nginx/nginx.conf

         ports:

             - 80:80

         labels:

             - "interlock.ext.name=nginx"

         restart: always

         environment:

             constraint:node==${UCP_NODE_NAME}:
           

注:請替換 UCP_NODE_NAME 和 UCP_CONTROLLER_IP。UCP_NODE_NAME 是要在其上運作 Interlock 和 NGINX 的節點的名稱(如資源/節點部分下所示)。應用的 DNS 名稱需要解析到該節點。UCP_CONTROLLER_IP 是一個或多個 UCP 控制器的 IP 或 DNS 名稱。

6.單擊建立來部署 Interlock 和 NGINX。

注:可通過重複上述的步驟 3-6 并更改應用名稱和 UCP_NODE_NAME 來在多個節點上部署 Interlock 和 NGINX。這使您可以在這些節點前端放置外部負載均衡器(例如,ELB 或 F5)以實作高可用性。然後需要将應用的 DNS 記錄注冊到外部負載均衡器 IP。

II.容器必須釋出一個或多個端口。

III.容器必須使用 Interlock 标記啟動。

例如,要部署公開端口 8080 且通過 DNS 名稱 demo.app.example.com 接受通路的容器,請按照下面的方法啟動它。

docker run --name demo -p 8080 --label interlock.hostname=demo --label interlock.domain=app.example.com ehazlett/docker-demo:dcus
           

使用正确的标記和已釋出端口啟動容器後,就可使用所需的 DNS 名稱通路它。

總 結

在 Docker 中,擴充和發現服務的功能比以往任何時候都更簡單。由于服務發現和負載均衡功能已内置到 Docker 中,工程師可以節約親自建立這些類型的支援功能的時間,進而将更多精力集中在應用上。無需建立調用設定的 DNS 的 API 就可實作服務發現,因為 Docker 會自動幫您處理。如果應用需要擴充,Docker 也會自動将其添加到負載均衡器池。借助這些功能,組織可以在更短的時間内傳遞高度可用的應用。

繼續閱讀