10 vanliga misstag när du använder Kubernetes

Notera. transl.: Författarna till den här artikeln är ingenjörer från ett litet tjeckiskt företag, pipetail. De lyckades sätta ihop en underbar lista över [ibland banala, men fortfarande] mycket pressande problem och missuppfattningar relaterade till driften av Kubernetes-kluster.

10 vanliga misstag när du använder Kubernetes

Under åren vi använde Kubernetes har vi arbetat med ett stort antal kluster (både hanterade och ohanterade - på GCP, AWS och Azure). Med tiden började vi märka att vissa misstag ständigt upprepades. Det är dock ingen skam i detta: vi har gjort de flesta själva!

Artikeln innehåller de vanligaste felen och nämner även hur man rättar till dem.

1. Resurser: förfrågningar och begränsningar

Denna artikel förtjänar definitivt den största uppmärksamheten och förstaplatsen på listan.

CPU-begäran vanligtvis antingen inte specificerat alls eller har ett mycket lågt värde (för att placera så många pods på varje nod som möjligt). Således blir noderna överbelastade. Under tider med hög belastning utnyttjas nodens processorkraft fullt ut och en viss arbetsbelastning får bara vad den "begärt" av CPU-strypning. Detta leder till ökad applikationslatens, timeouts och andra obehagliga konsekvenser. (Läs mer om detta i vår andra nya översättning: "CPU-gränser och aggressiv strypning i Kubernetes" - cirka. översätt.)

Bästa insats (ytterst ingen rekommenderad):

resources: {}

Extremt låg CPU-begäran (extremt ingen rekommenderad):

   resources:
      Requests:
        cpu: "1m"

Å andra sidan kan närvaron av en CPU-gräns leda till orimliga överhoppningar av klockcykler av pods, även om nodprocessorn inte är fulladdad. Återigen kan detta leda till ökade förseningar. Kontroversen fortsätter kring parametern CPU CFS-kvot i Linux-kärnan och processorbegränsning beroende på inställda gränser, samt inaktivering av CFS-kvoten... Tyvärr kan CPU-gränser orsaka fler problem än de kan lösa. Mer information om detta finns på länken nedan.

Överdrivet urval (överenga) minnesproblem kan leda till större problem. Att nå CPU-gränsen innebär att man hoppar över klockcykler, medan att nå minnesgränsen innebär att podden dödas. Har du någonsin observerat OOMkill? Ja, det är precis vad vi pratar om.

Vill du minimera sannolikheten för att detta händer? Överallokera inte minne och använd Garanterad QoS (Quality of Service) genom att ställa in minnesbegäran till gränsen (som i exemplet nedan). Läs mer om detta i Henning Jacobs presentationer (Lead Engineer på Zalando).

Sprängbar (högre chans att bli OOMdödad):

   resources:
      requests:
        memory: "128Mi"
        cpu: "500m"
      limits:
        memory: "256Mi"
        cpu: 2

Garanterat:

   resources:
      requests:
        memory: "128Mi"
        cpu: 2
      limits:
        memory: "128Mi"
        cpu: 2

Vad kan potentiellt hjälpa när du ställer in resurser?

Med metrics-server du kan se den aktuella CPU-resursförbrukningen och minnesanvändningen av pods (och behållare inuti dem). Troligtvis använder du det redan. Kör bara följande kommandon:

kubectl top pods
kubectl top pods --containers
kubectl top nodes

Men de visar bara aktuell användning. Det kan ge dig en ungefärlig uppfattning om storleksordningen, men i slutändan kommer du att behöva historik över förändringar i mått över tid (för att svara på frågor som: "Vad var den maximala CPU-belastningen?", "Vad var belastningen i går morse?", etc.). För detta kan du använda Prometheus, DataDog och andra verktyg. De hämtar helt enkelt mätvärden från metrics-server och lagrar dem, och användaren kan fråga dem och plotta dem därefter.

VerticalPodAutoscaler det gör automatisera denna process. Den spårar CPU- och minnesanvändningshistorik och ställer in nya förfrågningar och begränsningar baserat på denna information.

Att använda datorkraft effektivt är ingen lätt uppgift. Det är som att spela Tetris hela tiden. Om du betalar för mycket för datorkraft med låg genomsnittlig förbrukning (säg ~10%) rekommenderar vi att du tittar på produkter baserade på AWS Fargate eller Virtual Kubelet. De är byggda på en faktureringsmodell utan server/pay-per-usage, som kan visa sig vara billigare under sådana förhållanden.

2. Livs- och beredskapssonder

Som standard är inte kontroller av liv och beredskap aktiverade i Kubernetes. Och ibland glömmer de att slå på dem...

