Todos os produtos
Search
Central de documentação

Container Service for Kubernetes:Use elastic container instances to run Spark jobs

Última atualização: Jun 27, 2026

Este tópico descreve como executar jobs do Spark no ACK usando recursos de Elastic Container Instance (ECI). Configure estratégias de agendamento adequadas para criar pods ECI sob demanda e pagar apenas pelos recursos consumidos. Essa abordagem reduz custos com ociosidade e torna a execução de jobs do Spark mais econômica.

Pré-requisitos

Você precisa de:

Como funciona

Ao implantar o ack-virtual-node, cada nó ECI recebe um taint virtual-kubelet.io/provider=alibabacloud:NoSchedule. Sem uma toleration correspondente, o agendador ignora os nós ECI e aloca todos os pods no ECS. Para direcionar pods do Spark ao ECI, adicione tolerations e regras de afinidade de nó à especificação do SparkApplication.

O ECI executa cada contêiner em uma sandbox virtual leve e isola totalmente os pods. No modelo de pagamento conforme o uso, você paga apenas pela CPU e memória que cada pod consome durante a execução, sem custos por capacidade ociosa do nó.

Posicionamento de driver versus executor

Pods de driver e executor apresentam falhas diferentes, o que afeta o posicionamento:

  • Se um executor falhar, o Spark o substitui automaticamente. Executors são stateless e tolerantes a falhas.

  • Se o driver falhar, todo o job falha e reinicia do início. Mantenha o driver estável.

Mantenha o driver em nós ECS confiáveis e direcione os executors para instâncias ECI ou preemptíveis de menor custo quando o preço for um fator crítico.

Características do ECI para cargas de trabalho do Spark:

Característica

Valor

Escala

Mais de 50.000 pods em um cluster ACK Serverless; sem configuração extra

Velocidade de provisionamento

Milhares de pods em segundos

Faturamento

Pagamento conforme o uso; instâncias preemptíveis disponíveis para redução adicional de custos

Escolha uma estratégia de agendamento

Três estratégias cobrem cenários comuns de implantação:

Estratégia

Quando usar

Posicionamento do driver

Posicionamento do executor

Apenas ECS

Cargas de trabalho previsíveis e constantes

ECS

ECS

Apenas ECI

Jobs em lote ou com picos que exigem elasticidade total

ECI

ECI

ECS prioritário com fallback para ECI

Carga normal no ECS; expansão automática para ECI durante picos

ECS (preferencial)

ECS (preferencial), ECI (overflow)

Para controle refinado, como limitar pods por tipo de recurso, use uma ResourcePolicy.

Agende jobs do Spark em nós ECS e ECI

Todos os exemplos usam SparkApplication (CRD sparkoperator.k8s.io/v1beta2) com estas configurações base:

Campo

Valor

Imagem

registry-cn-hangzhou.ack.aliyuncs.com/ack-demo/spark:3.5.2

mainClass

org.apache.spark.examples.SparkPi

sparkVersion

3.5.2

driver.serviceAccount

spark-operator-spark

Apenas ECS

Nenhuma toleration ou afinidade é necessária. O taint padrão do ECI impede que o agendador coloque pods nesses nós.

apiVersion: sparkoperator.k8s.io/v1beta2
kind: SparkApplication
metadata:
  name: spark-pi-ecs-only
  namespace: default
spec:
  type: Scala
  mode: cluster
  image: registry-cn-hangzhou.ack.aliyuncs.com/ack-demo/spark:3.5.2
  mainClass: org.apache.spark.examples.SparkPi
  mainApplicationFile: local:///opt/spark/examples/jars/spark-examples_2.12-3.5.2.jar
  arguments:
  - "5000"
  sparkVersion: 3.5.2
  driver:
    cores: 1
    coreLimit: 1200m
    memory: 512m
    serviceAccount: spark-operator-spark
  executor:
    instances: 2
    cores: 2
    memory: 4g

