在CoreOS上使用etcd进行服务发现时如何处理陈旧的数据?

我目前正在修补CoreOS并基于它创build一个集群。 到目前为止,在单个主机上使用CoreOS的经验非常stream畅。 但是在服务发现方面,事情会变得有些模糊。 不知何故,我没有得到整体的想法,因此我现在要求在这里寻求帮助。

我想要做的是让两个Docker容器在第一个依赖于第二个容器的位置运行。 如果我们正在谈论纯粹的Docker,我可以使用链接容器来解决这个问题。 到现在为止还挺好。

但是这种方法不能跨越机器边界,因为Docker不能跨多个主机链接容器。 所以我想知道如何做到这一点。

到目前为止,我所了解的是CoreOS关于如何处理这个问题的想法是使用它的etcd服务,它基本上是一个分布式的key-value-store,可以通过端口4001在每台主机上访问,所以你没有作为etcd一个消费者来处理任何networking信息:只要访问localhost:4001就行了。

所以,在我的脑海中,我现在有这样的想法,这意味着,当一个提供服务的Docker启动时,它将本身(即它的IP地址和端口)注册到本地etcdetcd负责分发信息通过networking。 这样,例如,您可以获得键值对,例如:

 RedisService => 192.168.3.132:49236 

现在,当另一个Docker容器需要访问一个RedisService ,至less在信息分发到networking上的RedisService ,它会从他们自己的本地etcd获取IP地址和端口。 到现在为止还挺好。

但现在我有一个问题,我不能回答,这已经困扰了我几天:当一个服务失败时会发生什么? 谁清理了etcd里面的数据? 如果没有清理,所有客户端都尝试访问不在那里的服务。

我现在所能想到的唯一(可靠的)解决scheme是利用etcd的数据TTLfunction,但这涉及到一个折衷:要么你有很高的networkingstream量,因为你需要发送一个心跳秒,或者你必须忍受陈旧的数据。 两者都不好。

另一方面,我所能想到的“解决scheme”就是在服务失效的时候让服务自行注销,但这只适用于计划中的closures,而不是针对崩溃,权限限制,…

那么,你如何解决这个问题呢?

有几种不同的方法可以解决这个问题:使用ExecStopPost的sidekick方法,并在失败时移除。 我假设了三个CoreOS , etcd和systemd ,但这些概念也可以适用于其他地方。

Sidekick方法

这包括在你的主应用程序旁边运行一个单独的进程,心跳到etcd 。 简而言之,这只是一个永远运行的for循环。 你可以使用systemd的BindsTo来确保当你的主单元停止时,这个服务注册单元也会停止。 在ExecStop中,您可以显式删除您正在设置的密钥。 我们还设置了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 

复杂的一面,这可能是一个容器,启动并命中您的应用程序提供运行健康检查之前发送数据到etcd /health端点。

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是一个systemdvariables,它replace了机器的主机名。 如果您对更多variables的使用感兴趣,请查看CoreOS系统指南入门 。

删除失败

在客户端,您可以删除任何连接失败次数超过X次的实例。 如果你从/services/myapp/instance1得到一个500或超时,你可以运行并持续增加失败计数,然后尝试连接到/services/myapp/目录中的其他主机。

 etcdctl set /services/myapp/instance1 '{ \"host\": \"%H\", \"port\": 8084, \"version\": \"52c7248a14\", \"failures\": 1 }' 

当你达到你想要的门槛时,用etcdctl删除密钥。

关于心跳会造成的networkingstream量 – 在大多数情况下,您应该通过您的提供商运行的本地专用networking发送此stream量,所以它应该是免费且非常快的。 etcd总是和同伴一起心跳加速,所以这只是stream量的一点点增加。

如果您有任何其他问题,跳进Freenode的#coreos!