Quality in Clouds Michał Dec
Czym jest Voluum Tracker? 1. dedykowany system dla marketingu afiliacyjnego (cloud hosted), 2. prawie 250mln transakcji obsługiwanych każdego dnia, 3. w szczycie blisko 3500 transakcji na sekundę, 4. serwery w każdym regionie AWS (9 regionów): łącznie ok. 50-55 serwerów, 5. 85GB danych produkcyjnych każdego dnia, 6. 80GB logów z poziomu LB, 7. 8GB logów systemowych, 8. własne rozwiązanie DB
Amazon WebServices Amazon Web Services offers a broad set of global compute, storage, database, analytics, application, and deployment services that help organizations move faster, lower IT costs, and scale applications. Obrazy instancji (AMI) Serwery (Elastic Cloud Computing) Load Balancing + AutoScaling + Launch Config Routing S3, SQS, SNS DynamoDB AWS - 5 razy większy niż 14 pozostały kokurentów...razem wziętych.
Architektura mikroserwisów źródło: http://martinfowler.com/articles/microservices.html
Architektura mikroserwisów Architektura monolityczna: - wprowadzenie małej zmiany wymaga przebudowy całego systemu, - bardzo trudno utrzymać przejrzystą, modularną strukturę, - skalowanie - cały system Architektura mikroserwisów: - funkcjonalnie wydzielone komponenty, - izolowany process deploymentu, - przystosowane do skalowania, - Fat JAR : np. Spring Boot, - zdecentralizowane zarządzanie
Rambo Architecture 1. każdy serwis powinien funkcjonować mimo degradacji serwisów, od których zależy (przynajmniej przez jakiś czas...), 2. każdy serwis powinien być odporny na degradację jakości odpowiedzi serwisów, od których zależy, 3. automatyczne analiza w czasie rzeczywistym stanu serwisów - odłączanie niestabilnych elementów systemu, 4. testy serwisu w sytuacji degradacji zależnych systemów. cache, failover policy, retry policy, data replication Serwis zależy od 30 innych serwisów - każdy ma 99,99% uptime -> sam serwis ma 99,7% uptime - 2h downtime każdego miesiąca!
Testowalny system (?) Problem: niskie pokrycie testami, które są fatalnej jakości a może system, którego nie można efektywnie testować! powszechny dostęp do kodu źródłowego + kod testów jest kodem produkcyjnym, łatwy i przejrzysty proces deploymentu ( Fat JAR, Spring Boot, AWS deployment), reużywalne komponent (commons) + bill of materials (BOM) DM, przejrzysty proces migracji DB (Flyway), przejrzyste i ujednolicone API
Developer + STE deployment kilkanaście razy w tygodniu -> nie ma testów manualnych, developer odpowiada za swój kod (również o 4 nad ranem:), developerzy piszą kod testowy, STE odpowiada za architekturę testów (..pisze kod, który pozwala developerom testować swój kod... ) bugfixing ma priorytet, review + pair programming
Testowanie 1. Testy unitowe (poznajemy funkcjonalność), 2. Testy integracyjne (pojedynczego serwisu), 3. Testy e2e (całego systemu), 4. Failure Injection Testing, 5. Testy obciążeniowe (replikacja ruchu produkcyjnego?) W testach integracyjnych mockujemy serwisy, od których dany komponent zależy (drugi serwis, ew. zewnętrzna usługa). REST na bazie HTTP - Jetty Server, MockServer, WireMock, InProcess DynamoDB etc.
Testowanie - mocking new MockServerClient("localhost", 9999).when( request().withmethod("post").withpath("/login").withquerystringparameters( new Parameter("returnUrl", "/account") ).withcookies( new Cookie("sessionId", "2By8LOhBmaW5nZXJwcmludCIlMDAzMW") ).withbody("{username: 'foo', password: 'bar'}"), Times.exactly(1) ).respond( response().withstatuscode(401).withheaders( new Header("Content-Type", "application/json; charset=utf-8"), new Header("Cache-Control", "public, max-age=86400") ).withbody("{ message: 'incorrect username and password combination' }").withdelay(new Delay(TimeUnit.SECONDS, 1)) );
DSL + Fluent Interface @Test(expectedExceptions = {ResponseValidationException.class}) public void shouldnotcreatelanderifurlcontainsdoublequote() throws Exception { //Given LanderModelBuilder lander = newlander().withurl("http://landers.com/bestlander?param1={country}¶m2={os}\"></head><body><script>alert('asd')</script>") DSL.withCountry(CountryModel.PL).withNamePostfix("NewLander1").withNumberOfOffers(2); //When createlander(lander); //Then fail("exception due to validation error should be thrown"); } public EntityResponse<T, JsonNode> create(t modelbean) { log.debug("about to create entity of type {}. Entity: \'{}\'", this.type, modelbean); Response response = this.restclient.executejsonpost(this.getpath(), this.resourcecodec.serialize(modelbean)); log.info("created entity of type {} with id: {} - \'{}\'", new Object[]{this.type, this.getid(modelbean), this.getname(modelbean)}); return this.createresponse(response, modelbean, this.resourcecodec); }
DSL + Fluent Interface //When LanderModel lander = createlander( //Given newlander() DSL.withUrl("http://landers.com/bestlander?param1={country}¶m2={os").withCountry(CountryModel.PL).withNamePostfix("NewLander1").withNumberOfOffers(2) ); //Then assertthat(lander).hasname("poland - NewLander1").hasNamePostfix("NewLander1").hasClientId(null).hasCountry(CountryModel.PL).hasNumberOfOffers("2").hasUrl("http://landers.com/bestlander?param1={country}¶m2={os");
Testowanie Czy infrastruktura testowa jest identyczna z produkcyjną? Jak długo jesteśmy w stanie pozwolić sobie na replikację ruchu produkcyjnego? Zawsze nadchodzi moment gdy koszty replikacji ruchu produkcyjnego/utrzymania infrastruktury są zby wysokie. Testujmy na Produkcji:)
Chaos engineer - Simian Army Chaos Monkey - zabija losowo wybraną inctancję na środowisku produkcyjnym, Latency Monkey - wprowadz opóźnienia w komunikacji klient - serwer symulując degradację stanu systemu Conformity Monkey - skanuje instance w poszukiwaniu, tych które odbiegaja pod względem konfiguracji/wydajności itd. Chaos Gorilla - symuluję degradację całego regionu AWS EC2 Maintenance Update - blisko 2700 serwerów Cassandry, 218 zostało zresetowanych, 22 serwery nie wstały po restarcie....data was deleted by a maintenance process that was inadvertently run against the production ELB state data - 24 December 2012, AWS outage
Production Code Coverage Canary Deployment Production Code Coverage wykrywanie martwych funkcjonalności, uszczelnianie testów najczęściej wykorzystywanych funkcjonalności Canary Deployment deployment na niewielką część instancji (jeden region, 1/10 serwerów...), analiza zachowanie zdeployowanego serwisu, szybki rollback, niewielki negatywny wpływ regresji
Monitoring aplikacji Zbieranie i raportowanie (Codehale Metrics) metryk do zewnętrznego systemu (Graphite): wskaźników (np. długość kolejki) liczników (np. ilość requestów/sekunda) histogramy czas operacji (np. czas odpowiedzi DB) Amazon CloudWatch - metryki z poziomu instancji (CPU, dysk, sieć...) lub LoadBalancera (czas odpowiedzi, ilość requestów, błędy...) Loggly - analiza logów aplikacji
Q&A michaldec@gmail.com codewise.com