Comportamento esperado: Todos os pods de driver e executor são agendados em nós ECS. Se a capacidade do ECS for insuficiente, os pods permanecem em Pending até que haja capacidade disponível.

Apenas ECI

Adicione uma toleration para o taint padrão do ECI e uma regra de afinidade requiredDuringSchedulingIgnoredDuringExecution para fixar os pods nos nós ECI. Aplique ambas nas especificações do driver e do executor.

apiVersion: sparkoperator.k8s.io/v1beta2
kind: SparkApplication
metadata:
  name: spark-pi-eci-only
  namespace: default
spec:
  type: Scala
  mode: cluster
  image: registry-cn-hangzhou.ack.aliyuncs.com/ack-demo/spark:3.5.2
  mainClass: org.apache.spark.examples.SparkPi
  mainApplicationFile: local:///opt/spark/examples/jars/spark-examples_2.12-3.5.2.jar
  arguments:
  - "5000"
  sparkVersion: 3.5.2
  driver:
    cores: 1
    coreLimit: 1200m
    memory: 512m
    serviceAccount: spark-operator-spark
    affinity:
      nodeAffinity:
        # Pin driver to ECI nodes only.
        requiredDuringSchedulingIgnoredDuringExecution:
          nodeSelectorTerms:
          - matchExpressions:
            - key: type
              operator: In
              values:
              - virtual-kubelet
    tolerations:
    # Tolerate the default ECI taint.
    - key: virtual-kubelet.io/provider
      operator: Equal
      value: alibabacloud
      effect: NoSchedule
  executor:
    instances: 2
    cores: 2
    memory: 4g
    affinity:
      nodeAffinity:
        # Pin executors to ECI nodes only.
        requiredDuringSchedulingIgnoredDuringExecution:
          nodeSelectorTerms:
          - matchExpressions:
            - key: type
              operator: In
              values:
              - virtual-kubelet
    tolerations:
    # Tolerate the default ECI taint.
    - key: virtual-kubelet.io/provider
      operator: Equal
      value: alibabacloud
      effect: NoSchedule

Comportamento esperado: Todos os pods são agendados em nós ECI e o job é executado em modo totalmente serverless. Se a capacidade regional do ECI for limitada, os pods permanecem em Pending até que os recursos estejam disponíveis.

ECS prioritário com fallback para ECI

Use a afinidade preferredDuringSchedulingIgnoredDuringExecution para favorecer o ECS e permitir overflow para o ECI. Adicione a toleration do ECI para que o agendador possa colocar pods no ECI quando o ECS estiver cheio.

apiVersion: sparkoperator.k8s.io/v1beta2
kind: SparkApplication
metadata:
  name: spark-pi-ecs-first
  namespace: default
spec:
  type: Scala
  mode: cluster
  image: registry-cn-hangzhou.ack.aliyuncs.com/ack-demo/spark:3.5.2
  mainClass: org.apache.spark.examples.SparkPi
  mainApplicationFile: local:///opt/spark/examples/jars/spark-examples_2.12-3.5.2.jar
  arguments:
  - "5000"
  sparkVersion: 3.5.2
  driver:
    cores: 1
    coreLimit: 1200m
    memory: 512m
    serviceAccount: spark-operator-spark
    affinity:
      nodeAffinity:
        # Prefer ECS nodes; fall back to ECI if ECS capacity is exhausted.
        preferredDuringSchedulingIgnoredDuringExecution:
        - weight: 1
          preference:
            matchExpressions:
            - key: type
              operator: NotIn
              values:
              - virtual-kubelet
    tolerations:
    # Allow scheduling to ECI nodes when needed.
    - key: virtual-kubelet.io/provider
      operator: Equal
      value: alibabacloud
      effect: NoSchedule
  executor:
    instances: 2
    cores: 2
    memory: 4g
    affinity:
      nodeAffinity:
        # Prefer ECS nodes; fall back to ECI if ECS capacity is exhausted.
        preferredDuringSchedulingIgnoredDuringExecution:
        - weight: 1
          preference:
            matchExpressions:
            - key: type
              operator: NotIn
              values:
              - virtual-kubelet
    tolerations:
    # Allow scheduling to ECI nodes when needed.
    - key: virtual-kubelet.io/provider
      operator: Equal
      value: alibabacloud
      effect: NoSchedule

