Перейти к основному содержимому

For clean Markdown content of this page, append .md to this URL. For the complete documentation index, see https://docs.nvidia.com/dynamo/llms.txt. For full content including API reference and SDK examples, see https://docs.nvidia.com/dynamo/llms-full.txt.

Amazon Elastic Kubernetes Service (EKS)

Шаги по созданию кластера EKS

Это руководство демонстрирует платформу Dynamo в Amazon Elastic Kubernetes Service (EKS).

Настройка переменных окружения

Мы будем использовать эти переменные окружения на протяжении всего руководства. Если вы хотите использовать другой регион, измените переменную AWS_REGION

export AWS_REGION="us-east-1"
export CLUSTER_NAME="ai-dynamo"
export DYNAMO_NAMESPACE="dynamo-system"
export DYNAMO_RELEASE_VERSION="1.0.0"

Установите CLI

Установите AWS CLI (руководство по установке AWS CLI)

sudo apt install unzip
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install

Установите Kubernetes CLI (руководство по установке kubectl для EKS)

curl -O https://s3.us-west-2.amazonaws.com/amazon-eks/1.35.2/2026-02-27/bin/darwin/amd64/kubectl
chmod +x ./kubectl
mkdir -p $HOME/bin && cp ./kubectl $HOME/bin/kubectl && export PATH=$HOME/bin:$PATH
echo 'export PATH=$HOME/bin:$PATH' >> ~/.bashrc

Установите Eksctl CLI (руководство по установке eksctl)

ARCH=amd64
PLATFORM=$(uname -s)_$ARCH
curl -sLO "https://github.com/eksctl-io/eksctl/releases/latest/download/eksctl_$PLATFORM.tar.gz"
curl -sL "https://github.com/eksctl-io/eksctl/releases/latest/download/eksctl_checksums.txt" | grep $PLATFORM | sha256sum --check
tar -xzf eksctl_$PLATFORM.tar.gz -C /tmp && rm eksctl_$PLATFORM.tar.gz
sudo mv /tmp/eksctl /usr/local/bin

Установите Helm CLI (настройка Helm для EKS)

curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 > get_helm.sh
chmod 700 get_helm.sh
./get_helm.sh

Создайте кластер EKS Auto Mode

Создание кластера EKS Auto Mode с помощью Eksctl и eksctl.yaml. Это создаст кластер EKS Auto Mode с Amazon EFS CSI Driver, установленным как addon. Позже мы будем использовать Amazon EFS для хранения весов модели и компиляции, которые использует Dynamo.

# Use all availability zones in a region, exclude use1-az3 where EKS control plane is not available
export EKS_CP_AZS=$(aws ec2 describe-availability-zones \
--region ${AWS_REGION} \
--filters "Name=opt-in-status,Values=opt-in-not-required" \
--query "AvailabilityZones[?ZoneId!='use1-az3'].[ZoneName]" \
--output text | sed 's/ /, /g; s/^/ - /')

eksctl create cluster -f <(envsubst < templates/eksctl.yaml)

Примечание: eksctl автоматически настроит kubeconfig context; если этого не произошло, можно выполнить: aws eks update-kubeconfig --region $AWS_REGION --name $CLUSTER_NAME

Создайте GPU NodePool в EKS Auto Mode

Создание GPU NodePool, ориентированного на семейства инстансов g5,g6,g6e,g7e,p5,p5e,p5en.

kubectl apply -f automode-np-gpu.yaml

Создайте StorageClass по умолчанию

Создайте StorageClass по умолчанию, чтобы использовать возможности хранения EKS Auto Mode. Это сделает StorageClass по умолчанию таким, который использует EBS volumes для stateful-нагрузок, необходимых NATS, используемому с Dynamo.

kubectl apply -f - << EOF
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: auto-ebs-sc
annotations:
storageclass.kubernetes.io/is-default-class: "true"
allowedTopologies:
- matchLabelExpressions:
- key: eks.amazonaws.com/compute-type
values:
- auto
provisioner: ebs.csi.eks.amazonaws.com
volumeBindingMode: WaitForFirstConsumer
parameters:
type: gp3
encrypted: "true"
EOF

