10 vanlige feil ved bruk av Kubernetes

Merk. overs.: Forfatterne av denne artikkelen er ingeniører fra et lite tsjekkisk selskap, pipetail. De klarte å sette sammen en fantastisk liste over [noen ganger banale, men fortsatt] svært presserende problemer og misoppfatninger knyttet til driften av Kubernetes-klynger.

10 vanlige feil ved bruk av Kubernetes

I løpet av årene med bruk av Kubernetes har vi jobbet med et stort antall klynger (både administrerte og uadministrerte - på GCP, AWS og Azure). Over tid begynte vi å legge merke til at noen feil stadig ble gjentatt. Det er imidlertid ingen skam i dette: Vi har gjort de fleste selv!

Artikkelen inneholder de vanligste feilene og nevner også hvordan du kan rette dem.

1. Ressurser: forespørsler og begrensninger

Dette elementet fortjener definitivt den nærmeste oppmerksomheten og førsteplassen på listen.

CPU-forespørsel vanligvis enten ikke spesifisert i det hele tatt eller har en veldig lav verdi (for å plassere så mange pods på hver node som mulig). Dermed blir nodene overbelastet. I tider med høy belastning blir nodens prosessorkraft utnyttet fullt ut og en bestemt arbeidsbelastning mottar bare det den "ber om" av CPU struping. Dette fører til økt programforsinkelse, tidsavbrudd og andre ubehagelige konsekvenser. (Les mer om dette i vår andre nyere oversettelse: "CPU-grenser og aggressiv struping i Kubernetes"- ca. oversett.)

BestEffort (ekstremt no anbefalt):

resources: {}

Ekstremt lav CPU-forespørsel (ekstremt no anbefalt):

   resources:
      Requests:
        cpu: "1m"

På den annen side kan tilstedeværelsen av en CPU-grense føre til urimelig hopping av klokkesykluser av pods, selv om nodeprosessoren ikke er fullastet. Igjen kan dette føre til økte forsinkelser. Kontroversen fortsetter rundt parameteren CPU CFS-kvote i Linux-kjernen og CPU-struping avhengig av de angitte grensene, samt deaktivering av CFS-kvoten... Akk, CPU-grenser kan forårsake flere problemer enn de kan løse. Mer informasjon om dette finner du på lenken under.

Overdreven utvalg (overforpliktende) hukommelsesproblemer kan føre til større problemer. Å nå CPU-grensen innebærer å hoppe over klokkesykluser, mens å nå minnegrensen innebærer å drepe poden. Har du noen gang observert OOMkill? Ja, det er akkurat det vi snakker om.

Vil du minimere sannsynligheten for at dette skjer? Ikke over-alloker minne og bruk garantert QoS (Quality of Service) ved å sette minneforespørselen til grensen (som i eksemplet nedenfor). Les mer om dette i Henning Jacobs presentasjoner (Lead Engineer hos Zalando).

Burstable (høyere sjanse for å bli OOM-drept):

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

Garantert:

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

Hva vil potensielt hjelpe når du setter opp ressurser?

Med metrics-server du kan se gjeldende CPU-ressursforbruk og minnebruk etter pods (og beholdere inni dem). Mest sannsynlig bruker du den allerede. Bare kjør følgende kommandoer:

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

Imidlertid viser de bare gjeldende bruk. Det kan gi deg en grov ide om størrelsesordenen, men til slutt trenger du historie med endringer i beregninger over tid (for å svare på spørsmål som: "Hva var den høyeste CPU-belastningen?", "Hva var belastningen i går morges?", osv.). Til dette kan du bruke Prometheus, DataDog og andre verktøy. De henter ganske enkelt beregninger fra metrics-server og lagrer dem, og brukeren kan spørre dem og plotte dem deretter.

VerticalPodAutoscaler den lar automatisere этот процесс. Он отслеживает историю использования процессора и памяти и настраивает новые request’ы и limit’ы на основе этой информации.

Å bruke datakraft effektivt er ikke en lett oppgave. Det er som å spille Tetris hele tiden. Hvis du betaler for mye for datakraft med lavt gjennomsnittlig forbruk (si ~10%), anbefaler vi å se på produkter basert på AWS Fargate eller Virtual Kubelet. De er bygget på en serverløs/betal-per-bruk-faktureringsmodell, som kan vise seg å være billigere under slike forhold.

2. Liveness og beredskapssonder

Som standard er ikke kontroll av liveness og beredskap aktivert i Kubernetes. Og noen ganger glemmer de å slå dem på...

Men hvordan kan du ellers starte en omstart av tjenesten i tilfelle en fatal feil? Og hvordan vet lastbalanseren at en pod er klar til å ta imot trafikk? Eller at den kan håndtere mer trafikk?