Comportamento esperado: Os pods são alocados no ECS quando há capacidade disponível. Quando o ECS está cheio, os pods excedentes são agendados no ECI. Isso evita filas de jobs durante picos sem reservar permanentemente recursos do ECI.

Consulte Configurar alocação de recursos com base em instâncias ECS e instâncias de contêiner elásticas para obter detalhes sobre taints, tolerations e afinidade de nó.

Configure o agendamento de recursos baseado em prioridade

Use uma ResourcePolicy para definir unidades de agendamento com limites de pods por unidade. O agendador do ACK preenche as unidades em ordem de prioridade durante o scale-out e remove pods na ordem inversa durante o scale-in. Consulte Configurar agendamento de recursos baseado em prioridade.

  1. Crie o arquivo resourcepolicy.yaml para pods do Spark Operator no namespace default (máximo de 2 em ECS AMD64, depois 3 em ECI):

    apiVersion: scheduling.alibabacloud.com/v1alpha1
    kind: ResourcePolicy
    metadata:
      name: sparkapplication-resource-policy
      namespace: default                      # Applies only to pods in this namespace.
    spec:
      ignorePreviousPod: true
      ignoreTerminatingPod: false
      matchLabelKeys:
      - sparkoperator.k8s.io/submission-id    # Group pods by Spark job submission ID.
      preemptPolicy: AfterAllUnits            # Attempt preemption only after all units are exhausted.
      selector:
        sparkoperator.k8s.io/launched-by-spark-operator: "true"
      strategy: prefer
      units:
      - max: 2                               # Up to 2 pods on AMD64 ECS nodes (first priority).
        resource: ecs
        nodeSelector:
          kubernetes.io/arch: amd64
      - max: 3                               # Up to 3 pods on ECI (second priority).
        resource: eci
  2. Aplique a ResourcePolicy:

    kubectl apply -f resourcepolicy.yaml
  3. Crie o arquivo spark-pi.yaml. Este SparkApplication solicita 1 driver e 5 executors, todos tolerando o taint do ECI para que a ResourcePolicy possa distribuí-los entre ambos os tipos de unidade.

    apiVersion: sparkoperator.k8s.io/v1beta2
    kind: SparkApplication
    metadata:
      name: spark-pi
      namespace: default
    spec:
      type: Scala
      mode: cluster
      image: registry-cn-hangzhou.ack.aliyuncs.com/ack-demo/spark:3.5.2
      mainClass: org.apache.spark.examples.SparkPi
      mainApplicationFile: local:///opt/spark/examples/jars/spark-examples_2.12-3.5.2.jar
      arguments:
      - "5000"
      sparkVersion: 3.5.2
      driver:
        cores: 1
        coreLimit: 1200m
        memory: 512m
        serviceAccount: spark-operator-spark
        tolerations:
        - key: virtual-kubelet.io/provider    # Allow the driver to be scheduled to ECI if needed.
          operator: Equal
          value: alibabacloud
          effect: NoSchedule
      executor:
        instances: 5
        cores: 1
        coreLimit: 1200m
        memory: 512m
        tolerations:
        - key: virtual-kubelet.io/provider    # Allow executors to be scheduled to ECI.
          operator: Equal
          value: alibabacloud
          effect: NoSchedule
  4. Envie o job do Spark:

    kubectl apply -f spark-pi.yaml
  5. Verifique os resultados do agendamento:

    kubectl get pods -o wide -l sparkoperator.k8s.io/app-name=spark-pi

    Saída esperada:

    NAME                                        READY   STATUS      RESTARTS   AGE       IP                  NODE
    spark-pi-34c0998f9f832e61-exec-1            1/1     Running     0          28s       192.XXX.XX.34       cn-beijing.192.XXX.XX.250
    spark-pi-34c0998f9f832e61-exec-2            1/1     Running     0          28s       192.XXX.XX.87       virtual-kubelet-cn-beijing-i
    spark-pi-34c0998f9f832e61-exec-3            1/1     Running     0          28s       192.XXX.XX.88       virtual-kubelet-cn-beijing-i
    spark-pi-34c0998f9f832e61-exec-4            1/1     Running     0          28s       192.XXX.XX.86       virtual-kubelet-cn-beijing-i
    spark-pi-34c0998f9f832e61-exec-5            0/1     Pending     0          28s       <none>              <none>
    spark-pi-driver                             1/1     Running     0          34s       192.XXX.XX.37       cn-beijing.192.XXX.XXX.250

    Resultado: O driver e o exec-1 são alocados na unidade ECS AMD64 (máximo de 2 pods). Os exec-2, exec-3 e exec-4 são alocados na unidade ECI (máximo de 3 pods). O exec-5 permanece em Pending porque ambas as unidades atingiram seu limite de pods.