Создайте общий файловый system Amazon EFS

Следуйте руководству по настройке EFS, чтобы создать файловую систему EFS и сделать ее доступной как общее хранилище для workloads Dynamo.

Установите Dynamo Kubernetes Platform

Установите Dynamo Platform

helm fetch https://helm.ngc.nvidia.com/nvidia/ai-dynamo/charts/dynamo-platform-"$DYNAMO_RELEASE_VERSION".tgz
helm install dynamo-platform dynamo-platform-"$DYNAMO_RELEASE_VERSION".tgz \
--namespace "$DYNAMO_NAMESPACE" \
--create-namespace

Настройте HuggingFace TOKEN

export HF_TOKEN=<HF_TOKEN>
kubectl create secret generic hf-token-secret \
--from-literal=HF_TOKEN=${HF_TOKEN} \
-n ${DYNAMO_NAMESPACE}

Проверьте установку

Проверьте, что pod'ы платформы Dynamo запущены; вы должны увидеть вывод, похожий на приведенный ниже.

kubectl get pods -n ${DYNAMO_NAMESPACE}
NAME READY STATUS RESTARTS AGE
dynamo-platform-dynamo-operator-controller-manager-ff54b5dstgcq 1/1 Running 0 106s
dynamo-platform-nats-0 2/2 Running 0 106s

Проверьте, что CRD Dynamo были установлены

kubectl get crds | grep dynamo
dynamocheckpoints.nvidia.com 2026-03-17T13:18:05Z
dynamocomponentdeployments.nvidia.com 2026-03-17T13:18:06Z
dynamographdeploymentrequests.nvidia.com 2026-03-17T13:18:08Z
dynamographdeployments.nvidia.com 2026-03-17T13:18:09Z
dynamographdeploymentscalingadapters.nvidia.com 2026-03-17T13:18:10Z
dynamomodels.nvidia.com 2026-03-17T13:18:10Z
dynamoworkermetadatas.nvidia.com 2026-03-17T13:18:11Z

Разверните Dynamo DynamoGraphDeployment (DGD)

МанифестОписание
manifests/vllm/disagg.yamlРаздельный DGD для prefill/decode с использованием NIXL и backend'а LIBFABRIC поверх EFA. Ориентирован на инстансы g7e.12xlarge с поддержкой GPUDirect RDMA для высокопроизводительной передачи KV-cache между worker'ами prefill и decode.
manifests/vllm/disagg-p5.yamlРаздельный DGD для prefill/decode с использованием NIXL и backend'а LIBFABRIC поверх EFA. Ориентирован на зарезервированные инстансы p5.48xlarge с 8 устройствами EFA (4 EFA на 1 GPU для p5.48xlarge) и TP-2 для Qwen3-32B. Использует 2 реплики decode и 6 реплик prefill на зарезервированной емкости (karpenter.sh/capacity-type: reserved).
manifests/vllm/disagg-tcp.yamlАльтернативный раздельный inference-граф для prefill/decode, использующий TCP вместо EFA. Ориентирован на инстансы g6e.2xlarge и подходит для типов инстансов без поддержки EFA.
manifests/vllm/agg.yamlАгрегированный inference-граф (один worker), где один worker vLLM обрабатывает и prefill, и decode. Более простое развертывание без overhead на передачу KV-cache.

Кэшируйте модели на EFS

Перед развертыванием inference-графа загрузите веса модели в общее файловое system EFS. Каждый рецепт Dynamo включает Job-манифест model-cache/model-download.yaml, который загружает модель из HuggingFace.

Скопируйте манифест загрузки из рецепта в локальный каталог kustomize и примените его:

# Example: cache the Qwen3-32B model which we will be using later
cp ../../../recipes/qwen3-32b/model-cache/model-download.yaml manifests/model-download/model-download.yaml
kubectl kustomize manifests/model-download | kubectl -n ${DYNAMO_NAMESPACE} apply -f -
rm -f manifests/model-download/model-download.yaml

