我目前正在研究 CoreOS 并基于它创建一个集群。到目前为止,在单主机上使用 CoreOS 的体验还是相当流畅的。但是当涉及到服务发现时,事情变得有点模糊。不知何故,我不明白总体思路,因此我现在在这里寻求帮助。
我想要做的是让两个 Docker 容器运行,第一个依赖于第二个。如果我们谈论的是纯 Docker,我可以使用 linked containers 解决这个问题.到目前为止,还不错。
但是这种方法不能跨机器边界工作,因为Docker不能跨多个主机链接容器。所以我想知道如何做到这一点。
到目前为止,我所了解的是 CoreOS 的处理方法是使用其 etcd
服务,它基本上是一个分布式键值存储,可在每个主机上访问在本地通过端口 4001
,因此您不必(作为 etcd
的消费者)处理任何网络细节:只需访问 localhost:4001
你很好。
所以,在我看来,我现在的想法是,当提供服务的 Docker 启动时,它会在本地 etcd
中注册自己(即它的 IP 地址和端口) > 和 etcd
负责在网络上分发信息。这样,例如你得到键值对,例如:
RedisService => 192.168.3.132:49236
现在,当另一个 Docker 容器需要访问一个 RedisService
时,它会从他们自己的本地 etcd
获取 IP 地址和端口,至少一旦信息被分布在整个网络中。到目前为止,还不错。
但现在我有一个我无法回答的问题,这已经困扰了我几天:当服务出现故障时会发生什么?谁清理etcd
里面的数据?如果未清理,所有客户端都会尝试访问不再存在的服务。
目前我能想到的唯一(可靠)解决方案是利用 etcd
的数据 TTL 功能,但这涉及一个权衡:要么你有相当高的网络流量,因为您需要每隔几秒发送一次心跳,否则您将不得不忍受陈旧的数据。两者都不好。
我能想到的另一种“解决方案”是让服务在出现故障时自行注销,但这仅适用于计划关闭,不适用于崩溃、停电......
那么,你是如何解决这个问题的?
最佳答案
有几种不同的方法可以解决这个问题:sidekick 方法,使用 ExecStopPost
并在失败时删除。我假设三个 CoreOS , etcd和 systemd ,但这些概念也可以应用于其他地方。
Sidekick 方法
这涉及在您的主应用程序旁边运行一个单独的进程,该进程向 etcd
发送心跳。简单来说,这只是一个永远运行的 for 循环。您可以使用 systemd 的 BindsTo 来确保当您的主单元停止时,该服务注册单元也会停止。在 ExecStop 中,您可以明确删除您正在设置的 key 。我们还设置了 60 秒的 TTL 来处理任何不正常的停止。
[Unit]
Description=Announce nginx1.service
# Binds this unit and nginx1 together. When nginx1 is stopped, this unit will be stopped too.
BindsTo=nginx1.service
[Service]
ExecStart=/bin/sh -c "while true; do etcdctl set /services/website/nginx1 '{ \"host\": \"10.10.10.2\", \"port\": 8080, \"version\": \"52c7248a14\" }' --ttl 60;sleep 45;done"
ExecStop=/usr/bin/etcdctl delete /services/website/nginx1
[Install]
WantedBy=local.target
在复杂的方面,这可能是一个启动并命中 /health
端点的容器,您的应用程序提供该端点以在将数据发送到 etcd
之前运行运行状况检查.
ExecStopPost
如果您不想在主应用程序旁边运行某些东西,您可以在主单元中使用 etcdctl
命令在启动和停止时运行。请注意,正如您提到的,这不会捕获所有失败。
[Unit]
Description=MyWebApp
After=docker.service
Require=docker.service
After=etcd.service
Require=etcd.service
[Service]
ExecStart=/usr/bin/docker run -rm -name myapp1 -p 8084:80 username/myapp command
ExecStop=/usr/bin/etcdctl set /services/myapp/%H:8084 '{ \"host\": \"%H\", \"port\": 8084, \"version\": \"52c7248a14\" }'
ExecStopPost=/usr/bin/etcdctl rm /services/myapp/%H:8084
[Install]
WantedBy=local.target
%H 是一个 systemd 变量,用于替代机器的主机名。如果您对更多变量用法感兴趣,请查看 CoreOS Getting Started with systemd指导。
失败时删除
在客户端,您可以删除连接失败次数超过 X 次的任何实例。如果您从 /services/myapp/instance1
获得 500 或超时,您可以运行并继续增加失败计数,然后尝试连接到 /services/myapp/< 中的其他主机
目录。
etcdctl set /services/myapp/instance1 '{ \"host\": \"%H\", \"port\": 8084, \"version\": \"52c7248a14\", \"failures\": 1 }'
当您达到所需的阈值时,使用 etcdctl
删除 key 。
关于心跳会导致的网络流量——在大多数情况下,您应该通过您的提供商运行的本地专用网络发送此流量,因此它应该是免费且非常快速的。 etcd
无论如何都在不断地与其同行进行心跳,所以这只是流量的一点点增加。
如果您有任何其他问题,请访问 Freenode 上的#coreos!
关于cluster-computing - 在 CoreOS 上使用 etcd 进行服务发现时如何处理陈旧数据?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21597039/