9

Sto sperimentando con una configurazione che è molto simile a quello descritto nell'immagine qui: https://raw.githubusercontent.com/Oreste-Luci/netflix-oss-example/master/netflix-oss-example.pngQual è il modo canonico per eseguire la distribuzione blu/verde con lo stack Spring Cloud/Netflix su PWS?

enter image description here

Nel mio setup, sto usando un'applicazione client (https://www.joedog.org/siege-home/), un proxy (Zuul), un servizio di scoperta (Eureka) e un semplice microservizio. Tutto è distribuito su PWS.

Desidero migrare da una versione del mio semplice microservizio al successivo senza alcun tempo di inattività. Inizialmente ho iniziato con la tecnica descritta qui: https://docs.cloudfoundry.org/devguide/deploy-apps/blue-green.html

A mio parere, questo approccio non è "compatibile" con un servizio di rilevamento come Eureka. In effetti, la nuova versione del mio servizio è registrata in Eureka e riceve traffico anche prima che possa rimappare tutti i percorsi (router CF).

Questo mi ha portato a un altro approccio, in cui mi baso sui meccanismi di failover in primavera Nuvola/Netflix:

  1. mi giro una nuova versione (compatibile) del mio servizio.
  2. Quando questa versione viene rilevata da Zuul/Eureka, inizia a ricevere il 50% del traffico.
  3. Una volta verificato che la nuova versione funziona correttamente, prendo la "vecchia" istanza. (Mi basta fare clic sul pulsante "Stop" in PWS)

quanto ho capito, Zuul utilizza nastro (bilanciamento del carico) sotto il cofano in modo in quella frazione di secondo in cui l'istanza è ancora in Eureka, ma in realtà l'arresto , Mi aspetto un nuovo tentativo sulla nuova istanza senza alcun impatto sul client.

Tuttavia, la mia ipotesi è errata. Ho un paio di 502 errori nel mio cliente:

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 

Parte del mio 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 

Non sono sicuro di quello che va male.

Si tratta di un problema tecnico?

Oppure sto facendo ipotesi sbagliate (ho letto da qualche parte che i POST non sono ancora riprovati, cosa che non capisco davvero)?

Mi piacerebbe sapere come lo si fa.

Grazie, Andy

risposta

2

Mi sono chiesto anche questo. Non pretenderò di aver usato Spring Cloud "In Anger". Ho appena sperimentato per un po '.

Assunzione: supponiamo che la fonte della verità per tutti gli stati di istanza sia memorizzata in Eureka, quindi Eureka dovrebbe essere il nostro meccanismo di controllo operativo. Possiamo utilizzare Eureka per disattivare un'istanza impostando lo stato dell'istanza su OUT_OF_SERVICE. Quando Ribbon aggiorna il suo elenco di server, non userà queste istanze fuori servizio. Eureka fornisce un'API REST per interrogare le istanze e impostare lo stato dell'istanza. Grande.

Il problema è: come identificare quali istanze sono nel gruppo blu e quali istanze sono nel gruppo verde?

Stavo pensando ... Eureka fornisce una mappa di metadati per ogni istanza. Di 'nel passo build/bake impostiamo un id della versione nella mappa dei metadati? Potremmo usare un ID commit Git o qualche schema di versioning semantico o qualsiasi altra cosa. Ok, ora posso esaminare i metadati Eureka e identificare le istanze Blue contro Green dato il valore della versione. Possiamo impostare i valori dei metadati in ogni servizio utilizzando le proprietà.

ad es. eureka.instance.metadataMap.version=8675309

Ora, sarebbe bello se potessimo dirlo a Eureka. "Prendi tutte le istanze per il servizio FUBAR e la versione 8675309 fuori servizio." Beh, non penso che sia fornito fuori dagli schemi. La cosa interessante di Spring Cloud è che tutti questi servizi, incluso Eureka Server, sono solo app di Spring che possiamo modificare per le nostre esigenze. Il codice seguente espone un punto finale che imposta le istanze "fuori servizio" in base a un nome app e una versione. Basta aggiungere questo controller al tuo Eureka Server. Non è pronto per la produzione, è solo un'idea in realtà.

Ora, una volta che Eureka ha messo fuori servizio questi casi e la barra multifunzione ha aggiornato il proprio elenco di server, è possibile eliminarli o eliminarli in modo sicuro.

POST:

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

Speranza che aiuta?

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

Buona idea. Sto guardando anche a questo. Ma non sono sicuro di farlo sul lato Eureka - se il servizio invia un nuovo battito cardiaco, non cambierà nuovamente il suo stato in SU? Il cloud Spring viene fornito con/pause e/resume endpoint, che a mio avviso modifica lo stato del client su OUT_OF_SERVICE o DOWN. Stavo pensando a uno script di implementazione che invia/interrompe prima della distribuzione. L'elenco delle istanze a cui inviare questo potrebbe ancora essere estratto da Eureka e filtrato dalla versione o qualcosa del genere. – nedenom

+0

Stavo controllando anche lo stato OUT_OF_SERVICE. Da quello che ho capito, sembra che Asgard abbia un approccio simile: https://github.com/Netflix/asgard/wiki/Eureka-Integration La mia conclusione è che, per implementare aggiornamenti continui su PWS, abbiamo bisogno di un cruscotto personalizzato, homebrewn (come Asgard) che faciliterà questo. La vista PWS è troppo limitata per farlo. AFAIK non esiste una libreria Spring che faccia questo. Non mi ero reso conto che avrei potuto sviluppare i miei endpoint REST per questo come hai fatto tu, quindi ho iniziato con l'API REST di Eureka stessa. Lo darò un'occhiata - grazie! –

+0

@nedenom se si imposta lo stato su GIÙ verrà automaticamente impostato su SU nuovamente dopo 30 secondi. Se si imposta lo stato su OUT_OF_SERVICE, rimarrà tale fino a quando non manualmente (tramite REST api) si imposta su UP/DOWN. –

Problemi correlati