2

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

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

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

到目前为止我所了解的是,CoreOS 处理这个问题的想法是使用它的etcd服务,它基本上是一个分布式键值存储,可以在每个主机上通过 port 本地访问4001,所以你不必处理(作为 的消费者etcd)具有任何网络详细信息:只需访问localhost:4001即可。

所以,在我的脑海里,我现在有这样的想法,这意味着当一个提供服务的 Docker 启动时,它会在本地注册自己(即它的 IP 地址和它的端口)etcd,并etcd负责在整个网络。这样,例如,您将获得键值对,例如:

RedisService => 192.168.3.132:49236

现在,当另一个 Docker 容器需要访问 aRedisService时,它会从它们自己的本地获取 IP 地址和端口etcd,至少在信息已通过网络分发后。到现在为止还挺好。

但是现在我有一个我无法回答的问题,这已经让我困惑了几天:当服务出现故障时会发生什么?谁清理里面的数据etcd?如果没有清理,所有客户端都会尝试访问不再存在的服务。

目前我能想到的唯一(可靠)解决方案是使用etcd的 TTL 功能来处理数据,但这需要权衡:要么您的网络流量很高,因为您需要每隔几秒发送一次心跳,否则您必须忍受陈旧的数据。两者都不好。

我能想到的另一个“解决方案”是让服务在出现故障时自行注销,但这仅适用于计划中的关闭,不适用于崩溃、停电……</p>

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

4

1 回答 1

8

有几种不同的方法可以解决这个问题:sidekick 方法,ExecStopPost失败时使用和删除。我假设三个CoreOSetcdsystemd,但这些概念也可以应用于其他地方。

搭档方法

这涉及在心跳到etcd. 简单来说,这只是一个永远运行的 for 循环。您可以使用 systemd 的 BindsTo 来确保当您的主单元停止时,该服务注册单元也停止。在 ExecStop 中,您可以显式删除您正在设置的密钥。我们还将 TTL 设置为 60 秒,以处理任何不正常的停止。

[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 次的任何实例。如果您收到 500 或超时,/services/myapp/instance1您可以运行并不断增加故障计数,然后尝试连接到/services/myapp/目录中的其他主机。

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

当您达到所需的阈值时,使用 删除键etcdctl

关于心跳会导致的网络流量 - 在大多数情况下,您应该通过您的提供商运行的本地专用网络发送此流量,因此它应该是免费且非常快速的。etcd无论如何,它一直在与同行心跳,所以这只是流量的一点增加。

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

于 2014-02-06T18:32:43.920 回答