Манифесты рецептов не задают memory resources для контейнера загрузки. Без memory request pod Job может получить OOMKilled во время загрузки - особенно для больших моделей. kustomization.yaml в manifests/model-download/ добавляет memory request, чтобы этого избежать. По умолчанию он добавляет 4Gi.

Для более крупных моделей (например, DeepSeek-R1, Nemotron-3-Super-120B) увеличьте это значение в manifests/model-download/kustomization.yaml перед применением:

patches:
- target:
kind: Job
name: model-download
patch: |
apiVersion: batch/v1
kind: Job
metadata:
name: model-download
spec:
template:
spec:
containers:
- name: model-download
resources:
requests:
memory: "16Gi" # increase for larger models

Затем примените:

kubectl kustomize manifests/model-download | kubectl -n ${DYNAMO_NAMESPACE} apply -f -

Отслеживайте Job загрузки:

kubectl -n ${DYNAMO_NAMESPACE} get jobs model-download
kubectl -n ${DYNAMO_NAMESPACE} logs -f job/model-download

Чтобы повторно запустить загрузку (например, после смены модели или исправления OOM), сначала удалите предыдущий Job:

kubectl -n ${DYNAMO_NAMESPACE} delete job model-download

Затем скопируйте манифест нового рецепта и примените его снова.

Раздельное обслуживание

Этот пример разворачивает раздельный Dynamo Inference Graph для prefill/decode, который использует NIXL с backend'ом LIBFABRIC и Elastic Fabric Adapter (EFA) для высокопроизводительной передачи KV-cache между worker'ами.

Он ориентирован на инстансы g7e.12xlarge, которые поддерживают GPUDirect RDMA, и использует контейнер vLLM Dynamo с поддержкой EFA nvcr.io/nvidia/ai-dynamo/vllm-runtime:1.0.0-efa-amd64, в котором EFA Installer уже предустановлен.

Примечание: полный список типов инстансов с поддержкой EFA см. в документации AWS EC2.

nodeSelector:
node.kubernetes.io/instance-type: g7e.12xlarge

KV-cache transfer between workers uses NIXL with the LIBFABRIC backend. Enable it by passing the following argument to vLLM:

--kv-transfer-config '{"kv_connector":"NixlConnector","kv_role":"kv_both","kv_connector_extra_config": {"backends": ["LIBFABRIC"]}}'

Примечание: на типах инстансов без поддержки EFA backend libfabric в NIXL автоматически переходит на TCP. Однако NixlConnector в vLLM по умолчанию использует cuda в качестве buffer device, поэтому для работы раздельного обслуживания без EFA нужно добавить "kv_buffer_device":"cpu" к аргументу kv-transfer-config.

Request an EFA device for each worker pod using the vpc.amazonaws.com/efa extended resource:

resources:
requests:
gpu: "1"
custom:
vpc.amazonaws.com/efa: "1"
limits:
gpu: "1"
custom:
vpc.amazonaws.com/efa: "1"

Примечание: EKS Auto Mode включает device plugin EFA, поэтому extended resource vpc.amazonaws.com/efa доступен.

Все worker'ы (prefill и decode) должны находиться в одной availability zone, поскольку трафик EFA не проходит между AZ. Используйте правило pod affinity, чтобы это обеспечить:

affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- topologyKey: "topology.kubernetes.io/zone"
labelSelector:
matchLabels:
nvidia.com/dynamo-graph-deployment-name: "vllm-disagg"
kubectl -n ${DYNAMO_NAMESPACE} apply -f manifests/vllm/disagg.yaml

Примечание: manifests/vllm/disagg-tcp.yaml содержит альтернативный пример, который использует TCP вместо EFA и ориентирован на инстансы g6e.2xlarge.

Проверьте, что все pod'ы достигли состояния Running:

