Orkestrering

Kubernetes

En praktisk översikt över de centrala Kubernetes-objekten som kör applikationer i ett kluster.

Kubernetes är ett kontrollsystem för att köra containeriserade applikationer i stor skala. Du beskriver önskat tillstånd , vilka arbetslaster som ska köras, hur många repliker, vart trafik ska gå , och Kubernetes stämmer kontinuerligt av klustrets faktiska tillstånd mot det. Modellen skapades för att lösa de operativa problem som uppstår när många team kör många tjänster på många maskiner: manuella omstarter, sköra driftsättningar, inkonsekvent konfiguration och oförutsägbar återhämtning från fel.

Lärandemål

Det här ska du kunna efter genomläsning.
  • Förklara rollen för de viktigaste arbetslast- och nätverksobjekten i ett Kubernetes-kluster.
  • Beskriva hur Pods, Deployments och Services samverkar för att hålla en applikation i drift.
  • Känna igen var konfiguration och routning normalt ligger i klustret.

I korthet

En snabb mental modell innan du går på djupet.
Centrala arbetslastobjekt
  • Pods
  • Deployments
  • Namespaces
Åtkomst och trafik
  • Services
  • Ingress
  • Nätvägar
Konfiguration
  • ConfigMaps
  • Secrets
  • Deklarativa uppdateringar

Kärnidén

Kubernetes är byggt kring en avstämningsloop. Varje komponent i systemet bevakar gapet mellan önskat tillstånd (vad API-servern har registrerat) och faktiskt tillstånd (vad som körs på noderna). När ett gap uppstår, en Pod kraschar, en ny Deployment skapas, en nod går offline, skapar, raderar eller justerar relevant kontroller arbetslaster tills faktiskt tillstånd matchar önskat tillstånd igen.

Kontrollplanet lagrar allt önskat tillstånd i etcd, ett distribuerat nyckel-värde-lager. API-servern är den enda ingångspunkten för alla läsningar och skrivningar till det tillståndet. Kontroller kör i en loop och läser från API-servern och utfärdar instruktioner till arbetarnoder via kubelet. Denna åtskillnad, att deklarera avsikt i API:t och utföra avsikt på noder, är vad som gör Kubernetes både kraftfullt och komplext.

En Deployment är ett bra exempel på hur detta fungerar i praktiken. Du deklarerar 'Jag vill att tre repliker av denna containerimage ska köras hela tiden.' Deployment-kontrollern skapar en ReplicaSet, som i sin tur skapar tre Pods. Om en Pod kraschar märker ReplicaSet att det bara finns två faktiska repliker och skapar en ny.

Denna modell gör miljöer mer reproducerbara och återhämtning mer automatisk, men det innebär också att klustret är beroende av tydliga, disciplinerade objektdefinitioner. Arbetslaster som avviker från deklarerat tillstånd eller som beror på klustertillstånd utanför versionskontroll är svårare att driva och felsöka.

Driftsmodell

  • Pods är den minsta körbara enheten, de grupperar containrar som måste köras tillsammans och delar ett nätverksnamnrymd och lagringsvolymer.
  • Deployments hanterar önskat antal Pod-repliker och rollout-beteende, och hanterar både skalning och uppdateringar.
  • Services ger en stabil nätverksidentitet för en uppsättning Pods, och abstraherar bort det faktum att enskilda Pods skapas och raderas kontinuerligt.
  • ConfigMaps och Secrets separerar miljöspecifik och känslig konfiguration från arbetslasteimagen.

Basnivå

  • Använd namnrymder för att organisera arbetslaster efter team, miljö eller funktion och för att avgränsa åtkomstkontroll och resurskvoter.
  • Håll alla arbetslastedefinitioner i versionskontroll och driftsätt via en kontrollerad process snarare än att tillämpa kubectl-kommandon direkt i produktion.
  • Använd liveness- och readiness-prober på varje arbetslastedefinition så att klustret automatiskt kan detektera och ersätta ohälsosamma containrar.
  • Behandla Kubernetes API som en privilegierad yta. Begränsa vem och vad som kan tala med det och granska ändringar i RBAC noggrant.

Signaler att bevaka