Men hur kan du annars initiera en omstart av tjänsten i händelse av ett allvarligt fel? Och hur vet lastbalanseraren att en pod är redo att ta emot trafik? Eller att den klarar mer trafik?

Dessa tester förväxlas ofta med varandra:

  • Livlighet — Kontroll av "överlevnadsförmåga", som startar om kapseln om den misslyckas.
  • Beredskap — beredskapskontroll, om den misslyckas kopplar den bort podden från Kubernetes-tjänsten (detta kan kontrolleras med kubectl get endpoints) och trafik kommer inte till den förrän nästa kontroll är klar.

Båda dessa kontroller UTFÖRD UNDER HELA PODENS LIVSCYKEL. Det är väldigt viktigt.

En vanlig missuppfattning är att beredskapssonder endast körs vid start så att balansören kan veta att podden är klar (Ready) och kan börja bearbeta trafik. Detta är dock bara ett av alternativen för deras användning.

En annan är möjligheten att få reda på att trafiken på podden är överdriven och överbelastar det (eller så utför podden resurskrävande beräkningar). I det här fallet hjälper beredskapskontrollen minska belastningen på podden och "kyla" den. Framgångsrikt slutförande av en beredskapskontroll i framtiden tillåter öka belastningen på podden igen. I det här fallet (om beredskapstestet misslyckas) skulle ett misslyckande med livenesstestet vara mycket kontraproduktivt. Varför starta om en pod som är frisk och jobbar hårt?

Därför, i vissa fall, är inga kontroller alls bättre än att aktivera dem med felaktigt konfigurerade parametrar. Som nämnts ovan, om livlighetskontroll kopior beredskapskontroll, då har du stora problem. Möjligt alternativ är att konfigurera endast beredskapstestOch farlig livlighet lämna åt sidan.

Båda typerna av kontroller bör inte misslyckas när vanliga beroenden misslyckas, annars kommer detta att leda till ett kaskadande (lavinliknande) misslyckande för alla pods. Med andra ord, skada inte dig själv.

3. LoadBalancer för varje HTTP-tjänst

Troligtvis har du HTTP-tjänster i ditt kluster som du skulle vilja vidarebefordra till omvärlden.

Om du öppnar tjänsten som type: LoadBalancer, kommer dess styrenhet (beroende på tjänsteleverantör) att tillhandahålla och förhandla en extern LoadBalancer (körs inte nödvändigtvis på L7, utan snarare till och med på L4), och detta kan påverka kostnaden (extern statisk IPv4-adress, datorkraft, fakturering per sekund ) på grund av behovet av att skapa ett stort antal sådana resurser.

I det här fallet är det mycket mer logiskt att använda en extern lastbalanserare, öppna tjänster som type: NodePort. Eller ännu bättre, utöka något liknande nginx-ingress-controller (eller traefik), som kommer att vara den enda Nodport slutpunkt associerad med den externa lastbalanseraren och kommer att dirigera trafik i klustret med hjälp av inträde-Kubernetes resurser.

Andra intra-kluster (mikro)tjänster som interagerar med varandra kan "kommunicera" med hjälp av tjänster som ClusterIP och en inbyggd tjänsteupptäcktsmekanism via DNS. Använd bara inte deras offentliga DNS/IP, eftersom detta kan påverka latens och öka kostnaderna för molntjänster.

4. Autoskalning av ett kluster utan att ta hänsyn till dess funktioner

När du lägger till noder till och tar bort dem från ett kluster bör du inte lita på vissa grundläggande mätvärden som CPU-användning på dessa noder. Podplanering måste ta hänsyn till många restriktioner, såsom pod/nod-affinitet, fläckar och toleranser, resursbegäranden, QoS, etc. Att använda en extern autoscaler som inte tar hänsyn till dessa nyanser kan leda till problem.

Föreställ dig att en viss pod ska schemaläggas, men all tillgänglig CPU-kraft begärs/demonteras och podden fastnar i ett tillstånd Pending. Extern autoscaler ser den genomsnittliga nuvarande CPU-belastningen (inte den begärda) och initierar inte expansion (skala ut) - lägger inte till ytterligare en nod. Som ett resultat kommer denna pod inte att schemaläggas.

Omvänd skalning i det här fallet (skala in) — att ta bort en nod från ett kluster är alltid svårare att implementera. Föreställ dig att du har en tillståndsfull pod (med beständig lagring ansluten). Ihållande volymer brukar tillhöra specifik tillgänglighetszon och replikeras inte i regionen. Således, om en extern autoscaler tar bort en nod med denna pod, kommer schemaläggaren inte att kunna schemalägga denna pod på en annan nod, eftersom detta endast kan göras i tillgänglighetszonen där den persistenta lagringen finns. Podden kommer att fastna i staten Pending.