kubectl -n ${DYNAMO_NAMESPACE} get pods
NAME READY STATUS RESTARTS AGE
dynamo-platform-dynamo-operator-controller-manager-ff54b5dstgcq 1/1 Running 0 39m
dynamo-platform-nats-0 2/2 Running 0 39m
vllm-disagg-frontend-85f8476887-wwtwk 1/1 Running 0 2m13s
vllm-disagg-vllmdecodeworker-510a1741-7666987b-tp58w 1/1 Running 0 2m13s
vllm-disagg-vllmprefillworker-510a1741-54f76d7954-tjgn8 1/1 Running 0 2m13s
kubectl -n ${DYNAMO_NAMESPACE} port-forward svc/vllm-disagg-frontend 8000:8000

curl localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "Qwen/Qwen3-32B",
"messages": [
{
"role": "user",
"content": "In the heart of Eldoria, an ancient land of boundless magic and mysterious creatures, lies the long-forgotten city of Aeloria. Once a beacon of knowledge and power, Aeloria was buried beneath the shifting sands of time, lost to the world for centuries. You are an intrepid explorer, known for your unparalleled curiosity and courage, who has stumbled upon an ancient map hinting at ests that Aeloria holds a secret so profound that it has the potential to reshape the very fabric of reality. Your journey will take you through treacherous deserts, enchanted forests, and across perilous mountain ranges. Your Task: Character Background: Develop a detailed background for your character. Describe their motivations for seeking out Aeloria, their skills and weaknesses, and any personal connections to the ancient city or its legends. Are they driven by a quest for knowledge, a search for lost familt clue is hidden."
}
],
"stream": false,
"max_tokens": 30
}'

Вы должны увидеть вывод, похожий на приведенный ниже

{"id":"chatcmpl-23a7c94b-99cb-42ca-ae56-2397aa5a560f","choices":[{"index":0,"message":{"content":"<think>\nOkay, so I need to develop a character background for someone who's an intrepid explorer in Eldoria, specifically focusing on their motivations,","role":"assistant","reasoning_content":null},"finish_reason":"length"}],"created":1773336002,"model":"Qwen/Qwen3-0.6B","object":"chat.completion","usage":{"prompt_tokens":196,"completion_tokens":30,"total_tokens":226,"prompt_tokens_details":{"audio_tokens":null,"cached_tokens":192}},"nvext":{"worker_id":{"prefill_worker_id":4265733549773195,"prefill_dp_rank":0,"decode_worker_id":7535192362430132,"decode_dp_rank":0},"timing":{"request_received_ms":1773336002136,"prefill_wait_time_ms":0.852483,"prefill_time_ms":12.90597,"ttft_ms":13.758453000000001,"total_time_ms":110.89621500000001,"kv_hit_rate":0.0}}}

Примечание: первый запрос для каждого worker'а будет сопровождаться повышенной задержкой; это связано с handshake и overhead инициализации backend'а NIXL, и такая операция происходит только при самой первой передаче

Просмотр логов

kubectl logs -n ${DYNAMO_NAMESPACE} -l nvidia.com/dynamo-graph-deployment-name=vllm-disagg --all-containers=true --max-log-requests=20 --prefix=true --timestamps -f

Очистка

kubectl -n ${DYNAMO_NAMESPACE} delete -f manifests/vllm/disagg.yaml

Агрегированное обслуживание

kubectl -n ${DYNAMO_NAMESPACE} apply -f manifests/vllm/agg.yaml

Ваши pod'ы должны иметь вид, похожий на приведенный ниже вывод, и находиться в статусе "Running".

kubectl -n ${DYNAMO_NAMESPACE} get pods
NAME READY STATUS RESTARTS AGE
dynamo-platform-dynamo-operator-controller-manager-ff54b5dstgcq 1/1 Running 0 12m
dynamo-platform-nats-0 2/2 Running 0 12m
vllm-agg-frontend-ff8457bcf-tq9jh 1/1 Running 0 4m46s
vllm-agg-vllmdecodeworker-d0a70291-759df94478-8lc74 1/1 Running 0 4m46s

Просмотр логов

kubectl logs -n ${DYNAMO_NAMESPACE} -l nvidia.com/dynamo-graph-deployment-name=vllm-agg --all-containers=true --max-log-requests=20 --prefix=true --timestamps -f
kubectl -n ${DYNAMO_NAMESPACE} port-forward svc/vllm-agg-frontend 8000:8000

