Autoescalado e xestión de recursos en Kubernetes (descrición xeral e informe de vídeo)

27 de abril na conferencia Folga 2019, como parte da sección "DevOps", presentouse o informe "Auto-escalado e xestión de recursos en Kubernetes". Fala de como podes usar os K8 para garantir a alta dispoñibilidade das túas aplicacións e garantir o máximo rendemento.

Autoescalado e xestión de recursos en Kubernetes (descrición xeral e informe de vídeo)

Por tradición, temos o pracer de presentar vídeo do informe (44 minutos, moito máis informativo que o artigo) e o resumo principal en forma de texto. Vaia!

Analizemos o tema do informe palabra por palabra e comecemos polo final.

Kubernetes

Digamos que temos contedores Docker no noso servidor. Para qué? Para garantir a repetibilidade e o illamento, que á súa vez permiten unha implantación sinxela e boa, CI/CD. Temos moitos vehículos deste tipo con contedores.

Que ofrece Kubernetes neste caso?

  1. Deixamos de pensar nestas máquinas e comezamos a traballar coa "nube" agrupación de contedores ou vainas (grupos de recipientes).
  2. Ademais, nin sequera pensamos en vainas individuais, pero xestionamos máisоgrupos máis grandes. Tal primitivos de alto nivel permítenos dicir que hai un modelo para executar unha determinada carga de traballo e aquí tes o número necesario de instancias para executalo. Se posteriormente cambiamos o modelo, todas as instancias cambiarán.
  3. Con API declarativa En lugar de executar unha secuencia de comandos específicos, describimos a "estrutura do mundo" (en YAML), que é creada por Kubernetes. E de novo: cando a descrición cambia, a súa visualización real tamén cambiará.

Xestión de recursos

CPU

Imos executar nginx, php-fpm e mysql no servidor. Estes servizos terán aínda máis procesos en execución, cada un dos cales require recursos informáticos:

Autoescalado e xestión de recursos en Kubernetes (descrición xeral e informe de vídeo)
(os números da diapositiva son "loros", a necesidade abstracta de cada proceso de potencia de cálculo)

Para facilitar o traballo con isto, é lóxico combinar procesos en grupos (por exemplo, todos os procesos nginx nun grupo "nginx"). Unha forma sinxela e obvia de facelo é poñer cada grupo nun recipiente:

Autoescalado e xestión de recursos en Kubernetes (descrición xeral e informe de vídeo)

Para continuar, cómpre lembrar o que é un contedor (en Linux). A súa aparición foi posible grazas a tres características clave do núcleo, implementadas hai moito tempo: capacidades, espazos de nomes и cgroups. E o desenvolvemento adicional foi facilitado por outras tecnoloxías (incluíndo convenientes "shells" como Docker):

Autoescalado e xestión de recursos en Kubernetes (descrición xeral e informe de vídeo)

No contexto do informe, só nos interesa cgroups, porque os grupos de control son a parte da funcionalidade dos contedores (Docker, etc.) que implementa a xestión de recursos. Os procesos combinados en grupos, como queriamos, son grupos de control.

Volvamos aos requisitos de CPU para estes procesos, e agora para grupos de procesos:

Autoescalado e xestión de recursos en Kubernetes (descrición xeral e informe de vídeo)
(Repito que todos os números son unha expresión abstracta da necesidade de recursos)

Ao mesmo tempo, a propia CPU ten un certo recurso finito (no exemplo é 1000), que todos poden carecer (a suma das necesidades de todos os grupos é 150+850+460=1460). Que pasará neste caso?

O núcleo comeza a distribuír recursos e faino de forma "xusta", dando a mesma cantidade de recursos a cada grupo. Pero no primeiro caso, hai máis dos necesarios (333>150), polo que o exceso (333-150=183) queda en reserva, que tamén se reparte equitativamente entre outros dous contedores:

Autoescalado e xestión de recursos en Kubernetes (descrición xeral e informe de vídeo)

Como resultado: o primeiro contedor tiña recursos suficientes, o segundo -non tiña recursos suficientes, o terceiro- non tiña recursos suficientes. Este é o resultado das accións programador "honesto" en Linux - CFS. O seu funcionamento pódese axustar mediante a asignación peso cada un dos recipientes. Por exemplo, así:

