Բոլոր դասերը
DevOps և ամպԱռաջադեմ15 րոպե ընթերցումԱրժե իմանալ

Kubernetes

Օրկեստրատոր, որը շատ կոնտեյներներ պահում է աշխատող, առողջ և հասանելի՝ բազմաթիվ մեքենաների վրա։

  • Pods
  • Deployments
  • Services
  • Ingress
  • Helm
Քայլ 1/4
Kubernetes-ը նավապետն է։ Դու ասում ես, թե ինչ ես ուզում՝ «իմ հավելվածի 3 օրինակ», և նա դա իրականացնում է։

30 վայրկյանում

Kubernetes-ը կոնտեյներներ է աշխատեցնում մեքենաների cluster-ի վրա և պահում դրանք այն վիճակում, որը դու ես նկարագրում՝ քանի պատճեն, որ տարբերակը, որքան հիշողություն։ Եթե կոնտեյները crash է լինում կամ մեքենան «մահանում» է, այն փոխարինողներ է գործարկում այլ տեղում։ Այն տալիս է նաև load balancing, rolling update-ներ և autoscaling, բայց իրական բարդության գնով։

Իմ ամենօրյա stack-ում չէ․ գիտեմ հիմնական գաղափարները և մանրամասները կսովորեմ, երբ նախագիծը դա պահանջի։

Ինչու է սա կարևոր

  • Kubernetes-ը կարևոր է, որովհետև այն դարձել է մեծ մասշտաբով կոնտեյներներ աշխատեցնելու ստանդարտ պլատֆորմ. բոլոր խոշոր cloud-ները առաջարկում են այն որպես managed ծառայություն։
  • Թիմին այն պետք է, երբ բազմաթիվ ծառայություններ է աշխատեցնում բազմաթիվ մեքենաների վրա և ուզում է, որ self-healing-ը, autoscaling-ը և անվտանգ rolling update-ները պլատֆորմն իրականացնի։
  • Դրա դեկլարատիվ մոդելը, որտեղ ցանկալի վիճակը նկարագրում ես YAML-ով, իսկ cluster-ը այն իրականություն է դարձնում, լավ է համադրվում Git-ի վրա հիմնված workflow-ների ու review-ի հետ։
  • Իմ նախագծերում լավ կազմակերպված սերվերի վրա Docker Compose-ը ծածկում է կարիքները գործառնական ծախսի մի փոքր մասով. Kubernetes-ին կդիմեի, երբ համակարգին պետք լինեն մի քանի node և ավտոմատ failover։

Ինչպես է աշխատում

01Ինչ խնդիրներ է լուծում Kubernetes-ը

Մեկ սերվերի վրա մի քանի կոնտեյների համար Docker Compose-ը բավական է։ Քսան մեքենայի վրա տասնյակ ծառայությունների դեպքում նոր հարցեր են առաջանում։ Որ մեքենան տեղ ունի այս կոնտեյների համար (scheduling)։ Ով կվերագործարկի այն, երբ crash լինի, կամ կտեղափոխի, երբ մեքենան մահանա (self-healing)։ Ինչպես ավելացնել պատճեններ, երբ տրաֆիկը մեծանում է (scaling)։ Ինչպես թողարկել նոր տարբերակ առանց downtime-ի և հետ գլորել, եթե այն ձախողվի (rolling update-ներ)։ Ինչպես են ծառայությունները գտնում իրար, երբ դրանց հասցեները անընդհատ փոխվում են (service discovery)։ Kubernetes-ը բոլորին պատասխանում է մեկ գաղափարով. դու հայտարարում ես ցանկալի վիճակը, օրինակ՝ web-ի 1.4.2 տարբերակի երեք replica, իսկ controller-ները անընդհատ համեմատում են իրական վիճակը դրա հետ և գործում՝ տարբերությունը վերացնելու համար։ Այս reconciliation loop-ը Kubernetes-ի սիրտն է։

  • Control plane՝ API server, scheduler, controller-ներ, etcd (cluster-ի բազան)։
  • Worker node-եր՝ kubelet-ը աշխատեցնում է pod-երը, container runtime-ը՝ կոնտեյներները։
  • Ամեն ինչ API օբյեկտ է, որը ստեղծում, կարդում և թարմացնում ես kubectl-ով կամ GitOps գործիքներով։

02Pod-եր և Deployment-ներ

Pod-ը Kubernetes-ի աշխատեցրած ամենափոքր միավորն է՝ մեկ կամ ավելի կոնտեյներ, որոնք կիսում են մեկ ցանցային հասցե և կարող են կիսել volume-ներ։ Pod-երը մեկանգամյա են. փոխարինվելիս նոր անուններ ու IP-ներ են ստանում, ուստի դրանք գրեթե երբեք ուղղակիորեն չես ստեղծում։ Deployment-ը քո փոխարեն կառավարում է pod-երը. այն պահում է replica-ների պահանջված քանակը աշխատող վիճակում և rolling update է անում, երբ փոխում ես image-ը։ Երկու health check է կարևոր։ Readiness probe-ը Kubernetes-ին ասում է, թե երբ pod-ը կարող է տրաֆիկ ստանալ, liveness probe-ը՝ երբ pod-ը կախվել է և պետք է վերագործարկվի։ Resource request-ները scheduler-ին ասում են, թե որքան CPU և հիշողություն ամրագրել, limit-ները սահմանափակում են օգտագործումը։ Առանց request-ների scheduling-ն ու autoscaling-ը վատ որոշումներ են կայացնում։

