出品丨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 的流量:
- 在 UCP UI 中,转至管理员设置 -> 日志。
- 将日志严重级别设置为调试。
- 更新 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 网络模型的以下类型的应用:
- HTTP 路由
- 粘性会话
- HTTPS
- 多个端口
- 重定向
要练习这些展示服务发现和负载均衡的示例,必须满足以下条件:
- 安装了具有 UCP 客户端证书包的 Docker 客户端,而且其可以与 UCP 集群通信。
- 指向负载均衡器的 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} 变量。
HRM 一旦发现新建服务,就会在 UCP 中将其列出。转至管理员设置 -> 网格路由。新应用应在已配置的主机下列出。
HRM 服务部署详解
HRM 每 30 秒进行一次轮询,因此应用一启动,HRM 就会对新服务进行轮询并在 http://发现它。
部署 compose 文件时,会出现以下情况:
- 将创建两个服务,前端 Spring Boot 应用和存储会话数据的 Redis 服务。
- 将创建特定于此特定应用应用栈的新 overlay 网络,名称为_backend。
- 该 Redis 任务将创建 redis 的 DNS 记录(在 backend 网络上)。该 DNS 记录指向 Redis 容器的 IP 地址。
-前端应用的配置无需在访问 Redis 时为每个应用栈更改。对于每个环境,在应用配置中,它都保持为 spring.redis.host=redis。 - 然后前端服务名称的 DNS 条目 session-example 将在 ucp-hrm 网络上注册。
- 前端服务将创建并将连接到 ucp-hrm 服务。
- 已声明的服务的良好运行状态为 5 个从节点,因此将会创建 5 个从节点任务。
- HRM 每 30 秒对 Docker 事件进行轮询,并且它将拾取新建服务上的 com.docker.ucp.mesh.http.8080 标记。
- HRM 将创建一个条目,使所有的 5 个前端从节点根据为应用栈部署而传递的 $DOMAIN 环境变量进行负载均衡。
- 通过在 Web 浏览器中刷新 [http://$DOMAIN](http://$DOMAIN "http://$DOMAIN"),每次请求发送后都会显示新的主机名。它会在所有前端服务从节点间进行负载均衡。
- 单击链接登录。凭据是用户名: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
访问示例应用,网址为 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,sticky_sessions=SESSION
networks:
back-end:
driver: overlay
ucp-hrm:
external:
name: ucp-hrm
通过 UI 部署时,请务必替换 ${DOMAIN} 变量。
HRM 粘性会话详解
访问和登录应用时,您应看到与下图类似的页面。
会话示例屏幕截图
这与 HTTP 路由示例相同,但是具有附加的 sticky_sessions=SESSION 键值条目。
将 sticky_sessions 添加到 com.docker.ucp.mesh.http 具有什么作用?
- HRM 将创建一个条目,使所有 5 个前端从节点的 IP 都添加到配置中。除该配置之外,还将添加作为持久化的基础的会话 cookie 的名称。
- 在 Web 浏览器中加载 [http://$DOMAIN](http://$DOMAIN "http://$DOMAIN"),并使用用户名:user、密码:password 登录。对于粘性会话,刷新页面应显示同一后端服务器。到后端实例的连接将基于 SESSION cookie 的值持久存储。
该演示应用使用 Redis 作为会话数据的分布式存储。可以通过在 UCP UI 中打开到 Redis 容器的控制台来查看存储在 Redis 中的 SESSION cookie。
- 登录 UCP UI。
- 在左侧导航窗格中单击应用栈和应用。
- 从列表中选择应用栈。
- 从列表中选择 redis 服务。
- 单击顶部的任务。
- 选择 Redis 容器。
- 单击顶部的控制台。
- 使用 sh 来连接到控制台应用栈。
- 在控制台上运行 redis-cli keys "*"。
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 也会自动将其添加到负载均衡器池。借助这些功能,组织可以在更短的时间内交付高度可用的应用。