Introdução: Superando desafios de gerenciamento de GPU
Na Parte 1 desta série de blogs, exploramos os desafios de hospedar grandes modelos de linguagem (LLMs) em cargas de trabalho baseadas em CPU em um cluster EKS. Discutimos as ineficiências associadas ao uso de CPUs para tais tarefas, principalmente devido aos grandes tamanhos dos modelos e às velocidades de inferência mais lentas. A introdução de recursos de GPU ofereceu um aumento significativo de desempenho, mas também trouxe a necessidade de um gerenciamento eficiente desses recursos de alto custo.
Nesta segunda parte, nos aprofundaremos em como otimizar o uso da GPU para essas cargas de trabalho. Cobriremos as seguintes áreas principais:
- Configuração do plug-in de dispositivo NVIDIA: esta seção explicará a importância do plug-in de dispositivo NVIDIA para Kubernetes, detalhando sua função na descoberta, alocação e isolamento de recursos.
- Fatiação do tempo: Discutiremos como a divisão de tempo permite que vários processos compartilhem recursos de GPU de maneira eficaz, garantindo a utilização máxima.
- Escalonamento automático de nós com Karpenter: Esta seção descreverá como o Karpenter gerencia dinamicamente o dimensionamento de nós com base na demanda em tempo actual, otimizando a utilização de recursos e reduzindo custos.
Desafios abordados
- Gerenciamento eficiente de GPU: Garantir que as GPUs sejam totalmente utilizadas para justificar seu alto custo.
- Tratamento de simultaneidade: permitindo que várias cargas de trabalho compartilhem recursos de GPU de maneira eficaz.
- Dimensionamento Dinâmico: ajusta automaticamente o número de nós com base nas demandas da carga de trabalho.
Seção 1: Introdução ao plug-in de dispositivo NVIDIA
O plugin de dispositivo NVIDIA para Kubernetes é um componente que simplifica o gerenciamento e o uso de GPUs NVIDIA em clusters Kubernetes. Ele permite que o Kubernetes reconheça e aloque recursos de GPU para pods, permitindo cargas de trabalho aceleradas por GPU.
Por que precisamos do plug-in de dispositivo NVIDIA
- Descoberta de recursos: detecta automaticamente recursos de GPU NVIDIA em cada nó.
- Alocação de Recursos: gerencia a distribuição de recursos de GPU para pods com base em suas solicitações.
- Isolamento: Garante a utilização segura e eficiente dos recursos da GPU entre diferentes pods.
O plugin de dispositivo NVIDIA simplifica o gerenciamento de GPU em clusters Kubernetes. Automatiza a instalação do Controlador NVIDIA, equipment de ferramentas de contêinere CUDAgarantindo que os recursos da GPU estejam disponíveis para cargas de trabalho sem exigir configuração handbook.
- Controlador NVIDIA: necessário para operações básicas de GPU e nvidia-smi. Interface com o {hardware} da GPU. A captura de tela abaixo mostra a saída do comando nvidia-smi, que mostra informações importantes como versão do driver, versão CUDA e configuração detalhada da GPU, confirmando se a GPU está configurada corretamente e pronta para uso
- Equipment de ferramentas de contêiner NVIDIA: necessário para usar GPUs com containerd. Abaixo podemos ver a versão do equipment de ferramentas do contêiner e o standing do serviço em execução na instância
#Put in Model rpm -qa | grep -i nvidia-container-toolkit nvidia-container-toolkit-base-1.15.0-1.x86_64 nvidia-container-toolkit-1.15.0-1.x86_64
- CUDA: necessário para aplicativos e bibliotecas acelerados por GPU. Abaixo está a saída do comando nvcc, mostrando a versão do CUDA instalada no sistema:
/usr/native/cuda/bin/nvcc --model nvcc: NVIDIA (R) Cuda compiler driver Copyright (c) 2005-2023 NVIDIA Company Constructed on Tue_Aug_15_22:02:13_PDT_2023 Cuda compilation instruments, launch 12.2, V12.2.140 Construct cuda_12.2.r12.2/compiler.33191640_0
Configurando o plug-in de dispositivo NVIDIA
Para garantir que o DaemonSet seja executado exclusivamente em instâncias baseadas em GPU, rotulamos o nó com a chave “nvidia.com/gpu” e o valor “true”. Isto é conseguido usando Afinidade do nó, Seletor de nó e Máculas e tolerâncias.
Vamos agora nos aprofundar em cada um desses componentes.
- Afinidade do nó: A afinidade do nó permite agendar o pod nos nós com base nos rótulos dos nós obrigatórioDuringSchedulingIgnoredDuringExecution: o agendador não pode agendar o pod a menos que a regra seja atendida e a chave seja “nvidia.com/gpu” e o operador esteja “in” e os valores sejam “true”.
affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: function.node.kubernetes.io/pci-10de.current operator: In values: - "true" - matchExpressions: - key: function.node.kubernetes.io/cpu-mannequin.vendor_id operator: In values: - NVIDIA - matchExpressions: - key: nvidia.com/gpu operator: In values: - "true"
- Seletor de nó: NO seletor de ode é o formulário de recomendação mais simples para restrições de seleção de nós nvidia.com/gpu: “verdadeiro”
- Máculas e tolerâncias: Tolerâncias são adicionadas ao Daemon Set para garantir que ele possa ser agendado nos nós de GPU contaminados (nvidia.com/gpu=true:Noschedule).
kubectl taint node ip-10-20-23-199.us-west-1.compute.inner nvidia.com/gpu=true:Noschedule kubectl describe node ip-10-20-23-199.us-west-1.compute.inner | grep -i taint Taints: nvidia.com/gpu=true:NoSchedule tolerations: - impact: NoSchedule key: nvidia.com/gpu operator: Exists
Depois de implementar a rotulagem do nó, afinidade, seletor de nó e taints/tolerações, podemos garantir que o Daemon Set seja executado exclusivamente em instâncias baseadas em GPU. Podemos verificar a implantação do plugin do dispositivo NVIDIA usando o seguinte comando:
kubectl get ds -n kube-system NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE nvidia-system-plugin 1 1 1 1 1 nvidia.com/gpu=true 75d nvidia-system-plugin-mps-management-daemon 0 0 0 0 0 nvidia.com/gpu=true,nvidia.com/mps.succesful=true 75d
Mas o desafio aqui é que as GPUs são muito caras e precisam garantir a utilização máxima das GPUs e nos deixar explorar mais sobre a simultaneidade de GPU.
Simultaneidade de GPU:
Refere-se à capacidade de executar múltiplas tarefas ou threads simultaneamente em uma GPU
- Processo único: em uma configuração de processo único, apenas um aplicativo ou contêiner usa a GPU por vez. Essa abordagem é simples, mas pode levar à subutilização dos recursos da GPU se o aplicativo não carregar totalmente a GPU.
- Serviço Multiprocesso (MPS): O Serviço Multiprocesso (MPS) da NVIDIA permite que vários aplicativos CUDA compartilhem uma única GPU simultaneamente, melhorando a utilização da GPU e reduzindo a sobrecarga da alternância de contexto.
- Fatiamento de tempo: O fatiamento de tempo envolve dividir o tempo da GPU entre diferentes processos, em outras palavras, vários processos se revezam nas GPUs (Spherical Robin context Switching)
- GPU de múltiplas instâncias (MIG): MIG é um recurso disponível nas GPUs NVIDIA A100 que permite que uma única GPU seja particionada em várias instâncias menores e isoladas, cada uma se comportando como uma GPU separada.
- Virtualização: a virtualização de GPU permite que uma única GPU física seja compartilhada entre várias máquinas virtuais (VMs) ou contêineres, fornecendo a cada uma delas uma GPU digital.
Seção 2: Implementando Time Slicing para GPUs
A divisão de tempo no contexto de GPUs NVIDIA e Kubernetes refere-se ao compartilhamento de uma GPU física entre vários contêineres ou pods em um cluster Kubernetes. A tecnologia envolve particionar o tempo de processamento da GPU em intervalos menores e alocar esses intervalos para diferentes contêineres ou pods.
- Alocação de intervalo de tempo: o agendador de GPU aloca intervalos de tempo para cada vGPU configurado na GPU física.
- Preempção e troca de contexto: no closing do intervalo de tempo de uma vGPU, o agendador da GPU interrompe sua execução, salva seu contexto e alterna para o contexto da próxima vGPU.
- Troca de contexto: o agendador de GPU garante uma troca de contexto suave entre vGPUs, minimizando a sobrecarga e garantindo o uso eficiente dos recursos da GPU.
- Conclusão de tarefas: os processos dentro dos contêineres concluem suas tarefas aceleradas por GPU dentro dos intervalos de tempo alocados.
- Gerenciamento e monitoramento de recursos
- Liberação de recursos: conforme as tarefas são concluídas, os recursos da GPU são liberados de volta ao Kubernetes para realocação para outros pods ou contêineres
Por que precisamos de redução de tempo
- Eficiência de custos: garante que GPUs de alto custo não sejam subutilizadas.
- Simultaneidade: permite que vários aplicativos usem a GPU simultaneamente.
Exemplo de configuração para divisão de tempo
Vamos aplicar a configuração de divisão de tempo usando o mapa de configuração conforme mostrado abaixo. Aqui réplicas: 3 especifica o número de réplicas para recursos de GPU, o que significa que o recurso de GPU pode ser dividido em 3 instâncias de compartilhamento
apiVersion: v1 form: ConfigMap metadata: identify: nvidia-system-plugin namespace: kube-system information: any: |- model: v1 flags: migStrategy: none sharing: timeSlicing: assets: - identify: nvidia.com/gpu replicas: 3 #We will confirm the GPU assets obtainable in your nodes utilizing the next command: kubectl get nodes -o json | jq -r '.gadgets() | choose(.standing.capability."nvidia.com/gpu" != null) | {identify: .metadata.identify, capability: .standing.capability}' { "identify": "ip-10-20-23-199.us-west-1.compute.inner", "capability": { "cpu": "4", "ephemeral-storage": "104845292Ki", "hugepages-1Gi": "0", "hugepages-2Mi": "0", "reminiscence": "16069060Ki", "nvidia.com/gpu": "3", "pods": "110" } } #The above output reveals that the node ip-10-20-23-199.us-west-1. compute.inner has 3 digital GPUs obtainable. #We will request GPU assets of their pod specs by setting useful resource limits assets: limits: cpu: "1" reminiscence: 2G nvidia.com/gpu: "1" requests: cpu: "1" reminiscence: 2G nvidia.com/gpu: "1"
No nosso caso, podemos hospedar 3 pods em um único nó ip-10-20-23-199.us-west-1. calcular. Internos e devido à divisão do tempo, esses 3 pods podem usar 3 GPUs virtuais conforme abaixo
As GPUs foram compartilhadas virtualmente entre os pods e podemos ver os PIDS atribuídos para cada um dos processos abaixo.
Agora que otimizamos a GPU no nível do pod, vamos agora nos concentrar na otimização dos recursos da GPU no nível do nó. Podemos conseguir isso usando uma solução de escalonamento automático de cluster chamada carpinteiro. Isso é particularmente importante porque os laboratórios de aprendizagem nem sempre têm carga ou atividade do usuário constante, e as GPUs são extremamente caras. Ao aproveitar carpinteiropodemos aumentar ou diminuir dinamicamente os nós da GPU com base na demanda, garantindo economia e utilização supreme de recursos.
Seção 3: Escalonamento automático de nós com Karpenter
carpinteiro é um gerenciamento de ciclo de vida de nó de código aberto para Kubernetes. Ele automatiza o provisionamento e desprovisionamento de nós com base nas necessidades de agendamento dos pods, permitindo escalonamento eficiente e otimização de custos
- Provisionamento dinâmico de nós: dimensiona nós automaticamente com base na demanda.
- Otimiza a utilização de recursos: combina a capacidade do nó com as necessidades da carga de trabalho.
- Reduz custos operacionais: Minimiza despesas desnecessárias com recursos.
- Melhora a eficiência do cluster: melhora o desempenho geral e a capacidade de resposta.
Por que usar o Karpenter para dimensionamento dinâmico
- Dimensionamento Dinâmico: ajusta automaticamente a contagem de nós com base nas demandas da carga de trabalho.
- Otimização de custos: Garante que os recursos sejam provisionados apenas quando necessários, reduzindo despesas.
- Gestão eficiente de recursos: Rastreia pods que não podem ser agendados devido à falta de recursos, analisa seus requisitos, provisiona nós para acomodá-los, agenda os pods e desativa nós quando redundantes.
Instalando o Karpenter:
#Set up Karpenter utilizing HELM: helm improve --set up karpenter oci://public.ecr.aws/karpenter/karpenter --model "${KARPENTER_VERSION}" --namespace "${KARPENTER_NAMESPACE}" --create-namespace --set "settings.clusterName=${CLUSTER_NAME}" --set "settings.interruptionQueue=${CLUSTER_NAME}" --set controller.assets.requests.cpu=1 --set controller.assets.requests.reminiscence=1Gi --set controller.assets.limits.cpu=1 --set controller.assets.limits.reminiscence=1Gi #Confirm Karpenter Set up: kubectl get pod -n kube-system | grep -i karpenter karpenter-7df6c54cc-rsv8s 1/1 Working 2 (10d in the past) 53d karpenter-7df6c54cc-zrl9n 1/1 Working 0 53d
Configurando Karpenter com NodePools e NodeClasses:
Karpenter pode ser configurado com NodePools e NodeClasses para automatizar o provisionamento e o escalonamento de nós com base nas necessidades específicas de suas cargas de trabalho
- NodePool do Karpenter: Nodepool é um recurso personalizado que outline um conjunto de nós com especificações e restrições compartilhadas em um cluster Kubernetes. Karpenter usa NodePools para gerenciar e dimensionar dinamicamente os recursos do nó com base nos requisitos de execução de cargas de trabalho
apiVersion: karpenter.sh/v1beta1 form: NodePool metadata: identify: g4-nodepool spec: template: metadata: labels: nvidia.com/gpu: "true" spec: taints: - impact: NoSchedule key: nvidia.com/gpu worth: "true" necessities: - key: kubernetes.io/arch operator: In values: ("amd64") - key: kubernetes.io/os operator: In values: ("linux") - key: karpenter.sh/capability-sort operator: In values: ("on-demand") - key: node.kubernetes.io/occasion-sort operator: In values: ("g4dn.xlarge" ) nodeClassRef: apiVersion: karpenter.k8s.aws/v1beta1 form: EC2NodeClass identify: g4-nodeclass limits: cpu: 1000 disruption: expireAfter: 120m consolidationPolicy: WhenUnderutilized
- NodeClasses são configurações que definem as características e parâmetros dos nós que o Karpenter pode provisionar em um cluster Kubernetes. Uma NodeClass especifica os detalhes da infraestrutura subjacente para nós, como tipos de instância, configurações de modelo de execução e configurações específicas do provedor de nuvem.
Observação: a seção userData contém scripts para inicializar a instância do EC2, incluindo extrair uma imagem do Docker de GPU do TensorFlow e configurar a instância para ingressar no cluster Kubernetes.
apiVersion: karpenter.k8s.aws/v1beta1 form: EC2NodeClass metadata: identify: g4-nodeclass spec: amiFamily: AL2 launchTemplate: identify: "ack_nodegroup_template_new" model: "7" position: "KarpenterNodeRole" subnetSelectorTerms: - tags: karpenter.sh/discovery: "nextgen-learninglab" securityGroupSelectorTerms: - tags: karpenter.sh/discovery: "nextgen-learninglab" blockDeviceMappings: - deviceName: /dev/xvda ebs: volumeSize: 100Gi volumeType: gp3 iops: 10000 encrypted: true deleteOnTermination: true throughput: 125 tags: Title: Learninglab-Staging-Auto-GPU-Node userData: | MIME-Model: 1.0 Content material-Sort: multipart/combined; boundary="//" --// Content material-Sort: textual content/x-shellscript; charset="us-ascii" set -ex sudo ctr -n=k8s.io picture pull docker.io/tensorflow/tensorflow:2.12.0-gpu --// Content material-Sort: textual content/x-shellscript; charset="us-ascii" B64_CLUSTER_CA=" " API_SERVER_URL="" /and so on/eks/bootstrap.sh nextgen-learninglab-eks --kubelet-further-args '--node-labels=eks.amazonaws.com/capacityType=ON_DEMAND --pod-max-pids=32768 --max-pods=110' -- b64-cluster-ca $B64_CLUSTER_CA --apiserver-endpoint $API_SERVER_URL --use-max-pods false --// Content material-Sort: textual content/x-shellscript; charset="us-ascii" KUBELET_CONFIG=/and so on/kubernetes/kubelet/kubelet-config.json echo "$(jq ".podPidsLimit=32768" $KUBELET_CONFIG)" > $KUBELET_CONFIG --// Content material-Sort: textual content/x-shellscript; charset="us-ascii" systemctl cease kubelet systemctl daemon-reload systemctl begin kubelet --//--
Neste cenário, cada nó (por exemplo, ip-10-20-23-199.us-west-1.compute.inner) pode acomodar até três pods. Se a implantação for dimensionada para adicionar outro pod, os recursos serão insuficientes, fazendo com que o novo pod permaneça em estado pendente.
O Karpenter monitora esses pods não programáveis e avalia seus requisitos de recursos para agir de acordo. Haverá nodeclaim que reivindica o nó do nodepool e o Karpenter provisiona assim um nó com base no requisito.
Conclusão: gerenciamento eficiente de recursos de GPU no Kubernetes
Com a crescente demanda por cargas de trabalho aceleradas por GPU no Kubernetes, o gerenciamento eficaz dos recursos de GPU é essencial. A combinação de Plug-in de dispositivo NVIDIA, divisão do tempoe carpinteiro fornece uma abordagem poderosa para gerenciar, otimizar e dimensionar recursos de GPU em um cluster Kubernetes, oferecendo alto desempenho com utilização eficiente de recursos. Esta solução foi implementada para hospedar laboratórios de aprendizagem piloto habilitados para GPU em desenvolvedor.cisco.com/studyingproporcionando experiências de aprendizagem baseadas em GPU.
Compartilhar: