ABONAMENTE VIDEO REDACȚIA
RO
EN
NOU
Numărul 150
Numărul 149 Numărul 148 Numărul 147 Numărul 146 Numărul 145 Numărul 144 Numărul 143 Numărul 142 Numărul 141 Numărul 140 Numărul 139 Numărul 138 Numărul 137 Numărul 136 Numărul 135 Numărul 134 Numărul 133 Numărul 132 Numărul 131 Numărul 130 Numărul 129 Numărul 128 Numărul 127 Numărul 126 Numărul 125 Numărul 124 Numărul 123 Numărul 122 Numărul 121 Numărul 120 Numărul 119 Numărul 118 Numărul 117 Numărul 116 Numărul 115 Numărul 114 Numărul 113 Numărul 112 Numărul 111 Numărul 110 Numărul 109 Numărul 108 Numărul 107 Numărul 106 Numărul 105 Numărul 104 Numărul 103 Numărul 102 Numărul 101 Numărul 100 Numărul 99 Numărul 98 Numărul 97 Numărul 96 Numărul 95 Numărul 94 Numărul 93 Numărul 92 Numărul 91 Numărul 90 Numărul 89 Numărul 88 Numărul 87 Numărul 86 Numărul 85 Numărul 84 Numărul 83 Numărul 82 Numărul 81 Numărul 80 Numărul 79 Numărul 78 Numărul 77 Numărul 76 Numărul 75 Numărul 74 Numărul 73 Numărul 72 Numărul 71 Numărul 70 Numărul 69 Numărul 68 Numărul 67 Numărul 66 Numărul 65 Numărul 64 Numărul 63 Numărul 62 Numărul 61 Numărul 60 Numărul 59 Numărul 58 Numărul 57 Numărul 56 Numărul 55 Numărul 54 Numărul 53 Numărul 52 Numărul 51 Numărul 50 Numărul 49 Numărul 48 Numărul 47 Numărul 46 Numărul 45 Numărul 44 Numărul 43 Numărul 42 Numărul 41 Numărul 40 Numărul 39 Numărul 38 Numărul 37 Numărul 36 Numărul 35 Numărul 34 Numărul 33 Numărul 32 Numărul 31 Numărul 30 Numărul 29 Numărul 28 Numărul 27 Numărul 26 Numărul 25 Numărul 24 Numărul 23 Numărul 22 Numărul 21 Numărul 20 Numărul 19 Numărul 18 Numărul 17 Numărul 16 Numărul 15 Numărul 14 Numărul 13 Numărul 12 Numărul 11 Numărul 10 Numărul 9 Numărul 8 Numărul 7 Numărul 6 Numărul 5 Numărul 4 Numărul 3 Numărul 2 Numărul 1
×
▼ LISTĂ EDIȚII ▼
Numărul 93
Abonament PDF

Poveste despre cloud (I)

Lucian Torje
Senior Java Developer @ Siemens



PROGRAMARE


Acest articol se dorește a fi o prezentare despre conceptul cloud, despre modul cum acesta a apărut, a evoluat, cât și despre problemele cu care s-a confruntat programarea în cloud.

Istoric

Cloud computing a fost definit prima dată de prof. John McCarthy în 1961 ca fiind:

"Folosirea calculatoarelor va fi probabil organizată cândva ca un serviciu public gen telefonul... Unde fiecare abonat trebuie să plătească doar cât a folosit, dar are acces la toate limbajele de programare de care are nevoie... Unii abonați își vor putea oferi serviciile altor abonați... Serviciul public de calculatoare va putea deveni baza unei noi și importante industrii."

În aceeași perioada a început dezvoltarea tehnologiilor care stau la baza cloudului:

Mulți ani după aceea, o dată cu creșterea masivă a utilizatorilor de Internet (1995-2000) a fost necesară adaptarea la noile probleme (mulți utilizatori, răspândire mare, servicii oferite fără întrerupere, performanță). Astfel au apărut modele computaționale gen:

Cloud-ul ca serviciu public a apărut în 2002, o dată cu lansarea de către Amazon a primului cloud comercial. Mai apoi, prin dezvoltarea OpenNebula și OpenStack, alte companii au putut să implementeze soluții de cloud privat în propriile centre de date.

Cronologie