curl localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "Qwen/Qwen3-0.6B",
"messages": [
{
"role": "user",
"content": "In the heart of Eldoria, an ancient land of boundless magic and mysterious creatures, lies the long-forgotten city of Aeloria. Once a beacon of knowledge and power, Aeloria was buried beneath the shifting sands of time, lost to the world for centuries. You are an intrepid explorer, known for your unparalleled curiosity and courage, who has stumbled upon an ancient map hinting at ests that Aeloria holds a secret so profound that it has the potential to reshape the very fabric of reality. Your journey will take you through treacherous deserts, enchanted forests, and across perilous mountain ranges. Your Task: Character Background: Develop a detailed background for your character. Describe their motivations for seeking out Aeloria, their skills and weaknesses, and any personal connections to the ancient city or its legends. Are they driven by a quest for knowledge, a search for lost familt clue is hidden."
}
],
"stream": false,
"max_tokens": 30
}'

Вы должны увидеть вывод, похожий на приведенный ниже

{"id":"chatcmpl-093fac0e-f75e-43b5-90dc-96c8c77a2e7c","choices":[{"index":0,"message":{"content":"<think>\nOkay, I need to develop a character background for the explorer in Eldoria. Let me start by understanding the user's query. They mentioned","role":"assistant","reasoning_content":null},"finish_reason":"length"}],"created":1773443560,"model":"Qwen/Qwen3-0.6B","object":"chat.completion","usage":{"prompt_tokens":196,"completion_tokens":30,"total_tokens":226},"nvext":{"timing":{"request_received_ms":1773443560878,"total_time_ms":99.89782}}}%

Очистка

kubectl -n ${DYNAMO_NAMESPACE} delete -f manifests/vllm/agg.yaml

Использование On-Demand Capacity Reservations (ODCR) и Capacity Blocks (CBs) для ML

GPU instances бывает трудно получить по требованию. AWS предоставляет два механизма резервирования, чтобы гарантировать capacity для ML workloads:

  • On-Demand Capacity Reservations (ODCRs) резервируют capacity в конкретной AZ на любой срок. Вы платите за зарезервированную capacity независимо от того, используете вы ее или нет.
  • Capacity Blocks for ML резервируют GPU instances на фиксированное окно времени (от часов до дней). Instances размещаются в EC2 UltraClusters для low-latency networking. У Capacity Blocks есть определенное время окончания, и EC2 завершит instances до истечения block.

EKS Auto Mode использует Karpenter под капотом, который моделирует reserved capacity как karpenter.sh/capacity-type: reserved и приоритизирует ее выше on-demand и spot.

По умолчанию EKS Auto Mode может автоматически запускаться в открытые ODCR, но не приоритизирует их. Capacity Blocks никогда не используются автоматически. И ODCR, и Capacity Blocks требуют явной настройки capacityReservationSelectorTerms на NodeClass, чтобы они получили приоритет и были помечены как reserved.

Создайте NodeClass с Capacity Reservation

Создайте NodeClass, который ссылается на вашу ODCR или Capacity Block reservation. Вы можете выбирать по reservation ID или по tags.

Сначала извлеките конфигурацию subnet, security group и role из NodeClass default, который EKS Auto Mode уже создал:

export NC_SUBNETS=$(kubectl get nodeclass default -o json | jq -c '.spec.subnetSelectorTerms')
export NC_SG=$(kubectl get nodeclass default -o json | jq -c '.spec.securityGroupSelectorTerms')
export NC_ROLE=$(kubectl get nodeclass default -o json | jq -r '.spec.role')

Замените <CR ID> на фактический reservation ID из консоли EC2.

export CR_ID=<CR ID>
kubectl apply -f - << EOF
apiVersion: eks.amazonaws.com/v1
kind: NodeClass
metadata:
name: gpu-reserved
spec:
role: ${NC_ROLE}
subnetSelectorTerms: ${NC_SUBNETS}
securityGroupSelectorTerms: ${NC_SG}
capacityReservationSelectorTerms:
# Select by reservation ID (ODCR or Capacity Block)
- id: "${CR_ID}"
# Or select by tags (can be combined)
# - tags:
# team: "dynamo"
EOF