Autoescalado e xestión de recursos en Kubernetes (descrición xeral e informe de vídeo)

Vexamos o caso de falta de recursos no segundo contedor (php-fpm). Todos os recursos do contedor distribúense por igual entre os procesos. Como resultado, o proceso mestre funciona ben, pero todos os traballadores ralentizan, recibindo menos da metade do que necesitan:

Autoescalado e xestión de recursos en Kubernetes (descrición xeral e informe de vídeo)

Así funciona o programador CFS. Chamaremos ademais os pesos que asignamos aos contedores solicitudes. Por que isto é así - ver máis adiante.

Vexamos toda a situación dende o outro lado. Como sabedes, todos os camiños levan a Roma, e no caso dun ordenador, á CPU. Unha CPU, moitas tarefas: necesitas un semáforo. A forma máis sinxela de xestionar os recursos é o "semáforo": dábanlle a un proceso un tempo de acceso fixo á CPU, despois ao seguinte, etc.

Autoescalado e xestión de recursos en Kubernetes (descrición xeral e informe de vídeo)

Este enfoque chámase cotas duras (limitación dura). Lembrámolo simplemente como límites. Non obstante, se distribúes límites a todos os contedores, xorde un problema: mysql circulaba pola estrada e nalgún momento a súa necesidade de CPU rematou, pero todos os demais procesos vense obrigados a esperar ata que a CPU ocioso.

Autoescalado e xestión de recursos en Kubernetes (descrición xeral e informe de vídeo)

Volvamos ao núcleo de Linux e á súa interacción coa CPU: a imaxe xeral é a seguinte:

Autoescalado e xestión de recursos en Kubernetes (descrición xeral e informe de vídeo)

cgroup ten dúas opcións - esencialmente estas son dúas "xiros" simples que che permiten determinar:

  1. peso para recipiente (solicitudes) é partes;
  2. porcentaxe do tempo total da CPU para traballar en tarefas de contedores (límites). cota.

Como medir a CPU?

Hai diferentes formas:

  1. O que é papagaios, ninguén sabe - ten que negociar cada vez.
  2. Interese máis claro, pero relativo: o 50% dun servidor con 4 núcleos e con 20 núcleos son cousas completamente diferentes.
  3. Podes usar os xa mencionados peso, que Linux coñece, pero tamén son relativos.
  4. A opción máis adecuada é medir os recursos informáticos en segundos. Eses. en segundos de tempo do procesador en relación con segundos de tempo real: deuse 1 segundo de tempo de procesador por 1 segundo real: este é un núcleo completo da CPU.

Para facer aínda máis fácil falar, comezaron a medir directamente núcleos, o que significa por eles o mesmo tempo de CPU en relación co real. Dado que Linux entende os pesos, pero non tanto o tempo/núcleos de CPU, era necesario un mecanismo para traducir dun a outro.

Consideremos un exemplo sinxelo cun servidor con 3 núcleos de CPU, onde se darán pesos a tres pods (500, 1000 e 1500) que se converten facilmente nas correspondentes partes dos núcleos asignados a eles (0,5, 1 e 1,5).

Autoescalado e xestión de recursos en Kubernetes (descrición xeral e informe de vídeo)

Se tomas un segundo servidor, onde haberá o dobre de núcleos (6), e colocas alí os mesmos pods, a distribución dos núcleos pódese calcular facilmente multiplicando por 2 (1, 2 e 3, respectivamente). Pero prodúcese un momento importante cando neste servidor aparece un cuarto pod, cuxo peso, por comodidade, será de 3000. Quita parte dos recursos da CPU (a metade dos núcleos), e para os pods restantes recalcúlanse (redúcense á metade):

Autoescalado e xestión de recursos en Kubernetes (descrición xeral e informe de vídeo)

Kubernetes e recursos da CPU

En Kubernetes, os recursos da CPU adoitan medirse en miliadrax, é dicir. Tómanse 0,001 núcleos como peso base. (O mesmo na terminoloxía de Linux/cgroups chámase CPU compartido, aínda que, máis precisamente, 1000 milicores = 1024 CPU compartidos). K8s garante que non coloca máis pods no servidor dos que hai recursos da CPU para a suma dos pesos de todos os pods.