yaml
apiVersion: apps/v1
kind: Deployment
metadata: { name: web }
spec:
  replicas: 3
  selector: { matchLabels: { app: web } }
  template:
    metadata: { labels: { app: web } }
    spec:
      containers:
        - name: web
          image: registry.example.com/web:1.4.2
          ports: [{ containerPort: 3000 }]
          readinessProbe: { httpGet: { path: /healthz, port: 3000 } }
          resources:
            requests: { cpu: 250m, memory: 256Mi }
            limits: { memory: 512Mi }

03Service-ներ, կոնֆիգուրացիա և autoscaling

Service-ը կայուն անուն և վիրտուալ IP է տալիս pod-երի փոփոխվող բազմությանը, որոնք ընտրվում են label-ներով, և load balance է անում պատրաստ pod-երի միջև։ Մյուս pod-երը հասնում են դրան DNS-ով, օրինակ՝ http://web։ ClusterIP service-ները միայն ներքին են, LoadBalancer service-ները cloud-ից արտաքին load balancer են խնդրում։ Կոնֆիգուրացիան պահվում է image-ներից դուրս. ConfigMap-ը պահում է ոչ գաղտնի կարգավորումներ, Secret-ը՝ գաղտնաբառեր և key-եր։ Ուշադրություն. լռելյայն Secret-ները միայն base64-ով են կոդավորված, ուստի միացրու encryption at rest և սահմանափակիր, թե ով կարող է կարդալ դրանք, կամ համաժամեցրու դրանք արտաքին secret manager-ից։ HorizontalPodAutoscaler-ը (HPA) կարգավորում է replica-ների քանակը CPU-ի, հիշողության կամ custom մետրիկաների հիման վրա՝ քո սահմանած նվազագույնի ու առավելագույնի միջակայքում։

yaml
apiVersion: v1
kind: Service
metadata: { name: web }
spec:
  selector: { app: web }
  ports: [{ port: 80, targetPort: 3000 }]
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata: { name: web }
spec:
  scaleTargetRef: { apiVersion: apps/v1, kind: Deployment, name: web }
  minReplicas: 3
  maxReplicas: 10
  metrics:
    - type: Resource
      resource: { name: cpu, target: { type: Utilization, averageUtilization: 70 } }

04Ինչպես է request-ը հասնում pod-ին

Հետևենք մեկ request-ի։ Բրաուզերը DNS-ով app.example.com-ը resolve է անում cloud load balancer-ի հասցեին։ Load balancer-ը փոխանցում է cluster-ի եզրային proxy-ին, որը կա՛մ Ingress controller է, կա՛մ, ավելի ու ավելի հաճախ, Gateway API-ի իրականացում։ Այդ proxy-ն կարդում է routing կանոնները (host և path) և request-ը ուղարկում համապատասխան Service-ին։ Service-ը, որը յուրաքանչյուր node-ի վրա իրականացվում է kube-proxy-ով կամ eBPF data plane-ով, օրինակ՝ Cilium-ով, իր endpoint-ներից ընտրում է մեկ պատրաստ pod և request-ը փոխանցում pod-ի IP-ին ու port-ին։ Այդ ցուցակում են միայն readiness probe-ն անցած pod-երը. հենց այդպես են rolling update-ները խուսափում դեռ գործարկվող կոնտեյներներին տրաֆիկ ուղարկելուց։ Նկատի ունեցիր, որ community-ի տարածված ingress-nginx controller-ը դադարեցվել է 2026-ի մարտին. նոր cluster-ները պետք է օգտագործեն Gateway API կամ սպասարկվող controller։

yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata: { name: web }
spec:
  parentRefs: [{ name: public-gateway }]
  hostnames: ["app.example.com"]
  rules:
    - matches: [{ path: { type: PathPrefix, value: / } }]
      backendRefs: [{ name: web, port: 80 }]

05Helm և managed Kubernetes

Իրական հավելվածներին պետք են բազմաթիվ YAML ֆայլեր, որոնք staging-ի ու production-ի միջև մի փոքր տարբերվում են։ Helm-ը դրանք փաթեթավորում է chart-ի մեջ՝ template-ներ գումարած values ֆայլ, որպեսզի ամբողջ հավելվածը տեղադրես կամ թարմացնես մեկ հրամանով և կարողանաս հետ գլորել նախորդ release-ին։ Երրորդ կողմի շատ գործիքներ (բազաներ, մոնիթորինգ, cert-manager) տարածվում են որպես Helm chart-եր։ Kustomize-ը առանց template-ների այլընտրանք է, որը ներկառուցված է kubectl-ում և օգտագործում է overlay-ներ ամեն միջավայրի համար։ Control plane-ը ինքդ աշխատեցնելը դժվար է, ուստի թիմերի մեծ մասն օգտագործում է managed Kubernetes՝ Amazon EKS, Google GKE, Azure AKS կամ DigitalOcean DOKS։ Մատակարարը աշխատեցնում և թարմացնում է control plane-ը, իսկ դու դեռ կառավարում ես node pool-երը, քո workload-ների թարմացումները, ցանցային add-on-ները, անվտանգության policy-ները և ծախսը։ Պատրաստ եղիր Kubernetes-ի տարբերակը պարբերաբար թարմացնելու, քանի որ upstream-ում յուրաքանչյուր minor release աջակցվում է մոտավորապես մեկ տարի։

bash
helm install web ./charts/web -f values.prod.yaml -n prod --create-namespace
helm upgrade web ./charts/web -f values.prod.yaml -n prod --atomic --wait
helm history web -n prod
helm rollback web 3 -n prod
kubectl rollout status deployment/web -n prod

06Երբ Kubernetes-ը չափազանց է, և ինչ օգտագործել փոխարենը