Mönster som är värda att undersöka vidare.
  • Arbetslaster är beroende av manuellt skapade resurser utanför versionskontroll.
  • Services eller Ingress-regler exponerar mer trafik än applikationen behöver.
  • Pods beter sig olika mellan miljöer eftersom konfigurationen är inkonsekvent.

FÖRDJUPNING

Pods

En Pod är den atomära enheten för schemaläggning i Kubernetes. Den kan hålla en eller flera containrar, men dessa containrar är samlokaliserade på samma nod, delar samma nätverksnamnrymd (de kommunicerar på localhost och delar en enda IP-adress) och kan dela lagringsvolymer. Det vanligaste fallet är en container per Pod, flera containrar i en Pod används när containrarna är tätt kopplade och måste alltid köras tillsammans, till exempel en huvudapplikation och en sidecar som hanterar loggning.

Kubelet på varje nod ansvarar för att starta och övervaka containrarna i Pods schemalagda till den noden. Den kommunicerar med containerkörningen för att skapa containrar och övervakar deras hälsa via de prober som definieras i Pod-specifikationen. Om en container misslyckas med sin liveness-prob upprepade gånger startar kubelet om den.

Pods är designade för att vara efemerala och utbytbara, inte långlivade och manuellt hanterade. En Pod som dödas av schemaläggaren, vräks eller raderas är permanent borta. Dess skrivbara containerfilsystem kasseras. Allt beständigt tillstånd måste lagras i en volym med externt stöd.

Ett vanligt misstag är att försöka SSH:a in i en Pod och göra ändringar direkt för att lösa ett problem. Detta skapar avvikelse. Det körande tillståndet matchar inte längre det deklarerade tillståndet, och ändringen försvinner när Poden ersätts. Det rätta tillvägagångssättet är att uppdatera arbetslastedefinitionen och distribuera om.

Deployments

En Deployment är standardsättet att köra tillståndslösa applikationsarbetslaster i Kubernetes. Den deklarerar önskat antal Pod-repliker, Pod-mallen (vilken image, miljövariabler, resursgränser, prober) och uppdateringsstrategin. Deployment-kontrollern bevakar kontinuerligt faktiska replikantal och skapar eller raderar Pods för att matcha önskat antal.

Kubernetes stöder två rollout-strategier i Deployments. RollingUpdate (standard) ersätter Pods inkrementellt. Den skapar en ny Pod med ny image, väntar på att den blir redo, avslutar sedan en gammal Pod och upprepar tills alla repliker är uppdaterade. Recreate avslutar alla befintliga Pods innan nya skapas. Det orsakar ett kort driftstopp men är enklare för applikationer som inte kan köra två versioner samtidigt.

Readiness-prober är kritiska för rullande uppdateringar. Rollout-kontrollern väntar på att en ny Pod passerar sin readiness-prob innan den går vidare för att avsluta en gammal Pod. Om proben aldrig passerar (eftersom den nya imagen har en startbugg) stannar rollout snarare än att ersätta friska Pods med ohälsosamma. Men bara om proben är korrekt konfigurerad.

Deployments behåller en historik över tidigare ReplicaSets, vilket möjliggör återställning. Att köra 'kubectl rollout undo deployment/myapp' talar om för Deployment-kontrollern att återställa den föregående ReplicaSet:s Pod-mall. Återställningen är i sig en rullande uppdatering och följer samma kontrollerade ersättningsprocess.

Services

En Service ger en stabil nätverksidentitet för en dynamisk uppsättning Pods. Pods skapas och raderas kontinuerligt. Deras IP-adresser ändras varje gång de ersätts. En Service löser discoverproblemet. Den upprätthåller en konsekvent klusterintern IP och DNS-namn som klienter kan ansluta till, medan Endpoints-objektet uppdateras automatiskt när Pods kommer och går.

Kubernetes stöder flera Service-typer. ClusterIP (standard) gör tjänsten nåbar bara inifrån klustret. Användbart för interna API:er och databaser. NodePort exponerar tjänsten på en hög port på varje nod i klustret. LoadBalancer avsätter en extern lastbalanserare från molnleverantören. ExternalName mappar tjänsten till ett externt DNS-namn utan proxying.

