Docker 内置的 Nameserver & Loadbalancer
Docker 带有一个内置的nameserver。默认情况下,可以通过127.0.0.11:53 访问服务器。
默认情况下,每个容器在/etc/resolv.conf 中都有一个名称服务器条目,因此不需要从容器中指定名称服务器的地址。这就是为什么您可以通过service 或task_service_n 在网络内找到您的服务。
如果你做task_service_n,那么你会得到对应服务副本的地址。
如果你只要求service docker 将在同一网络中的容器之间执行internal load balancing 和external load balancing 来处理来自外部的请求。
当使用 swarm 时,docker 会额外使用两个特殊的网络。
-
ingress network,实际上是一个overlay network,负责处理群的传入流量。它允许从 swarm 中的任何节点查询任何服务。
-
docker_gwbridge,bridge network,将各个主机的覆盖网络连接到它们的物理网络。 (包括入口)
当使用 swarm 到 deploy services 时,除非 endpointmode 设置为 dns roundrobin 而不是 vip,否则下面示例中描述的行为将不起作用。
endpoint_mode: vip - Docker 为服务分配一个虚拟 IP (VIP),作为客户端访问网络上服务的前端。 Docker 在客户端和服务的可用工作节点之间路由请求,而客户端不知道有多少节点参与服务或它们的 IP 地址或端口。 (这是默认设置。)
endpoint_mode: dnsrr - DNS 循环 (DNSRR) 服务发现不使用单个虚拟 IP。 Docker 为服务设置 DNS 条目,以便对服务名称的 DNS 查询返回 IP 地址列表,客户端直接连接到其中一个。如果您想使用自己的负载平衡器,或者对于混合 Windows 和 Linux 应用程序,DNS 循环非常有用。
示例
例如从 dig/docker-compose.yml
部署三个副本
version: '3.8'
services:
whoami:
image: "traefik/whoami"
deploy:
replicas: 3
DNS 查询
您可以使用dig 或nslookup 等工具对同一网络中的名称服务器进行DNS 查找。
docker run --rm --network dig_default tutum/dnsutils dig whoami
; <<>> DiG 9.9.5-3ubuntu0.2-Ubuntu <<>> whoami
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 58433
;; flags: qr rd ra; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;whoami. IN A
;; ANSWER SECTION:
whoami. 600 IN A 172.28.0.3
whoami. 600 IN A 172.28.0.2
whoami. 600 IN A 172.28.0.4
;; Query time: 0 msec
;; SERVER: 127.0.0.11#53(127.0.0.11)
;; WHEN: Mon Nov 16 22:36:37 UTC 2020
;; MSG SIZE rcvd: 90
如果只对IP感兴趣,可以提供+short选项
docker run --rm --network dig_default tutum/dnsutils dig +short whoami
172.28.0.3
172.28.0.4
172.28.0.2
或寻找具体服务
docker run --rm --network dig_default tutum/dnsutils dig +short dig_whoami_2
172.28.0.4
负载均衡
默认负载平衡发生在OSI Model 的传输层或第 4 层。所以它是基于 TCP/UDP 的。这意味着无法使用此方法检查和操作 http 标头。在企业版中,显然可以使用类似于 treafik 在下面的示例中使用的标签。
docker run --rm --network dig_default curlimages/curl -Ls http://whoami
Hostname: eedc94d45bf4
IP: 127.0.0.1
IP: 172.28.0.3
RemoteAddr: 172.28.0.5:43910
GET / HTTP/1.1
Host: whoami
User-Agent: curl/7.73.0-DEV
Accept: */*
这是 10 次 curl 的主机名:
Hostname: eedc94d45bf4
Hostname: 42312c03a825
Hostname: 42312c03a825
Hostname: 42312c03a825
Hostname: eedc94d45bf4
Hostname: d922d86eccc6
Hostname: d922d86eccc6
Hostname: eedc94d45bf4
Hostname: 42312c03a825
Hostname: d922d86eccc6
健康检查
Health checks,默认情况下,通过检查主机内核上容器的进程 ID (PID) 来完成。如果进程运行成功,则认为容器是健康的。
通常需要进行其他健康检查。容器可能正在运行,但里面的应用程序已经崩溃。在许多情况下,首选 TCP 或 HTTP 检查。
可以将自定义健康检查烘焙到图像中。例如,使用curl 执行 L7 健康检查。
FROM traefik/whoami
HEALTHCHECK CMD curl --fail http://localhost || exit 1
也可以在启动容器时通过cli指定健康检查。
docker run \
--health-cmd "curl --fail http://localhost || exit 1" \
--health-interval=5s \
--timeout=3s \
traefik/whoami
Swarm 示例
正如最初提到的,swarms 的行为不同,默认情况下它会为服务分配一个虚拟 IP。实际上并没有什么不同,它只是 docker 或 docker-compose 并没有创建真正的服务,它只是模仿 swarm 的行为但仍然正常运行容器,因为服务实际上只能由管理器节点创建。
请记住,我们使用的是 swarm manager,因此默认模式是 VIP
创建一个普通容器也可以使用的覆盖网络
$ docker network create --driver overlay --attachable testnet
用 2 个副本创建一些服务
$ docker service create --network testnet --replicas 2 --name digme nginx
现在让我们再次使用 dig 并确保我们将容器连接到同一个网络
$ docker run --network testnet --rm tutum/dnsutils dig digme
digme. 600 IN A 10.0.18.6
我们看到确实只得到了一个IP地址,所以看起来这是docker分配的虚拟IP。
Swarm 在这种情况下实际上允许获取单个 IP,而无需明确设置端点模式。
我们可以查询tasks.<servicename>,在这种情况下是tasks.digme
$ docker run --network testnet --rm tutum/dnsutils dig tasks.digme
tasks.digme. 600 IN A 10.0.18.7
tasks.digme. 600 IN A 10.0.18.8
这为我们带来了 2 个指向各个副本的 A 记录。
现在让我们创建另一个服务,将 endpointmode 设置为 dns roundrobin
docker service create --endpoint-mode dnsrr --network testnet --replicas 2 --name digme2 nginx
$ docker run --network testnet --rm tutum/dnsutils dig digme2
digme2. 600 IN A 10.0.18.21
digme2. 600 IN A 10.0.18.20
这样我们就可以得到两个 IP,而无需添加前缀 tasks。
服务发现和负载平衡策略
如果内置功能不够,可以实施一些策略来实现更好的控制。下面是一些例子。
HAProxy
Haproxy 可以结合使用 docker nameserver 和dynamic server templates 来发现正在运行的容器。然后可以利用传统的代理功能通过 http 标头操作和chaos engeering(例如重试)实现强大的第 7 层负载平衡。
version: '3.8'
services:
loadbalancer:
image: haproxy
volumes:
- ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro
ports:
- 80:80
- 443:443
whoami:
image: "traefik/whoami"
deploy:
replicas: 3
...
resolvers docker
nameserver dns1 127.0.0.11:53
resolve_retries 3
timeout resolve 1s
timeout retry 1s
hold other 10s
hold refused 10s
hold nx 10s
hold timeout 10s
hold valid 10s
hold obsolete 10s
...
backend whoami
balance leastconn
option httpchk
option redispatch 1
retry-on all-retryable-errors
retries 2
http-request disable-l7-retry if METH_POST
dynamic-cookie-key MY_SERVICES_HASHED_ADDRESS
cookie MY_SERVICES_HASHED_ADDRESS insert dynamic
server-template whoami- 6 whoami:80 check resolvers docker init-addr libc,none
...
Traefik
前面的方法已经很不错了。但是,您可能已经注意到,它需要知道应该发现哪些服务,并且要发现的副本数量是硬编码的。 Traefik,一个容器原生边缘路由器,解决了这两个问题。只要我们通过label启用Traefik,服务就会被发现。这分散了配置。就好像每个服务都注册自己一样。
标签也可以用于inspect and manipulate http headers。
version: "3.8"
services:
traefik:
image: "traefik:v2.3"
command:
- "--log.level=DEBUG"
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
ports:
- "80:80"
- "8080:8080"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
whoami:
image: "traefik/whoami"
labels:
- "traefik.enable=true"
- "traefik.port=80"
- "traefik.http.routers.whoami.entrypoints=web"
- "traefik.http.routers.whoami.rule=PathPrefix(`/`)"
- "traefik.http.services.whoami.loadbalancer.sticky=true"
- "traefik.http.services.whoami.loadbalancer.sticky.cookie.name=MY_SERVICE_ADDRESS"
deploy:
replicas: 3
领事
Consul 是一个服务发现和配置管理的工具。服务必须通过 API 请求注册。这是一个更复杂的解决方案,可能只在更大的集群中才有意义,但可能非常强大。通常它建议在裸机上而不是在容器中运行它。您可以将它与集群中每台服务器上的 docker 主机一起安装。
在本例中,它与 registrator image 配对,后者负责在 consuls 目录中注册 docker 服务。
可以通过多种方式利用目录。其中之一是使用consul-template。
请注意,consul 带有自己的 DNS 解析器,因此在这种情况下,docker DNS 解析器被忽略了。
version: '3.8'
services:
consul:
image: gliderlabs/consul-server:latest
command: "-advertise=${MYHOST} -server -bootstrap"
container_name: consul
hostname: ${MYHOST}
ports:
- 8500:8500
registrator:
image: gliderlabs/registrator:latest
command: "-ip ${MYHOST} consul://${MYHOST}:8500"
container_name: registrator
hostname: ${MYHOST}
depends_on:
- consul
volumes:
- /var/run/docker.sock:/tmp/docker.sock
proxy:
build: .
ports:
- 80:80
depends_on:
- consul
whoami:
image: "traefik/whoami"
deploy:
replicas: 3
ports:
- "80"
用于自定义代理映像的 Dockerfile,其中支持 consul 模板。
FROM nginx
RUN curl https://releases.hashicorp.com/consul-template/0.25.1/consul-template_0.25.1_linux_amd64.tgz \
> consul-template_0.25.1_linux_amd64.tgz
RUN gunzip -c consul-template_0.25.1_linux_amd64.tgz | tar xvf -
RUN mv consul-template /usr/sbin/consul-template
RUN rm /etc/nginx/conf.d/default.conf
ADD proxy.conf.ctmpl /etc/nginx/conf.d/
ADD consul-template.hcl /
CMD [ "/bin/bash", "-c", "/etc/init.d/nginx start && consul-template -config=consul-template.hcl" ]
Consul模板取一个模板文件,根据consuls目录的内容进行渲染。
upstream whoami {
{{ range service "whoami" }}
server {{ .Address }}:{{ .Port }};
{{ end }}
}
server {
listen 80;
location / {
proxy_pass http://whoami;
}
}
模板更改后,执行重启命令。
consul {
address = "consul:8500"
retry {
enabled = true
attempts = 12
backoff = "250ms"
}
}
template {
source = "/etc/nginx/conf.d/proxy.conf.ctmpl"
destination = "/etc/nginx/conf.d/proxy.conf"
perms = 0600
command = "/etc/init.d/nginx reload"
command_timeout = "60s"
}
特征表
|
Built In |
HAProxy |
Traefik |
Consul-Template |
| Resolver |
Docker |
Docker |
Docker |
Consul |
| Service Discovery |
Automatic |
Server Templates |
Label System |
KV Store + Template |
| Health Checks |
Yes |
Yes |
Yes |
Yes |
| Load Balancing |
L4 |
L4, L7 |
L4, L7 |
L4, L7 |
| Sticky Session |
No |
Yes |
Yes |
Depends on proxy |
| Metrics |
No |
Stats Page |
Dashboard |
Dashboard |
您可以查看部分代码samples in more detail on github。