【问题标题】:GCP Load Balancer behaviour with terminating preemptive instances终止抢占式实例的 GCP 负载均衡器行为
【发布时间】:2018-12-03 13:25:56
【问题描述】:

背景

我们有一个调度程序实例组,每个活动虚拟机每秒接收大约 700 个请求。此调度程序位于自动扩展的负载均衡器后面。到目前为止,我们所有的 VM 都是常规 VM,但我们一直在研究使它们成为抢占式的可能性。

抢占式实例的问题

根据文档,GCP 可以在 any time 终止抢占式实例。

假设每个调度程序 VM 都没有状态。它接收请求,处理它并向其他机器发出 HTTP 请求。

在任何给定时间,每个 VM 将同时处理大约 700 个请求,同时从负载平衡器接收数据。

问题

如果我的抢占式 VM(处理 700 个请求)收到要终止的信号,会发生什么情况?

嗯,理论上应该有一个shutdown script 来确保处理这些请求完成,然后终止应用程序(干净退出)。这就引出了一个大问题:

  • 但是负载平衡器是否知道我的 VM 正在关闭?它会继续向终止的虚拟机发送请求吗?

注意事项

如果是,则意味着某些请求将失败,因为一旦应用关闭,机器仍然处于启动状态,负载均衡器继续向机器发送请求,而不知道应用已经启动向下。

理想情况下,这些请求将作为失败的请求返回到负载均衡器,然后将请求发送到另一台机器。然而,GCP 负载平衡器不够聪明,无法做到这一点,因此它们不会。

如果负载均衡器以某种方式知道此 VM 被选择用于抢先终止,则无需执行任何特殊操作。

是哪一个?

【问题讨论】:

    标签: google-cloud-platform google-compute-engine load-balancing preemptive


    【解决方案1】:

    但是负载平衡器是否知道我的虚拟机正在关闭?会吗 继续向终止的 VM 发送请求?

    是的,负载均衡器会继续向实例发送请求。

    您需要创建一个关闭脚本并从负载均衡器中删除您的实例。

    并不是负载均衡器不够聪明。负载均衡器不知道您的请求是否可以重试。该决定应由客户端/后端逻辑做出。

    您的用例不是抢占式实例的好例子。抢占式实例将每 24 小时终止一次。如果您的目标是节省成本,请将长期实例定价的成本与抢先定价的成本进行比较。节省的成本不足以证明工程、测试和 QA 成本的合理性。

    架构应该为失败而设计,但我不会故意选择一个会不断失败的架构。在您的情况下,每 24 小时一次。还存在您将无法启动另一个实例来弥补增加的负载的风险。并且存在您的所有实例都将被终止的风险。

    【讨论】:

    • 那么抢占式实例的好用例是什么?我对此很好奇,因为保持状态的机器看起来确实是最糟糕的情况。
    • 有很多用例。我经常构建的一个流行的是具有数百个节点的 Hadoop 集群。在某些情况下,分析需要 24 小时还是 120 小时确实很重要。通过使用抢占式实例,成本可以显着下降。 Kubernetes 集群是另一个很好的用例。有状态节点是可以的——你只需要设计它并有一个足够大的工作集来证明管理开销是合理的。
    • 我们一直在与该地区的其他人交谈,他们说如果我们首先从负载均衡器中删除实例(使用关闭脚本),可以降低风险,从而允许连接耗尽。您认为这是个好主意吗?
    • 是的,我在回答中建议这样做。但是,请查看连接耗尽所需的时间,并将其与抢先关闭可能花费的最长时间进行比较。还需要维护会话亲和性等吗?
    【解决方案2】:

    我们遇到了类似的问题。我们几乎通过负载均衡器健康检查解决了这个问题(在非常高的负载条件下存在一些问题)。 技巧现在在抢占信号的 10-15 秒内,负载均衡器会将实例标记为不健康,并停止向该实例发送新请求。

    解决方案:

    1. 负载均衡器每 3 秒检查一次实例的健康状况,并在第三次健康检查失败后将实例标记为不健康。因此负载均衡器会在大约 10 秒内标记实例并停止发送新请求。
    2. 在 Java 中使用 ContextCloseEvent (Spring boot)Runtime.getRuntime().addShutdownHook() 捕获抢占信号(在我的情况下,JVM 接收到信号需要几秒钟)
    3. 将运行状况检查设置为失败,即运行状况检查端点将开始返回 404。
    4. 在关闭块中休眠 15-25 秒,以让进程继续进行并完成新请求
    5. 释放资源并进行关闭日志记录。

      
      @EventListener
      public void onShutdown(ContextClosedEvent event) {
      
      
      log.warn("shutdown event received {}", event.getSource().toString());
      log.warn("/ping will respond 404, Main thread will sleep for 20 seconds to allow pending tasks to complete");
      
      isShuttingDown = true;
      try {
          Thread.sleep(SLEEP_BEFORE_SHUTDOWN_MILLIS);
      } catch (InterruptedException e) {
          log.error("sleep before shutdown interrupted", e);
      }
      
      log.warn("Shutting down now, daemon threads will continue work");
      releaseResources(); 
      
      log.info("{} {} on {} stopped.", NAME, VERSION, HOSTNAME);
      
      } //health endpoint @RequestMapping(value = "ping", produces = MediaType.TEXT_PLAIN_VALUE) public ResponseEntity ping() { if(isShuttingDown()) { log.warn("health failed - shutting down soon"); return new ResponseEntity(HttpStatus.NOT_FOUND); } return ResponseEntity.ok("pong"); }

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-08-31
      • 2021-01-04
      • 2020-05-08
      • 2020-01-30
      • 2021-12-07
      • 2014-05-05
      • 2018-02-18
      相关资源
      最近更新 更多