Acelere o pull de imagens com ImageCache

Baixar uma imagem grande do Spark a cada inicialização de pod adiciona latência. O ImageCache do ECI pré-armazena imagens na infraestrutura subjacente e reduz a inicialização do pod de cerca de 100 segundos para quase instantânea em caso de acerto no cache. Consulte Usar ImageCache para acelerar a criação de instâncias de contêiner elásticas.

Compare a inicialização com e sem cache de imagem

Sem cache, o comando kubectl describe pod spark-pi-driver após enviar um SparkApplication mostra:

Events:
  ...
  Warning  ImageCacheMissed       24m   EciService         [eci.imagecache]Missed image cache.
  Normal   ImageCacheAutoCreated  24m   EciService         [eci.imagecache]Image cache imc-2zeXXXXXXXXXXXXXXXXX is auto created
  Normal   Pulling                24m   kubelet            Pulling image "registry-cn-hangzhou.ack.aliyuncs.com/ack-demo/spark:3.5.2"
  Normal   Pulled                 23m   kubelet            Successfully pulled image "registry-cn-hangzhou.ack.aliyuncs.com/ack-demo/spark:3.5.2" in 1m41.289s (1m41.289s including waiting)
  ...

A imagem levou cerca de 100 segundos para ser baixada. O ECI criou automaticamente um cache para execuções futuras.

Com acerto no cache, os eventos mostram:

Events:
  ...
  Normal  SuccessfulHitImageCache  23s   EciService         [eci.imagecache]Successfully hit image cache imc-2zeXXXXXXXXXXXXXXXXX, eci will be scheduled with this image cache.
  Normal  Pulled                   4s    kubelet            Container image "registry-cn-hangzhou.ack.aliyuncs.com/ack-demo/spark:3.5.2" already present on machine
  ...

Nenhum download de imagem foi necessário.

Especifique um ID de cache de imagem

Adicione a anotação k8s.aliyun.com/eci-image-snapshot-id nas especificações do driver e do executor para fixar um cache de imagem específico:

apiVersion: sparkoperator.k8s.io/v1beta2
kind: SparkApplication
metadata:
  name: spark-pi-eci-only
  namespace: default
