Docker内置的Nameserver和负载均衡器
Docker自带一个内置的nameserver。默认情况下,该服务器可以通过127.0.0.11:53
访问。
每个容器默认都在/etc/resolv.conf
中有一个nameserver条目,因此不需要在容器内指定nameserver的地址。这就是为什么你可以使用service
或task_service_n
在网络内查找服务。
如果你使用task_service_n
,那么你将得到相应服务副本的地址。
如果你只请求service
,Docker将在同一网络中的容器之间执行内部负载平衡
,并处理来自外部的请求的外部负载平衡
。
当使用swarm时,Docker还会使用两个特殊网络。
ingress网络
,实际上是一个overlay网络,用于处理进入swarm的流量。它允许从swarm中的任何节点查询任何服务。
docker_gwbridge
,桥接网络,连接单个主机的覆盖网络与其物理网络。(包括ingress)
当使用swarm来部署服务时,除非将endpointmode设置为dns roundrobin而不是vip,否则以下示例中描述的行为将无法正常工作。
endpoint_mode: vip - Docker分配给服务一个虚拟IP(VIP),该IP充当客户端在网络上访问服务的前端。Docker在客户端和参与服务的可用工作节点之间路由请求,而客户端不知道有多少个节点参与服务或它们的IP地址或端口。(这是默认值。)
endpoint_mode: dnsrr - DNS轮询(DNSRR)服务发现不使用单个虚拟IP。Docker为服务设置DNS条目,以便服务名称的DNS查询返回IP地址列表,并且客户端直接连接到其中之一。DNS轮询在你想要使用自己的负载均衡器或混合Windows和Linux应用程序的情况下很有用。
示例
例如,从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模型的传输层或第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
健康检查
健康检查默认情况下通过检查主机内核上容器的进程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进行示例
正如最初提到的那样,swarm的行为不同之处在于默认情况下会为服务分配虚拟IP。实际上,它并没有什么不同,只是docker或docker-compose没有创建真正的服务,它只模拟了swarm的行为,但仍然以普通方式运行容器,因为服务实际上只能由管理节点创建。
请记住我们正在一个swarm管理器上,因此默认模式是VIP。
创建一个可以被常规容器使用的覆盖网络。
$ docker network create --driver overlay --attachable testnet
创建2个副本的一些服务。
$ docker service create --network testnet --replicas 2 --name digme nginx
现在让我们再次使用dig,并确保将容器附加到相同的网络{{network}}上。
$ 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轮询。
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名称服务器与动态服务器模板结合使用,以发现正在运行的容器。然后可以利用传统代理功能来实现强大的第7层负载平衡以及对http头操作和混沌工程(例如重试)的支持。
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,一个容器本地边缘路由器,解决了这两个问题。只要我们通过标签启用Traefik,服务就会被发现。这样分散了配置。就好像每个服务都在注册自己一样。
标签还可以用于检查和操作http头。
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
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"
用于支持Consul模板的自定义代理镜像的Dockerfile。
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模板会根据Consul目录的内容获取一个模板文件并进行渲染。
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"
}
功能表
|
内置 |
HAProxy |
Traefik |
Consul-Template |
解析器 |
Docker |
Docker |
Docker |
Consul |
服务发现 |
自动 |
服务器模板 |
标签系统 |
KV存储+模板 |
健康检查 |
是 |
是 |
是 |
是 |
负载均衡 |
L4 |
L4,L7 |
L4,L7 |
L4,L7 |
粘性会话 |
否 |
是 |
是 |
取决于代理 |
指标 |
否 |
统计页面 |
仪表盘 |
仪表盘 |
您可以在github上查看更多详细代码示例。