Kubernetes-ը շատ բան է բերում՝ ցանցային plugin-ներ, ingress կամ gateway-ներ, սերտիֆիկատներ, secret-ներ, RBAC, մոնիթորինգ, թարմացումներ և YAML ամեն ինչի համար։ Մի քանի ծրագրավորողից բաղկացած թիմի համար, որը մեկ արտադրանք է աշխատեցնում, այդ պլատֆորմային աշխատանքը կարող է ավելի թանկ նստել, քան արտադրանքի իրական ենթակառուցվածքը։ Եթե հավելվածդ տեղավորվում է մեկ կամ երկու սերվերի վրա, Docker Compose-ը լավ backup-ներով ու մոնիթորինգով ավելի պարզ է, էժան և հեշտ debug անելու համար։ Եթե ուզում ես կոնտեյներներ ավելի քիչ գործառնական աշխատանքով, managed կոնտեյներային ծառայությունները, ինչպիսիք են AWS ECS-ը Fargate-ով, Google Cloud Run-ը կամ Fly.io-ն, իրականացնում են scheduling և scaling՝ առանց cluster կառավարելու։ Kamal-ի նման գործիքները կոնտեյներներ են deploy անում սովորական սերվերների վրա՝ zero-downtime փոխարկումով։ Ընտրիր Kubernetes, երբ ունես բազմաթիվ ծառայություններ, իրական scaling-ի կարիքներ և մարդիկ, ովքեր կարող են տիրապետել պլատֆորմին։

  • Մեկ կամ երկու սերվեր՝ Docker Compose։
  • Կոնտեյներներ առանց cluster ops-ի՝ ECS/Fargate, Cloud Run, Fly.io։
  • Պարզ multi-server deploy-ներ՝ Kamal, Docker Swarm, Nomad։

Օգտագործեք, երբ

  • Մի քանի թիմերի պատկանող բազմաթիվ ծառայություններ, որոնց պետք է ընդհանուր deploy պլատֆորմ։
  • Բեռներ, որոնք պետք է վերապրեն node-երի խափանումները և ավտոմատ scale լինեն տրաֆիկի հետ։
  • Կազմակերպություններ, որոնք ուզում են cloud-ների կամ on-premises-ի միջև տեղափոխելի ենթակառուցվածք։
  • Երբ թիմն ունի, կամ կվարձի, մարդիկ, ովքեր կտիրապետեն cluster-ին։

Մի՛ օգտագործեք, երբ

  • Մեկ մոնոլիտ կանխատեսելի տրաֆիկով, որը տեղավորվում է մեկ կամ երկու սերվերի վրա։
  • Փոքր թիմ առանց պլատֆորմային փորձի. cluster-ն ինքն է դառնում արտադրանքը։
  • Stateful բազաներ որպես առաջին քայլ. աշխատեցրու դրանք managed կամ առանձին սերվերների վրա, մինչև փորձ ձեռք բերես։

Այլընտրանքներ

  • Docker Compose VPS-ի վրա

    Մեկ ֆայլ, մեկ սերվեր, հեշտ հասկանալի. ավտոմատ multi-node failover չկա։

  • AWS ECS / Fargate

    AWS-ի սեփական կոնտեյներային օրկեստրացիա՝ Kubernetes-ից շատ ավելի քիչ հասկացություններով։

  • Google Cloud Run

    Կոնտեյներներ ամեն request-ի համար, scale մինչև զրո, կառավարելու cluster չկա։

  • Kamal / Nomad / Docker Swarm

    Մի քանի սերվերի վրա կոնտեյներներ աշխատեցնելու ավելի թեթև ձևեր։

Լավագույն պրակտիկաներ

  1. Յուրաքանչյուր կոնտեյների համար սահմանիր resource request-ներ և հիշողության limit-ներ։
  2. Readiness և liveness probe-երը ճիշտ օգտագործիր. readiness-ը կառավարում է տրաֆիկը, liveness-ը՝ վերագործարկումը։
  3. Manifest-ները պահիր Git-ում և deploy արա CI-ով կամ GitOps-ով (Argo CD, Flux), ոչ թե ձեռքով։
  4. Օգտագործիր namespace-ներ, RBAC և network policy-ներ՝ սահմանափակելու, թե ուր կարող է հասնել յուրաքանչյուր workload։
  5. Ամրագրիր image tag-երը կամ digest-ները. երբեք latest մի deploy արա։
  6. Պլանավորիր cluster-ի պարբերական թարմացումներ. չաջակցվող տարբերակները դադարում են անվտանգության ուղղումներ ստանալ։

Տարածված սխալներ

  • Secret-ները գաղտնագրված համարել, երբ լռելյայն դրանք միայն base64 են։
  • Resource request-ներ չունենալ, ինչի պատճառով pod-երը լցվում են մեկ node-ի վրա և ճնշման տակ evict են լինում։
  • Liveness probe, որը ստուգում է բազան, ու բազայի կարճ խափանումը միանգամից վերագործարկում է բոլոր pod-երը։
  • Kubernetes ընդունել մեկ փոքր հավելվածի համար և ավելի շատ ժամանակ ծախսել cluster-ի, քան արտադրանքի վրա։
  • kubectl apply աշխատեցնել լափթոփներից, ու ոչ ոք չգիտի, թե իրականում ինչ է deploy արված։

Խորը վերլուծություն

07Control plane-ի ներսում՝ API server, etcd և watch-եր

