我目前正在修改CoreOS并基于它创建一个集群.到目前为止,CoreOS在单个主机上的体验非常顺畅.但是在服务发现方面,事情变得有点模糊.不知怎的,我没有得到整体想法,因此我现在在这里寻求帮助.
我想要做的是让两个Docker容器运行,第一个依赖于第二个.如果我们谈论纯粹的Docker,我可以使用链接容器解决这个问题.到现在为止还挺好.
但是这种方法不适用于跨机器边界,因为Docker无法跨多个主机链接容器.所以我想知道如何做到这一点.
到目前为止我所了解的是,CoreOS关于如何处理这个问题的想法是使用它的etcd
服务,它基本上是一个分布式键值存储,可以通过端口在本地的每个主机上访问4001
,所以你不必处理(作为消费者etcd
)有任何网络细节:只是访问localhost:4001
,你没事.
所以,在我看来,我现在认为这意味着当提供服务的Docker旋转时,它会在本地注册自己(即其IP地址和端口)etcd
,并etcd
负责在整个网络中分发信息.网络.这样,例如,您获得键值对,例如:
RedisService => 192.168.3.132:49236
现在,当另一个Docker容器需要访问a时RedisService
,它从其自己的本地获取IP地址和端口etcd
,至少一旦信息已经通过网络分发.到现在为止还挺好.
但现在我有一个我无法回答的问题,这让我困惑了几天:当服务出现故障时会发生什么?谁清理了里面的数据etcd
?如果未清除,则所有客户端都会尝试访问不再存在的服务.
我现在能想到的唯一(可靠)解决方案是利用etcd
数据的TTL功能,但这需要权衡:要么你有相当高的网络流量,因为你需要每隔几秒发送一次心跳,或者你必须忍受过时的数据.两者都不好.
另一个,我能想到的"解决方案"是让服务在停机时注销,但这只适用于计划停机,而不是崩溃,电力,......
那么,你是如何解决这个问题的?
有几种不同的方法可以解决这个问题: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入门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!