Microservices voorzien van containers
Tip
Deze inhoud is een fragment uit het eBook, Enterprise Application Patterns Using .NET MAUI, beschikbaar op .NET Docs of als een gratis downloadbare PDF die offline kan worden gelezen.
Het ontwikkelen van client-servertoepassingen heeft geleid tot een focus op het bouwen van gelaagde toepassingen die gebruikmaken van specifieke technologieën in elke laag. Dergelijke toepassingen worden vaak monolithisch genoemd en worden verpakt op hardware die vooraf is geschaald voor piekbelastingen. De belangrijkste nadelen van deze ontwikkelbenadering zijn de nauwe koppeling tussen onderdelen binnen elke laag, die afzonderlijke onderdelen niet eenvoudig kunnen worden geschaald en de kosten van het testen. Een eenvoudige update kan onvoorziene gevolgen hebben voor de rest van de laag, dus voor een wijziging in een toepassingsonderdeel moet de hele laag opnieuw worden getest en opnieuw worden geïmplementeerd.
Met name met betrekking tot de leeftijd van de cloud is dat afzonderlijke onderdelen niet eenvoudig kunnen worden geschaald. Een monolithische toepassing bevat domeinspecifieke functionaliteit en wordt doorgaans gedeeld door functionele lagen zoals front-end, bedrijfslogica en gegevensopslag. In de onderstaande afbeelding ziet u dat een monolithische toepassing wordt geschaald door de hele toepassing op meerdere computers te klonen.
Microservices
Microservices bieden een andere benadering voor het ontwikkelen en implementeren van toepassingen, een benadering die geschikt is voor de vereisten voor flexibiliteit, schaal en betrouwbaarheid van moderne cloudtoepassingen. Een microservicestoepassing wordt gesplitst in onafhankelijke onderdelen die samenwerken om de algehele functionaliteit van de toepassing te leveren. De term microservice benadrukt dat toepassingen moeten bestaan uit services die klein genoeg zijn om specifieke zorgen te weerspiegelen, zodat elke microservice één functie implementeert. Bovendien heeft elke microservice goed gedefinieerde contracten waarmee andere microservices communiceren en gegevens delen. Typische voorbeelden van microservices zijn winkelwagens, voorraadverwerking, aankoopsubsystemen en betalingsverwerking.
Microservices kunnen onafhankelijk worden geschaald in vergelijking met gigantische monolithische toepassingen die samen worden geschaald. Dit betekent dat een specifiek functioneel gebied dat meer verwerkingskracht of netwerkbandbreedte nodig heeft om de vraag te ondersteunen, kan worden geschaald in plaats van onnodig andere toepassingsgebieden uit te schalen. In de onderstaande afbeelding ziet u deze benadering, waarbij microservices onafhankelijk worden geïmplementeerd en geschaald, waardoor exemplaren van services op verschillende computers worden gemaakt.
Uitschalen van microservices kan vrijwel onmiddellijk zijn, waardoor een toepassing zich kan aanpassen aan veranderende belastingen. Een enkele microservice in de webgerichte functionaliteit van een toepassing kan bijvoorbeeld de enige microservice zijn die moet worden uitgeschaald om extra binnenkomend verkeer af te handelen.
Het klassieke model voor schaalbaarheid van toepassingen is het hebben van een staatloze laag met gelijke taakverdeling met een gedeeld extern gegevensarchief om permanente gegevens op te slaan. Stateful microservices beheren hun eigen permanente gegevens, meestal lokaal opslaan op de servers waarop ze worden geplaatst, om de overhead van netwerktoegang en complexiteit van crossservicebewerkingen te voorkomen. Dit maakt de snelst mogelijke verwerking van gegevens mogelijk en kan de noodzaak van cachingsystemen elimineren. Daarnaast partitioneren schaalbare stateful microservices meestal gegevens tussen hun exemplaren, om de gegevensgrootte te beheren en doorvoer over te dragen die door één server kan worden ondersteund.
Microservices ondersteunen ook onafhankelijke updates. Deze losse koppeling tussen microservices biedt een snelle en betrouwbare toepassingsontwikkeling. Hun onafhankelijke, gedistribueerde aard helpt bij het rollen van updates, waarbij slechts een subset van exemplaren van één microservice op elk gewenst moment wordt bijgewerkt. Als er een probleem wordt gedetecteerd, kan een buggy-update daarom worden teruggedraaid, voordat alle exemplaren worden bijgewerkt met de foutieve code of configuratie. Op dezelfde manier maken microservices doorgaans gebruik van schemaversiebeheer, zodat clients een consistente versie zien wanneer updates worden toegepast, ongeacht met welk microservice-exemplaar wordt gecommuniceerd.
Daarom hebben microservicetoepassingen veel voordelen ten opzichte van monolithische toepassingen:
- Elke microservice is relatief klein, gemakkelijk te beheren en te ontwikkelen.
- Elke microservice kan onafhankelijk van andere services worden ontwikkeld en geïmplementeerd.
- Elke microservice kan onafhankelijk worden uitgeschaald. Een catalogusservice of winkelwagenservice moet bijvoorbeeld meer worden uitgeschaald dan een bestelservice. Daarom verbruikt de resulterende infrastructuur efficiënter resources bij het uitschalen.
- Elke microservice isoleert eventuele problemen. Als er bijvoorbeeld een probleem is in een service, heeft dit alleen invloed op die service. De andere services kunnen aanvragen blijven verwerken.
- Elke microservice kan gebruikmaken van de nieuwste technologieën. Omdat microservices autonoom zijn en naast elkaar worden uitgevoerd, kunnen de nieuwste technologieën en frameworks worden gebruikt in plaats van een ouder framework te gebruiken dat kan worden gebruikt door een monolithische toepassing.
Een oplossing op basis van een microservice heeft echter ook mogelijke nadelen:
- Het kan lastig zijn om een toepassing te partitioneren in microservices, omdat elke microservice volledig autonoom, end-to-end moet zijn, inclusief verantwoordelijkheid voor de gegevensbronnen.
- Ontwikkelaars moeten communicatie tussen services implementeren, waardoor complexiteit en latentie aan de toepassing worden toegevoegd.
- Atomische transacties tussen meerdere microservices zijn meestal niet mogelijk. Daarom moeten bedrijfsvereisten uiteindelijk consistentie tussen microservices omvatten.
- In productie is er een operationele complexiteit bij het implementeren en beheren van een systeem dat is aangetast door veel onafhankelijke services.
- Directe communicatie tussen clients en microservices kan het lastig maken om de contracten van microservices te herstructureren. Na verloop van tijd moet het systeem bijvoorbeeld worden gepartitioneerd in services. Een enkele service kan worden gesplitst in twee of meer services en twee services kunnen worden samengevoegd. Wanneer clients rechtstreeks communiceren met microservices, kan dit herstructureringswerk de compatibiliteit met client-apps verbreken.
Containervorming
Containerisatie is een benadering van softwareontwikkeling waarbij een toepassing en de versie-set afhankelijkheden, plus de omgevingsconfiguratie die is geabstraheerd als distributiemanifestbestanden, worden verpakt als een containerinstallatiekopie, getest als een eenheid en geïmplementeerd op een hostbesturingssysteem.
Een container is een geïsoleerde, door resources beheerde en draagbare besturingsomgeving, waar een toepassing kan worden uitgevoerd zonder de resources van andere containers of de host aan te raken. Daarom ziet een container eruit als een zojuist geïnstalleerde fysieke computer of een virtuele machine.
Er zijn veel overeenkomsten tussen containers en virtuele machines, zoals hieronder wordt geïllustreerd.
Een container voert een besturingssysteem uit, heeft een bestandssysteem en kan worden geopend via een netwerk alsof het een fysieke of virtuele machine is. De technologie en concepten die door containers worden gebruikt, verschillen echter erg van virtuele machines. Virtuele machines omvatten de toepassingen, de vereiste afhankelijkheden en een volledig gastbesturingssysteem. Containers omvatten de toepassing en de bijbehorende afhankelijkheden, maar delen het besturingssysteem met andere containers, die worden uitgevoerd als geïsoleerde processen op het hostbesturingssysteem (afgezien van Hyper-V-containers die binnen een speciale virtuele machine per container worden uitgevoerd). Containers delen daarom resources en vereisen doorgaans minder resources dan virtuele machines.
Het voordeel van een containergeoriënteerde ontwikkelings- en implementatiebenadering is dat het grootste deel van de problemen die zich voordoen als gevolg van inconsistente omgevingsinstellingen en de problemen die hiermee worden geleverd, elimineert. Bovendien kunnen containers snelle functionaliteit voor het omhoog schalen van toepassingen toestaan door nieuwe containers naar behoefte in te schakelen.
De belangrijkste concepten bij het maken en werken met containers zijn:
Concept | Beschrijving |
---|---|
Containerhost | De fysieke of virtuele machine die is geconfigureerd voor het hosten van containers. De containerhost voert een of meer containers uit. |
Containerinstallatiekopie | Een installatiekopieën bestaan uit een samenvoeging van gelaagde bestandssysteemen die op elkaar zijn gestapeld en is de basis van een container. Een installatiekopie heeft geen status en verandert nooit omdat deze wordt geïmplementeerd in verschillende omgevingen. |
Container | Een container is een runtime-exemplaar van een installatiekopieën. |
Installatiekopieën van containerbesturingssystemen | Containers worden geïmplementeerd vanuit installatiekopieën. De installatiekopieën van het containerbesturingssysteem zijn de eerste laag in mogelijk veel lagen van installatiekopieën waaruit een container bestaat. Een containerbesturingssysteem is onveranderbaar en kan niet worden gewijzigd. |
Containeropslagplaats | Telkens wanneer een containerinstallatiekopieën worden gemaakt, worden de installatiekopieën en de bijbehorende afhankelijkheden opgeslagen in een lokale opslagplaats. Deze installatiekopieën kunnen vaak opnieuw worden gebruikt op de containerhost. De containerinstallatiekopieën kunnen ook worden opgeslagen in een openbaar of privéregister, zoals Docker Hub, zodat ze kunnen worden gebruikt op verschillende containerhosts. |
Ondernemingen gebruiken steeds vaker containers bij het implementeren van microservicetoepassingen en Docker is de standaardcontainer-implementatie geworden die door de meeste softwareplatforms en cloudleveranciers is aangenomen.
De eShop-referentietoepassing maakt gebruik van Docker voor het hosten van vier microservices in containers, zoals wordt geïllustreerd in het onderstaande diagram.
De architectuur van de back-endservices in de referentietoepassing is onderverdeeld in meerdere autonome subsystemen in de vorm van samenwerkende microservices en containers. Elke microservice biedt één functionaliteitsgebied: een identiteitsservice, een catalogusservice, een bestelservice en een mandservice.
Elke microservice heeft een eigen database, zodat deze volledig kan worden losgekoppeld van de andere microservices. Waar nodig wordt consistentie tussen databases van verschillende microservices bereikt met behulp van gebeurtenissen op toepassingsniveau. Zie Communicatie tussen microservices voor meer informatie.
Communicatie tussen client en microservices
De eShop-app voor meerdere platforms communiceert met de in containers geplaatste back-end microservices met behulp van directe client-naar-microservicecommunicatie , zoals hieronder wordt weergegeven.
Met directe client-naar-microservice-communicatie doet de app met meerdere platforms aanvragen rechtstreeks via het openbare eindpunt aan elke microservice, met een andere TCP-poort per microservice. In productie wordt het eindpunt meestal toegewezen aan de load balancer van de microservice, waarmee aanvragen over de beschikbare exemplaren worden gedistribueerd.
Tip
Overweeg het gebruik van API-gatewaycommunicatie.
Directe client-naar-microservice-communicatie kan nadelen hebben bij het bouwen van een grote en complexe microservicetoepassing, maar het is meer dan voldoende voor een kleine toepassing. Overweeg het gebruik van API-gatewaycommunicatie bij het ontwerpen van een grote toepassing op basis van microservices met tientallen microservices.
Communicatie tussen microservices
Een toepassing op basis van microservices is een gedistribueerd systeem dat mogelijk op meerdere computers wordt uitgevoerd. Elk service-exemplaar is doorgaans een proces. Daarom moeten services communiceren met behulp van een communicatieprotocol tussen processen, zoals HTTP, TCP, Advanced Message Queuing Protocol (AMQP) of binaire protocollen, afhankelijk van de aard van elke service.
De twee algemene benaderingen voor microservice-naar-microservice-communicatie zijn OP HTTP gebaseerde REST-communicatie bij het uitvoeren van query's op gegevens en lichtgewicht asynchrone berichten bij het communiceren van updates over meerdere microservices.
Asynchrone gebeurtenisgestuurde communicatie op basis van berichten is essentieel bij het doorgeven van wijzigingen in meerdere microservices. Met deze methode publiceert een microservice een gebeurtenis wanneer er iets opmerkelijks gebeurt, bijvoorbeeld wanneer een bedrijfsentiteit wordt bijgewerkt. Andere microservices abonneren zich op deze gebeurtenissen. Wanneer een microservice vervolgens een gebeurtenis ontvangt, worden de eigen bedrijfsentiteiten bijgewerkt, wat op zijn beurt kan leiden tot meer gebeurtenissen die worden gepubliceerd. Deze functionaliteit voor publiceren/abonneren wordt meestal bereikt met een gebeurtenisbus.
Met een gebeurtenisbus kunt u communicatie tussen microservices publiceren en abonneren zonder dat de onderdelen expliciet van elkaar op de hoogte moeten zijn, zoals hieronder wordt weergegeven.
Vanuit het perspectief van een toepassing is de gebeurtenisbus gewoon een kanaal voor publiceren-abonneren dat beschikbaar is via een interface. De manier waarop de gebeurtenisbus wordt geïmplementeerd, kan echter variëren. Een event bus-implementatie kan bijvoorbeeld gebruikmaken van RabbitMQ, Azure Service Bus of andere servicebussen, zoals NServiceBus en MassTransit. In het onderstaande diagram ziet u hoe een gebeurtenisbus wordt gebruikt in de eShop-referentietoepassing.
De eShop-gebeurtenisbus, geïmplementeerd met RabbitMQ, biedt een-op-veel asynchrone functionaliteit voor publiceren/abonneren. Dit betekent dat er na het publiceren van een gebeurtenis meerdere abonnees kunnen zijn die naar dezelfde gebeurtenis luisteren. In het onderstaande diagram ziet u deze relatie.
Deze een-op-veel-communicatiebenadering maakt gebruik van gebeurtenissen voor het implementeren van zakelijke transacties die meerdere services omvatten, waardoor uiteindelijke consistentie tussen de services wordt gegarandeerd. Een uiteindelijke consistente transactie bestaat uit een reeks gedistribueerde stappen. Wanneer de microservice van het gebruikersprofiel de opdracht UpdateUser ontvangt, worden de gegevens van de gebruiker in de database bijgewerkt en wordt de gebeurtenis UserUpdated gepubliceerd naar de gebeurtenisbus. Zowel de basket microservice als de bestellende microservice hebben zich geabonneerd om deze gebeurtenis te ontvangen, en in reactie daarop hun kopersinformatie bij te werken in hun respectieve databases.
Samenvatting
Microservices bieden een benadering voor het ontwikkelen en implementeren van toepassingen die geschikt zijn voor de vereisten voor flexibiliteit, schaal en betrouwbaarheid van moderne cloudtoepassingen. Een van de belangrijkste voordelen van microservices is dat ze onafhankelijk van elkaar kunnen worden uitgeschaald, wat betekent dat een specifiek functioneel gebied kan worden geschaald waarvoor meer verwerkingskracht of netwerkbandbreedte nodig is om de vraag te ondersteunen zonder onnodige schaalgebieden van de toepassing die geen verhoogde vraag ondervinden.
Een container is een geïsoleerde, door resources beheerde en draagbare besturingsomgeving waar een toepassing kan worden uitgevoerd zonder de resources van andere containers of de host aan te raken. Ondernemingen gebruiken steeds vaker containers bij het implementeren van op microservices gebaseerde toepassingen en Docker is de standaardcontainer-implementatie geworden die de meeste softwareplatforms en cloudleveranciers hebben aangenomen.