Kubernetes-ի յուրաքանչյուր բաղադրիչ խոսում է միայն API server-ի հետ. ոչ ոք ուղղակիորեն չի դիմում etcd-ին կամ միմյանց։ API server-ը նույնականացնում է հարցումը, ստուգում RBAC-ը, գործարկում admission controller-ները (mutating-ները կարող են ավելացնել լռելյայն արժեքներ կամ sidecar-ներ, validating-ները՝ մերժել policy-ն խախտող օբյեկտները) և օբյեկտը պահում etcd-ում՝ Raft պրոտոկոլով աշխատող consistent key-value պահոցում։ Գրելու համար etcd-ին անհրաժեշտ է անդամների մեծամասնությունը, այդ պատճառով cluster-ները պահում են երեք կամ հինգ անդամ. երեքը դիմանում են մեկ խափանման, հինգը՝ երկուսի։ Controller-ները polling չեն անում. նրանք բացում են երկարակյաց watch-եր, ստանում փոփոխությունների հոսք, պահում լոկալ քեշ և կատարում reconcile։ Scheduler-ը հետևում է առանց node-ի pod-երին, զտում է այն node-երը, որոնց վրա pod-ը չի տեղավորվում, գնահատում մնացածները և իր ընտրությունը գրում հետ։ Ընտրված node-ի kubelet-ը տեսնում է իրեն նշանակված pod-ը և container runtime-ի միջոցով մեկնարկում դրա կոնտեյներները։

  • Admission՝ սկզբում mutating webhook-ներ, ապա validating webhook-ներ և policy-ներ։
  • etcd quorum՝ 3 անդամը դիմանում է 1 խափանման, 5-ը՝ 2-ի։
  • Backup արա etcd-ն կամ համոզվիր, որ դա անում է managed մատակարարը։

08Requests, limits, QoS դասեր և CPU throttling

Requests-ը այն է, ինչ ամրագրում է scheduler-ը, limits-ը՝ այն, ինչ պարտադրում է kernel-ը։ Միասին դրանք յուրաքանչյուր pod-ի համար որոշում են quality of service դասը։ Guaranteed՝ յուրաքանչյուր կոնտեյների CPU-ի և հիշողության requests-ը հավասար են limits-ին։ Burstable՝ սահմանված են որոշ requests կամ limits։ BestEffort՝ ոչինչ սահմանված չէ։ Երբ node-ի հիշողությունը պակասում է, kubelet-ը նախ դուրս է հանում BestEffort pod-երը, ապա իրենց requests-ը գերազանցած Burstable pod-երը։ Հիշողության limit-ի գերազանցումը քննարկման ենթակա չէ. կոնտեյները սպանվում է OOM-ով, ցույց է տալիս exit code 137 և OOMKilled պատճառը ու վերագործարկվում։ CPU limit-ի գերազանցումը փոխարենը բերում է throttling-ի kernel-ի CFS quota-ի միջոցով, և throttling-ը ծանրաբեռնված service-ին կարող է տալ latency-ի մեծ ցատկեր, նույնիսկ երբ node-ում ազատ CPU կա։ Ահա թե ինչու շատ թիմեր հիշողության limit-ը դնում են request-ին հավասար, CPU requests-ը ընտրում են զգուշորեն և latency-ի նկատմամբ զգայուն service-ների համար CPU limit չեն դնում, իսկ ընդհանուր ծավալը վերահսկում են namespace quota-ներով։

yaml
resources:
  requests: { cpu: 500m, memory: 512Mi }
  limits:   { memory: 512Mi }       # no CPU limit: avoid throttling
 
# diagnose
# kubectl describe pod web-7d9f -n prod | grep -A3 "Last State"
# kubectl top pod -n prod --containers

09Graceful shutdown և կամավոր ընդհատումներ

Երբ pod-ը ջնջվում է՝ rollout-ի, replica-ների կրճատման կամ node drain-ի ժամանակ, միաժամանակ տեղի է ունենում երկու բան. kubelet-ը կոնտեյներներին ուղարկում է SIGTERM, իսկ endpoint controller-ը pod-ը հանում է Service-ի endpoint-ներից։ Proxy-ները և load balancer-ները այդ մասին իմանում են մի փոքր ուշ, ուստի մեկ-երկու վայրկյան տրաֆիկը դեռ կարող է հասնել արդեն անջատվող պրոցեսին։ Կարճ preStop sleep-ը հետաձգում է SIGTERM-ը, մինչև routing-ը «հասնի»։ Այնուհետև հավելվածը պետք է դադարի նոր աշխատանք ընդունել, ավարտի ընթացիկ հարցումները և դուրս գա terminationGracePeriodSeconds-ի ընթացքում (լռելյայն՝ 30), որից հետո այն սպանվում է։ Պլանավորված իրադարձությունների համար, օրինակ node-երի թարմացման, PodDisruptionBudget-ը Kubernetes-ին ասում է, թե քանի replica կարող է միաժամանակ անհասանելի լինել, և drain-երը դա հարգում են։ Topology spread constraint-ները replica-ները բաշխում են տարբեր node-երի և zone-երի վրա, որպեսզի մեկ խափանումը երբեք բոլորին չտանի։

yaml
spec:
  terminationGracePeriodSeconds: 45
  topologySpreadConstraints:
    - maxSkew: 1
      topologyKey: topology.kubernetes.io/zone
      whenUnsatisfiable: ScheduleAnyway
      labelSelector: { matchLabels: { app: web } }
  containers:
    - name: web
      lifecycle:
        preStop: { exec: { command: ["sleep", "5"] } }
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata: { name: web }
spec: { minAvailable: 2, selector: { matchLabels: { app: web } } }

10Անվտանգության բազային մակարդակ՝ Pod Security, RBAC և NetworkPolicy

