Cinco faltas ao despregar a primeira aplicación en Kubernetes

Cinco faltas ao despregar a primeira aplicación en KubernetesFail de Aris Dreamer

Moita xente pensa que é suficiente con transferir a aplicación a Kubernetes (xa sexa usando Helm ou manualmente) e haberá felicidade. Pero non todo é tan sinxelo.

Equipo Solucións na nube Mail.ru traduciu un artigo do enxeñeiro de DevOps Julian Gindy. Conta cales son os escollos aos que se enfrontou a súa empresa durante o proceso de migración para que non pise o mesmo anciño.

Primeiro paso: configurar as solicitudes e os límites de pod

Comecemos configurando un ambiente limpo onde se executarán as nosas vainas. Kubernetes é excelente na programación de pod e na conmutación por fallo. Pero resultou que o programador ás veces non pode colocar un pod se é difícil estimar cantos recursos necesita para funcionar con éxito. Aquí é onde aparecen as solicitudes de recursos e límites. Hai moito debate sobre o mellor enfoque para establecer solicitudes e límites. Ás veces parece que isto é realmente máis unha arte que unha ciencia. Aquí está o noso enfoque.

Solicitudes de pod é o valor principal utilizado polo planificador para colocar de forma óptima o pod.

De Documentación de Kubernetes: o paso do filtro define un conxunto de nodos onde se pode programar un Pod. Por exemplo, o filtro PodFitsResources comproba se un nodo ten recursos suficientes para satisfacer solicitudes de recursos específicas dun pod.

Usamos as solicitudes de aplicacións de tal xeito que podemos estimar cantos recursos de feito A aplicación necesita que funcione correctamente. Deste xeito, o planificador pode colocar os nodos de forma realista. Inicialmente, queriamos programar en exceso as solicitudes para garantir os recursos suficientes para cada Pod, pero observamos que o tempo de programación aumentou significativamente e algúns Pods non estaban totalmente programados, como se non houbese solicitudes de recursos para eles.

Neste caso, o programador a miúdo "exprimiría" os módulos e non podía reprogramalos porque o plano de control non tiña idea de cantos recursos necesitaría a aplicación, que é un compoñente clave do algoritmo de programación.

Límites de pod é un límite máis claro para unha vaina. Representa a cantidade máxima de recursos que o clúster asignará ao contedor.

De novo, dende documentación oficial: Se un contedor ten un límite de memoria de 4 GiB, entón o kubelet (e o tempo de execución do contenedor) aplicarano. O tempo de execución impide que o contedor use máis do límite de recursos especificado. Por exemplo, cando un proceso nun contenedor intenta utilizar máis da cantidade permitida de memoria, o núcleo do sistema finaliza o proceso cun erro de "memoria sen memoria" (OOM).

Un contedor sempre pode usar máis recursos dos que especifica a solicitude de recursos, pero nunca pode usar máis que o límite. Este valor é difícil de establecer correctamente, pero é moi importante.

Idealmente, queremos que os requisitos de recursos dun pod cambien durante o ciclo de vida dun proceso sen interferir con outros procesos do sistema; este é o propósito de establecer límites.

Desafortunadamente, non podo dar instrucións específicas sobre que valores establecer, pero nós mesmos cumprimos as seguintes regras:

  1. Usando unha ferramenta de proba de carga, simulamos un nivel base de tráfico e observamos o uso dos recursos do pod (memoria e procesador).
  2. Establece as solicitudes de pod nun valor arbitrariamente baixo (cun ​​límite de recursos de aproximadamente 5 veces o valor das solicitudes) e observa. Cando as solicitudes están a un nivel demasiado baixo, o proceso non pode iniciarse, causando moitas veces erros crípticos de execución de Go.

Observo que os límites de recursos máis altos dificultan a programación porque o pod necesita un nodo de destino con suficientes recursos dispoñibles.

Imaxina unha situación na que tes un servidor web lixeiro cun límite de recursos moi alto, como 4 GB de memoria. É probable que este proceso teña que escalar horizontalmente e cada novo pod terá que programarse nun nodo con polo menos 4 GB de memoria dispoñible. Se non existe tal nodo, o clúster debe introducir un novo nodo para procesar este pod, o que pode levar algún tempo. É importante conseguir unha diferenza mínima entre as solicitudes de recursos e os límites para garantir un escalado rápido e suave.

Segundo paso: configurar probas de vivacidade e preparación

Este é outro tema sutil que adoita discutir na comunidade de Kubernetes. É importante ter unha boa comprensión das probas de Liveness e Readiness, xa que proporcionan un mecanismo para o funcionamento estable do software e minimizan o tempo de inactividade. Non obstante, poden afectar seriamente o rendemento da súa aplicación se non se configuran correctamente. A continuación móstrase un resumo do que son ambas as mostras.