Дождитесь, пока состояние capacityReservation не станет active.

kubectl get nodeclass gpu-reserved -o json | jq '.status.capacityReservations'
[
{
"availabilityZone": "us-east-2c",
"endTime": "2026-03-18T11:30:00Z",
"id": "cr-xxxxxxxxxxxxxx",
"instanceMatchCriteria": "targeted",
"instanceType": "p5.48xlarge",
"ownerID": "xxxxxxxxxxx",
"reservationType": "capacity-block",
"state": "active"
}
]

Создайте NodePool для reserved capacity

Создайте NodePool, который ссылается на NodeClass gpu-reserved и использует тип capacity reserved. При желании можно добавить on-demand и spot как fallback, если reservation исчерпан.

kubectl apply -f - << EOF
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: gpu-reserved
spec:
disruption:
budgets:
- nodes: 10%
consolidateAfter: 300s
consolidationPolicy: WhenEmptyOrUnderutilized
template:
spec:
nodeClassRef:
group: eks.amazonaws.com
kind: NodeClass
name: gpu-reserved
requirements:
- key: karpenter.sh/capacity-type
operator: In
values:
- reserved
# Uncomment to fallback to on-demand or spot when reservation is exhausted
# - on-demand
# - spot
- key: eks.amazonaws.com/instance-family
operator: In
values:
- g6e
- g7e
- p5
- p5e
- p5en
taints:
- effect: NoSchedule
key: nvidia.com/gpu
value: Exists
EOF

Проверьте, что NodePool gpu-reserved готов

kubectl get nodepool gpu-reserved
NAME NODECLASS NODES READY AGE
gpu-reserved gpu-reserved 0 True 8s

When configuring capacityReservationSelectorTerms on any NodeClass in the cluster, EKS Auto Mode will stop automatically using open ODCRs for all NodeClasses. Make sure all NodeClasses that should use ODCRs have explicit selector terms configured.

Направление workloads на reserved nodes

Pod'ы назначаются на reserved nodes через существующие требования и taints NodePool. Если вы хотите гарантировать, что workload запускается только на reserved capacity, добавьте node selector:

nodeSelector:
karpenter.sh/capacity-type: reserved
tolerations:
- key: nvidia.com/gpu
operator: Exists
effect: NoSchedule

Особенности Capacity Blocks

У Capacity Blocks фиксированное время окончания. EC2 начинает завершать instances за 30 минут до истечения block (за 60 минут для типов UltraServer). Karpenter начинает draining nodes за 10 минут до начала завершения EC2, давая вашим workloads время корректно завершиться.

Планируйте inference workloads с учетом этого и рассмотрите on-demand как fallback capacity type в NodePool, если вам нужна непрерывность за пределами окна Capacity Block.

Очистка

Удалите все DynamoGraphDeployment

kubectl -n ${DYNAMO_NAMESPACE} get dgd

# Если они есть, удалите их
kubectl -n ${DYNAMO_NAMESPACE} delete dgd <name>

Удалите Dynamo platform

helm uninstall -n ${DYNAMO_NAMESPACE} dynamo-platform

Очистите оставшиеся PVC, связанные с NATS

kubectl -n ${DYNAMO_NAMESPACE} get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
dynamo-platform-nats-js-dynamo-platform-nats-0 Bound pvc-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx 10Gi RWO auto-ebs-sc <unset> 75m

kubectl -n ${DYNAMO_NAMESPACE} delete pvc dynamo-platform-nats-js-dynamo-platform-nats-0

Удалите GPU nodepool AutoMode

kubectl delete nodepool gpu

Для очистки ресурсов, связанных с EFS, следуйте разделу cleanup в руководстве по настройке EFS

Удалите кластер EKS Auto Mode с помощью Eksctl

eksctl delete cluster -f <(envsubst < templates/eksctl.yaml)