Disse testene forveksles ofte med hverandre:

  • Livsstil — «overlevelsesevne»-sjekk, som starter poden på nytt hvis den mislykkes;
  • Beredskap — beredskapssjekk, hvis den mislykkes, kobler den poden fra Kubernetes-tjenesten (dette kan kontrolleres ved å bruke kubectl get endpoints) og trafikk kommer ikke til den før neste kontroll er fullført.

Обе эти проверки UTFØRES UNDER HELE LIVSSYKLUSEN TIL PODEN. Det er veldig viktig.

En vanlig misforståelse er at beredskapsprober kun kjøres ved oppstart slik at balansereren kan vite at poden er klar (Ready) og kan begynne å behandle trafikk. Dette er imidlertid bare ett av alternativene for deres bruk.

En annen er muligheten for å finne ut at trafikken på poden er overdreven og overbelaster det (eller poden utfører ressurskrevende beregninger). I dette tilfellet hjelper beredskapskontrollen reduser belastningen på poden og "avkjøl" den. Vellykket gjennomføring av en beredskapssjekk i fremtiden tillater det øke belastningen på poden igjen. I dette tilfellet (hvis beredskapstesten mislykkes), vil svikt i livenesstesten være svært kontraproduktiv. Hvorfor starte en pod på nytt som er sunn og jobber hardt?

Derfor, i noen tilfeller, er ingen kontroller i det hele tatt bedre enn å aktivere dem med feil konfigurerte parametere. Som nevnt ovenfor, if livlighetssjekk kopier beredskapssjekk, da er du i store problemer. Mulig alternativ er å konfigurere kun beredskapstestOg farlig livlighet la til side.

Begge typer sjekker bør ikke mislykkes når vanlige avhengigheter mislykkes, ellers vil dette føre til en gjennomgripende (skred-lignende) feil i alle pods. Med andre ord, ikke skade deg selv.

3. LoadBalancer for hver HTTP-tjeneste

Mest sannsynlig har du HTTP-tjenester i klyngen din som du ønsker å videresende til omverdenen.

Hvis du åpner tjenesten som type: LoadBalancer, vil kontrolleren (avhengig av tjenesteleverandøren) levere og forhandle en ekstern LoadBalancer (ikke nødvendigvis kjører på L7, men heller til og med på L4), og dette kan påvirke kostnadene (ekstern statisk IPv4-adresse, datakraft, fakturering per sekund ) på grunn av behovet for å opprette et stort antall slike ressurser.

I dette tilfellet er det mye mer logisk å bruke en ekstern lastbalanser, åpne tjenester som type: NodePort. Eller enda bedre, utvide noe sånt som nginx-ingress-controller (eller traefik), som vil være den eneste Nodeport endepunkt knyttet til den eksterne lastbalanseren og vil rute trafikk i klyngen ved hjelp av ingress-Kubernetes ressurser.

Andre intra-klynge (mikro)tjenester som samhandler med hverandre kan "kommunisere" ved hjelp av tjenester som ClusterIP og en innebygd tjenesteoppdagelsesmekanisme via DNS. Bare ikke bruk deres offentlige DNS/IP, da dette kan påvirke ventetiden og øke kostnadene for skytjenester.

4. Autoskalering av en klynge uten å ta hensyn til funksjonene

Når du legger til noder til og fjerner dem fra en klynge, bør du ikke stole på noen grunnleggende beregninger som CPU-bruk på disse nodene. Podplanlegging må ta hensyn til mange begrensninger, slik som pod/node-tilhørighet, flekker og tolerasjoner, ressursforespørsler, QoS, etc. Bruk av en ekstern autoscaler som ikke tar hensyn til disse nyansene kan føre til problemer.

Tenk deg at en bestemt pod skal planlegges, men all tilgjengelig CPU-kraft blir forespurt/demontert og poden blir sittende fast i en tilstand Pending. Ekstern autoscaler ser den gjennomsnittlige gjeldende CPU-belastningen (ikke den forespurte) og starter ikke utvidelse (skalere ut) - legger ikke til en annen node. Som et resultat av dette blir ikke denne poden planlagt.

I dette tilfellet, omvendt skalering (innskalering) — å fjerne en node fra en klynge er alltid vanskeligere å implementere. Tenk deg at du har en stateful pod (med vedvarende lagring tilkoblet). Vedvarende volumer hører vanligvis til spesifikk tilgjengelighetssone og er ikke replikert i regionen. Således, hvis en ekstern autoscaler sletter en node med denne poden, vil ikke planleggeren kunne planlegge denne poden på en annen node, siden dette kun kan gjøres i tilgjengelighetssonen der den vedvarende lagringen er plassert. Pod vil bli sittende fast i tilstanden Pending.