Mycket populär i Kubernetes-gemenskapen kluster-autoscaler. Det körs på ett kluster, stöder API:er från stora molnleverantörer, tar hänsyn till alla begränsningar och kan skalas i ovanstående fall. Den kan också skala in samtidigt som alla uppsatta gränser bibehålls, vilket sparar pengar (som annars skulle spenderas på outnyttjad kapacitet).

5. Försummar IAM/RBAC-förmåga

Se upp för att använda IAM-användare med beständiga hemligheter för maskiner och applikationer. Organisera tillfällig åtkomst med hjälp av roller och tjänstekonton (tjänstkonton).

Vi stöter ofta på det faktum att åtkomstnycklar (och hemligheter) är hårdkodade i applikationskonfigurationen, samt försummar rotationen av hemligheter trots att vi har tillgång till Cloud IAM. Använd IAM-roller och tjänstekonton istället för användare där det är lämpligt.

10 vanliga misstag när du använder Kubernetes

Glöm kube2iam och gå direkt till IAM-roller för tjänstekonton (som beskrivs i anteckning med samma namn Štěpán Vraný):

apiVersion: v1
kind: ServiceAccount
metadata:
  annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/my-app-role
  name: my-serviceaccount
  namespace: default

En anteckning. Inte så svårt, eller hur?

Ge inte heller tjänstekonton och instansprofiler behörigheter admin и cluster-adminom de inte behöver det. Detta är lite svårare att implementera, särskilt i RBAC K8s, men definitivt värt ansträngningen.

6. Lita inte på automatisk anti-affinitet för poddar

Föreställ dig att du har tre repliker av någon distribution på en nod. Noden faller, och tillsammans med den alla replikerna. Obehaglig situation, eller hur? Men varför var alla replikerna på samma nod? Är det inte meningen att Kubernetes ska tillhandahålla hög tillgänglighet (HA)?!

Tyvärr följer Kubernetes-schemaläggaren, på eget initiativ, inte reglerna för separat existens (anti-affinitet) för baljor. De måste uttryckligen anges:

// опущено для краткости
      labels:
        app: zk
// опущено для краткости
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            - labelSelector:
                matchExpressions:
                  - key: "app"
                    operator: In
                    values:
                    - zk
              topologyKey: "kubernetes.io/hostname"

Det är allt. Nu kommer pods att schemaläggas på olika noder (detta villkor kontrolleras endast under schemaläggning, men inte under deras drift - därför requiredDuringSchedulingIgnoredDuringExecution).

Här pratar vi om podAntiAffinity på olika noder: topologyKey: "kubernetes.io/hostname", - och inte om olika tillgänglighetszoner. För att implementera en fullfjädrad HA måste du gräva djupare i detta ämne.

7. Ignorera PodDisruptionBudgets

Föreställ dig att du har en produktionsbelastning på ett Kubernetes-kluster. Med jämna mellanrum måste noder och själva klustret uppdateras (eller tas ur drift). PodDisruptionBudget (PDB) är något som ett servicegarantiavtal mellan klusteradministratörer och användare.

PDB låter dig undvika tjänsteavbrott orsakade av brist på noder:

apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
  name: zk-pdb
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: zookeeper

I det här exemplet säger du som användare av klustret till administratörerna: "Hej, jag har en djurskötaretjänst, och oavsett vad du gör, skulle jag vilja ha minst två kopior av den här tjänsten alltid tillgängliga."

Du kan läsa mer om detta här.

8. Flera användare eller miljöer i ett gemensamt kluster

Kubernetes namnrymder (namnrymder) ger inte stark isolering.

En vanlig missuppfattning är att om du distribuerar en icke-prod-laddning i ett namnområde och en prod-laddning i en annan, då kommer inte att påverka varandra på något sätt... Men en viss nivå av isolering kan uppnås genom att använda resursbegäranden/begränsningar, sätta kvoter och sätta priorityClass. Viss "fysisk" isolering i dataplanet tillhandahålls av affiniteter, tolerationer, fläckar (eller nodeselektorer), men sådan separation är ganska svårt genomföra.

De som behöver kombinera båda typerna av arbetsbelastningar i samma kluster kommer att behöva hantera komplexitet. Om det inte finns något sådant behov, och du har råd att ha en ett kluster till (säg, i ett offentligt moln), då är det bättre att göra det. Detta kommer att uppnå en mycket högre isoleringsnivå.

9. externalTrafficPolicy: Kluster

Mycket ofta ser vi att all trafik inuti klustret kommer via en tjänst som NodePort, för vilken standardpolicyn är inställd externalTrafficPolicy: Cluster... Det betyder att Nodport är öppen på varje nod i klustret, och du kan använda vilken som helst av dem för att interagera med önskad tjänst (uppsättning pods).

10 vanliga misstag när du använder Kubernetes