Vida mostra se o contedor está funcionando. Se falla, o kubelet mata o contedor e a política de reinicio está habilitada para el. Se o contenedor non está equipado cunha sonda Liveness, o estado predeterminado será satisfactorio, como se indica en Documentación de Kubernetes.

As sondas de liveness deben ser baratas, é dicir, non consumir moitos recursos, porque se executan con frecuencia e deben informar a Kubernetes de que a aplicación se está a executar.

Se configuras a opción para que se execute cada segundo, engadirase 1 solicitude por segundo, polo que teña en conta que se necesitarán recursos adicionais para procesar este tráfico.

Na nosa empresa, as probas de Liveness proban os compoñentes fundamentais dunha aplicación, aínda que os datos (por exemplo, dunha base de datos remota ou da caché) non estean completamente dispoñibles.

Configuramos un punto final de "saúde" nas aplicacións que simplemente devolve un código de resposta 200. Esta é unha indicación de que o proceso está en execución e é capaz de xestionar solicitudes (pero aínda non o tráfico).

Mostra Preparación indica se o contedor está preparado para atender solicitudes. Se a sonda de preparación falla, o controlador do punto final elimina o enderezo IP do pod dos puntos finais de todos os servizos que coincidan co pod. Isto tamén se indica na documentación de Kubernetes.

As sondas de preparación consumen máis recursos, xa que deben chegar ao backend de forma que se mostre que a aplicación está lista para aceptar solicitudes.

Hai moito debate na comunidade sobre se acceder á base de datos directamente. Tendo en conta a sobrecarga (as comprobacións son frecuentes, pero poden controlarse), decidimos que para algunhas aplicacións, a preparación para servir o tráfico só se conta despois de comprobar que os rexistros son devoltos da base de datos. As probas de preparación ben deseñadas aseguraron niveis máis altos de dispoñibilidade e eliminaron o tempo de inactividade durante a implantación.

Se decides consultar a base de datos para probar a preparación da túa aplicación, asegúrate de que sexa o máis barata posible. Tomemos esta consulta:

SELECT small_item FROM table LIMIT 1

Aquí tes un exemplo de como configuramos estes dous valores en Kubernetes:

livenessProbe: 
 httpGet:   
   path: /api/liveness    
   port: http 
readinessProbe:  
 httpGet:    
   path: /api/readiness    
   port: http  periodSeconds: 2

Podes engadir algunhas opcións de configuración adicionais:

  • initialDelaySeconds - cantos segundos pasarán entre o lanzamento do contedor e o inicio do lanzamento das sondas.
  • periodSeconds - Intervalo de espera entre as probas.
  • timeoutSeconds — o número de segundos transcorridos os cales o pod se considera de emerxencia. Tempo de espera normal.
  • failureThreshold é o número de fallos de proba antes de enviar un sinal de reinicio ao pod.
  • successThreshold é o número de probas exitosas antes de que o pod pase ao estado listo (despois dun fallo cando se inicia ou se recupera).

Paso tres: Configurar as políticas de rede predeterminadas do Pod

Kubernetes ten unha topografía de rede "plana", de forma predeterminada, todos os pods comunícanse directamente entre si. Nalgúns casos, isto non é desexable.

Un problema de seguranza potencial é que un atacante pode usar unha única aplicación vulnerable para enviar tráfico a todos os pods da rede. Como en moitas áreas de seguridade, o principio de privilexio mínimo aplícase aquí. Idealmente, as políticas de rede deberían indicar de forma explícita que conexións entre pods están permitidas e cales non.

Por exemplo, a seguinte é unha política sinxela que nega todo o tráfico entrante para un espazo de nomes concreto:

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:  
 name: default-deny-ingress
spec:  
 podSelector: {}  
 policyTypes:  
   - Ingress

Visualización desta configuración:

