9

Я экспериментировал с установкой, которая очень похожа на один подробно в изображении здесь: https://raw.githubusercontent.com/Oreste-Luci/netflix-oss-example/master/netflix-oss-example.pngЧто такое канонический способ развертывания синего/зеленого с стеком Spring Cloud/Netflix на PWS?

enter image description here

В моей установке, я использую клиентское приложение (https://www.joedog.org/siege-home/), прокси-сервер (Zuul), сервис обнаружения (Eureka) и простой микросервис. Все развертывается на PWS.

Я хочу перейти от одной версии моего простого микросервиса к следующему без простоя. Первоначально я начал с описанной здесь методики: 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 не подвергаются повторному рассмотрению, что я действительно не понимаю)?

Хотелось бы услышать, как вы это делаете.

Спасибо, Andy

ответ

2

Я также задавался вопросом об этом. Я не буду утверждать, что использовал Spring Cloud «In Anger». Я просто экспериментировал с ним некоторое время.

Успение: мы предполагаем, что источник истины для всех состояний экземпляра хранится в Эврика, тогда Эврика должна быть нашим механизмом оперативного управления. Мы можем использовать Eureka, чтобы вывести экземпляр из строя, установив состояние экземпляра на OUT_OF_SERVICE. Когда Ribbon обновляет список своих серверов, он не будет использовать эти экземпляры вне службы. Eureka предоставляет API REST для запросов экземпляров и установки состояния экземпляра. Отлично.

Проблема заключается в следующем: как определить, какие экземпляры находятся в группе Blue, а какие - в группе Green?

Я думал ... Eureka предоставляет карту метаданных для каждого экземпляра. Скажем, в нашем шаге build/bake мы устанавливаем идентификатор версии на карте метаданных? Мы могли бы использовать Git commit Id или некоторую семантическую схему управления версиями или что угодно. Хорошо, теперь я могу посмотреть метаданные Eureka и идентифицировать экземпляры Blue против Green, учитывая это значение версии. Мы можем установить значения метаданных в каждой службе, используя свойства.

например. eureka.instance.metadataMap.version=8675309

Теперь, что было бы хорошо, если бы мы могли просто рассказать Эврика. «Возьмите все экземпляры для службы FUBAR и версии 8675309 без обслуживания». Ну, я не думаю, что это было из коробки. Приятная вещь о Spring Cloud заключается в том, что все эти сервисы, включая Eureka Server, - это просто приложения Spring, которые мы можем взломать для наших собственных нужд. В приведенном ниже коде представлена ​​конечная точка, которая устанавливает экземпляры в «неработающие» с учетом имени приложения и версии. Просто добавьте этот контроллер на ваш сервер Eureka. Это не производство готово, просто идея действительно.

Теперь, после того, как Eureka выведет эти экземпляры из эксплуатации, а лента обновит список своих серверов, можно безопасно убить или отойти от этих экземпляров.

POST для:

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(); 
    } 
} 
+0

Хорошая идея. Я тоже смотрю на это. Но я не уверен в том, чтобы сделать это на стороне Eureka - если служба отправит новое сердцебиение, не изменит ли он свое состояние снова на UP? Весеннее облако содержит конечную точку/pause и/resume, которая, как я думаю, изменяет состояние клиента на OUT_OF_SERVICE или DOWN. Я думал о сценарии развертывания, который отправляет/приостанавливает перед развертыванием. Список экземпляров, чтобы отправить это, можно было потянуть из Eureka и отфильтровать по версии или что-то еще. – nedenom

+0

Я тоже смотрел в состояние OUT_OF_SERVICE. Из того, что я понимаю, похоже, что Asgard придерживается аналогичного подхода: https://github.com/Netflix/asgard/wiki/Eureka-Integration Я пришел к выводу, что для реализации скользящих обновлений на PWS нам нужно обычная, домашняя приборная панель (например, Asgard), которая будет способствовать этому. Представление PWS слишком ограничено для этого. AFAIK нет библиотеки Spring, которая делает это. Я не понимал, что для этого я мог бы создать свои собственные конечные точки REST, поэтому я начал с REST api самой Eureka. Я посмотрю на это - спасибо! –

+0

@nedenom, если вы установите статус в положение «ВНИЗ», он автоматически будет установлен на UP снова через 30 секунд. Если вы установите статус OUT_OF_SERVICE, он останется таким, пока вы вручную (через REST api) не установите его UP/DOWN. –