【问题标题】:How to deal with stale data when doing service discovery with etcd on CoreOS?在 CoreOS 上使用 etcd 进行服务发现时如何处理陈旧数据?
【发布时间】:2014-02-07 07:22:40
【问题描述】:

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

我想做的是让两个 Docker 容器在第一个依赖于第二个的地方运行。如果我们谈论的是纯 Docker,我可以使用 linked containers 解决这个问题。到目前为止,一切顺利。

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

到目前为止,我所了解的是,CoreOS 处理此问题的想法是使用其etcd 服务,该服务基本上是一个分布式键值存储,可在每个主机上通过端口@987654323 本地访问@,因此您不必(作为etcd 的消费者)处理任何网络细节:只需访问localhost:4001 就可以了。

所以,在我的脑海中,我现在有这样的想法,这意味着当一个提供服务的 Docker 启动时,它会在本地 etcd 和 @987654327 中注册自己(即它的 IP 地址和它的端口) @ 负责在网络上分发信息。这样,例如你会得到键值对,例如:

RedisService => 192.168.3.132:49236

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

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

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

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

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

【问题讨论】:

    标签: cluster-computing docker service-discovery coreos


    【解决方案1】:

    有几种不同的方法可以解决这个问题: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 次的实例。如果您从 /services/myapp/instance1 收到 500 或超时,您可以运行并不断增加失败计数,然后尝试连接到 /services/myapp/ 目录中的其他主机。

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

    当您达到所需的阈值时,删除带有etcdctl 的密钥。

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

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

    【讨论】:

    • 非常感谢,这对我帮助很大:-)!
    猜你喜欢
    • 2017-09-24
    • 2012-05-31
    • 1970-01-01
    • 2011-05-22
    • 2014-10-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-08
    相关资源
    最近更新 更多