Como ocorre isto? Cando engades un servidor a un clúster de Kubernetes, infórmase de cantos núcleos de CPU ten dispoñibles. E ao crear un novo pod, o programador de Kubernetes sabe cantos núcleos necesitará este pod. Así, o pod asignarase a un servidor onde haxa núcleos suficientes.

Que pasará se non se especifica a solicitude (é dicir, o pod non ten un número definido de núcleos que necesita)? Imos descubrir como Kubernetes conta xeralmente os recursos.

Para un pod podes especificar tanto solicitudes (programador CFS) como límites (lembras do semáforo?):

  • Se se especifican iguais, asígnaselle ao pod unha clase de QoS garantida. Este número de núcleos sempre dispoñibles está garantido.
  • Se a solicitude é inferior ao límite - clase de QoS estourable. Eses. Esperamos que un pod, por exemplo, use sempre 1 núcleo, pero este valor non é unha limitación para el: ás veces pod pode usar máis (cando o servidor ten recursos gratuítos para iso).
  • Tamén hai unha clase de QoS mellor esforzo — Inclúe aqueles mesmos pods para os que non se especifica a solicitude. Os recursos danlles os últimos.

memoria

Coa memoria, a situación é similar, pero lixeiramente diferente; despois de todo, a natureza destes recursos é diferente. En xeral, a analoxía é a seguinte:

Autoescalado e xestión de recursos en Kubernetes (descrición xeral e informe de vídeo)

Vexamos como se implementan as solicitudes na memoria. Deixa que os pods vivan no servidor, cambiando o consumo de memoria, ata que un deles se faga tan grande que se quede sen memoria. Neste caso, aparece o asasino OOM e mata o proceso máis grande:

Autoescalado e xestión de recursos en Kubernetes (descrición xeral e informe de vídeo)

Isto non sempre nos convén, polo que é posible regular que procesos son importantes para nós e que non deben ser eliminados. Para iso, use o parámetro oom_score_adj.

Volvamos ás clases de QoS da CPU e trazamos unha analoxía cos valores oom_score_adj que determinan as prioridades de consumo de memoria dos pods:

  • O valor oom_score_adj máis baixo para un pod - -998 - significa que ese pod debe ser eliminado o último, este garantida.
  • O máis alto - 1000 - é mellor esforzo, tales vainas mátanse primeiro.
  • Para calcular os valores restantes (estourable) hai unha fórmula, cuxa esencia se resume no feito de que cantos máis recursos solicitou unha vaina, menos probabilidades de ser asasinada.

Autoescalado e xestión de recursos en Kubernetes (descrición xeral e informe de vídeo)

O segundo "torcer" - limit_in_bytes - para os límites. Con el, todo é máis sinxelo: simplemente asignamos a cantidade máxima de memoria emitida e aquí (a diferenza da CPU) non hai dúbida de como medila (memoria).

En total

Cada pod en Kubernetes dáse requests и limits - Ambos parámetros para CPU e memoria:

  1. en función das solicitudes, funciona o planificador de Kubernetes, que distribúe pods entre os servidores;
  2. en función de todos os parámetros, determínase a clase de QoS do pod;
  3. Os pesos relativos calcúlanse en función das solicitudes da CPU;
  4. o planificador CFS está configurado en función das solicitudes da CPU;
  5. OOM killer está configurado en función das solicitudes de memoria;
  6. configúrase un "semáforo" en función dos límites da CPU;
  7. En función dos límites de memoria, configúrase un límite para o cgroup.

Autoescalado e xestión de recursos en Kubernetes (descrición xeral e informe de vídeo)

En xeral, esta imaxe responde a todas as preguntas sobre como se produce a parte principal da xestión de recursos en Kubernetes.

Autoescalado

K8s cluster-autoscaler

Imaxinemos que todo o clúster xa está ocupado e hai que crear un novo pod. Aínda que o pod non pode aparecer, colócase en estado Pendente. Para que apareza, podemos conectar un novo servidor ao clúster ou... instalar cluster-autoscaler, que o fará por nós: encarga unha máquina virtual ao provedor da nube (mediante unha solicitude de API) e conéctaa ao clúster. , despois do cal engadirase o pod .

