我目前正在修改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功能进行数据处理,但这需要权衡:要么你拥有相当高的网络流量,要么就像你一样需要每隔几秒发送一次心跳,或者你必须忍受过时的数据。两者都不好。
另一个,我能想到的“解决方案”是让服务在停机时注销,但这仅适用于计划停机,而不适用于崩溃,电力供应等......
那么,你是如何解决这个问题的?
答案 0 :(得分:8)
有几种不同的方法可以解决这个问题:sidekick方法,使用ExecStopPost
并在失败时删除。我假设有三个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
在复杂方面,这可能是一个容器,它会启动并点击您的应用提供的/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是系统变量,可替换机器的主机名。如果您对更多变量的使用感兴趣,请查看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
删除密钥。
关于心跳会导致的网络流量 - 在大多数情况下,您应该通过您的提供商运行的本地专用网络发送此流量,因此它应该是免费且非常快的。 etcd
无论如何都会与同伴保持同心心,所以这只是流量的一点点增加。
如果您有任何其他问题,请跳至Freenode上的#coreos!