Լռելյայն Kubernetes cluster-ը թույլատու է. ցանկացած pod կարող է խոսել ցանկացած այլ pod-ի հետ, և pod-ը կարող է աշխատել root-ով՝ լայն արտոնություններով։ Երեք ներկառուցված մեխանիզմ սա խստացնում են։ Pod Security Admission-ը namespace-ի մակարդակով label-ների միջոցով կիրառում է Pod Security Standards-ը՝ privileged, baseline կամ restricted։ Restricted-ը պահանջում է non-root, արտոնությունների բարձրացման արգելք, հանված capability-ներ և seccomp profile, և դա լավ նպատակ է հավելվածների namespace-ների համար։ RBAC-ը պետք է հետևի նվազագույն արտոնությունների սկզբունքին. հնարավորության դեպքում namespace-ային Role-եր՝ ClusterRole-երի փոխարեն, ոչ մի wildcard verb, և անջատված service account token-ի ավտոմատ mount այն pod-երի համար, որոնք երբեք API չեն կանչում։ NetworkPolicy-ն firewall է pod-երի համար. յուրաքանչյուր namespace սկսիր default deny-ով, ապա թույլ տուր միայն անհրաժեշտ հոսքերը, օրինակ՝ ingress gateway-ից և egress դեպի բազա ու DNS։ NetworkPolicy-ները կիրառվում են միայն այն դեպքում, եթե CNI plugin-ը դրանք աջակցում է, ինչպես Calico-ն և Cilium-ը։

yaml
apiVersion: v1
kind: Namespace
metadata:
  name: prod
  labels:
    pod-security.kubernetes.io/enforce: restricted
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata: { name: default-deny, namespace: prod }
spec:
  podSelector: {}
  policyTypes: [Ingress, Egress]

Իրական իրավիճակներ

01

Release-ից հետո pod-երը CrashLoopBackOff-ում են

Խնդիր

Նոր image է rollout արվել, և նոր pod-երը անընդհատ վերագործարկվում են՝ աճող back-off ուշացումներով։ Հին ReplicaSet-ը դեռ սպասարկում է տրաֆիկը, բայց rollout-ը կախվել է, և թիմը չգիտի՝ որտեղ նայել։

Ինչպես լուծել

Սկսիր kubectl describe pod-ից՝ վերջին վիճակը, exit code-ը և event-ները կարդալու համար. 137-ը OOMKilled-ի հետ ցույց է տալիս հիշողությունը, 1-ը՝ հավելվածի սխալ, իսկ probe-ների ձախողումները երևում են event-ներում։ Ապա գործարկիր kubectl logs <pod> --previous՝ ընկած կոնտեյների output-ը տեսնելու համար, ոչ թե ընթացիկ փորձինը։ Տարածված պատճառներն են բացակայող environment variable-ը կամ Secret-ի key-ը, մեկնարկի ժամանակ ձախողվող migration-ը կամ liveness probe-ը, որը գործարկվում է մինչև հավելվածի պատրաստ լինելը, ինչը լուծվում է startupProbe-ով։ Քանի որ readiness-ը հին pod-երը պահել է աշխատանքում, outage չկա. եթե ուղղումը ժամանակ կպահանջի, գործարկիր kubectl rollout undo deployment/web և բացակայող ստուգումը ավելացրու CI-ում։

02

Նոր pod-երը մնում են Pending

Խնդիր

Արշավի համար Deployment-ը 3-ից 10 replica scale անելուց հետո յոթ pod երկար րոպեներ մնում են Pending վիճակում, և լրացուցիչ հզորությունը այդպես էլ չի գալիս։

Ինչպես լուծել

Դրանցից մեկի վրա գործարկիր kubectl describe pod և կարդա scheduler-ի event-ը. այնտեղ հստակ գրված է պատճառը, օրինակ՝ բոլոր node-երում CPU-ի պակաս, չհամընկնող node selector կամ taint, կամ այլ zone-ի կապված volume։ Հզորության խնդիրների դեպքում ստուգիր, որ cluster autoscaler-ը կամ Karpenter-ը տեղադրված է, և node pool-ի առավելագույնը թույլ է տալիս աճել, ու համեմատիր requests-ը իրական օգտագործման հետ, որովհետև ուռճացված requests-ը վատնում է հզորությունը։ kubectl describe node-ը ցույց է տալիս allocated-ն ընդդեմ allocatable ռեսուրսների։ Zone-ին կապված volume-ների համար օգտագործիր storage class՝ volumeBindingMode WaitForFirstConsumer-ով։ Հաջորդ արշավից առաջ նախապես scale արա կամ բարձրացրու minReplicas-ը, այլ ոչ թե ծանրաբեռնվածության տակ ապավինիր autoscaling-ին։

03

Node-երի թարմացումները կարճ outage-ներ են առաջացնում

Խնդիր

Node pool-ի յուրաքանչյուր ամսական թարմացում մի քանի րոպե սխալներ է առաջացնում։ Deployment-ն ունի երեք replica, բայց օգտատերերը դեռ ձախողումներ են տեսնում node-երի drain-ի ժամանակ։

Ինչպես լուծել

Նայիր, թե որտեղ են աշխատում replica-ները. հաճախ երեքն էլ հայտնվում են մեկ-երկու node-ի վրա, և մեկ node-ի drain-ը հանում է հզորության մեծ մասը։ Ավելացրու topology spread constraint-ներ node-երի և zone-երի միջև։ Ավելացրու PodDisruptionBudget՝ minAvailable: 2-ով, որպեսզի drain-ը սպասի, այլ ոչ թե միանգամից շատ pod-եր դուրս հանի։ Ապա ստուգիր անջատումը. մի քանի վայրկյանանոց preStop sleep, SIGTERM-ի մշակում՝ կապերը փակելով, և grace period, որն ավելի երկար է, քան ամենադանդաղ հարցումը։ Համոզվիր, որ readiness probe-ը իսկապես արտացոլում է սպասարկելու ունակությունը։ Վերջում թեստավորիր. staging-ում kubectl drain-ով drain արա node՝ սինթետիկ ծանրաբեռնվածության տակ, և համոզվիր, որ սխալների մակարդակը չի աճում։