Cinco faltas ao despregar a primeira aplicación en Kubernetes
(https://miro.medium.com/max/875/1*-eiVw43azgzYzyN1th7cZg.gif)
Máis detalles aquí.

Paso catro: comportamento personalizado con ganchos e contedores de inicio

Un dos nosos obxectivos principais era ofrecer implementacións en Kubernetes sen tempo de inactividade para os desenvolvedores. Isto é difícil porque hai moitas opcións para pechar as aplicacións e liberar os recursos utilizados.

Dificultades particulares xurdiron con Nginx. Observamos que ao implementar estes Pods en secuencia, as conexións activas interrompéronse antes de completarse con éxito.

Despois dunha ampla investigación en Internet, descubriuse que Kubernetes non espera a que as conexións de Nginx se esgoten antes de apagar o pod. Coa axuda do gancho de pre-parada, implementamos a seguinte funcionalidade e eliminamos completamente o tempo de inactividade:

lifecycle: 
 preStop:
   exec:
     command: ["/usr/local/bin/nginx-killer.sh"]

Pero nginx-killer.sh:

#!/bin/bash
sleep 3
PID=$(cat /run/nginx.pid)
nginx -s quit
while [ -d /proc/$PID ]; do
   echo "Waiting while shutting down nginx..."
   sleep 10
done

Outro paradigma extremadamente útil é o uso de contedores init para xestionar o lanzamento de aplicacións específicas. Isto é especialmente útil se tes un proceso de migración de base de datos que require moitos recursos que debe executarse antes de que se inicie a aplicación. Tamén pode especificar un límite de recursos máis elevado para este proceso sen establecer ese límite para a aplicación principal.

Outro esquema común é acceder aos segredos no contedor de inicio, que proporciona estas credenciais ao módulo principal, o que impide o acceso non autorizado aos segredos desde o propio módulo da aplicación principal.

Como é habitual, unha cita da documentación: os contedores init executan con seguridade código de usuario ou utilidades que, doutro xeito, comprometerían a seguridade da imaxe do contedor da aplicación. Ao manter separadas as ferramentas innecesarias, limitas a superficie de ataque da imaxe do contedor da aplicación.

Paso cinco: configuración do núcleo

Para rematar, imos falar dunha técnica máis avanzada.

Kubernetes é unha plataforma extremadamente flexible que che permite executar cargas de traballo como consideres conveniente. Temos unha serie de aplicacións altamente eficientes que consumen moito recursos. Despois de realizar probas de carga exhaustivas, descubrimos que unha das aplicacións estaba a ter dificultades para manterse ao día coa carga de tráfico esperada cando a configuración predeterminada de Kubernetes estaba en vigor.

Non obstante, Kubernetes permítelle executar un contedor privilexiado que só cambia os parámetros do núcleo para un pod específico. Isto é o que usamos para cambiar o número máximo de conexións abertas:

initContainers:
  - name: sysctl
     image: alpine:3.10
     securityContext:
         privileged: true
      command: ['sh', '-c', "sysctl -w net.core.somaxconn=32768"]

Esta é unha técnica máis avanzada que moitas veces non é necesaria. Pero se a túa aplicación ten dificultades para facer fronte a unha carga pesada, podes probar a modificar algunhas destas configuracións. Máis información sobre este proceso e a configuración de diferentes valores, coma sempre na documentación oficial.

En conclusión

Aínda que Kubernetes pode parecer unha solución lista para usar, hai algúns pasos clave que se deben tomar para manter as aplicacións funcionando sen problemas.

Ao longo da migración a Kubernetes, é importante seguir o "ciclo de probas de carga": executar a aplicación, probala baixo carga, observar as métricas e o comportamento de escalado, axustar a configuración en función destes datos e, a continuación, repetir este ciclo de novo.

Sexa realista sobre o tráfico esperado e tenta ir máis aló para ver que compoñentes rompen primeiro. Con este enfoque iterativo, só algunhas das recomendacións enumeradas poden ser suficientes para acadar o éxito. Ou pode requirir unha personalización máis profunda.

Fai sempre estas preguntas:

  1. Cantos recursos consomen as aplicacións e como cambiará esta cantidade?
  2. Cales son os requisitos reais de escalado? Canto tráfico manexará a aplicación de media? E o tráfico pico?
  3. Cantas veces terá que ampliar o servizo? Con que rapidez teñen que estar en funcionamento os novos pods para recibir tráfico?
  4. Con que gracia se apagan as vainas? É necesario en absoluto? É posible lograr a implantación sen tempo de inactividade?
  5. Como minimizar os riscos de seguridade e limitar o dano de calquera pod comprometida? Algún servizo ten permisos ou accesos que non precisan?

Kubernetes ofrece unha plataforma incrible que che permite utilizar as mellores prácticas para implantar miles de servizos nun clúster. Non obstante, todas as aplicacións son diferentes. Ás veces, a implementación require un pouco máis de traballo.

Afortunadamente, Kubernetes ofrece a configuración necesaria para acadar todos os obxectivos técnicos. Usando unha combinación de solicitudes e límites de recursos, sondas de Liveness e Readiness, contenedores de inicio, políticas de rede e axustes personalizados do núcleo, pode acadar un alto rendemento xunto coa tolerancia a fallos e unha escalabilidade rápida.

Que máis ler:

  1. Mellores prácticas e boas prácticas para executar contedores e Kubernetes en entornos de produción.
  2. Máis de 90 ferramentas útiles para Kubernetes: implantación, xestión, vixilancia, seguridade e moito máis.
  3. A nosa canle En torno a Kubernetes en Telegram.

Fonte: www.habr.com

Engadir un comentario