12

ここの画像で詳しく説明されているものと非常によく似たセットアップを試しています: https://raw.githubusercontent.com/Oreste-Luci/netflix-oss-example/master/netflix-oss-example.png

ここに画像の説明を入力

私のセットアップでは、クライアント アプリケーション ( https://www.joedog.org/siege-home/ )、プロキシ (Zuul)、検出サービス (Eureka)、および単純なマイクロサービスを使用しています。すべてが PWS に展開されます。

シンプルなマイクロサービスの 1 つのバージョンから次のバージョンに、ダウンタイムなしで移行したいと考えています。最初は、 https ://docs.cloudfoundry.org/devguide/deploy-apps/blue-green.html で説明されている手法から始めました。

私の意見では、このアプローチは Eureka などの発見サービスと「互換性」がありません。実際、私のサービスの新しいバージョンは Eureka に登録されており、すべてのルート (CF ルーター) を再マッピングする前でもトラフィックを受信して​​います。

これにより、Spring Cloud/Netflix のフェイルオーバー メカニズムに依存する別のアプローチにたどり着きました。

  1. サービスの新しい (下位互換性がある) バージョンをスピンアップします。
  2. このバージョンが Zuul/Eureka によってピックアップされると、トラフィックの 50% を取得し始めます。
  3. 新しいバージョンが正しく動作することを確認したら、「古い」インスタンスを削除します。(PWSの「停止」ボタンをクリックするだけです)

私が理解しているように、Zuul はボンネットの下でリボン (負荷分散) を使用しているため、古いインスタンスがまだ Eureka にあるが実際にはシャットダウンしている一瞬で、クライアントに影響を与えずに新しいインスタンスで再試行することを期待しています。

しかし、私の仮定は間違っています。クライアントで 502 エラーがいくつか発生します。

Lifting the server siege...      done.

Transactions:               5305 hits
Availability:              99.96 %
Elapsed time:              59.61 secs
Data transferred:          26.06 MB
Response time:              0.17 secs
Transaction rate:          89.00 trans/sec
Throughput:             0.44 MB/sec
Concurrency:               14.96
Successful transactions:        5305
Failed transactions:               2
Longest transaction:            3.17
Shortest transaction:           0.14

私のapplication.ymlの一部

server:
  port: ${PORT:8765}

info:
  component: proxy

ribbon:
  MaxAutoRetries: 2   # Max number of retries on the same server (excluding the first try)
  MaxAutoRetriesNextServer: 2 # Max number of next servers to retry (excluding the first server)
  OkToRetryOnAllOperations: true # Whether all operations can be retried for this client
  ServerListRefreshInterval: 2000 # Interval to refresh the server list from the source
  ConnectTimeout: 3000 # Connect timeout used by Apache HttpClient
  ReadTimeout: 3000 # Read timeout used by Apache HttpClient

hystrix:
  threadpool:
      default:
        coreSize: 50
        maxQueueSize: 100
        queueSizeRejectionThreshold: 50
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 10000

何がうまくいかないのかわかりません。

これは技術的な問題ですか?

それとも、私は間違った仮定をしていますか (POST はとにかく再試行されないことをどこかで読みましたが、これはよくわかりません)。

どうやってそれをするのか聞いてみたいです。

ありがとう、アンディ

4

1 に答える 1

2

私もこれについて疑問に思いました。Spring Cloud を「In Anger」で使用したとは言いません。私はしばらくそれを試してきました。

仮定: すべてのインスタンス状態の信頼できる情報源が Eureka に格納されていると仮定すると、Eureka は操作制御のメカニズムになるはずです。インスタンスの状態を に設定することで、Eureka を使用してインスタンスを停止できますOUT_OF_SERVICE。リボンがサーバー リストを更新すると、これらのアウト オブ サービス インスタンスは使用されません。Eureka は、インスタンスを照会し、インスタンスの状態を設定するための REST API を提供します。偉大な。

問題は、どのインスタンスが Blue グループに属し、どのインスタンスが Green グループに属しているかをどのように特定するかということです。

私は考えていました... Eureka は各インスタンスのメタデータ マップを提供します。ビルド/ベイクのステップで、メタデータ マップにバージョン ID を設定するとしますか? Git コミット ID やセマンティック バージョニング スキームなどを使用できます。わかりました。これで、Eureka メタデータを見て、そのバージョン値から Blue インスタンスと Green インスタンスを識別できます。プロパティを使用して、各サービスのメタデータ値を設定できます。

例えばeureka.instance.metadataMap.version=8675309

あとは、エウレカに教えてあげればよかったのに。「FUBAR サービスとバージョン 8675309 のすべてのインスタンスをサービスから外します。」まあ、それはすぐに提供されるとは思いません。Spring Cloud の優れた点は、Eureka Server を含むこれらすべてのサービスが、必要に応じてハッキングできる単なる Spring アプリであることです。以下のコードは、アプリ名とバージョンを指定して、インスタンスを「サービス停止」に設定するエンドポイントを公開します。このコントローラーを Eureka サーバーに追加するだけです。それは生産準備ができていません。本当にただのアイデアです。

Eureka がこれらのインスタンスをサービスから外し、Ribbon がそのサーバー リストを更新すると、安全にこれらのインスタンスを強制終了またはルーティングできます。

に投稿:

http://[eurekahost:port]/takeInstancesOutOfService?applicationName=FOOBAR&version=8675309

それが役立つことを願っていますか?

import java.util.Collection;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.netflix.appinfo.InstanceInfo;
import com.netflix.appinfo.InstanceInfo.InstanceStatus;
import com.netflix.discovery.shared.Application;
import com.netflix.eureka.EurekaServerContextHolder;
import com.netflix.eureka.registry.PeerAwareInstanceRegistry;

@RestController
public class EurekaInstanceStateController {

    @RequestMapping(value="/instancesQuery", method=RequestMethod.POST)
    public Collection<String> queryInstancesByMetaData(
            @RequestParam("applicationName") String applicationNameCriteria,
            @RequestParam("version") String versionCriteria)
    {
        return getRegistry().getSortedApplications()
                .stream()
                .filter(hasApplication(applicationNameCriteria))
                .flatMap(app -> app.getInstances().stream())
                .filter(hasVersion(versionCriteria))
                .map(info -> info.getAppName() + " - " + info.getId() + " - " + info.getStatus() + " - " + info.getMetadata().get("version"))
                .collect(Collectors.toList());
    }

    @RequestMapping(value="/takeInstancesOutOfService", method=RequestMethod.POST)
    public Collection<String> takeInstancesOutOfService(
            @RequestParam("applicationName") String applicationNameCriteria,
            @RequestParam("version") String versionCriteria)
    {
        return getRegistry().getSortedApplications()
                .stream()
                .filter(hasApplication(applicationNameCriteria))
                .flatMap(app -> app.getInstances().stream())
                .filter(hasVersion(versionCriteria))
                .map(instance -> updateInstanceStatus(instance, InstanceStatus.OUT_OF_SERVICE) )
                .collect(Collectors.toList());
    }

    /**
     * @param instance
     * @return
     */
    private String updateInstanceStatus(InstanceInfo instance, InstanceStatus status)
    {
        boolean isSuccess = getRegistry().statusUpdate(instance.getAppName(), instance.getId(),
        status, String.valueOf(System.currentTimeMillis()),
        true);

        return (instance.getAppName() + " - " + instance.getId() + " result: " + isSuccess);
    }

    /**
     * Application Name Predicate
     * @param applicationNameCriteria
     * @return
     */
    private Predicate<Application> hasApplication(final String applicationNameCriteria)
    {
        return application -> applicationNameCriteria.toUpperCase().equals(application.getName());
    }

    /**
     * Instance Version Predicate.  Uses Eureka Instance Metadata value name "version".</br>
     * 
     * Set / Bake the instance metadata map to contain a version value.</br>  
     * e.g. eureka.instance.metadataMap.version=85839c2
     * 
     * @param versionCriteria
     * @return
     */
    private Predicate<InstanceInfo> hasVersion(final String versionCriteria)
    {
        return info -> versionCriteria.equals(info.getMetadata().get("version"));
    }

    private PeerAwareInstanceRegistry getRegistry() {
        return EurekaServerContextHolder.getInstance().getServerContext().getRegistry();
    }
}
于 2016-05-05T21:53:43.073 に答える