Veldig populær i Kubernetes-fellesskapet cluster-autoscaler. Den kjører på en klynge, støtter APIer fra store skyleverandører, tar hensyn til alle begrensninger og kan skaleres i de ovennevnte tilfellene. Den er også i stand til å skalere inn mens den opprettholder alle fastsatte grenser, og dermed spare penger (som ellers ville blitt brukt på ubrukt kapasitet).

5. Forsømmelse av IAM/RBAC-evner

Pass på å bruke IAM-brukere med vedvarende hemmeligheter for maskiner og applikasjoner. Organiser midlertidig tilgang ved å bruke roller og tjenestekontoer (tjenestekontoer).

Vi møter ofte det faktum at tilgangsnøkler (og hemmeligheter) er hardkodet i applikasjonskonfigurasjonen, i tillegg til å neglisjere rotasjonen av hemmeligheter til tross for at vi har tilgang til Cloud IAM. Bruk IAM-roller og tjenestekontoer i stedet for brukere der det er hensiktsmessig.

10 vanlige feil ved bruk av Kubernetes

Glem kube2iam og gå rett til IAM-roller for tjenestekontoer (som beskrevet i seddel med samme navn Š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 merknad. Ikke så vanskelig, ikke sant?

Ikke gi tjenestekontoer og forekomstprofiler rettigheter admin и cluster-adminhvis de ikke trenger det. Dette er litt vanskeligere å implementere, spesielt i RBAC K8-er, men absolutt verdt innsatsen.

6. Ikke stol på automatisk anti-affinitet for pods

Tenk deg at du har tre replikaer av en eller annen distribusjon på en node. Noden faller, og sammen med den alle replikaene. Ubehagelig situasjon, ikke sant? Men hvorfor var alle kopiene på samme node? Er det ikke meningen at Kubernetes skal gi høy tilgjengelighet (HA)?!

Dessverre overholder ikke Kubernetes-planleggeren, på eget initiativ, reglene for separat eksistens (anti-affinitet) для pod’ов. Их необходимо явно прописать:

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

Det er alt. Nå vil pods bli planlagt på forskjellige noder (denne tilstanden sjekkes bare under planlegging, men ikke under driften - derfor requiredDuringSchedulingIgnoredDuringExecution).

Her snakker vi om podAntiAffinity på forskjellige noder: topologyKey: "kubernetes.io/hostname", - og ikke om ulike tilgjengelighetssoner. For å implementere en fullverdig HA, må du grave dypere inn i dette emnet.

7. Ignorer PodDisruptionBudgets

Tenk deg at du har en produksjonsbelastning på en Kubernetes-klynge. Med jevne mellomrom må noder og selve klyngen oppdateres (eller tas ut). PodDisruptionBudget (PDB) er noe sånt som en servicegarantiavtale mellom klyngeadministratorer og brukere.

PDB lar deg unngå tjenesteavbrudd forårsaket av mangel på noder:

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

I dette eksemplet sier du, som bruker av klyngen, til administratorene: "Hei, jeg har en dyrepassertjeneste, og uansett hva du gjør, vil jeg gjerne ha minst 2 kopier av denne tjenesten alltid tilgjengelig."

Du kan lese mer om dette her.

8. Flere brukere eller miljøer i en felles klynge

Kubernetes navnerom (navneområder) ikke gi sterk isolasjon.

En vanlig misforståelse er at hvis du distribuerer en ikke-prod-last i ett navneområde og en prod-last i et annet, så vil ikke påvirke hverandre på noen måte... Imidlertid kan et visst nivå av isolasjon oppnås ved å bruke ressursforespørsler/begrensninger, sette kvoter og angi prioritetsklasser. Noe "fysisk" isolasjon i dataplanet er gitt av affiniteter, tolerasjoner, flekker (eller nodeselektorer), men slik separasjon er ganske vanskelig implementere.

De som trenger å kombinere begge typer arbeidsbelastninger i samme klynge vil måtte forholde seg til kompleksitet. Hvis det ikke er et slikt behov, og du har råd til å ha en en klynge til (si, i en offentlig sky), da er det bedre å gjøre det. Dette vil oppnå et mye høyere isolasjonsnivå.

9. ekstern trafikkpolicy: Klynge

Svært ofte ser vi at all trafikk inne i klyngen kommer gjennom en tjeneste som NodePort, som standardpolicyen er satt for externalTrafficPolicy: Cluster... Det betyr at Nodeport er åpen på hver node i klyngen, og du kan bruke hvilken som helst av dem til å samhandle med ønsket tjeneste (sett med pods).

10 vanlige feil ved bruk av Kubernetes

Samtidig er ekte pods knyttet til den ovennevnte NodePort-tjenesten vanligvis kun tilgjengelig på en viss delsett av disse nodene. Med andre ord, hvis jeg kobler til en node som ikke har den nødvendige poden, vil den videresende trafikk til en annen node, legge til en humle og økende latens (hvis noder er plassert i forskjellige tilgjengelighetssoner/datasentre, kan ventetiden være ganske høy; i tillegg vil utgangstrafikkkostnadene øke).

På den annen side, hvis en bestemt Kubernetes-tjeneste har et policysett externalTrafficPolicy: Local, så åpnes NodePort bare på de nodene der de nødvendige podene faktisk kjører. Ved bruk av ekstern lastbalanser som sjekker tilstanden (helsesjekking) endepunkter (hvordan fungerer det AWS ELB), Han vil sende trafikk kun til de nødvendige nodene, som vil ha en gunstig effekt på forsinkelser, databehov, utgangsregninger (og sunn fornuft tilsier det samme).

Det er stor sjanse for at du allerede bruker noe lignende traefik eller nginx-ingress-controller som et NodePort-endepunkt (eller LoadBalancer, som også bruker NodePort) for å rute HTTP-inngående trafikk, og å sette dette alternativet kan redusere ventetiden for slike forespørsler betydelig.

В denne publikasjonen можно более подробно узнать об externalTrafficPolicy, ее преимуществах и недостатках.

10. Ikke bli bundet til klynger og ikke misbruk kontrollplanet

Tidligere var det vanlig å kalle servere med egennavn: Anton, HAL9000 og Colossus... I dag har de blitt erstattet av tilfeldig genererte identifikatorer. Vanen holdt seg imidlertid, og nå går egennavn til klynger.

En typisk historie (basert på virkelige hendelser): det hele startet med et proof of concept, så klyngen hadde et stolt navn testing... År har gått og den brukes FORTSATT i produksjon, og alle er redde for å ta på den.

Det er ikke noe morsomt med klynger som blir til kjæledyr, så vi anbefaler å fjerne dem med jevne mellomrom mens du øver katastrofegjenoppretting (dette vil hjelpe kaosteknikk — ca. oversett.). I tillegg ville det ikke skade å jobbe med kontrolllaget (kontrollfly). Å være redd for å ta på ham er ikke et godt tegn. Etc. død? Gutter, dere er virkelig i trøbbel!

På den annen side bør du ikke la deg rive med av å manipulere den. Med tiden kontrolllaget kan bli tregt. Mest sannsynlig skyldes dette at et stort antall objekter blir opprettet uten at de roterer (en vanlig situasjon når du bruker Helm med standardinnstillinger, som er grunnen til at tilstanden i konfigurasjonskart/hemmeligheter ikke oppdateres - som et resultat akkumuleres tusenvis av objekter i kontrolllaget) eller med konstant redigering av kube-api-objekter (for automatisk skalering, for CI/CD, for overvåking, hendelseslogger, kontrollere osv.).

I tillegg anbefaler vi å sjekke SLA/SLO-avtalene med den administrerte Kubernetes-leverandøren og ta hensyn til garantiene. Leverandøren kan garantere kontrolllags tilgjengelighet (eller dens underkomponenter), men ikke p99-forsinkelsen av forespørsler du sender til den. Du kan med andre ord gå inn kubectl get nodes, og motta svar først etter 10 minutter, og dette vil ikke være et brudd på vilkårene i tjenesteavtalen.

11. Bonus: ved å bruke den siste taggen

Men dette er allerede en klassiker. I det siste har vi møtt denne teknikken sjeldnere, siden mange, etter å ha lært av bitter erfaring, har sluttet å bruke taggen :latest og begynte å feste versjoner. Hurra!

ECR opprettholder uforanderlighet av bildekoder; Vi anbefaler at du gjør deg kjent med denne bemerkelsesverdige funksjonen.

Oppsummering

Ikke forvent at alt fungerer over natten: Kubernetes er ikke et universalmiddel. Dårlig app vil forbli slik selv i Kubernetes (og det blir nok verre). Uforsiktighet vil føre til overdreven kompleksitet, sakte og stressende arbeid av kontrolllaget. I tillegg risikerer du å stå uten en katastrofegjenopprettingsstrategi. Ikke forvent at Kubernetes gir isolasjon og høy tilgjengelighet rett ut av esken. Bruk litt tid på å gjøre applikasjonen din virkelig skybasert.

Du kan bli kjent med de mislykkede opplevelsene til ulike team i этой подборке историй av Henning Jacobs.

De som ønsker å legge til listen over feil gitt i denne artikkelen kan kontakte oss på Twitter (@MarekBartik, @MstrsObserver).

PS fra oversetter

Les også på bloggen vår:

Kilde: www.habr.com

Legg til en kommentar