Արագ ուղեցույց

Վերջին event-ները, նորերը վերջում
kubectl get events -n prod --sort-by=.metadata.creationTimestamp
Ի՞նչ է կատարվում այս pod-ի հետ
kubectl describe pod <pod> -n prod
Ընկած փորձի log-երը
kubectl logs <pod> -n prod --previous
Ռեսուրսների ընթացիկ օգտագործումը
kubectl top pod -n prod --containers
Rollback անել Deployment-ը
kubectl rollout undo deployment/web -n prod
Debug անել distroless pod-ը ժամանակավոր shell-ով
kubectl debug -it <pod> -n prod --image=busybox:1.37 --target=web
Հասնել ClusterIP service-ին laptop-ից
kubectl port-forward svc/web 8080:80 -n prod
Ստուգել, թե ինչ է թույլատրված identity-ին
kubectl auth can-i list secrets -n prod --as=system:serviceaccount:prod:web
Drain անել node-ը սպասարկման համար
kubectl drain <node> --ignore-daemonsets --delete-emptydir-data
Վերագործարկել pod-երը նոր config-ը վերցնելու համար
kubectl rollout restart deployment/web -n prod

Հարցեր և պատասխաններ

Q1Ո՞րն է տարբերությունը Pod-ի և Deployment-ի միջև։

Pod-ը deploy արվող ամենափոքր միավորն է՝ մեկ կամ ավելի կոնտեյներ, որոնք կիսում են network namespace և storage և միասին են տեղաբաշխվում մեկ node-ի վրա։ Pod-երը կարճատև են. եթե մեկը մահանում է կամ դրա node-ը խափանվում է, այն չի «հարություն առնում», և փոխարինողը նոր անուն ու IP է ստանում։ Deployment-ը controller է, որը կառավարում է ReplicaSet, իսկ վերջինս իր հերթին պահում է նույնական pod-երի նշված քանակը աշխատող վիճակում։ Այն նաև իրականացնում է rolling update-ներ և rollback-ներ, երբ փոխում ես pod template-ը, օրինակ՝ image tag-ը։ Գործնականում դու ստեղծում ես Deployment-ներ, կամ StatefulSet-ներ stateful workload-ների համար, և գրեթե երբեք՝ մերկ Pod-եր։

Q2Service-ի ինչ տեսակներ կան, և երբ է օգտագործվում յուրաքանչյուրը։

ClusterIP-ը լռելյայնն է՝ կայուն ներքին վիրտուալ IP և DNS անուն, հասանելի միայն cluster-ի ներսում, օգտագործվում է ծառայությունների միջև տրաֆիկի համար։ NodePort-ը նույն port-ը բացում է յուրաքանչյուր node-ի վրա, հիմնականում օգտակար է թեստավորման կամ արտաքին load balancer-ին տրաֆիկ մատուցելու համար։ LoadBalancer-ը cloud մատակարարից խնդրում է ստեղծել արտաքին load balancer, որը նայում է service-ին. դա պարզ է, բայց արժե մեկ load balancer ամեն service-ի համար։ ExternalName-ը service-ի անունը քարտեզագրում է արտաքին DNS անվան։ HTTP տրաֆիկի համար սովորական մոդելը ClusterIP service-ներն են մեկ Gateway-ի կամ Ingress-ի հետևում, որը host-ով ու path-ով routing և TLS է անում բազմաթիվ service-ների համար մեկ load balancer-ի միջոցով։

Q3Ինչպե՞ս է աշխատում rolling update-ը, և ո՞րն է readiness probe-երի դերը։

Երբ փոխում ես Deployment-ի pod template-ը, այն ստեղծում է նոր ReplicaSet և աստիճանաբար մեծացնում այն՝ փոքրացնելով հինը։ maxSurge-ը որոշում է, թե թարմացման ընթացքում քանի լրացուցիչ pod կարող է լինել, իսկ maxUnavailable-ը՝ քանիսը կարող են բացակայել։ Նոր pod-ը հասանելի է համարվում միայն այն ժամանակ, երբ անցնում է readiness probe-ը, և Service-ներից տրաֆիկ են ստանում միայն պատրաստ pod-երը։ Այսպիսով, եթե նոր տարբերակը չի գործարկվում կամ readiness ստուգումը ձախողվում է, rollout-ը կանգ է առնում՝ առողջ pod-երը փոխարինելու փոխարեն, և կարող ես աշխատեցնել kubectl rollout undo՝ նախորդ ReplicaSet-ին վերադառնալու համար։ Առանց ճիշտ readiness probe-երի տրաֆիկը հասնում է դեռ չպատրաստված pod-երին։

Q4Ի՞նչ է տեղի ունենում, երբ node-ը մահանում է։

Այդ node-ի kubelet-ը դադարում է հաշվետվություն ուղարկել control plane-ին, և timeout-ից հետո node-ը նշվում է NotReady։ Դրա վրայի pod-երը դառնում են անհասանելի, endpoint-ները հեռացվում են Service-ներից, որպեսզի տրաֆիկն այլևս այնտեղ չգնա, և eviction-ի ուշացումից հետո այդ pod-երը նշվում են ջնջման համար։ Controller-ները, օրինակ՝ Deployment-ները, նկատում են, որ ունեն ցանկալիից քիչ replica, և ստեղծում են նոր pod-եր, որոնք scheduler-ը տեղադրում է բավարար հզորությամբ առողջ node-երի վրա։ Stateless հավելվածները վերականգնվում են ավտոմատ։ Stateful workload-ները ավելի շատ ուշադրություն են պահանջում. մեկ zone-ի հետ կապված volume-ները պետք է նորից կցվեն, իսկ StatefulSet-ները սպասում են՝ համոզվելու, որ հին pod-ը վերացել է, նախքան փոխարինողը գործարկելը։

