如何優(yōu)雅的在 Docker 中運(yùn)行高性能負(fù)載均衡器 HAProxy
您可以將 HAProxy 作為 Docker 容器運(yùn)行嗎?是的!這還需要問嗎?如今 Docker 無處不在,您會(huì)發(fā)現(xiàn)許多應(yīng)用程序都已被 Docker 化;HAProxy 負(fù)載均衡器也不例外,但 HAProxy 就是為此而生的。作為在 Linux 上運(yùn)行的獨(dú)立服務(wù),將其移植到 Docker 似乎很自然。
為什么要在 Docker 容器內(nèi)運(yùn)行負(fù)載均衡器?這樣做性能會(huì)有折扣么?它會(huì)引入任何安全問題嗎?
在這篇博文中,您將了解為什么要考慮在容器內(nèi)運(yùn)行 HAProxy 以及可能產(chǎn)生的后果。然后你會(huì)看到如何去做。請(qǐng)注意,我們介紹的是如何運(yùn)行 HAProxy,而不是 HAProxy Kubernetes Ingress Controller。
HAProxy Technologies 在其命名空間 haproxytech 下構(gòu)建自己的一組 Docker 鏡像。這些會(huì)定期更新最新的補(bǔ)丁和安全更新。我將在這篇博文中使用這些鏡像。你會(huì)在這里找到它們:
- HAProxy(Alpine Linux 基礎(chǔ))- https://hub.docker.com/r/haproxytech/haproxy-alpine
- HAProxy (Ubuntu 基礎(chǔ)) – https://hub.docker.com/r/haproxytech/haproxy-ubuntu
- HAProxy (Debian 基礎(chǔ)) – https://hub.docker.com/r/haproxytech/haproxy-debian
我演示的命令是在 Linux 工作站上執(zhí)行的,如果您在使用 Docker Desktop for Windows 或 Docker Desktop for Mac 時(shí)也能正常工作。
1使用 Docker 的好處
您是否希望能夠運(yùn)行 HAProxy 而無需編譯、安裝依賴項(xiàng)或以其他方式更改您的系統(tǒng)?
Docker 容器帶來了可觀的好處,其中最主要的是安裝和執(zhí)行的操作較少。Docker 允許您將容器放到主機(jī)系統(tǒng)上并立即獲得正在運(yùn)行的服務(wù)——無需安裝腳本,無需安裝 C 庫(kù)。該服務(wù)完全包含在容器中,您需要做的就是啟動(dòng)它,然后將 TCP 端口映射到它。當(dāng)您部署一個(gè)容器時(shí),您可以獲得運(yùn)行完整的應(yīng)用程序及其運(yùn)行時(shí)環(huán)境的能力,而無需將其實(shí)際安裝到主機(jī)系統(tǒng)上。
生命周期管理也變得標(biāo)準(zhǔn)化。啟動(dòng)、停止和刪除容器就像調(diào)用一行 docker 命令一樣簡(jiǎn)單。這反過來又使部署成為一個(gè)可重復(fù)和可測(cè)試的過程。它還有助于更輕松地進(jìn)行軟件升級(jí)。
2使用 Docker 的性能影響
您希望您的負(fù)載均衡器運(yùn)行速度快,且不會(huì)增加環(huán)境延遲。那么,問題是,在容器內(nèi)運(yùn)行 HAProxy 有什么影響?
在 CPU 開銷方面,記住,與虛擬機(jī)不同,Docker 不需要在主機(jī)操作系統(tǒng)之上的虛擬化層。容器在主機(jī)的內(nèi)核上運(yùn)行,基本上只是另一個(gè)進(jìn)程,盡管它與主機(jī)上運(yùn)行的其他進(jìn)程具有更好的隔離性(它使用命名空間來實(shí)現(xiàn)這一點(diǎn))。IBM 研究人員的一項(xiàng)研究發(fā)現(xiàn),使用 Docker 的 CPU 開銷可以忽略不計(jì),這應(yīng)該不足為奇。
網(wǎng)絡(luò)是另一回事。默認(rèn)情況下,Docker 允許您通過創(chuàng)建到主機(jī)的橋接網(wǎng)絡(luò)來訪問在容器內(nèi)運(yùn)行的服務(wù)。由于必須在容器的本地網(wǎng)絡(luò)和主機(jī)的橋接網(wǎng)絡(luò)之間發(fā)生的網(wǎng)絡(luò)地址轉(zhuǎn)換 (NAT),這確實(shí)會(huì)導(dǎo)致延遲。在之前引用的同一 IBM 研究中,研究人員發(fā)現(xiàn) Docker 的 NAT 將來自客戶端的 100 字節(jié)請(qǐng)求和來自應(yīng)用程序的 200 字節(jié)響應(yīng)的延遲從大約 35 微秒增加到 70 微秒。
另一方面,橋接網(wǎng)絡(luò)很有用,因?yàn)樗鼈冊(cè)试S您將容器組隔離到容器網(wǎng)絡(luò)中,并且僅將其中一些容器暴露給主機(jī),這對(duì)于減少主機(jī)網(wǎng)絡(luò)所需的 IP 地址數(shù)量非常方便(想想運(yùn)行數(shù)百甚至數(shù)千個(gè)容器所需的 IP 數(shù)量)。如果您有興趣了解有關(guān) Docker 中網(wǎng)絡(luò)如何工作的更多信息,可以在 YouTube 上觀看 Docker 團(tuán)隊(duì)提供的深入探討。
如果您需要非常低的延遲,您可以切換到使用 Docker 的主機(jī)網(wǎng)絡(luò)功能,它允許您的容器與主機(jī)共享相同的網(wǎng)絡(luò),從而無需 NAT。再說一次,如果你想運(yùn)行 Docker Swarm 或 Kubernetes,它們使用覆蓋網(wǎng)絡(luò),對(duì)于不同的網(wǎng)絡(luò)驅(qū)動(dòng)程序,如 Project Calico 和 Cilium 有解決方案,本篇文章文章并不涉及該怎么做。
簡(jiǎn)而言之,除非您需要非常低的延遲,否則您應(yīng)該堅(jiān)持使用默認(rèn)的橋接網(wǎng)絡(luò)選項(xiàng)。請(qǐng)務(wù)必對(duì)其進(jìn)行測(cè)試,看看您是否達(dá)到了所需的吞吐量。
3使用 Docker 的安全考慮
您可能會(huì)擔(dān)心許多 Docker 容器以 root 身份運(yùn)行他們的服務(wù),而這個(gè) root 用戶與主機(jī)系統(tǒng)上的 root 用戶相同。對(duì)容器突破的擔(dān)憂是合理的。HAProxy 也以 root 身份運(yùn)行。但是,讓您放心:HAProxy 需要 root 訪問權(quán)限,因?yàn)樗枰壎ǖ绞芟拗频?TCP 端口,如 80 和 443。但是,一旦完成啟動(dòng),它就會(huì)放棄其 root 權(quán)限并以非特權(quán)用戶身份運(yùn)行。
人們還會(huì)權(quán)衡容器可能是惡意的風(fēng)險(xiǎn)。這是堅(jiān)持使用由 HAProxy Technologies 制作的 haproxytech Docker 鏡像的一個(gè)很好的理由。
4使用 Docker 運(yùn)行 HAProxy
我們將創(chuàng)建一個(gè) Web 應(yīng)用程序的三個(gè)實(shí)例、一個(gè) HAProxy 實(shí)例和一個(gè)將它們連接在一起的橋接網(wǎng)絡(luò)。因此,一旦您安裝了 Docker,請(qǐng)使用以下命令在 Docker 中創(chuàng)建一個(gè)新的橋接網(wǎng)絡(luò):
- $ sudo docker network create --driver=bridge mynetwork
然后使用該 docker run 命令創(chuàng)建并運(yùn)行 Web 應(yīng)用程序的三個(gè)實(shí)例。在此示例中,我使用 Docker 鏡像 jmalloc/echo-server https://hub.docker.com/r/jmalloc/echo-server。這是一個(gè)簡(jiǎn)單的 Web 應(yīng)用程序,可返回您發(fā)送給它的 HTTP 請(qǐng)求的詳細(xì)信息。
- $ sudo docker run -d \
- --name web1 --net mynetwork jmalloc/echo-server:latest
- $ sudo docker run -d \
- --name web2 --net mynetwork jmalloc/echo-server:latest
- $ sudo docker run -d \
- --name web3 --net mynetwork jmalloc/echo-server:latest
請(qǐng)注意,我們?yōu)槊總€(gè)服務(wù)分配了一個(gè)唯一名稱并將其附加到我們創(chuàng)建的橋接網(wǎng)絡(luò)。您現(xiàn)在應(yīng)該運(yùn)行了三個(gè) Web 應(yīng)用程序,您可以通過調(diào)用以下 docker ps 命令進(jìn)行驗(yàn)證:
- $ sudo docker ps
- CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
- 98216bb8c5ff jmalloc/echo-server:latest "/bin/echo-server" About a minute ago Up About a minute 8080/tcp web3
- ae6accc111d9 jmalloc/echo-server:latest "/bin/echo-server" About a minute ago Up About a minute 8080/tcp web2
- 554fafbc2b3b jmalloc/echo-server:latest "/bin/echo-server" About a minute ago Up About a minute 8080/tcp web1
這些容器監(jiān)聽自己的端口 8080,但我們沒有將這些端口映射到主機(jī),因此它們不可路由。我們將通過 HAProxy 負(fù)載均衡器將流量中繼到這些容器。接下來,讓我們?cè)谒鼈兦懊嫣砑?HAProxy。在當(dāng)前目錄中創(chuàng)建一個(gè)名為 haproxy.cfg 的文件,并在其中添加以下內(nèi)容:
- global
- stats socket /var/run/api.sock user haproxy group haproxy mode 660 level admin expose-fd listeners
- log stdout format raw local0 info
- defaults
- mode http
- timeout client 10s
- timeout connect 5s
- timeout server 10s
- timeout http-request 10s
- log global
- frontend stats
- bind *:8404
- stats enable
- stats uri /
- stats refresh 10s
- frontend myfrontend
- bind :80
- default_backend webservers
- backend webservers
- server s1 web1:8080 check
- server s2 web2:8080 check
- server s3 web3:8080 check
需要注意的幾點(diǎn):
- 在該 global 部分中,該 stats socket 行啟用了 HAProxy 運(yùn)行時(shí) API,還啟用了 HAProxy 的無縫重新加載。
- 第一個(gè)前端監(jiān)聽端口 8404 并啟用 HAProxy Stats 儀表板,該儀表板顯示有關(guān)您的負(fù)載均衡器的實(shí)時(shí)統(tǒng)計(jì)信息。
- 另一個(gè)前端監(jiān)聽端口 80,并將請(qǐng)求分派到 Web 服務(wù)器后端中列出的三個(gè) Web 應(yīng)用程序之一。
- 我們沒有使用每個(gè) Web 應(yīng)用程序的 IP 地址,而是使用它們的主機(jī)名 web1、web2 和 web3。當(dāng)您像我們一樣創(chuàng)建 Docker 橋接網(wǎng)絡(luò)時(shí),您也可以使用這種基于 DNS 的路由。
接下來,創(chuàng)建并運(yùn)行一個(gè) HAProxy 容器,并通過包含-p 參數(shù)將其端口 80 映射到主機(jī)上的相同端口。還要為 HAProxy Stats 頁面映射端口 8404:
- $ sudo docker run -d \
- --name haproxy \
- --net mynetwork \
- -v $(pwd):/usr/local/etc/haproxy:ro \
- -p 80:80 \
- -p 8404:8404 \
- haproxytech/haproxy-alpine:2.4
docker ps 之后調(diào)用顯示 HAProxy 正在運(yùn)行:
- $ sudo docker ps
- CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
- d734d0ef2635 haproxytech/haproxy-alpine:2.4 "/docker-entrypoint.…" 3 seconds ago Up 2 seconds 0.0.0.0:80->80/tcp, 0.0.0.0:8404->8404/tcp haproxy
您可以通過http://localhost訪問echo-server Web 應(yīng)用程序。每個(gè)對(duì)它的請(qǐng)求都將由 HAProxy 進(jìn)行負(fù)載平衡。此外,您可以在http://localhost:8404看到 HAProxy Stats 頁面。
如果您對(duì) haproxy.cfg 文件進(jìn)行了更改,則可以通過調(diào)用以下 docker kill 命令重新加載負(fù)載均衡器,而不會(huì)損失流量:
- $ sudo docker kill -s HUP haproxy
要?jiǎng)h除容器和網(wǎng)絡(luò),運(yùn)行 docker stop,docker rm 和 docker network rm 命令:
- $ sudo docker stop web1 && sudo docker rm web1
- $ sudo docker stop web2 && sudo docker rm web2
- $ sudo docker stop web3 && sudo docker rm web3
- $ sudo docker stop haproxy && sudo docker rm haproxy
- $ sudo docker network rm mynetwork
5總結(jié)
在這篇博文中,您了解了如何在 Docker 容器內(nèi)運(yùn)行 HAProxy 簡(jiǎn)化其部署和生命周期管理。Docker 提供了一種用于部署應(yīng)用程序的標(biāo)準(zhǔn)化方法,使該過程具有可重復(fù)性和可測(cè)試性。雖然運(yùn)行 Docker 的 CPU 開銷可以忽略不計(jì),但它可能會(huì)導(dǎo)致額外的網(wǎng)絡(luò)延遲,但其影響取決于您的場(chǎng)景和吞吐量需求。
要運(yùn)行 HAProxy,只需創(chuàng)建一個(gè) HAProxy 配置文件,然后使用 docker run 命令調(diào)用 HAProxy Docker 鏡像的名稱。HAProxy Technologies 在 Docker Hub 上提供最新的 Docker 鏡像。