Samtidigt är riktiga poddar associerade med ovan nämnda NodePort-tjänst vanligtvis endast tillgängliga på en viss delmängd av dessa noder. Med andra ord, om jag ansluter till en nod som inte har den nödvändiga podden kommer den att vidarebefordra trafik till en annan nod, lägga till en humle och ökande latens (om noder finns i olika tillgänglighetszoner/datacenter kan latensen vara ganska hög; dessutom kommer kostnaderna för utgående trafik att öka).

Å andra sidan, om en viss Kubernetes-tjänst har en policyuppsättning externalTrafficPolicy: Local, då öppnas NodePort endast på de noder där de nödvändiga podarna faktiskt körs. Vid användning av en extern lastbalanserare som kontrollerar tillståndet (hälsokontroll) slutpunkter (hur fungerar det AWS ELB), Han kommer endast att skicka trafik till de nödvändiga noderna, vilket kommer att ha en gynnsam effekt på förseningar, datorbehov, utgående räkningar (och sunt förnuft dikterar detsamma).

Det finns en stor chans att du redan använder något liknande traefik eller nginx-ingress-controller som en NodePort-slutpunkt (eller LoadBalancer, som också använder NodePort) för att dirigera HTTP-inkommande trafik, och att ställa in detta alternativ kan avsevärt minska latensen för sådana förfrågningar.

В denna publikation Du kan lära dig mer om externalTrafficPolicy, dess fördelar och nackdelar.

10. Bli inte bunden till kluster och missbruk inte kontrollplanet

Tidigare var det vanligt att anropa servrar med egennamn: Anton, HAL9000 och Colossus... Idag har de ersatts av slumpmässigt genererade identifierare. Vanan fanns dock kvar, och nu går egennamn till kluster.

En typisk berättelse (baserad på verkliga händelser): allt började med ett proof of concept, så klustret hade ett stolt namn testning… Åren har gått och den används FORTSÄTTIG i produktionen, och alla är rädda för att röra vid den.

Det finns inget roligt med att klasar förvandlas till husdjur, så vi rekommenderar att du tar bort dem med jämna mellanrum medan du övar katastrofåterställning (detta kommer hjälpa kaosteknik - cirka. översätt.). Dessutom skulle det inte skada att arbeta på kontrollskiktet (kontrollplan). Att vara rädd för att röra vid honom är inget gott tecken. Etc. död? Killar, ni har verkligen problem!

Å andra sidan ska du inte ryckas med att manipulera den. Med tid kontrollskiktet kan bli långsamt. Troligtvis beror detta på att ett stort antal objekt skapas utan att de roteras (en vanlig situation när man använder Helm med standardinställningar, varför dess tillstånd i configmaps/hemligheter inte uppdateras - som ett resultat ackumuleras tusentals objekt i kontrollskiktet) eller med konstant redigering av kube-api-objekt (för automatisk skalning, för CI/CD, för övervakning, händelseloggar, kontroller, etc.).

Dessutom rekommenderar vi att du kontrollerar SLA/SLO-avtalen med den hanterade Kubernetes-leverantören och uppmärksammar garantierna. Säljaren kan garantera kontrolllagers tillgänglighet (eller dess underkomponenter), men inte p99-fördröjningen av förfrågningar du skickar till den. Du kan med andra ord gå in kubectl get nodes, och få ett svar först efter 10 minuter, och detta kommer inte att vara ett brott mot villkoren i serviceavtalet.

11. Bonus: använder den senaste taggen

Men det här är redan en klassiker. På senare tid har vi stött på denna teknik mer sällan, eftersom många, efter att ha lärt sig av bitter erfarenhet, har slutat använda taggen :latest och började fästa versioner. Hurra!

ECR bibehåller oföränderlighet av bildtaggar; Vi rekommenderar att du bekantar dig med denna enastående funktion.

Sammanfattning

Förvänta dig inte att allt ska fungera över en natt: Kubernetes är inget universalmedel. Dålig app kommer att förbli så här även i Kubernetes (och det kommer förmodligen att bli värre). Slarv kommer att leda till överdriven komplexitet, långsamt och stressigt arbete av kontrollskiktet. Dessutom riskerar du att lämnas utan en katastrofåterställningsstrategi. Förvänta dig inte att Kubernetes ska ge isolering och hög tillgänglighet direkt. Lägg lite tid på att göra din applikation riktigt molnbaserad.

Du kan bekanta dig med de misslyckade erfarenheterna av olika team i denna samling berättelser av Henning Jacobs.

De som vill lägga till i listan över fel i den här artikeln kan kontakta oss på Twitter (@MarekBartik, @MstrsObserver).

PS från översättaren

Läs även på vår blogg:

Källa: will.com

Lägg en kommentar