spec:
  type: Scala
  mode: cluster
  image: registry-cn-hangzhou.ack.aliyuncs.com/ack-demo/spark:3.5.2
  mainClass: org.apache.spark.examples.SparkPi
  mainApplicationFile: local:///opt/spark/examples/jars/spark-examples_2.12-3.5.2.jar
  arguments:
  - "5000"
  sparkVersion: 3.5.2
  driver:
    annotations:
      k8s.aliyun.com/eci-image-snapshot-id: imc-2zeXXXXXXXXXXXXXXXXX  # Image cache ID.
    cores: 1
    coreLimit: 1200m
    memory: 512m
    serviceAccount: spark-operator-spark
    affinity:
      nodeAffinity:
        requiredDuringSchedulingIgnoredDuringExecution:
          nodeSelectorTerms:
          - matchExpressions:
            - key: type
              operator: In
              values:
              - virtual-kubelet
    tolerations:
    - key: virtual-kubelet.io/provider
      operator: Equal
      value: alibabacloud
      effect: NoSchedule
  executor:
    annotations:
      k8s.aliyun.com/eci-image-snapshot-id: imc-2zeXXXXXXXXXXXXXXXXX  # Image cache ID.
    instances: 2
    cores: 2
    memory: 4g
    affinity:
      nodeAffinity:
        requiredDuringSchedulingIgnoredDuringExecution:
          nodeSelectorTerms:
          - matchExpressions:
            - key: type
              operator: In
              values:
              - virtual-kubelet
    tolerations:
    - key: virtual-kubelet.io/provider
      operator: Equal
      value: alibabacloud
      effect: NoSchedule

Ative a criação e correspondência automática de cache de imagem

Para permitir que o ECI gerencie a criação e a correspondência de cache automaticamente — sem um ID de cache — defina a anotação k8s.aliyun.com/eci-image-cache como "true" no driver e no executor:

apiVersion: sparkoperator.k8s.io/v1beta2
kind: SparkApplication
metadata:
  name: spark-pi-eci-only
  namespace: default
spec:
  type: Scala
  mode: cluster
  image: registry-cn-hangzhou.ack.aliyuncs.com/ack-demo/spark:3.5.2
  mainClass: org.apache.spark.examples.SparkPi
  mainApplicationFile: local:///opt/spark/examples/jars/spark-examples_2.12-3.5.2.jar
  arguments:
  - "5000"
  sparkVersion: 3.5.2
  driver:
    annotations:
      k8s.aliyun.com/eci-image-cache: "true"  # Enable automatic image cache creation and matching.
    cores: 1
    coreLimit: 1200m
    memory: 512m
    serviceAccount: spark-operator-spark
    affinity:
      nodeAffinity:
        requiredDuringSchedulingIgnoredDuringExecution:
          nodeSelectorTerms:
          - matchExpressions:
            - key: type
              operator: In
              values:
              - virtual-kubelet
    tolerations:
    - key: virtual-kubelet.io/provider
      operator: Equal
      value: alibabacloud
      effect: NoSchedule
  executor:
    annotations:
      k8s.aliyun.com/eci-image-cache: "true"  # Enable automatic image cache creation and matching.
    instances: 2
    cores: 2
    memory: 4g
    affinity:
      nodeAffinity:
        requiredDuringSchedulingIgnoredDuringExecution:
          nodeSelectorTerms:
          - matchExpressions:
            - key: type
              operator: In
              values:
              - virtual-kubelet
    tolerations:
    - key: virtual-kubelet.io/provider
      operator: Equal
      value: alibabacloud
      effect: NoSchedule

Na primeira execução, o ECI cria um cache automaticamente. Execuções subsequentes correspondem ao cache e ignoram o download da imagem.

Aplicação em produção

  • Use ECS prioritário com fallback para ECI em jobs de produção. Mantém cargas sensíveis à latência em nós ECS persistentes, enquanto o ECI absorve tráfego de pico sem provisionar nós extras antecipadamente.

  • Execute o driver no ECS e os executors no ECI. Uma falha no driver reinicia todo o job. Mantenha o driver em nós ECS estáveis e direcione executors stateless para instâncias ECI ou preemptíveis de menor custo.

  • Ative o cache automático de imagens para jobs recorrentes. A primeira execução cria o cache; as execuções seguintes ignoram o download da imagem e reduzem a inicialização de cerca de 100 segundos para quase instantânea.

  • Use ResourcePolicy quando a distribuição de pods entre tipos de nó precisar ser precisa. O campo max por unidade limita os pods, útil para restringir gastos com ECI por envio de job.

Próximos passos