Trafikroutning i Services implementeras av kube-proxy, som körs på varje nod och underhåller en uppsättning iptables- eller IPVS-regler. När en förfrågan görs till Service-IP:n dirigerar kube-proxy den till en av de för tillfället friska Pods bakom tjänsten. Detta är inte en fullfjädrad lastbalanserare den har ingen förståelse för HTTP och ingen hälsobaserad routning.

Ett nyckelkoncept som förvirrar nybörjare du behöver inte känna till IP-adresserna för Pods för att koppla ihop tjänster. Tjänstens DNS-namn löser sig till Service:ns stabila ClusterIP. Applikationen ansluter till namnet och Kubernetes hanterar resten. Det är därför hårdkodning av Pod-IP:er i konfigurationsfiler alltid är fel.

Ingress

En Ingress är en uppsättning routningsregler som dirigerar extern HTTP och HTTPS-trafik till Services inne i klustret. Medan en Service av typen LoadBalancer ger en tjänst sin egen externa IP, tillåter Ingress att många tjänster delar en enda extern ingångspunkt genom att routa baserat på värdnamn, URL-sökväg eller andra HTTP-attribut.

Ett Ingress-objekt gör ingenting av sig självt. Det kräver en Ingress-kontroller för att tolka och implementera routningsreglerna. Vanliga Ingress-kontroller inkluderar nginx-ingress, Traefik, HAProxy Ingress och molnbaserade alternativ som AWS Load Balancer Controller. Valet av kontroller påverkar tillgängliga funktioner och hur avancerade routningsregler uttrycks.

TLS-terminering är ett vanligt Ingress-användningsfall. Ingress-kontrollern accepterar HTTPS-anslutningar, terminerar TLS med ett certifikat lagrat i en Kubernetes Secret och vidarebefordrar plain HTTP till backend-tjänsten. Certifikathantering hanteras vanligtvis av cert-manager, som automatiserar utfärdande och förnyelse av TLS-certifikat.

En vanlig förvirring är att Ingress opererar på Layer 7 (HTTP), medan Services opererar på Layer 4 (TCP/UDP). Ingress kan routa efter värdnamn och URL-sökväg. Services kan bara routa efter destinationsport. För icke-HTTP-protokoll som behöver extern åtkomst är en LoadBalancer Service vanligtvis det rätta valet.

Namnrymder

Namnrymder delar upp ett Kubernetes-kluster i logiska segment. De flesta Kubernetes-objekt är namnrymdsscopade, vilket innebär att namn bara behöver vara unika inom en namnrymd och att RBAC, ResourceQuotas och LimitRanges kan tillämpas per namnrymd. Detta gör namnrymder till den primära organisationsenheten för kluster med flera team och miljöer.

Vanliga namnrymdsmönster inkluderar en namnrymd per team (alla Team A:s arbetslaster i namnrymd team-a), en namnrymd per miljö (dev, staging, produktion i separata namnrymder) eller en kombination. Det rätta mönstret beror på klustrets syfte.

Namnrymder är en organisations- och åtkomstkontrollgräns, men de är inte en säkerhetsisolationsgräns i sig. Utan NetworkPolicies kan Pods i olika namnrymder fortfarande kommunicera fritt med varandra. Namnrymder minskar oavsiktliga kollisioner men ger inte samma isolering som separata kluster för arbetslaster med starka säkerhetskrav.

Ett vanligt misstag är att behandla standardnamnrymden som ett giltigt operativt val. Standardnamnrymden har inga speciella skydd. Produktionsarbetslaster ska alltid köras i namngivna, explicit konfigurerade namnrymder med lämpliga resurskvoter, nätverkspolicyer och RBAC.

ConfigMaps

En ConfigMap lagrar icke-känslig konfigurationsdata som nyckel-värdepar och frikopplar miljöspecifika inställningar från containerimagen. Detta gör att samma image kan köras i utveckling, staging och produktion med olika databasvärdnamn, funktionsflaggor eller loggningsnivåer. Utan att bygga om imagen för varje miljö.