Q5ConfigMap, թե՞ Secret. ինչպե՞ս ես աշխատում կոնֆիգուրացիայի և secret-ների հետ։

Երկուսն էլ կոնֆիգուրացիան ներարկում են pod-երի մեջ որպես environment variable-ներ կամ mount արված ֆայլեր։ ConfigMap-ները պահում են ոչ զգայուն կարգավորումներ, օրինակ՝ feature flag-եր կամ URL-ներ։ Secret-ները նախատեսված են credentials-ի համար, բայց լռելյայն դրանք միայն base64-ով են կոդավորված և պահվում են etcd-ում, ուստի յուրաքանչյուր ոք, ով դրանք կարդալու մուտք ունի, տեսնում է արժեքները։ Ես միացնում եմ etcd-ի encryption at rest-ը, RBAC-ով սահմանափակում Secret-ների մուտքը, խուսափում դրանք Git-ում commit անելուց և նախընտրաբար համաժամեցնում դրանք արտաքին manager-ից, ինչպիսիք են AWS Secrets Manager-ը կամ Vault-ը, External Secrets Operator-ի միջոցով, կամ GitOps-ի համար օգտագործում sealed կամ SOPS-ով գաղտնագրված secret-ներ։ Փոխված արժեքները ստանալու համար pod-երին սովորաբար պետք է վերագործարկում կամ reload մեխանիզմ։

Q6Ե՞րբ չէիր օգտագործի Kubernetes։

Երբ գործառնական ծախսը գերազանցում է օգուտը։ Մեկ հավելված կայուն տրաֆիկով, որը տեղավորվում է մեկ կամ երկու սերվերի վրա, կատարյալ աշխատում է Docker Compose-ով, լավ backup-ներով ու մոնիթորինգով, և ամբողջ թիմը կարող է հասկանալ ու debug անել այն։ Kubernetes-ը ավելացնում է ցանց, gateway-ներ, սերտիֆիկատներ, RBAC, թարմացումներ և YAML-ի մեծ ծավալ, և ինչ-որ մեկը պետք է տիրապետի այդ ամենին։ Եթե ուզում ես կոնտեյներներ առանց cluster կառավարելու, ECS-ը Fargate-ով կամ Cloud Run-ը տալիս են autoscaling և rolling deploy-ներ շատ ավելի քիչ հասկացություններով։ Ես կընտրեի Kubernetes բազմաթիվ ծառայությունների, մի քանի թիմերի, իրական autoscaling-ի կամ տեղափոխելիության կարիքների և պլատֆորմը վարելու պատրաստ մարդկանց դեպքում։

Սենիոր մակարդակի հարցեր

S1Ինչպե՞ս Kubernetes-ում տեղավորել մի քանի թիմ կամ հաճախորդ՝ namespace-նե՞ր, թե՞ առանձին cluster-ներ։

Namespace-ները տալիս են փափուկ multi-tenancy. յուրաքանչյուր թիմ ստանում է իր namespace-ը՝ RBAC-ով, resource quota-ներով, limit range-ներով, network policy-ներով և Pod Security label-ներով։ Դա արդյունավետ է և բավարար, երբ tenant-ները վստահում են միմյանց, օրինակ՝ մեկ ընկերության թիմերը։ Բայց բոլոր tenant-ները կիսում են control plane-ը, node-երը և kernel-ը, ուստի «աղմկոտ հարևանը» կամ container escape-ը կարող է ազդել ուրիշների վրա, իսկ cluster-ային օբյեկտները, օրինակ CRD-ները, ընդհանուր են։ Առանձին cluster-ները տալիս են կոշտ մեկուսացում, անկախ թարմացումներ և ծախսերի հստակ բաշխում՝ ավելի շատ control plane-եր սպասարկելու և ավելի քիչ արդյունավետ տեղաբաշխման գնով։ Տարածված միջին տարբերակը մի քանի cluster է՝ բաժանված ըստ միջավայրի և զգայունության, օրինակ՝ production և non-production, ներսում՝ namespace-ներ ըստ թիմերի, գումարած առանձին node pool-եր կամ cluster-ներ անվստահելի կամ կարգավորվող workload-ների համար։

S2Արժե՞ արդյոք բազաներ գործարկել Kubernetes-ի ներսում։

Դա հնարավոր է և գնալով ավելի տարածված, բայց պետք է գիտակցված որոշում լինի։ StatefulSet-ները տալիս են կայուն identity-ներ և persistent volume-ներ, իսկ operator-ները, օրինակ CloudNativePG-ն PostgreSQL-ի համար, ավտոմատացնում են replication-ը, failover-ը, backup-ները և թարմացումները, ինչը շատ ավելի լավ է, քան ձեռքով գրված manifest-ները։ Կողմ փաստարկները՝ մեկ հարթակ, տեղափոխելիություն և մասշտաբի դեպքում ավելի ցածր արժեք, քան managed ծառայություններինը։ Դեմ փաստարկները՝ storage-ի արտադրողականությունը և volume-ների վարքը կախված են CSI driver-ից և cloud-ից, node-երի drain-երն ու թարմացումները հիմա ազդում են քո primary-ի վրա, և թիմին իրական փորձ է պետք և՛ բազաներում, և՛ Kubernetes-ում, երբ գիշերը ինչ-որ բան կոտրվում է։ Թիմերի մեծ մասի համար ավելի անվտանգ լռելյայն տարբերակը managed բազան է, օրինակ RDS-ը կամ Cloud SQL-ը, իսկ Kubernetes-ը՝ stateless շերտերի համար. cluster-ի ներսի բազաները հարմար են platform թիմերին, որոնք արդեն ապացուցել են իրենց backup-ի և failover-ի վարժանքները։

