您可以将 HAProxy 作为 Docker 容器运行吗?是的!这还需要问吗?如今 Docker 无处不在,您会发现许多应用程序都已被 Docker 化;HAProxy 负载均衡器也不例外,但 HAProxy 就是为此而生的。作为在 Linux 上运行的独立服务,将其移植到 Docker 似乎很自然。
为什么要在 Docker 容器内运行负载均衡器?这样做性能会有折扣么?它会引入任何安全问题吗?
在这篇博文中,您将了解为什么要考虑在容器内运行 HAProxy 以及可能产生的后果。然后你会看到如何去做。请注意,我们介绍的是如何运行 HAProxy,而不是 HAProxy Kubernetes Ingress Controller。
HAProxy Technologies 在其命名空间 haproxytech 下构建自己的一组 Docker 镜像。这些会定期更新最新的补丁和安全更新。我将在这篇博文中使用这些镜像。你会在这里找到它们:
我演示的命令是在 Linux 工作站上执行的,如果您在使用 Docker Desktop for Windows 或 Docker Desktop for Mac 时也能正常工作。
您是否希望能够运行 HAProxy 而无需编译、安装依赖项或以其他方式更改您的系统?
Docker 容器带来了可观的好处,其中最主要的是安装和执行的操作较少。Docker 允许您将容器放到主机系统上并立即获得正在运行的服务——无需安装脚本,无需安装 C 库。该服务完全包含在容器中,您需要做的就是启动它,然后将 TCP 端口映射到它。当您部署一个容器时,您可以获得运行完整的应用程序及其运行时环境的能力,而无需将其实际安装到主机系统上。
生命周期管理也变得标准化。启动、停止和删除容器就像调用一行 docker 命令一样简单。这反过来又使部署成为一个可重复和可测试的过程。它还有助于更轻松地进行软件升级。
您希望您的负载均衡器运行速度快,且不会增加环境延迟。那么,问题是,在容器内运行 HAProxy 有什么影响?
在 CPU 开销方面,记住,与虚拟机不同,Docker 不需要在主机操作系统之上的虚拟化层。容器在主机的内核上运行,基本上只是另一个进程,尽管它与主机上运行的其他进程具有更好的隔离性(它使用命名空间来实现这一点)。IBM 研究人员的一项研究发现,使用 Docker 的 CPU 开销可以忽略不计,这应该不足为奇。
网络是另一回事。默认情况下,Docker 允许您通过创建到主机的桥接网络来访问在容器内运行的服务。由于必须在容器的本地网络和主机的桥接网络之间发生的网络地址转换 (NAT),这确实会导致延迟。在之前引用的同一 IBM 研究中,研究人员发现 Docker 的 NAT 将来自客户端的 100 字节请求和来自应用程序的 200 字节响应的延迟从大约 35 微秒增加到 70 微秒。
另一方面,桥接网络很有用,因为它们允许您将容器组隔离到容器网络中,并且仅将其中一些容器暴露给主机,这对于减少主机网络所需的 IP 地址数量非常方便(想想运行数百甚至数千个容器所需的 IP 数量)。如果您有兴趣了解有关 Docker 中网络如何工作的更多信息,可以在 YouTube 上观看 Docker 团队提供的深入探讨。
如果您需要非常低的延迟,您可以切换到使用 Docker 的主机网络功能,它允许您的容器与主机共享相同的网络,从而无需 NAT。再说一次,如果你想运行 Docker Swarm 或 Kubernetes,它们使用覆盖网络,对于不同的网络驱动程序,如 Project Calico 和 Cilium 有解决方案,本篇文章文章并不涉及该怎么做。
简而言之,除非您需要非常低的延迟,否则您应该坚持使用默认的桥接网络选项。请务必对其进行测试,看看您是否达到了所需的吞吐量。
您可能会担心许多 Docker 容器以 root 身份运行他们的服务,而这个 root 用户与主机系统上的 root 用户相同。对容器突破的担忧是合理的。HAProxy 也以 root 身份运行。但是,让您放心:HAProxy 需要 root 访问权限,因为它需要绑定到受限制的 TCP 端口,如 80 和 443。但是,一旦完成启动,它就会放弃其 root 权限并以非特权用户身份运行。
人们还会权衡容器可能是恶意的风险。这是坚持使用由 HAProxy Technologies 制作的 haproxytech Docker 镜像的一个很好的理由。
我们将创建一个 Web 应用程序的三个实例、一个 HAProxy 实例和一个将它们连接在一起的桥接网络。因此,一旦您安装了 Docker,请使用以下命令在 Docker 中创建一个新的桥接网络:
$ sudo docker network create --driver=bridge mynetwork
然后使用该 docker run 命令创建并运行 Web 应用程序的三个实例。在此示例中,我使用 Docker 镜像 jmalloc/echo-server https://hub.docker.com/r/jmalloc/echo-server
。这是一个简单的 Web 应用程序,可返回您发送给它的 HTTP 请求的详细信息。
$ 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
请注意,我们为每个服务分配了一个唯一名称并将其附加到我们创建的桥接网络。您现在应该运行了三个 Web 应用程序,您可以通过调用以下 docker ps 命令进行验证:
$ 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
这些容器监听自己的端口 8080,但我们没有将这些端口映射到主机,因此它们不可路由。我们将通过 HAProxy 负载均衡器将流量中继到这些容器。接下来,让我们在它们前面添加 HAProxy。在当前目录中创建一个名为 haproxy.cfg 的文件,并在其中添加以下内容:
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
需要注意的几点:
接下来,创建并运行一个 HAProxy 容器,并通过包含-p 参数将其端口 80 映射到主机上的相同端口。还要为 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 之后调用显示 HAProxy 正在运行:
$ 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 应用程序。每个对它的请求都将由 HAProxy 进行负载平衡。此外,您可以在http://localhost:8404看到 HAProxy Stats 页面。
如果您对 haproxy.cfg 文件进行了更改,则可以通过调用以下 docker kill 命令重新加载负载均衡器,而不会损失流量:
$ sudo docker kill -s HUP haproxy
要删除容器和网络,运行 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
在这篇博文中,您了解了如何在 Docker 容器内运行 HAProxy 简化其部署和生命周期管理。Docker 提供了一种用于部署应用程序的标准化方法,使该过程具有可重复性和可测试性。虽然运行 Docker 的 CPU 开销可以忽略不计,但它可能会导致额外的网络延迟,但其影响取决于您的场景和吞吐量需求。
要运行 HAProxy,只需创建一个 HAProxy 配置文件,然后使用 docker run 命令调用 HAProxy Docker 镜像的名称。HAProxy Technologies 在 Docker Hub 上提供最新的 Docker 镜像。