ConfigMaps kan konsumeras av Pods på tre sätt. Som miljövariabler (värdet på en nyckel injiceras som en env-var i containern), som kommandoradsargument (refererad i containerspecifikationen) eller som monterade filer (ConfigMap-data presenteras som en katalog med filer inne i containern).

En viktig operativ detalj. Miljövariabelinjicering från en ConfigMap sker vid Pod-skapande. Om ConfigMap:en uppdateras efter att Poden startar ändras inte miljövariablerna i den körande containern. För att plocka upp de nya värdena måste Poden startas om. Monterade volymer uppdateras däremot så småningom på plats. Kubernetes synkroniserar volyminnehållet inom ungefär en minut efter en ConfigMap-ändring.

ConfigMaps ska inte användas för känsliga värden. De lagras i klartext i etcd och kan läsas av vilket subjekt som helst med 'get' eller 'list'-behörighet på ConfigMaps i namnrymden. Känsliga värden, lösenord, API-nycklar, certifikat, hör hemma i Secrets.

Secrets

Kubernetes Secrets lagrar känsliga värden som lösenord, tokens, TLS-certifikat och API-nycklar. Objekttypen är separat från ConfigMaps specifikt för att möjliggöra olika RBAC-policyer. Men typen ensam gör inte hemligheter säkra, flera ytterligare kontroller krävs för att använda dem säkert.

Den viktigaste missuppfattningen om Secrets är att base64-kodning är kryptering. Det är det inte. Som standard lagras Secret-värden i etcd i base64-kodad klartext. Alla med läsåtkomst till etcd har åtkomst till alla Secrets i klustret. Kryptering i vila kräver att etcd-kryptering aktiveras explicit i API-serverkonfigurationen.

För arbetslaster som behöver känsliga värden sträcker sig alternativen från Kubernetes-inhemska Secrets (bekväma men med begränsad säkerhet) till externa secrets-operatörer (External Secrets Operator, Sealed Secrets, Vault Agent Injector) som hämtar hemligheter från ett externt hemlighetslager vid Pod-start.

En vanlig säkerhetsbrist är att tillåta ServiceAccounts att automatiskt montera sin token i Pods som inte behöver API-åtkomst. Varje Pod i Kubernetes får en ServiceAccount-token monterad som standard. Denna token kan användas för att autentisera mot Kubernetes API. Arbetslaster som inte behöver kommunicera med Kubernetes API bör inaktivera detta automatiska montering.

Hur arbetslaster körs

När en Deployment skapas eller uppdateras sker följande sekvens. API-servern lagrar Deployment-objektet i etcd. Deployment-kontrollern märker det nya objektet och skapar eller uppdaterar en ReplicaSet. ReplicaSet-kontrollern skapar Pod-objekt. Scheduler tilldelar varje oschemalagd Pod till en nod baserat på resurstillgänglighet, affinitetsregler och taint/tolerations, kubelet på den valda noden läser Pod-specen och instruerar containerkörningen att starta containrarna.

Hälsokontroller styr vad som händer med containrar efter att de startar. En liveness-prob detekterar när en container är fast i ett trasigt tillstånd den inte kan återhämta sig från, när den misslyckas upprepade gånger startar kubelet om containern. En readiness-prob detekterar när en container ännu inte är redo att ta emot trafik, när den misslyckas tas Podens IP bort från Service-endpoints.

När en container misslyckas med sin liveness-prob och startas om tillämpar Kubernetes en exponentiell backoff mellan omstartsförsök, med start på 10 sekunder, 20 sekunder, 40 sekunder och så vidare upp till maximalt 5 minuter. En container som fortsätter krascha hamnar i CrashLoopBackOff-tillstånd. CrashLoopBackOff är inte ett Kubernetes-fel. Det är en signal om att containern i sig misslyckas.

Resursbegäranden och -gränser är en del av hur schemaläggaren placerar arbetslaster. En begäran (resources.requests) är vad schemaläggaren använder för att bestämma vilka noder som har kapacitet. En gräns (resources.limits) är vad noden tillämpar vid körning. En Pod som överskrider sin minnesgräns OOM-dödas omedelbart. Att ange begäranden och gränser korrekt är viktigt för både klusterstabilitet och applikationsprestanda.