În anii 90', majoritatea paginilor erau statice. Doar în a doua jumătate a anilor 90› debuta dezvoltarea paginilor dinamice simultan cu cererea utilizatorilor de avea conținut personalizat și posibilitatea de a executa funcționalități (gen login, upload de fișiere sau formular de contact), pe care o pagină statică nu le-ar fi putut oferi.

O pagină web în acea perioadă (Ebay) arăta așa:

În mai puțin de 20 de ani s-a ajuns de la a avea propriile servere, la a rula simple funcții în cloud a căror instalare durează sub 1 minut, versus o zi cât putea să dureze compilarea, împachetarea și instalarea pachetului nou la început.

În 1999, costul unei aplicații Java web era aproximativ 400.000 dolari (cheltuiala de capital - server + Weblogic + baza de date + IDE) și cam 80.000 dolari (cheltuiala operațională - mentenanța). Acum se poate ajunge la costuri operaționale lunare de aproximativ 250 dolari pentru funcționalități comparative (sursa cloudorado [47]), folosind tehnologii open source. Aceste reduceri de cost au permis răspândirea utilizării cloudului și implicit scăderea și mai mare a costurilor.

Expunem mai jos cronologia evenimentelor care au marcat evoluția cloudului:

-1990. Apare primul browser web numit WorldWideWeb.

-1993. Apar: HTML, specificații CGI (Common Gateway Interface - servere web sunt executate ca aplicații consolă), browserul NCSA Mosaic (suporta imagini inline) și organizația OASIS (Organization for the Advancement of Structured Information Standards).

-1994. Își face apariția PHP. Este creată World Wide Web Consortium - W3C (printre fondatori Comisia Europeana și DARPA). De notat și apariția Netscape Navigator care suporta HTTP Cookies.

-1995. Apache, IIS, MySQL.

-1996. Java, JavaScript, CSS, Adobe Flash (FutureSplash sau Flash 1.0) sunt definite IFrame și Java servleturile.

-1998. Tomcat, NoSQL. Sunt standardizate XML (W3C), SOAP și XML-RPC (Microsoft), Document Object Model - DOM (W3C), JavaServer Pages - JSP (Sun)

-1999. Apare Linux-HA (high availability), se definește AJAX - Asynchronous JavaScript + XML, Active Directory.

-2000. Sunt definite REST + HATEOAS (Roy Fielding), WSDL (IBM, Microsoft și Ariba), XHTML (W3C), JavaScript Object Notation - JSON (Douglas Crockford ).

-2001. VMware ESXi (hipervizor), Hibernate ORM. Apare HAProxy (load balancer open source). Este definit XSD (W3C) și Scalable Vector Graphics - SVG și se pun bazele Open Web Application Security Project (OWASP).

-2002. Amazon lansează primul cloud comercial, se definește Security Assertion Markup Language - SAML.

-2003. Se definește Advanced Message Queuing Protocol - AMQP (messaging pentru middleware), apare Memcached (caching tip cheie-valoare în memorie).

-2004. Nginx (server web care poate fi folosit și ca reverse proxy, load balancer, caching, media streaming și email server proxy), Selenium (teste automate). Se definește JavaServer Faces - JSF.

-2005. Începe lucrul la OpenNebula (cloud management - primul release public în 2008), apare conceptul de Data Lake, SoapUI, Git și Mercurial (revision control), Django (web framework pentru Python).

-2006. AWS EC2 (servere virtuale în cloud), AWS S3 (serviciu storare obiecte), OAuth (Twitter - protocol open source de securitate), SonarQube (inspecția continuă a calității codului), Liquibase.

-2007. KVM, RabbitMQ, Struts, OpenID Foundation, Neo4j, Bambo, Heroku (PaaS - Cloud Application Platform).

-2008. Apar Google Cloud Platform, Google App Engine, Alibaba cloud (Aliyun), Hadoop, Apache Cassandra, Cucumber.

-2009. cloudflare (servicii CDN), Apache Mesos (management cluster), Node.js, Redis, MongoDB.

-2010. PiCloud (FaaS), OpenStack (cloud management creat de Rackspace Hosting și NASA), CloudFoundry (PaaS - din 2019 suportă AWS, Azure, Google Compute Platform (GCP), OpenStack, VMware vSphere, SoftLayer, etc), apar Microsoft Azure, Elasticsearch, Vagrant (simularea environment-ului din producție pe calculatorul de development), Angular JS și Flask (web framework pentru Python).