Autoescalado e xestión de recursos en Kubernetes (descrición xeral e informe de vídeo)

Este é o escalado automático do clúster de Kubernetes, que funciona moi ben (según a nosa experiencia). Non obstante, como noutros lugares, aquí hai algúns matices...

Mentres aumentamos o tamaño do clúster, todo estaba ben, pero que pasa cando o clúster comezou a liberarse? O problema é que migrar pods (para liberar hosts) é moi difícil tecnicamente e caro en termos de recursos. Kubernetes usa un enfoque completamente diferente.

Considere un clúster de 3 servidores que teña Implementación. Ten 6 pods: agora hai 2 para cada servidor. Por algún motivo queriamos desactivar un dos servidores. Para iso usaremos o comando kubectl drain, que:

  • prohibirá enviar novos pods a este servidor;
  • eliminará os pods existentes no servidor.

Dado que Kubernetes é responsable de manter o número de pods (6), simplemente recreará noutros nodos, pero non no que está desactivado, xa que xa está marcado como non dispoñible para hospedar novos pods. Esta é unha mecánica fundamental para Kubernetes.

Autoescalado e xestión de recursos en Kubernetes (descrición xeral e informe de vídeo)

Non obstante, aquí tamén hai un matiz. Nunha situación similar, para StatefulSet (en lugar de Deployment), as accións serán diferentes. Agora xa temos unha aplicación con estado, por exemplo, tres pods con MongoDB, un dos cales ten algún tipo de problema (os datos corrompáronse ou outro erro que impide que o pod se inicie correctamente). E decidimos de novo desactivar un servidor. Que vai pasar?

Autoescalado e xestión de recursos en Kubernetes (descrición xeral e informe de vídeo)

MongoDB podería morrer porque necesita quórum: para un clúster de tres instalacións, deben funcionar polo menos dúas. Porén, isto non ocorrer - grazas a PodDisruptionBudget. Este parámetro determina o número mínimo necesario de vainas de traballo. Sabendo que un dos pods de MongoDB xa non funciona e vendo que PodDisruptionBudget está configurado para MongoDB minAvailable: 2, Kubernetes non che permitirá eliminar un pod.

Conclusión: para que o movemento (e, de feito, a recreación) dos pods funcione correctamente cando se libera o clúster, é necesario configurar PodDisruptionBudget.

Escalado horizontal

Consideremos outra situación. Hai unha aplicación en execución como Deployment en Kubernetes. O tráfico de usuarios chega aos seus módulos (por exemplo, hai tres) e medimos un determinado indicador neles (por exemplo, a carga da CPU). Cando a carga aumenta, gravámola nunha programación e aumentamos o número de pods para distribuír solicitudes.

Hoxe en Kubernetes non é necesario facelo manualmente: confírase un aumento/diminución automático do número de pods dependendo dos valores dos indicadores de carga medidos.

Autoescalado e xestión de recursos en Kubernetes (descrición xeral e informe de vídeo)

As principais preguntas aquí son: que medir exactamente и como interpretar valores obtidos (para tomar unha decisión sobre o cambio do número de vainas). Podes medir moito:

Autoescalado e xestión de recursos en Kubernetes (descrición xeral e informe de vídeo)

Como facelo tecnicamente: recolle métricas, etc. — Falei en detalle no informe Monitorización e Kubernetes. E o principal consello para escoller os parámetros óptimos é experimento!

Ten USE método (Saturación de uso e erros), cuxo significado é o seguinte. En que base ten sentido escalar, por exemplo, php-fpm? Con base en que os traballadores están esgotando, isto é utilización. E se os traballadores acaban e non se aceptan novas conexións, isto xa está saturación. Hai que medir estes dous parámetros e, en función dos valores, proceder á escala.

En vez de unha conclusión

O informe ten unha continuación: sobre a escala vertical e como seleccionar os recursos axeitados. Falarei disto en próximos vídeos o noso YouTube - Subscríbete para non perderte!

Vídeos e diapositivas

Vídeo da actuación (44 minutos):

Presentación do informe:

PS

Outros informes sobre Kubernetes no noso blog:

Fonte: www.habr.com

Engadir un comentario