S3Ի՞նչ է տալիս GitOps-ը, և որո՞նք են դրա փոխզիջումները։

GitOps-ում cluster-ի ներսում գտնվող controller-ը, օրինակ Argo CD-ն կամ Flux-ը, անընդհատ համեմատում է cluster-ը Git repository-ի manifest-ների հետ և կիրառում տարբերությունը։ Git-ը դառնում է ճշմարտության միակ աղբյուրը. յուրաքանչյուր փոփոխություն review արված commit է, պատմությունը՝ audit log, rollback-ը՝ revert, իսկ kubectl-ով ձեռքով արված փոփոխություններից առաջացած drift-ը հայտնաբերվում և չեղարկվում է։ CI-ին այլևս cluster-ի credential-ներ պետք չեն, որովհետև cluster-ն ինքն է քաշում փոփոխությունները, ինչը փոքրացնում է հարձակման մակերեսը։ Փոխզիջումները՝ ևս մեկ համակարգ, որը պետք է սպասարկել և պաշտպանել. secret-ների համար պետք է առանձին լուծում, օրինակ External Secrets կամ SOPS. image tag-երի թարմացումը պահանջում է ավտոմատացում, որը commit է անում հետ Git. իսկ միջավայրերի և cluster-ների համար repository-ի կառուցվածքը դառնում է առանձին նախագծային խնդիր։ Եթե service-ները կամ cluster-ները մեկ-երկուսից ավելի են, առավելությունները սովորաբար գերակշռում են։

S4Ինչպե՞ս անվտանգ թարմացնել Kubernetes cluster-ը։

Upstream-ը յուրաքանչյուր minor տարբերակ աջակցում է մոտ տասնչորս ամիս, և patch-եր ստանում են միայն երեք ամենավերջին minor տարբերակները, ուստի թարմացումները ռուտին են՝ մեկ minor տարբերակ միանգամից։ Թարմացումից առաջ կարդա release notes-ը՝ հեռացված API-ների համար, և manifest-ներն ու Helm chart-երը scan արա Pluto-ի կամ kubent-ի նման գործիքով, որովհետև հեռացված API տարբերակները կոտրում են deploy-երը։ Ստուգիր, որ add-on-ները՝ CNI-ը, CSI driver-ները, ingress կամ gateway controller-ը և cert-manager-ը, աջակցում են թիրախային տարբերակը։ Սկզբում թարմացրու staging-ը և թող որոշ ժամանակ աշխատի։ Production-ում թարմացրու control plane-ը, ապա node pool-երը՝ surge թարմացումներով կամ նոր pool ստեղծելով և հինը drain անելով, ինչը աշխատում է միայն այն դեպքում, եթե PodDisruptionBudget-ները, topology spread-ը և graceful shutdown-ը ճիշտ են։ Blue-green cluster-ները՝ նոր cluster կառուցել և տրաֆիկը տեղափոխել, ամենաանվտանգ և ամենաթանկ տարբերակն են։

Բառարան

Control plane
Բաղադրիչները, որոնք պահում են cluster-ի վիճակը և գործում ըստ դրա՝ API server, etcd, scheduler և controller manager։
etcd
Consistent key-value պահոց, որը պահում է cluster-ի բոլոր օբյեկտները. գրելու համար անհրաժեշտ է անդամների մեծամասնությունը։
kubelet
Յուրաքանչյուր node-ի գործակալը, որը մեկնարկում է իրեն նշանակված pod-երը և հաղորդում դրանց վիճակը։
Controller
Ցիկլ, որը հետևում է օբյեկտներին և իրական վիճակը մոտեցնում ցանկալի վիճակին։
ReplicaSet
Պահում է նույնական pod-երի ֆիքսված քանակ աշխատող վիճակում. Deployment-ները քո փոխարեն ստեղծում և կառավարում են ReplicaSet-ներ։
StatefulSet
Գործարկում է pod-եր կայուն անուններով, կարգավորված մեկնարկով և սեփական persistent volume-ներով՝ բազաների և նմանատիպ համակարգերի համար։
DaemonSet
Յուրաքանչյուր node-ի վրա գործարկում է pod-ի մեկ օրինակ, սովորաբար log collector-ների, մոնիթորինգի գործակալների կամ ցանցի համար։
PodDisruptionBudget
Կանոն, որը սահմանափակում է, թե հավելվածի քանի pod կարող է միաժամանակ անհասանելի լինել կամավոր ընդհատումների, օրինակ drain-ի ժամանակ։
QoS class
Guaranteed, Burstable կամ BestEffort՝ որոշվում է requests-ից և limits-ից. սահմանում է դուրս հանելու հերթականությունը հիշողության պակասի դեպքում։
CNI
Container Network Interface՝ plugin, օրինակ Cilium կամ Calico, որը pod-երին ցանց է տալիս և կիրառում policy-ները։

Պրոդուկտի գաղափար ունե՞ք։Եկեք ստեղծենք այն։

Պատմեք ձեր գաղափարի, պրոդուկտի կամ թիմի մասին։ Սովորաբար պատասխանում եմ մեկ աշխատանքային օրվա ընթացքում։