-2011. 12 Factor, Parse Platform (MBaaS open sourced în 2016 - folosit de Back4App), AWS Elastic Beanstalk (orchestrator), OpenShift, Vert.X, Dropwizard, Puppet, React, WebSockets (RFC 6455), Bootstrap, NATS, Jenkins, Ansible, Swagger, Netflix SimianArmy/ChaosMonkey.

-2012 . Firebase (platforma de dezvoltare de aplicații mobile), OAuth 2.0, Amazon DynamoDB, Eureka, Zipkin, Netflix Ribbon (load balancer), Thoughtworks (Radar - https://www.thoughtworks.com/radar), Arquillian, Baidu cloud (Wangpan) .

-2013. Spring Boot, Docker, Netflix Scryer outsourcing, JHipster.

-2014. The Reactive Manifesto, Kubernetes (open-source container storage), Terraform (IaaS), Hashicorp Consul (service discovery, DNS), RxJava, HTML5 (World Wide Web Consortium - W3C), AWS Lambda, AWS Aurora, Apache Spark.

-2015. Docker Swarm, Spring cloud Starter Zuul [4], Spring cloud Starter Ribbon [4], Pentaho (business intelligence), gRPC Remote Procedure Calls, HTTP/2, Flyway, Gatling (load testing).

-2016. Netflix Meson outsourcing, Spring cloud Starter Sleuth [4], Back4App (BaaS), Prometheus (monitorizare și alertare), OpenTracing3 (instrumentare pentru tracing distribuit), AWS X-Ray (debugging), IBM cloud .

-2017. Istio (service mesh), Netflix Conductor outsourcing, Squash și Telepresence (debuggere pentru microservicii în Kubernetes), Hapi, Jaeger, Kubernetes Ingress Nginx, AWS IoT Greengrass, Amazon SageMaker.

-2018. SuperGloo (platforma pentru mesh orchestration) [7], Micronaut, Helidon, Firecracker (tehnologie de virtualizare open source), Azure IoT Edge, Google AI Platform, Apache OpenWhisk (serverless), mai multe proiecte Netflix intră în maintenance mode (Hystrix înlocuit de Resilience4j, Hystrix Dashboard/Tubine înlocuit de Micrometer, Ribbon înlocuit de Spring Cloud Load Balancer, Zuul inlocuit de Spring Cloud Gateway, Archaius inlocuit de Spring Cloud Config)

-2019. AWS Outposts (servere on-premise), Quarkus, Outsource Netflix Mantis, Fluentd (data collector).

-2020. Netflix Zuul 2 outsourcing, Kubefed2 (Kubernetes Federation).

Tipuri de cloud

Înainte de a discuta despre evoluția cloudului, vom defini tipurile de cloud și servicii oferite.

Cloud public

Amazon Web Service, Google cloud Platform sau Microsoft Azure sunt clouduri publice unde mai mulți utilizatori (mediu multi-tenant) folosesc în comun mai multe resurse fizice, plătind doar pentru serviciile folosite și în funcție de durată, capacitate, viteza la care au fost folosite sau de specificațiile sistemului folosit (de exemplu, hardware, software, distribuire regională).

Aceste servicii au SLA - service level agreement - un contract prin care se garantează disponibilitatea serviciului la parametri conveniți.

Dacă SLA nu oferă ceea ce dorește organizația ca performanță, capabilități sau nivelul de securitate nu este de ajuns, se poate opta pentru un cloud privat.

Beneficii cloud public

Marco Meinardi, director de cercetare la Gartner, a publicat un studiu în care migrarea a 2500 servere virtuale proprii migrate în AWS EC2 a dus la o scădere cu 55% a costurilor totale.

Printre alte beneficii ale cloudului public, enumerăm:

Cloud privat

Cloudul privat este destinat nevoilor unei singure organizații. Poate fi de mai multe tipuri:

Există mai mulți furnizori/aplicații de soluții de cloud privat:

Cloud hibrid

Cloudul hibrid folosește ceea ce este mai bun din cele două soluții - flexibilitatea cloudului privat și costurile reduse ale cloudului public, făcând legătura între cele două.

Tipuri de modele de servicii cloud

Modelele de servicii merg de la a oferi doar infrastructura până la a oferi posibilitatea de a crea o nouă aplicație, doar valorificând servicii existente, gen pipeline.

Lista serviciilor conține [19]:

Cloud (2005 - 2011) - fundația/infrastructura

Înainte de prima generație de cloud, programarea aplicațiilor însemna de cele mai multe ori folosirea de editoare text cum ar fi emacs, vi, joe sau notepad (editoare WYSIWYG precum Macromedia Dreamweaver erau scumpe), compilarea și împachetarea aplicației, copierea pe discheta/CD sau ftp/shared folder. Odată creat manual pachetul de instalare, o altă persoană (ITOps) urma să facă instalarea și, de prea multe ori, nu funcționa manifestându-se prin căi către fișiere fixe în cod, pași de instalare incorecți, variabile de sistem diferite, router care trebuia repornit, etc.

Prima generație cloud a făcut trecerea de la centrul de date (care însemnau de obicei unul sau mai multe servere blade/PC) la o arhitectură virtuală (de exemplu, servere care rulau VMware ESXi ca sistem de operare).

Această arhitectură rezolva câteva probleme.

Problema 1 - nevoia unui calculator preconfigurat.

Uneori, apărea nevoia de a obține un calculator pentru o scurtă perioadă de timp. Virtualizarea a rezolvat această problemă pentru că nu mai era nevoie de cerere pentru buget, așteptarea livrării calculatorului și instalarea sistemului de operare dorit. În câteva minute era posibil să se cloneze o imagine deja definită și să se pornească. O altă soluție care a apărut mai târziu - folosirea de imagini Docker (de exemplu, nginx:alpine). În imaginea de mai jos, preluată din articolul [20], se poate observa o comparație între timpul necesar pornirii unei mașini virtuale, container sau o funcție:

Problema 2 - backup / restore

Backupul datelor era deja realizat în SAN/NAS/RAID, dar cum se putea face backup la un server în producție cu efecte minime asupra utilizatorilor ?

Virtualizarea a făcut acest lucru posibil prin crearea de snapshoturi (puncte de restaurare). Ca bonus, acestea puteau fi restaurate mai târziu nu numai în caz de probleme, dar și pentru a se ajunge la o stare a sistemului de operare dorită (de exemplu, în cazul reproducerii unui defect mai vechi).

Problema 3 - performanța

Chiar dacă viteza la Internet a crescut și modemurile de 56kbps sunt istorie, aplicațiile web încep să aibă cerințe mai mari ca performanță și bandwidth. Mai mult, aplicațiile devin globale și pagini web încep să fie accesate simultan din mai multe spații geografice. Pentru a îmbunătăți performanța, au fost implementate câteva tehnici:

SPAs (Single-Page Applications)

Înainte de aplicațiile web tip SPA (o singură pagină folosind AJAX pentru schimbările de UI), majoritatea interacțiunilor cu paginile web presupuneau încărcarea de pe server a unor pagini noi. Aplicațiile SPA permit interacțiunea dinamică (încarcă doar ceea ce este necesar), prevenind astfel întreruperile și, bineînțeles, oferind o experiență mai bună pentru utilizator.

Problema 4 - 10000 conexiuni per secundă (C10K)

Cererea cât mai mare a dus la probleme de conexiune. În modelul client-server tradițional, bazat pe threaduri limitate ca număr de către OS și ineficiente (vezi legea lui Amdahl) se ajunsese deja la maximul conexiunilor posibile.

O primă soluție la această problemă a oferit-o modelul Leaky Bucket (la nivel de network), mai apoi Reactor (servirea clienților în limitele posibile, implementată prin Nginx prin număr fix de workeri - un singur thread și pooling ciclic între procesele client) și versiunea asincronă - modelul Proactor.

O altă soluție se baza pe Websockets și modelul publish/subscribe.

Astfel s-a ajuns în 1999 la 10k conexiuni/secundă, iar în 2010 la 1M conexiuni (WhatsApp) și în 2015 la 10M-20M (MigratoryData).

Problema 5 - securitate

Deschiderea spre un mediu global a însemnat și mai multe amenințări de securitate. Drept consecință, au apărut standarde noi și moduri de protecție inovate sau readaptate:

Problema 6 - refolosirea codului ca serviciu

Deoarece într-o organizație mare se ajungea la aceleași tipuri de servicii - de exemplu, mai multe departamente aveau nevoie pentru aplicația lor de un serviciu de conversie a valutei - s-a ajuns la nevoia de a avea un singur serviciu care să servească alte servicii și nu doar aplicații client.

Schimbul de date (în format HTML/XML/JSON/serializare obiecte sau alte formate) se obținea, de obicei, prin apeluri RPC/HTTP/REST/SOAP/WSDL/JMS.

Problema 7 - high availability

Pentru că serviciile web serveau 24/7, mentenanța peste noapte nu mai era o opțiune, oprirea lor din cauza unor erori sau accidentală însemnând pierderi de bani.

Astfel a apărut necesitatea folosirii unor unelte gen watchdog care să verifice starea sistemului și în caz de probleme, să acționeze într-un mod predictibil. Cele mai simple erau cron jobs care verificau starea serviciilor (init.d). În unele cazuri un simplu restart nu mai era de ajuns, așa că au apărut soluții de high availability gen Linux-HA și Neverfail Heartbeat (dezvoltată și în Cluj-Napoca). Neverfail Heartbeat oferea posibilitatea de redundanță a serverelor și schimbarea serverului primar cu secundarul/terțiarul fără ca utilizatorul să observe o diferență (de exemplu, o interogare SQL începea pe un server/primar, iar alt server/secundar returna rezultatul). Pentru baze de date existau și alte soluții gen failover clustering.

Problema 8 - instalarea

Instalarea unei noi versiuni însemna, în cazul Java, încărcarea unui fișier war pe web server și rularea, de obicei manuală, a scripturilor SQL.

Aceasta însemna că serverul din producție trebuia oprit temporar. Pentru a avea o aplicație care să servească în continuare clienții, se opta fie pentru soluții de mai sus sau se folosea serverul de staging ca server de producție, iar după instalare se făcea schimbul din nou și migrarea datelor.

Acest tip de instalare se numește Blue-Green deployment. Pentru a oferi cât mai repede funcționalități noi unor grupuri de utilizatori, a apărut și noțiunea de canary deployment - instalare doar pentru anumite regiuni/utilizatori/grupuri.

Problema 9 - scalabilitate

Scalabilitatea a apărut ca necesitate, ca urmare a numărului din ce în ce mai mare de clienți. Scalabilitatea verticală se obținea prin creșterea capacității serverului (mai multe core-uri de CPU, memorie mai mare, etc.) și orizontal, prin folosirea de load balancer gen HAProxy cu o configurație statică (serverele erau adăugate manual, direct în fișierul de configurare sau se folosea o aplicație gen Puppet/Ansible pentru setarea configurării).

Problema 10 - performanță pentru baza comună de date

Uneori, aceeași bază de date era folosită în comun de mai multe procese - unele cu acces pentru citire, altele pentru citire/scriere.

Distributed Database Sharding (aceeași schemă, date diferite distribuite pe servere diferite) sau CQRS (baze de date separate pentru read și write care se sincronizează) rezolvă această problemă.

Cloud (2012 - 2016) - platforma

Scalabilitatea verticală era limitată, în mare parte, la capabilitățile aplicațiilor care rulau pe acel server. Creșterea performanțelor unei singure aplicații începea să coste din ce în ce mai mult, pentru rezultate modeste (o creștere de 1-2%). Era timpul pentru o schimbare de concepție; modelul Horizontally Scaling Compute [29], fiind deja folosit, promitea o creștere mult mai mare a performanțelor, ca sistem și nu ca aplicație singulară. În acest fel a început migrarea în masă de la arhitecturi gen monolit la microservicii. Acest lucru era deja posibil și înainte, prin event-driven development realizat, de obicei, cu RabbitMQ cluster drept coadă de mesaje (uneori implementat ca remote procedure call, în cazuri mai complicate în calitate de CQRS - vezi Queue-Centric Workflow [29]).

De asemenea, se observă trecerea cât mai mare de la centru de date propriu la cloud public.

... partea a doua a articolului va fi publicată în următoarea ediție a revistei Today Software Magazine, nr. 94 aprilie/2020

Bibliografie

  1. https://medium.com/threat-intel/cloud-computing-e5e746b282f5

  2. https://blogs.gartner.com/marco-meinardi/2018/11/30/public-cloud-cheaper-than-running-your-data-center/

  3. https://itif.org/publications/2017/05/01/cross-border-data-flows-where-are-barriers-and-what-do-they-cost

  4. https://mvnrepository.com/

  5. https://www.eweek.com/innovation/cloud-2020-canonical-s-six-trends-shaping-the-future-of-cloud

  6. https://www.gartner.com/smarterwithgartner/4-trends-impacting-cloud-adoption-in-2020/

  7. https://medium.com/solo-io/https-medium-com-solo-io-supergloo-ff2aae1fb96f

  8. https://imelgrat.me/cloud/cloud-services-models-help-business/

  9. https://blog.back4app.com/2019/07/24/backend-as-a-service-baas/

  10. https://www.martinfowler.com/eaaCatalog/index.html

  11. https://blog.cdemi.io/design-patterns-cache-aside-pattern/

  12. https://www.enterpriseintegrationpatterns.com/patterns/conversation/LoadBalancer.html

  13. https://martinfowler.com/bliki/CircuitBreaker.html

  14. https://martinfowler.com/articles/feature-toggles.html

  15. https://www.toptal.com/software/trunk-based-development-git-flow

  16. https://dzone.com/articles/event-driven-microservices-patterns

  17. https://microservices.io/patterns/monolithic.html

  18. https://dzone.com/articles/microservices-quarkus-vs-spring-boot

  19. https://imelgrat.me/cloud/cloud-services-models-help-business/

  20. https://www.cloudflare.com/learning/serverless/serverless-vs-containers/

  21. https://dzone.com/articles/the-future-of-spring-cloud-microservices-after-net

  22. https://medium.com/@priyalwalpita/software-architecture-patterns-layered-architecture-a3b89b71a057

  23. https://medium.com/@SoftwareDevelopmentCommunity/what-is-service-oriented-architecture-fa894d11a7ec

  24. https://restfulapi.net/versioning/

  25. https://dzone.com/articles/monolith-to-microservices-using-the-strangler-patt

  26. Working Effectively with Legacy Code by Michael Feathers

  27. Patterns of Enterprise Application Architecture by Martin Fowler

  28. cloud Native Development Patterns and Best Practices by John Gilbert

  29. cloud Architecture Patterns by Bill Wilder

  30. https://12factor.net/

  31. https://mikhail.io/serverless/coldstarts/aws/

  32. https://www.martinfowler.com/articles/micro-frontends.html

  33. https://thenewstack.io/nats-rest-alternative-provides-messaging-distributed-systems/

  34. https://thenewstack.io/10-key-attributes-of-cloud-native-applications/

  35. https://www.confluent.io/blog/event-sourcing-cqrs-stream-processing-apache-kafka-whats-connection/

  36. https://github.com/Netflix/eureka/wiki/Eureka-at-a-glance

  37. https://devops.com/feature-branching-vs-feature-flags-whats-right-tool-job/

  38. https://cloud.spring.io/spring-cloud-netflix/multi/multi__modules_in_maintenance_mode.html

  39. https://youtu.be/jufe_sHejXc

  40. https://pivotal.io/cloud-native

  41. https://docs.nats.io/nats-protocol/nats-protocol

  42. https://grpc.io/

  43. https://spring.io/blog/2019/03/14/lazy-initialization-in-spring-boot-2-2

  44. https://kubernetes.io/docs/concepts/cluster-administration/federation/#why-federationc

  45. https://docs.microsoft.com/en-us/azure/architecture/patterns/bulkhead

  46. https://www.networkworld.com/article/3224893/what-is-edge-computing-and-how-it-s-changing-the-network.html

  47. https://www.cloudorado.com/cloud_hosting_comparison.jsp

  48. https://docs.aws.amazon.com/lambda/latest/dg/gettingstarted-limits.html

  49. https://aws.amazon.com/blogs/compute/new-for-aws-lambda-predictable-start-up-times-with-provisioned-concurrency/

  50. https://mashable.com/2010/08/07/ebay-facts/?europe=true

  51. https://www.bcs.org/content-hub/history-of-the-cloud/

NUMĂRUL 149 - Development with AI

Sponsori

  • Accenture
  • BT Code Crafters
  • Accesa
  • Bosch
  • Betfair
  • MHP
  • BoatyardX
  • .msg systems
  • P3 group
  • Ing Hubs
  • Cognizant Softvision
  • Colors in projects