documentation.suse.com / Documentação do SUSE Edge / Documentação do produto / Configuração dos recursos de telecomunicações

41 Configuração dos recursos de telecomunicações

Esta seção documenta e explica a configuração dos recursos específicos de telecomunicações nos clusters implantados pelo SUSE Edge for Telco.

O método de implantação de provisionamento de rede direcionado é usado, conforme descrito na seção sobre provisionamento automatizado (Capítulo 42, Provisionamento de rede direcionado totalmente automatizado).

Os seguintes tópicos são abordados nesta seção:

41.1 Imagem do kernel para tempo real

A imagem do kernel Real-Time não é necessariamente melhor do que o kernel padrão. Trata-se de um kernel diferente adaptado a um caso de uso específico. O kernel Real-Time é adaptado para latência mais baixa em detrimento da taxa de transferência. Ele não é recomendado para fins de uso geral; mas, no nosso caso, esse é o kernel recomendado para cargas de trabalho de telecomunicações em que a latência é um fator importante.

Há quatro recursos principais:

  • Execução determinística:

    Aumente a previsibilidade: garanta que os processos de negócios críticos sejam sempre concluídos dentro do prazo e ofereçam um serviço de alta qualidade, mesmo com cargas pesadas do sistema. Ao proteger os recursos importantes do sistema nos processos de alta prioridade, você garante maior previsibilidade para aplicativos urgentes.

  • Baixa instabilidade:

    A baixa instabilidade decorrente da tecnologia altamente determinística ajuda a manter a sincronização dos aplicativos com o mundo real. Isso ajuda os serviços que precisam de cálculo contínuo e repetido.

  • Herança de prioridade:

    A herança de prioridade refere-se à capacidade de um processo de prioridade mais baixa assumir a prioridade mais alta quando há um processo de maior prioridade que requer a conclusão do processo de menor prioridade para então finalizar sua tarefa. O SUSE Linux Enterprise Real Time resolve esses problemas de inversão de prioridade para processos de extrema importância.

  • Interrupções de threads:

    Os processos executados no modo de interrupção em um sistema operacional de uso geral não são preemptíveis. Com o SUSE Linux Enterprise Real Time, essas interrupções foram encapsuladas pelos threads do kernel, que são interrompíveis e permitem a preempção de interrupções fixas e flexíveis por processos de prioridade mais alta definidos pelo usuário.

    No nosso caso, se você instalou uma imagem em tempo real, como SUSE Linux Micro RT, o kernel Real-Time já está instalado. Você pode fazer download da imagem do kernel Real-Time pelo SUSE Customer Center.

    Nota
    Nota

    Para obter mais informações sobre o kernel Real-Time, visite SUSE Real Time.

41.2 Argumentos do kernel para baixa latência e alto desempenho

É importante configurar os argumentos do kernel para que o kernel Real-Time funcione corretamente, apresentando o melhor desempenho e a baixa latência para executar as cargas de trabalho de telecomunicações. Há alguns conceitos importantes para manter em mente na hora de configurar os argumentos do kernel para este caso de uso:

  • Remova o kthread_cpus ao usar o kernel Real-Time da SUSE. Esse parâmetro controla em quais CPUs os threads do kernel serão criados. Ele também controla quais CPUs têm permissão para PID 1 e para carregar módulos do kernel (o auxiliar de espaço do usuário kmod). Esse parâmetro não é reconhecido e não tem nenhum efeito.

  • Isole os núcleos da CPU usando isolcpus, nohz_full, rcu_nocbs e irqaffinity. Para acessar a lista completa de técnicas de fixação de CPU, consulte o capítulo Fixação de CPU via TuneD e argumentos do kernel (Seção 41.3, “Fixação de CPU via TuneD e argumentos do kernel”).

  • Adicione os sinalizadores domain,nohz,managed_irq ao argumento do kernel isolcpus. Sem os sinalizadores, o isolcpus equivale a especificar apenas o sinalizador domain. Isso isola as CPUs especificadas da programação, incluindo as tarefas do kernel. O sinalizador nohz interrompe o tick do programador nas CPUs especificadas (se apenas uma tarefa for executável em determinada CPU), e o sinalizador managed_irq evita o roteamento de interrupções externas gerenciadas (dispositivos) nas CPUs especificadas. Observe que as linhas da IRQ dos dispositivos NVMe são totalmente gerenciadas pelo kernel e serão roteadas para os núcleos não isolados (manutenção) como consequência. Por exemplo, a linha de comando inserida no fim desta seção resultará apenas em quatro filas (mais uma fila de admin/controle) alocadas no sistema:

    for I in $(grep nvme0 /proc/interrupts | cut -d ':' -f1); do cat /proc/irq/${I}/effective_affinity_list; done | column
    39      0       19      20      39

    Esse comportamento impede interrupções causadas por E/S do disco em qualquer aplicativo urgente executado nos núcleos isolados, mas pode exigir atenção e definição cuidadosa para cargas de trabalho com foco em armazenamento.

  • Ajuste os ticks (interrupções periódicas do temporizador do kernel):

    • skew_tick=1: os ticks às vezes podem ocorrer ao mesmo tempo. Em vez de todas as CPUs receberem o tick do temporizador exatamente no mesmo momento, o skew_tick=1 faz com que ele ocorra em horários um pouco diferentes. Isso ajuda a reduzir a instabilidade do sistema, resultando em tempos de resposta mais consistentes e com menos interrupções (um requisito essencial para aplicativos sensíveis à latência).

    • nohz=on: interrompe o tick periódico do temporizador em CPUs ociosas.

    • nohz_full=<núcleos-cpu>: interrompe o tick periódico do temporizador nas CPUs especificadas que são dedicadas a aplicativos em tempo real.

  • Desabilite o processamento de exceção de verificação de máquina (MCE, Machine Check Exception) especificando mce=off. As MCEs são erros do hardware detectados pelo processador, e sua desabilitação pode evitar registros com muito ruído.

  • Adicione nowatchdog para desabilitar o watchdog de bloqueio flexível que é implementado como um temporizador em execução no contexto de interrupção fixa do temporizador. Quando ele expira (ou seja, um bloqueio flexível é detectado), um aviso é exibido (no contexto de interrupção fixa), executando os destinos de latência. Mesmo que nunca expire, ele é incluído na lista do temporizador, o que aumenta levemente a sobrecarga de cada interrupção do temporizador. Essa opção também desabilita o watchdog de NMI, assim as NMIs não podem interferir.

  • nmi_watchdog=0 desabilita o watchdog de NMI (interrupção não mascarável). Para omiti-lo, use nowatchdog.

  • RCU (Read-Copy-Update, Ler-Copiar-Atualizar) é um mecanismo que permite o acesso simultâneo e sem bloqueio de vários leitores aos dados compartilhados. O retorno de chamada RCU, função acionada após um "período extra", garante que todos os leitores anteriores tenham finalizado para que os dados antigos possam ser recuperados com segurança. Ajustamos o RCU, especificamente para cargas de trabalho confidenciais, para descarregar esses retornos de chamada das CPUs dedicadas (fixadas), evitando que as operações do kernel interfiram em tarefas críticas e urgentes.

    • Especifique as CPUs fixadas em rcu_nocbs para que os retornos de chamada RCU não sejam executados nelas. Isso ajuda a reduzir a instabilidade e a latência para cargas de trabalho em tempo real.

    • O rcu_nocb_poll faz com que as CPUs sem retorno de chamada realizem sondagens regulares para ver se há necessidade de gerenciar retornos de chamadas. Isso pode reduzir a sobrecarga de interrupções.

    • rcupdate.rcu_cpu_stall_suppress=1 suprime os avisos de parada de RCU da CPU, que às vezes podem ser falsos positivos nos sistemas em tempo real com carga elevada.

    • rcupdate.rcu_expedited=1 acelera o período extra das operações RCU, o que torna as seções críticas do lado da leitura mais responsivas.

    • rcupdate.rcu_normal_after_boot=1, quando usado com rcu_expedited, permite que o RCU volte à operação normal (não acelerada) após a inicialização do sistema.

    • rcupdate.rcu_task_stall_timeout=0 desabilita o detector de paradas de tarefas do RCU, evitando possíveis avisos ou paralisações do sistema provocadas por tarefas do RCU de longa duração.

    • rcutree.kthread_prio=99 define a prioridade do thread do kernel de retorno de chamada RCU como a mais alta possível (99), garantindo que ele seja programado e processe os retornos de chamada RCU prontamente, quando necessário.

  • Adicione o ignition.platform.id=openstack para que o Metal3 e a Cluster API provisionem/desprovisionem o cluster com sucesso. Ele é usado pelo agente Metal3 Python, que teve sua origem no Openstack Ironic.

  • Remova intel_pstate=passive. Essa opção configura o intel_pstate para operar com controladores cpufreq genéricos. No entanto, para que isso funcione, ele desabilita os estados P gerenciados pelo hardware (HWP) como efeito colateral. Para reduzir a latência do hardware, essa opção não é recomendada para cargas de trabalho em tempo real.

  • Substitua intel_idle.max_cstate=0 processor.max_cstate=1 por idle=poll. Para evitar transições de estado C, a opção idle=poll é usada para desabilitar essas transições e manter a CPU no estado C mais alto. A opção intel_idle.max_cstate=0 desabilita intel_idle para que acpi_idle seja usado e, em seguida, o acpi_idle.max_cstate=1 define o estado C máximo para acpi_idle. Nas arquiteturas AMD64/Intel 64, o primeiro estado C da ACPI sempre é POLL, mas ela usa uma função poll_idle(), que pode gerar uma pequena latência com a leitura periódica do relógio e a reinicialização do loop principal em do_idle() após o tempo limite (isso também envolve limpar e definir o sinalizador da tarefa TIF_POLL). Por outro lado, idle=poll é executado em um loop restrito, mantendo-se em espera ocupada até que uma tarefa seja reprogramada. Isso minimiza a latência de sair do estado ocioso, mas à custa de manter a CPU em execução na máxima velocidade no thread ocioso.

  • Desabilite C1E no BIOS. Essa opção é importante para desabilitar o estado C1E no BIOS para evitar que a CPU entre no estado C1E quando estiver ociosa. C1E é um estado de baixo consumo que pode gerar latência quando a CPU está ociosa.

O restante desta documentação aborda parâmetros adicionais, incluindo HugePages e IOMMU.

Este é um exemplo de argumentos do kernel para um servidor Intel de 32 núcleos, incluindo os ajustes já mencionados:

$ cat /proc/cmdline
BOOT_IMAGE=/boot/vmlinuz-6.4.0-9-rt root=UUID=77b713de-5cc7-4d4c-8fc6-f5eca0a43cf9 skew_tick=1 rd.timeout=60 rd.retry=45 console=ttyS1,115200 console=tty0 default_hugepagesz=1G hugepagesz=1G hugepages=40 hugepagesz=2M hugepages=0 ignition.platform.id=openstack intel_iommu=on iommu=pt irqaffinity=0,31,32,63 isolcpus=domain,nohz,managed_irq,1-30,33-62 nohz_full=1-30,33-62 nohz=on mce=off net.ifnames=0 nosoftlockup nowatchdog nmi_watchdog=0 quiet rcu_nocb_poll rcu_nocbs=1-30,33-62 rcupdate.rcu_cpu_stall_suppress=1 rcupdate.rcu_expedited=1 rcupdate.rcu_normal_after_boot=1 rcupdate.rcu_task_stall_timeout=0 rcutree.kthread_prio=99 security=selinux selinux=1 idle=poll

Este é outro exemplo de configuração para um servidor AMD de 64 núcleos. Dentre os 128 processadores lógicos (0-127), os primeiros 8 núcleos (0-7) são destinados à manutenção, enquanto os 120 núcleos restantes (8-127) são fixados para os aplicativos:

$ cat /proc/cmdline
BOOT_IMAGE=/boot/vmlinuz-6.4.0-9-rt root=UUID=575291cf-74e8-42cf-8f2c-408a20dc00b8 skew_tick=1 console=ttyS1,115200 console=tty0 default_hugepagesz=1G hugepagesz=1G hugepages=40 hugepagesz=2M hugepages=0 ignition.platform.id=openstack amd_iommu=on iommu=pt irqaffinity=0-7 isolcpus=domain,nohz,managed_irq,8-127 nohz_full=8-127 rcu_nocbs=8-127 mce=off nohz=on net.ifnames=0 nowatchdog nmi_watchdog=0 nosoftlockup quiet rcu_nocb_poll rcupdate.rcu_cpu_stall_suppress=1 rcupdate.rcu_expedited=1 rcupdate.rcu_normal_after_boot=1 rcupdate.rcu_task_stall_timeout=0 rcutree.kthread_prio=99 security=selinux selinux=1 idle=poll

41.3 Fixação de CPU via TuneD e argumentos do kernel

tuned é uma ferramenta de ajuste de sistema que monitora as condições do sistema para otimizar o desempenho usando vários perfis predefinidos. Um recurso importante é sua capacidade de isolar os núcleos da CPU para cargas de trabalho específicas, como os aplicativos em tempo real. Isso impede que o sistema operacional use esses núcleos e, possivelmente, aumente a latência.

Para habilitar e configurar esse recurso, a primeira etapa é criar um perfil para os núcleos da CPU que desejamos isolar. Neste exemplo, dos 64 núcleos, dedicamos 60 (1-30,33-62) para o aplicativo e os 4 restantes são usados para manutenção. Observe que o design das CPUs isoladas depende significativamente dos aplicativos em tempo real.

$ echo "export tuned_params" >> /etc/grub.d/00_tuned

$ echo "isolated_cores=1-30,33-62" >> /etc/tuned/cpu-partitioning-variables.conf

$ tuned-adm profile cpu-partitioning
Tuned (re)started, changes applied.

Na sequência, precisamos modificar a opção GRUB para isolar os núcleos da CPU e outros parâmetros importantes para uso da CPU. É importante personalizar as seguintes opções com suas especificações de hardware atuais:

parâmetrovalordescrição

isolcpus

domain,nohz,managed_irq,1-30,33-62

Isolar os núcleos 1-30 e 33-62. domain indica que as CPUs fazem parte do domínio de isolamento. nohz permite a operação sem ticks nessas CPUs isoladas quando estão ociosas, para reduzir interrupções. managed_irq isola as CPUs fixadas para que não sejam destinos das IRQs. Isso contempla o irqaffinity=0-7, que já direciona a maiorias das IRQs para os núcleos de manutenção.

skew_tick

1

Essa opção permite que o kernel distribua as interrupções do temporizador entre as CPUs isoladas.

nohz

on

Quando habilitada, a interrupção periódica do temporizador do kernel ("tick") vai parar em qualquer núcleo da CPU que esteja ocioso. Isso beneficia principalmente as CPUs de manutenção (0,31,32,63), além de economizar energia e reduzir ativações desnecessárias nos núcleos de uso geral.

nohz_full

1-30,33-62

Para os núcleos isolados, esse processo interrompe o tick, mesmo quando a CPU está executando uma única tarefa ativa. Dessa forma, ele faz com que a CPU seja executada no modo sem ticks total (ou "dyntick"). O kernel apenas enviará interrupções do temporizador quando forem de fato necessárias.

rcu_nocbs

1-30,33-62

Essa opção descarrega o processamento de retorno de chamada RCU dos núcleos especificados da CPU.

rcu_nocb_poll

 

Quando essa opção é definida, as CPUs sem retorno de chamada RCU fazem uma sondagem regular para ver se o processamento de retornos de chamada é necessário, em vez de ser explicitamente ativadas por outras CPUs. Isso pode reduzir a sobrecarga das interrupções.

irqaffinity

0,31,32,63

Essa opção permite que o kernel execute as interrupções nos núcleos de manutenção.

idle

poll

Isso minimiza a latência de sair do estado ocioso, mas à custa de manter a CPU em execução na velocidade máxima no thread ocioso.

nmi_watchdog

0

Essa opção desabilita apenas o watchdog de NMI. Para omiti-la, defina nowatchdog.

nowatchdog

 

Essa opção desabilita o watchdog de bloqueio flexível, que é implementado como um temporizador executado no contexto de interrupção fixa do temporizador.

Os seguintes comandos modificam a configuração do GRUB e aplicam as alterações mencionadas acima para que estejam presentes na próxima inicialização:

Edite o arquivo /etc/default/grub com os parâmetros acima, e ele terá esta aparência:

GRUB_CMDLINE_LINUX="BOOT_IMAGE=/boot/vmlinuz-6.4.0-9-rt root=UUID=77b713de-5cc7-4d4c-8fc6-f5eca0a43cf9 skew_tick=1 rd.timeout=60 rd.retry=45 console=ttyS1,115200 console=tty0 default_hugepagesz=1G hugepagesz=1G hugepages=40 hugepagesz=2M hugepages=0 ignition.platform.id=openstack intel_iommu=on iommu=pt irqaffinity=0,31,32,63 isolcpus=domain,nohz,managed_irq,1-30,33-62 nohz_full=1-30,33-62 nohz=on mce=off net.ifnames=0 nosoftlockup nowatchdog nmi_watchdog=0 quiet rcu_nocb_poll rcu_nocbs=1-30,33-62 rcupdate.rcu_cpu_stall_suppress=1 rcupdate.rcu_expedited=1 rcupdate.rcu_normal_after_boot=1 rcupdate.rcu_task_stall_timeout=0 rcutree.kthread_prio=99 security=selinux selinux=1 idle=poll"

Atualize a configuração do GRUB:

$ transactional-update grub.cfg
$ reboot

Para validar a aplicação dos parâmetros após a reinicialização, é possível usar o seguinte comando para verificar a linha de comando do kernel:

$ cat /proc/cmdline

Existe outro script que pode ser usado para ajustar a configuração da CPU que, basicamente, executa as seguintes etapas:

  • Definir o controlador da CPU como performance.

  • Cancelar a definição da migração do temporizador para as CPUs isoladas.

  • Migrar os threads kdaemon para as CPUs de manutenção.

  • Definir a latência das CPUs isoladas como o valor mais baixo possível.

  • Atrasar as atualizações de vmstat para 300 segundos.

O script está disponível no repositório de exemplos do SUSE Edge for Telco.

41.4 Configuração da CNI

41.4.1 Cilium

Cilium é o plug-in de CNI padrão para o SUSE Edge for Telco. Para habilitar o Cilium no cluster RKE2 como plug-in padrão, as seguintes configurações são necessárias no arquivo /etc/rancher/rke2/config.yaml:

cni:
- cilium

Também é possível especificar isso com argumentos de linha de comando, ou seja, --cni=cilium na linha do servidor no arquivo /etc/systemd/system/rke2-server.

Para usar o operador de rede SR-IOV descrito na próxima seção (Seção 41.5, “SR-IOV”), use o Multus com outro plug-in de CNI, como Cilium ou Calico, como o plug-in secundário.

cni:
- multus
- cilium
Nota
Nota

Para obter mais informações sobre os plug-ins de CNI, visite Network Options (Opções de rede).

41.5 SR-IOV

O SR-IOV permite que um dispositivo, por exemplo, adaptador de rede, separe o acesso a seus recursos entre várias funções de hardware PCIe. Há diversas maneiras de implantar o SR-IOV e, neste documento, mostramos duas opções diferentes:

  • Opção 1: usar os plug-ins de dispositivo CNI SR-IOV e um mapa de configuração para configurá-lo de maneira apropriada.

  • Opção 2 (recomendada): usar o gráfico Helm do SR-IOV do Rancher Prime para facilitar a implantação.

Opção 1 – Instalação dos plug-ins de dispositivo CNI SR-IOV e um mapa de configuração para configurá-lo de maneira apropriada

  • Preparar o mapa de configuração para o plug-in de dispositivo

Obtenha as informações para preencher o mapa de configuração executando o comando lspci:

$ lspci | grep -i acc
8a:00.0 Processing accelerators: Intel Corporation Device 0d5c

$ lspci | grep -i net
19:00.0 Ethernet controller: Broadcom Inc. and subsidiaries BCM57504 NetXtreme-E 10Gb/25Gb/40Gb/50Gb/100Gb/200Gb Ethernet (rev 11)
19:00.1 Ethernet controller: Broadcom Inc. and subsidiaries BCM57504 NetXtreme-E 10Gb/25Gb/40Gb/50Gb/100Gb/200Gb Ethernet (rev 11)
19:00.2 Ethernet controller: Broadcom Inc. and subsidiaries BCM57504 NetXtreme-E 10Gb/25Gb/40Gb/50Gb/100Gb/200Gb Ethernet (rev 11)
19:00.3 Ethernet controller: Broadcom Inc. and subsidiaries BCM57504 NetXtreme-E 10Gb/25Gb/40Gb/50Gb/100Gb/200Gb Ethernet (rev 11)
51:00.0 Ethernet controller: Intel Corporation Ethernet Controller E810-C for QSFP (rev 02)
51:00.1 Ethernet controller: Intel Corporation Ethernet Controller E810-C for QSFP (rev 02)
51:01.0 Ethernet controller: Intel Corporation Ethernet Adaptive Virtual Function (rev 02)
51:01.1 Ethernet controller: Intel Corporation Ethernet Adaptive Virtual Function (rev 02)
51:01.2 Ethernet controller: Intel Corporation Ethernet Adaptive Virtual Function (rev 02)
51:01.3 Ethernet controller: Intel Corporation Ethernet Adaptive Virtual Function (rev 02)
51:11.0 Ethernet controller: Intel Corporation Ethernet Adaptive Virtual Function (rev 02)
51:11.1 Ethernet controller: Intel Corporation Ethernet Adaptive Virtual Function (rev 02)
51:11.2 Ethernet controller: Intel Corporation Ethernet Adaptive Virtual Function (rev 02)
51:11.3 Ethernet controller: Intel Corporation Ethernet Adaptive Virtual Function (rev 02)

O mapa de configuração consiste em um arquivo JSON que descreve os dispositivos usando filtros para descobri-los e cria grupos para as interfaces. O principal é entender os filtros e os grupos. Os filtros são usados para descobrir os dispositivos, e os grupos para criar as interfaces.

É possível definir os filtros desta maneira:

  • vendorID: 8086 (Intel)

  • deviceID: 0d5c (placa aceleradora)

  • driver: vfio-pci (driver)

  • pfNames: p2p1 (nome da interface física)

É possível também definir os filtros para corresponder à uma sintaxe de interface mais complexa, por exemplo:

  • pfNames: ["eth1#1,2,3,4,5,6"] ou [eth1#1-6] (nome da interface física)

Em relação aos grupos, podemos criar um para a placa FEC e outro para a placa Intel, e até criar um prefixo dependendo do nosso caso de uso:

  • resourceName: pci_sriov_net_bh_dpdk

  • resourcePrefix: Rancher.io

Há inúmeras combinações para descobrir e criar o grupo de recursos para alocar algumas VFs aos pods.

Nota
Nota

Para obter mais informações sobre filtros e grupos, visite sr-iov network device plug-in (Plug-in de dispositivo de rede sr-iov).

Depois de definir os filtros e os grupos para corresponder as interfaces, dependendo do hardware e do caso de uso, o seguinte mapa de configuração mostrará um exemplo para ser usado:

apiVersion: v1
kind: ConfigMap
metadata:
  name: sriovdp-config
  namespace: kube-system
data:
  config.json: |
    {
        "resourceList": [
            {
                "resourceName": "intel_fec_5g",
                "devicetype": "accelerator",
                "selectors": {
                    "vendors": ["8086"],
                    "devices": ["0d5d"]
                }
            },
            {
                "resourceName": "intel_sriov_odu",
                "selectors": {
                    "vendors": ["8086"],
                    "devices": ["1889"],
                    "drivers": ["vfio-pci"],
                    "pfNames": ["p2p1"]
                }
            },
            {
                "resourceName": "intel_sriov_oru",
                "selectors": {
                    "vendors": ["8086"],
                    "devices": ["1889"],
                    "drivers": ["vfio-pci"],
                    "pfNames": ["p2p2"]
                }
            }
        ]
    }
  • Preparar o arquivo daemonset para implantar o plug-in de dispositivo

O plug-in de dispositivo oferece suporte a várias arquiteturas (arm, amd, ppc64le), portanto, é possível usar o mesmo arquivo para arquiteturas diferentes ao implantar vários daemonset para cada arquitetura.

apiVersion: v1
kind: ServiceAccount
metadata:
  name: sriov-device-plugin
  namespace: kube-system
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: kube-sriov-device-plugin-amd64
  namespace: kube-system
  labels:
    tier: node
    app: sriovdp
spec:
  selector:
    matchLabels:
      name: sriov-device-plugin
  template:
    metadata:
      labels:
        name: sriov-device-plugin
        tier: node
        app: sriovdp
    spec:
      hostNetwork: true
      nodeSelector:
        kubernetes.io/arch: amd64
      tolerations:
      - key: node-role.kubernetes.io/master
        operator: Exists
        effect: NoSchedule
      serviceAccountName: sriov-device-plugin
      containers:
      - name: kube-sriovdp
        image: rancher/hardened-sriov-network-device-plugin:v3.7.0-build20240816
        imagePullPolicy: IfNotPresent
        args:
        - --log-dir=sriovdp
        - --log-level=10
        securityContext:
          privileged: true
        resources:
          requests:
            cpu: "250m"
            memory: "40Mi"
          limits:
            cpu: 1
            memory: "200Mi"
        volumeMounts:
        - name: devicesock
          mountPath: /var/lib/kubelet/
          readOnly: false
        - name: log
          mountPath: /var/log
        - name: config-volume
          mountPath: /etc/pcidp
        - name: device-info
          mountPath: /var/run/k8s.cni.cncf.io/devinfo/dp
      volumes:
        - name: devicesock
          hostPath:
            path: /var/lib/kubelet/
        - name: log
          hostPath:
            path: /var/log
        - name: device-info
          hostPath:
            path: /var/run/k8s.cni.cncf.io/devinfo/dp
            type: DirectoryOrCreate
        - name: config-volume
          configMap:
            name: sriovdp-config
            items:
            - key: config.json
              path: config.json
  • Depois de aplicar o mapa de configuração e o daemonset, o plug-in de dispositivo será implantado, e as interfaces serão descobertas e estarão disponíveis para os pods.

    $ kubectl get pods -n kube-system | grep sriov
    kube-system  kube-sriov-device-plugin-amd64-twjfl  1/1  Running  0  2m
  • Verifique as interfaces descobertas e disponíveis nos nós usados pelos pods:

    $ kubectl get $(kubectl get nodes -oname) -o jsonpath='{.status.allocatable}' | jq
    {
      "cpu": "64",
      "ephemeral-storage": "256196109726",
      "hugepages-1Gi": "40Gi",
      "hugepages-2Mi": "0",
      "intel.com/intel_fec_5g": "1",
      "intel.com/intel_sriov_odu": "4",
      "intel.com/intel_sriov_oru": "4",
      "memory": "221396384Ki",
      "pods": "110"
    }
  • O FEC é intel.com/intel_fec_5g e o valor é 1.

  • A VF é intel.com/intel_sriov_odu ou intel.com/intel_sriov_oru, se você a implantar com um plug-in de dispositivo e um mapa de configuração sem gráficos Helm.

Importante
Importante

Se não há interfaces neste ponto, não faz muito sentido continuar porque a interface não estará disponível para os pods. Revise o mapa de configuração e os filtros para resolver o problema primeiro.

Opção 2 (recomendada) – Instalação usando o Rancher com gráficos Helm para plug-ins de dispositivo CNI SR-IOV

  • Obtenha o Helm se não estiver presente:

$ curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
  • Instale o SR-IOV.

helm install sriov-crd oci://registry.suse.com/edge/charts/sriov-crd -n sriov-network-operator
helm install sriov-network-operator oci://registry.suse.com/edge/charts/sriov-network-operator -n sriov-network-operator
  • Verifique os recursos crd e os pods implantados:

$ kubectl get crd
$ kubectl -n sriov-network-operator get pods
  • Verifique o rótulo nos nós.

Com todos os recursos em execução, o rótulo aparecerá automaticamente em seu nó:

$ kubectl get nodes -oyaml | grep feature.node.kubernetes.io/network-sriov.capable

feature.node.kubernetes.io/network-sriov.capable: "true"
  • Revise o daemonset para ver os novos sriov-network-config-daemon e sriov-rancher-nfd-worker já ativos e prontos:

$ kubectl get daemonset -A
NAMESPACE             NAME                            DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR                                           AGE
calico-system            calico-node                     1         1         1       1            1           kubernetes.io/os=linux                                  15h
sriov-network-operator   sriov-network-config-daemon     1         1         1       1            1           feature.node.kubernetes.io/network-sriov.capable=true   45m
sriov-network-operator   sriov-rancher-nfd-worker        1         1         1       1            1           <none>                                                  45m
kube-system              rke2-ingress-nginx-controller   1         1         1       1            1           kubernetes.io/os=linux                                  15h
kube-system              rke2-multus-ds                  1         1         1       1            1           kubernetes.io/arch=amd64,kubernetes.io/os=linux         15h

Em poucos minutos (a atualização pode levar até 10 minutos), os nós são detectados e configurados com os recursos do SR-IOV:

$ kubectl get sriovnetworknodestates.sriovnetwork.openshift.io -A
NAMESPACE             NAME     AGE
sriov-network-operator   xr11-2   83s
  • Verifique as interfaces detectadas.

As interfaces descobertas devem ser o endereço PCI do dispositivo de rede. Verifique essa informação com o comando lspci no host.

$ kubectl get sriovnetworknodestates.sriovnetwork.openshift.io -n kube-system -oyaml
apiVersion: v1
items:
- apiVersion: sriovnetwork.openshift.io/v1
  kind: SriovNetworkNodeState
  metadata:
    creationTimestamp: "2023-06-07T09:52:37Z"
    generation: 1
    name: xr11-2
    namespace: sriov-network-operator
    ownerReferences:
    - apiVersion: sriovnetwork.openshift.io/v1
      blockOwnerDeletion: true
      controller: true
      kind: SriovNetworkNodePolicy
      name: default
      uid: 80b72499-e26b-4072-a75c-f9a6218ec357
    resourceVersion: "356603"
    uid: e1f1654b-92b3-44d9-9f87-2571792cc1ad
  spec:
    dpConfigVersion: "356507"
  status:
    interfaces:
    - deviceID: "1592"
      driver: ice
      eSwitchMode: legacy
      linkType: ETH
      mac: 40:a6:b7:9b:35:f0
      mtu: 1500
      name: p2p1
      pciAddress: "0000:51:00.0"
      totalvfs: 128
      vendor: "8086"
    - deviceID: "1592"
      driver: ice
      eSwitchMode: legacy
      linkType: ETH
      mac: 40:a6:b7:9b:35:f1
      mtu: 1500
      name: p2p2
      pciAddress: "0000:51:00.1"
      totalvfs: 128
      vendor: "8086"
    syncStatus: Succeeded
kind: List
metadata:
  resourceVersion: ""
Nota
Nota

Se a sua interface não foi detectada neste momento, verifique se ela está presente no próximo mapa de configuração:

$ kubectl get cm supported-nic-ids -oyaml -n sriov-network-operator

Se o seu dispositivo não estiver presente lá, edite o mapa de configuração adicionando os valores corretos a serem descobertos (deve ser necessário reiniciar o daemonset sriov-network-config-daemon).

  • Crie a política NetworkNode para configurar as VFs.

Serão criadas algumas VFs (numVfs) do dispositivo (rootDevices), e ela será configurada com o deviceType do driver e a MTU:

Nota
Nota

O campo resourceName não deve conter caracteres especiais e deve ser exclusivo em todo o cluster. O exemplo usa deviceType: vfio-pci porque dpdk será usado em conjunto com sr-iov. Se você não usar dpdk, o deviceType deverá ser deviceType: netdevice (valor padrão).

apiVersion: sriovnetwork.openshift.io/v1
kind: SriovNetworkNodePolicy
metadata:
  name: policy-dpdk
  namespace: sriov-network-operator
spec:
  nodeSelector:
    feature.node.kubernetes.io/network-sriov.capable: "true"
  resourceName: intelnicsDpdk
  deviceType: vfio-pci
  numVfs: 8
  mtu: 1500
  nicSelector:
    deviceID: "1592"
    vendor: "8086"
    rootDevices:
    - 0000:51:00.0
  • Valide as configurações:

$ kubectl get $(kubectl get nodes -oname) -o jsonpath='{.status.allocatable}' | jq
{
  "cpu": "64",
  "ephemeral-storage": "256196109726",
  "hugepages-1Gi": "60Gi",
  "hugepages-2Mi": "0",
  "intel.com/intel_fec_5g": "1",
  "memory": "200424836Ki",
  "pods": "110",
  "rancher.io/intelnicsDpdk": "8"
}
  • Crie a rede sr-iov (opcional, caso seja necessária uma rede diferente):

apiVersion: sriovnetwork.openshift.io/v1
kind: SriovNetwork
metadata:
  name: network-dpdk
  namespace: sriov-network-operator
spec:
  ipam: |
    {
      "type": "host-local",
      "subnet": "192.168.0.0/24",
      "rangeStart": "192.168.0.20",
      "rangeEnd": "192.168.0.60",
      "routes": [{
        "dst": "0.0.0.0/0"
      }],
      "gateway": "192.168.0.1"
    }
  vlan: 500
  resourceName: intelnicsDpdk
  • Verifique a rede criada:

$ kubectl get network-attachment-definitions.k8s.cni.cncf.io -A -oyaml

apiVersion: v1
items:
- apiVersion: k8s.cni.cncf.io/v1
  kind: NetworkAttachmentDefinition
  metadata:
    annotations:
      k8s.v1.cni.cncf.io/resourceName: rancher.io/intelnicsDpdk
    creationTimestamp: "2023-06-08T11:22:27Z"
    generation: 1
    name: network-dpdk
    namespace: sriov-network-operator
    resourceVersion: "13124"
    uid: df7c89f5-177c-4f30-ae72-7aef3294fb15
  spec:
    config: '{ "cniVersion":"0.4.0", "name":"network-dpdk","type":"sriov","vlan":500,"vlanQoS":0,"ipam":{"type":"host-local","subnet":"192.168.0.0/24","rangeStart":"192.168.0.10","rangeEnd":"192.168.0.60","routes":[{"dst":"0.0.0.0/0"}],"gateway":"192.168.0.1"}
      }'
kind: List
metadata:
  resourceVersion: ""

41.6 DPDK

DPDK (Data Plane Development Kit) é um conjunto de bibliotecas e drivers para processamento rápido de pacotes. Ele é usado para acelerar as cargas de trabalho de processamento de pacotes executadas em uma ampla variedade de arquiteturas de CPU. O DPDK inclui as bibliotecas de plano de controle e os drivers otimizados de placa de interface de rede (NIC) para o seguinte:

  1. Um gerenciador de fila que implementa filas sem bloqueio.

  2. Um gerenciador de buffer que pré-aloca buffers de tamanho fixo.

  3. Um gerenciador de memória que aloca pools de objetos na memória e usa um anel para armazenar objetos livres; garante que os objetos sejam igualmente distribuídos por todos os canais DRAM.

  4. Drivers de modo de sondagem (PMD) desenvolvidos para operar sem notificações assíncronas, reduzindo a sobrecarga.

  5. Uma estrutura de pacotes como um conjunto de bibliotecas auxiliares para desenvolver o processamento de pacotes.

As seguintes etapas mostram como habilitar o DPDK e criar VFs das NICs usadas pelas interfaces do DPDK:

  • Instale o pacote DPDK:

$ transactional-update pkg install dpdk dpdk-tools libdpdk-23
$ reboot
  • Parâmetros do kernel:

Para usar o DPDK, aplique alguns drivers para habilitar determinados parâmetros no kernel:

parâmetrovalordescrição

iommu

pt

Essa opção permite usar o driver vfio para as interfaces do DPDK.

intel_iommu ou amd_iommu

on

Essa opção permite usar o vfio para VFs.

Para habilitar os parâmetros, adicione-os ao arquivo /etc/default/grub:

GRUB_CMDLINE_LINUX="BOOT_IMAGE=/boot/vmlinuz-6.4.0-9-rt root=UUID=77b713de-5cc7-4d4c-8fc6-f5eca0a43cf9 skew_tick=1 rd.timeout=60 rd.retry=45 console=ttyS1,115200 console=tty0 default_hugepagesz=1G hugepagesz=1G hugepages=40 hugepagesz=2M hugepages=0 ignition.platform.id=openstack intel_iommu=on iommu=pt irqaffinity=0,31,32,63 isolcpus=domain,nohz,managed_irq,1-30,33-62 nohz_full=1-30,33-62 nohz=on mce=off net.ifnames=0 nosoftlockup nowatchdog nmi_watchdog=0 quiet rcu_nocb_poll rcu_nocbs=1-30,33-62 rcupdate.rcu_cpu_stall_suppress=1 rcupdate.rcu_expedited=1 rcupdate.rcu_normal_after_boot=1 rcupdate.rcu_task_stall_timeout=0 rcutree.kthread_prio=99 security=selinux selinux=1 idle=poll"

Atualize a configuração do GRUB e reinicialize o sistema para aplicar as alterações:

$ transactional-update grub.cfg
$ reboot
  • Carregue o módulo do kernel vfio-pci e habilite o SR-IOV nas NICs:

$ modprobe vfio-pci enable_sriov=1 disable_idle_d3=1
  • Crie algumas funções virtuais (VFs) das NICs.

Por exemplo, para criar VFs para duas NICs diferentes, os seguintes comandos são necessários:

$ echo 4 > /sys/bus/pci/devices/0000:51:00.0/sriov_numvfs
$ echo 4 > /sys/bus/pci/devices/0000:51:00.1/sriov_numvfs
  • Vincule as novas VFs ao driver vfio-pci:

$ dpdk-devbind.py -b vfio-pci 0000:51:01.0 0000:51:01.1 0000:51:01.2 0000:51:01.3 \
                              0000:51:11.0 0000:51:11.1 0000:51:11.2 0000:51:11.3
  • Verifique se a configuração foi aplicada corretamente:

$ dpdk-devbind.py -s

Network devices using DPDK-compatible driver
============================================
0000:51:01.0 'Ethernet Adaptive Virtual Function 1889' drv=vfio-pci unused=iavf,igb_uio
0000:51:01.1 'Ethernet Adaptive Virtual Function 1889' drv=vfio-pci unused=iavf,igb_uio
0000:51:01.2 'Ethernet Adaptive Virtual Function 1889' drv=vfio-pci unused=iavf,igb_uio
0000:51:01.3 'Ethernet Adaptive Virtual Function 1889' drv=vfio-pci unused=iavf,igb_uio
0000:51:01.0 'Ethernet Adaptive Virtual Function 1889' drv=vfio-pci unused=iavf,igb_uio
0000:51:11.1 'Ethernet Adaptive Virtual Function 1889' drv=vfio-pci unused=iavf,igb_uio
0000:51:21.2 'Ethernet Adaptive Virtual Function 1889' drv=vfio-pci unused=iavf,igb_uio
0000:51:31.3 'Ethernet Adaptive Virtual Function 1889' drv=vfio-pci unused=iavf,igb_uio

Network devices using kernel driver
===================================
0000:19:00.0 'BCM57504 NetXtreme-E 10Gb/25Gb/40Gb/50Gb/100Gb/200Gb Ethernet 1751' if=em1 drv=bnxt_en unused=igb_uio,vfio-pci *Active*
0000:19:00.1 'BCM57504 NetXtreme-E 10Gb/25Gb/40Gb/50Gb/100Gb/200Gb Ethernet 1751' if=em2 drv=bnxt_en unused=igb_uio,vfio-pci
0000:19:00.2 'BCM57504 NetXtreme-E 10Gb/25Gb/40Gb/50Gb/100Gb/200Gb Ethernet 1751' if=em3 drv=bnxt_en unused=igb_uio,vfio-pci
0000:19:00.3 'BCM57504 NetXtreme-E 10Gb/25Gb/40Gb/50Gb/100Gb/200Gb Ethernet 1751' if=em4 drv=bnxt_en unused=igb_uio,vfio-pci
0000:51:00.0 'Ethernet Controller E810-C for QSFP 1592' if=eth13 drv=ice unused=igb_uio,vfio-pci
0000:51:00.1 'Ethernet Controller E810-C for QSFP 1592' if=rename8 drv=ice unused=igb_uio,vfio-pci

41.7 Aceleração vRAN (Intel ACC100/ACC200)

À medida que os provedores de serviços de comunicação migram da rede 4G para 5G, muitos deles estão adotando as arquiteturas de rede de acesso por rádio virtualizada (vRAN) para maior capacidade dos canais e implantação mais fácil dos serviços e aplicativos de borda. As soluções vRAN estão no local ideal para oferecer serviços de baixa latência com flexibilidade para aumentar ou reduzir a capacidade de acordo com o volume do tráfego e da demanda em tempo real na rede.

Uma das cargas de trabalho 4G e 5G com uso mais intenso de recursos é a FEC da RAN de camada 1 (L1), que resolve os erros de transmissão de dados por meio de canais de comunicação incertos ou ruidosos. A tecnologia FEC detecta e corrige um número limitado de erros em dados 4G ou 5G, eliminando a necessidade de retransmissão. Como a transação de aceleração da FEC não contém informações de estado da célula, é possível virtualizá-la com facilidade para aproveitar os benefícios dos agrupamentos e facilitar a migração de células.

  • Parâmetros do kernel

Para habilitar a aceleração da vRAN, precisamos habilitar os seguintes parâmetros do kernel (se ainda não estiverem presentes):

parâmetrovalordescrição

iommu

pt

Essa opção permite usar vfio paras as interfaces do DPDK.

intel_iommu ou amd_iommu

on

Essa opção permite usar vfio para VFs.

Modifique o arquivo GRUB /etc/default/grub para adicioná-los à linha de comando do kernel:

GRUB_CMDLINE_LINUX="BOOT_IMAGE=/boot/vmlinuz-6.4.0-9-rt root=UUID=77b713de-5cc7-4d4c-8fc6-f5eca0a43cf9 skew_tick=1 rd.timeout=60 rd.retry=45 console=ttyS1,115200 console=tty0 default_hugepagesz=1G hugepagesz=1G hugepages=40 hugepagesz=2M hugepages=0 ignition.platform.id=openstack intel_iommu=on iommu=pt irqaffinity=0,31,32,63 isolcpus=domain,nohz,managed_irq,1-30,33-62 nohz_full=1-30,33-62 nohz=on mce=off net.ifnames=0 nosoftlockup nowatchdog nmi_watchdog=0 quiet rcu_nocb_poll rcu_nocbs=1-30,33-62 rcupdate.rcu_cpu_stall_suppress=1 rcupdate.rcu_expedited=1 rcupdate.rcu_normal_after_boot=1 rcupdate.rcu_task_stall_timeout=0 rcutree.kthread_prio=99 security=selinux selinux=1 idle=poll"

Atualize a configuração do GRUB e reinicialize o sistema para aplicar as alterações:

$ transactional-update grub.cfg
$ reboot

Para verificar se os parâmetros foram aplicados após a reinicialização, consulte a linha de comando:

$ cat /proc/cmdline
  • Carregue os módulos do kernel vfio-pci para habilitar a aceleração da vRAN:

$ modprobe vfio-pci enable_sriov=1 disable_idle_d3=1
  • Obtenha as informações da interface Acc100:

$ lspci | grep -i acc
8a:00.0 Processing accelerators: Intel Corporation Device 0d5c
  • Vincule a interface física (PF) ao driver vfio-pci:

$ dpdk-devbind.py -b vfio-pci 0000:8a:00.0
  • Crie as funções virtuais (VFs) da interface física (PF).

Crie 2 VFs da PF e vincule a elas o vfio-pci seguindo estas etapas:

$ echo 2 > /sys/bus/pci/devices/0000:8a:00.0/sriov_numvfs
$ dpdk-devbind.py -b vfio-pci 0000:8b:00.0
  • Configure a acc100 com o arquivo de configuração proposto:

$ pf_bb_config ACC100 -c /opt/pf-bb-config/acc100_config_vf_5g.cfg
Tue Jun  6 10:49:20 2023:INFO:Queue Groups: 2 5GUL, 2 5GDL, 2 4GUL, 2 4GDL
Tue Jun  6 10:49:20 2023:INFO:Configuration in VF mode
Tue Jun  6 10:49:21 2023:INFO: ROM version MM 99AD92
Tue Jun  6 10:49:21 2023:WARN:* Note: Not on DDR PRQ version  1302020 != 10092020
Tue Jun  6 10:49:21 2023:INFO:PF ACC100 configuration complete
Tue Jun  6 10:49:21 2023:INFO:ACC100 PF [0000:8a:00.0] configuration complete!
  • Verifique as novas VFs criadas da PF FEC:

$ dpdk-devbind.py -s
Baseband devices using DPDK-compatible driver
=============================================
0000:8a:00.0 'Device 0d5c' drv=vfio-pci unused=
0000:8b:00.0 'Device 0d5d' drv=vfio-pci unused=

Other Baseband devices
======================
0000:8b:00.1 'Device 0d5d' unused=

41.8 HugePages

Quando um processo usa RAM, a CPU a marca como usada por esse processo. Para manter a eficiência, a CPU aloca a RAM em blocos de 4K bytes, que é o valor padrão em muitas plataformas. Esses blocos são chamados de páginas. As páginas podem ser substituídas por discos, entre outros.

Como o espaço do endereço do processo é virtual, a CPU e o sistema operacional precisam memorizar quais páginas pertencem a qual processo, e onde cada página é armazenada. Quanto maior o número de páginas, mais longa a pesquisa de mapeamento de memória. Quando um processo usa 1 GB de memória, isso equivale a 262144 entradas para pesquisa (1 GB/4 K). Se uma entrada da tabela de páginas consome 8 bytes, isso equivale a 2 MB (262144 * 8) para pesquisa.

As arquiteturas de CPU mais atuais oferecem suporte às páginas maiores que o padrão, o que reduz o número de entradas para a CPU/SO pesquisar.

  • Parâmetros do kernel

Para habilitar o HugePages, devemos adicionar os seguintes parâmetros do kernel. Neste exemplo, configuramos 40 páginas de 1G, portanto, o tamanho e o número exato de páginas enormes devem ser adaptados aos requisitos de memória do seu aplicativo:

parâmetrovalordescrição

hugepagesz

1G

Essa opção permite definir o tamanho das páginas enormes como 1 G

hugepages

40

Esse é o número de páginas enormes definido antes

default_hugepagesz

1G

Esse é o valor padrão para obter as páginas enormes

Modifique o arquivo GRUB /etc/default/grub para adicionar esses parâmetros a GRUB_CMDLINE_LINUX:

default_hugepagesz=1G hugepagesz=1G hugepages=40 hugepagesz=2M hugepages=0

Atualize a configuração do GRUB e reinicialize o sistema para aplicar as alterações:

$ transactional-update grub.cfg
$ reboot

Para validar se os parâmetros foram aplicados após a reinicialização, verifique a linha de comando:

$ cat /proc/cmdline
  • Usando o HugePages

Para usar o HugePages, precisamos montá-lo:

$ mkdir -p /hugepages
$ mount -t hugetlbfs nodev /hugepages

Implante a carga de trabalho Kubernetes criando os recursos e os volumes:

...
 resources:
   requests:
     memory: "24Gi"
     hugepages-1Gi: 16Gi
     intel.com/intel_sriov_oru: '4'
   limits:
     memory: "24Gi"
     hugepages-1Gi: 16Gi
     intel.com/intel_sriov_oru: '4'
...
...
volumeMounts:
  - name: hugepage
    mountPath: /hugepages
...
volumes:
  - name: hugepage
    emptyDir:
      medium: HugePages
...

41.9 Fixação da CPU em Kubernetes

41.9.1 Pré-requisito

A CPU deve estar ajustada de acordo com o perfil de desempenho abordado nesta seção (Seção 41.3, “Fixação de CPU via TuneD e argumentos do kernel”).

41.9.2 Configurar o Kubernetes para fixação da CPU

Configure os argumentos de kubelet para implementar o gerenciamento da CPU no cluster RKE2. Adicione o seguinte bloco de configuração, como no exemplo abaixo, ao arquivo /etc/rancher/rke2/config.yaml. Especifique os núcleos da CPU de manutenção nos argumentos kubelet-reserved e system-reserved:

kubelet-arg:
- "cpu-manager-policy=static"
- "cpu-manager-policy-options=full-pcpus-only=true"
- "cpu-manager-reconcile-period=0s"
- "kubelet-reserved=cpu=0,31,32,63"
- "system-reserved=cpu=0,31,32,63"

41.9.3 Aproveitando as CPUs fixadas para as cargas de trabalho

Há três maneiras de usar este recurso com a política estática definida no kubelet, dependendo das solicitações e dos limites definidos em sua carga de trabalho:

  1. Classe de QoS BestEffort: se você não definir uma solicitação ou um limite de CPU, o pod será programado na primeira CPU disponível no sistema.

    Um exemplo de uso da classe de QoS BestEffort é:

    spec:
      containers:
      - name: nginx
        image: nginx
  2. Classe de QoS Burstable: se você definir uma solicitação de CPU que não é igual aos limites, ou se não houver solicitações de CPU.

    Alguns exemplos de uso da classe de QoS Burstable são:

    spec:
      containers:
      - name: nginx
        image: nginx
        resources:
          limits:
            memory: "200Mi"
          requests:
            memory: "100Mi"

    ou

    spec:
      containers:
      - name: nginx
        image: nginx
        resources:
          limits:
            memory: "200Mi"
            cpu: "2"
          requests:
            memory: "100Mi"
            cpu: "1"
  3. Classe de QoS Guaranteed: se você definir uma solicitação de CPU igual aos limites.

    Um exemplo de uso da classe de QoS Guaranteed é:

    spec:
      containers:
        - name: nginx
          image: nginx
          resources:
            limits:
              memory: "200Mi"
              cpu: "2"
            requests:
              memory: "200Mi"
              cpu: "2"

41.10 Programação com reconhecimento de NUMA

Acesso não uniforme à memória ou arquitetura não uniforme de acesso à memória (NUMA, Non-Uniform Memory Access ou Non-Uniform Memory Architecture) é um projeto de memória física usado na arquitetura SMP (multiprocessadores), em que o tempo de acesso à memória depende do local da memória relativo ao processador. No NUMA, um processador pode acessar a própria memória local com mais rapidez do que a memória não local, ou seja, a memória local de outro processador ou a memória compartilhada entre processadores.

41.10.1 Identificando os nós NUMA

Para identificar os nós NUMA, execute o seguinte comando em seu sistema:

$ lscpu | grep NUMA
NUMA node(s):                       1
NUMA node0 CPU(s):                  0-63
Nota
Nota

Para este exemplo, temos apenas um nó NUMA com 64 CPUs.

É necessário habilitar o NUMA no BIOS. Se o dmesg não tem registros de inicialização do NUMA durante o bootup, as mensagens relacionadas ao NUMA no buffer de anel do kernel podem ter sido substituídas.

41.11 MetalLB

MetalLB é uma implementação de balanceador de carga para clusters Kubernetes bare metal, que usa os protocolos de roteamento padrão, como L2 e BGP, como protocolos de anúncio. Trata-se de um balanceador de carga de rede que pode ser usado para expor serviços em um cluster Kubernetes ao ambiente externo por causa da necessidade de usar serviços do Kubernetes do tipo LoadBalancer com bare metal.

Para habilitar o MetalLB no cluster RKE2, são necessárias as seguintes etapas:

  • Instale o MetalLB usando o seguinte comando:

$ kubectl apply <<EOF -f
apiVersion: helm.cattle.io/v1
kind: HelmChart
metadata:
  name: metallb
  namespace: kube-system
spec:
  chart: oci://registry.suse.com/edge/charts/metallb
  targetNamespace: metallb-system
  version: 303.0.0+up0.14.9
  createNamespace: true
---
apiVersion: helm.cattle.io/v1
kind: HelmChart
metadata:
  name: endpoint-copier-operator
  namespace: kube-system
spec:
  chart: oci://registry.suse.com/edge/charts/endpoint-copier-operator
  targetNamespace: endpoint-copier-operator
  version: 303.0.0+up0.2.1
  createNamespace: true
EOF
  • Crie a configuração de IpAddressPool e L2advertisement:

apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: kubernetes-vip-ip-pool
  namespace: metallb-system
spec:
  addresses:
    - 10.168.200.98/32
  serviceAllocation:
    priority: 100
    namespaces:
      - default
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: ip-pool-l2-adv
  namespace: metallb-system
spec:
  ipAddressPools:
    - kubernetes-vip-ip-pool
  • Crie o serviço de endpoint para expor o VIP:

apiVersion: v1
kind: Service
metadata:
  name: kubernetes-vip
  namespace: default
spec:
  internalTrafficPolicy: Cluster
  ipFamilies:
  - IPv4
  ipFamilyPolicy: SingleStack
  ports:
  - name: rke2-api
    port: 9345
    protocol: TCP
    targetPort: 9345
  - name: k8s-api
    port: 6443
    protocol: TCP
    targetPort: 6443
  sessionAffinity: None
  type: LoadBalancer
  • Verifique se o VIP foi criado e se os pods do MetalLB estão em execução:

$ kubectl get svc -n default
$ kubectl get pods -n default

41.12 Configuração do registro particular

É possível configurar o Containerd para conexão com os registros particulares e usá-los para extrair as imagens particulares de cada nó.

Na inicialização, o RKE2 verifica se existe um arquivo registries.yaml em /etc/rancher/rke2/ e instrui o containerd a usar os registros definidos no arquivo. Para usar um registro particular, crie esse arquivo como raiz em cada nó que usará o registro.

Para adicionar o registro particular, crie o arquivo /etc/rancher/rke2/registries.yaml com o seguinte conteúdo:

mirrors:
  docker.io:
    endpoint:
      - "https://registry.example.com:5000"
configs:
  "registry.example.com:5000":
    auth:
      username: xxxxxx # this is the registry username
      password: xxxxxx # this is the registry password
    tls:
      cert_file:            # path to the cert file used to authenticate to the registry
      key_file:             # path to the key file for the certificate used to authenticate to the registry
      ca_file:              # path to the ca file used to verify the registry's certificate
      insecure_skip_verify: # may be set to true to skip verifying the registry's certificate

ou sem autenticação:

mirrors:
  docker.io:
    endpoint:
      - "https://registry.example.com:5000"
configs:
  "registry.example.com:5000":
    tls:
      cert_file:            # path to the cert file used to authenticate to the registry
      key_file:             # path to the key file for the certificate used to authenticate to the registry
      ca_file:              # path to the ca file used to verify the registry's certificate
      insecure_skip_verify: # may be set to true to skip verifying the registry's certificate

Para que as alterações no registro entrem em vigor, você precisa configurar esse arquivo antes de iniciar o RKE2 no nó ou reiniciar o RKE2 em cada nó configurado.

Nota
Nota

Para obter mais informações sobre isso, acesse a configuração de registro containerd para RKE2.

41.13 Precision Time Protocol

Precision Time Protocol (PTP) é um protocolo de rede desenvolvido pelo Institute of Electrical and Electronics Engineers (IEEE) para permitir a sincronização de tempo em submicrossegundos em uma rede de computadores. Desde a sua origem e durante as duas últimas décadas, o PTP tem sido usado em diversos setores. Recentemente, observamos uma crescente adoção nas redes de telecomunicações como elemento essencial a redes 5G. Apesar de ser um protocolo relativamente simples, sua configuração pode mudar bastante de acordo com o aplicativo. Por essa razão, foram definidos e padronizados vários perfis.

Nesta seção, apenas os perfis específicos de telecomunicações serão apresentados. Portanto, vamos considerar o recurso de marcação de data e hora e um relógio de hardware PTP (PHC, PTP Hardware Clock) na NIC. Hoje em dia, todos os adaptadores de rede para telecomunicações contam com suporte a PTP no hardware, mas você pode verificar esses recursos com o seguinte comando:

# ethtool -T p1p1
Time stamping parameters for p1p1:
Capabilities:
        hardware-transmit
        software-transmit
        hardware-receive
        software-receive
        software-system-clock
        hardware-raw-clock
PTP Hardware Clock: 0
Hardware Transmit Timestamp Modes:
        off
        on
Hardware Receive Filter Modes:
        none
        all

Substitua p1p1 pelo nome da interface usada para PTP.

As seções a seguir orientam como instalar e configurar o PTP especificamente no SUSE Edge, mas deve haver uma familiaridade com os conceitos básicos do PTP. Para uma breve visão geral do PTP e a implementação incluída no SUSE Edge for Telco, acesse https://documentation.suse.com/sles/html/SLES-all/cha-tuning-ptp.html.

41.13.1 Instalar os componentes de software PTP

No SUSE Edge for Telco, a implementação do PTP é fornecida pelo pacote linuxptp, que inclui dois componentes:

  • ptp4l: um daemon que controla o PHC na NIC e executa o protocolo PTP

  • phc2sys: um daemon que mantém a sincronização do relógio do sistema com o PHC sincronizado por PTP na NIC

Os dois daemons são necessários para que a sincronização do sistema funcione por completo e devem ser definidos de maneira correta com a sua configuração, o que foi abordado na Seção 41.13.2, “Configurar o PTP para implantações de telecomunicações”.

A maneira melhor e mais fácil de integrar o PTP ao cluster downstream é adicionar o pacote linuxptp em packageList ao arquivo de definição do Edge Image Builder (EIB). Desse modo, o software de plano de controle PTP será instalado automaticamente durante o provisionamento do cluster. Consulte a documentação do EIB (Seção 3.3.4, “Configurando pacotes RPM”) para obter mais informações sobre como instalar os pacotes.

Veja a seguir um manifesto do EIB de amostra com linuxptp:

apiVersion: 1.0
image:
  imageType: RAW
  arch: x86_64
  baseImage: {micro-base-rt-image-raw}
  outputImageName: eibimage-slmicrort-telco.raw
operatingSystem:
  time:
    timezone: America/New_York
  kernelArgs:
    - ignition.platform.id=openstack
    - net.ifnames=1
  systemd:
    disable:
      - rebootmgr
      - transactional-update.timer
      - transactional-update-cleanup.timer
      - fstrim
      - time-sync.target
    enable:
      - ptp4l
      - phc2sys
  users:
    - username: root
      encryptedPassword: ${ROOT_PASSWORD}
  packages:
    packageList:
      - jq
      - dpdk
      - dpdk-tools
      - libdpdk-23
      - pf-bb-config
      - open-iscsi
      - tuned
      - cpupower
      - linuxptp
    sccRegistrationCode: ${SCC_REGISTRATION_CODE}
Nota
Nota

O pacote linuxptp incluído no SUSE Edge for Telco não habilita o ptp4l e o phc2sys por padrão. Se os arquivos de configuração específicos do sistema forem implantados no momento do provisionamento (consulte a Seção 41.13.3, “Integração da Cluster API”), eles deverão ser habilitados. Para isso, adicione-os à seção systemd do manifesto, conforme o exemplo acima.

Siga o processo normal para criar a imagem conforme descrito na documentação do EIB (Seção 3.4, “Criando a imagem”) e use-a para implantar o cluster. Se você não tem experiência com o EIB, comece pelo Capítulo 11, Edge Image Builder.

41.13.2 Configurar o PTP para implantações de telecomunicações

Muitos aplicativos de telecomunicações exigem uma sincronização rígida de fase e de tempo com pouca variação, o que resultou na definição de dois perfis orientados a telecomunicações: ITU-T G.8275.1 e ITU-T G.8275.2. Os dois têm alta taxa de mensagens de sincronização e outros aspectos diferenciados, como uso de um algoritmo BMCA (Best Master Clock Algorithm) alternativo. Esse comportamento exige definições específicas no arquivo de configuração consumido pelo ptp4l, apresentadas nas seções a seguir como referência.

Nota
Nota
  • As duas seções abordam apenas o caso de um relógio comum na configuração de receptor de tempo.

  • Esse tipo de perfil deve ser usado em uma infraestrutura PTP bem planejada.

  • Sua rede PTP específica pode exigir um ajuste de configuração adicional. Revise os exemplos apresentados e adapte-os se necessário.

41.13.2.1 Perfil de PTP ITU-T G.8275.1

O perfil G.8275.1 tem as seguintes especificações:

  • Executado diretamente em Ethernet e requer suporte completo à rede (nós/comutadores adjacentes devem dar suporte a PTP).

  • A configuração de domínio padrão é 24.

  • A comparação de conjunto de dados baseia-se no algoritmo G.8275.x e nos valores localPriority depois de priority2.

Copie o seguinte conteúdo para um arquivo chamado /etc/ptp4l-G.8275.1.conf:

# Telecom G.8275.1 example configuration
[global]
domainNumber                    24
priority2			255
dataset_comparison              G.8275.x
G.8275.portDS.localPriority     128
G.8275.defaultDS.localPriority  128
maxStepsRemoved                 255
logAnnounceInterval             -3
logSyncInterval                 -4
logMinDelayReqInterval          -4
announceReceiptTimeout		3
serverOnly                      0
ptp_dst_mac                     01:80:C2:00:00:0E
network_transport               L2

Após a criação do arquivo, faça referência a ele em /etc/sysconfig/ptp4l para o daemon ser iniciado corretamente. Para fazer isso, altere a linha OPTIONS=:

OPTIONS="-f /etc/ptp4l-G.8275.1.conf -i $IFNAME --message_tag ptp-8275.1"

Mais precisamente:

  • -f requer o nome do arquivo de configuração que será usado; neste caso, /etc/ptp4l-G.8275.1.conf.

  • -i requer o nome da interface que será usada. Substitua $IFNAME pelo nome da interface real.

  • --message_tag permite identificar melhor a saída de ptp4l nos registros do sistema e é opcional.

Após a conclusão das etapas acima, o daemon ptp4l deverá ser (re)iniciado:

# systemctl restart ptp4l

Verifique o status da sincronização observando os registros com:

# journalctl -e -u ptp4l

41.13.2.2 Perfil de PTP ITU-T G.8275.2

O perfil G.8275.2 tem as seguintes especificações:

  • Executado em IP e não requer suporte total à rede (nós/comutadores adjacentes podem não dar suporte a PTP).

  • A configuração de domínio padrão é 44.

  • A comparação de conjunto de dados baseia-se no algoritmo G.8275.x e nos valores localPriority depois de priority2.

Copie o seguinte conteúdo para o arquivo chamado /etc/ptp4l-G.8275.2.conf:

# Telecom G.8275.2 example configuration
[global]
domainNumber                    44
priority2			255
dataset_comparison              G.8275.x
G.8275.portDS.localPriority     128
G.8275.defaultDS.localPriority  128
maxStepsRemoved                 255
logAnnounceInterval             0
serverOnly                      0
hybrid_e2e                      1
inhibit_multicast_service       1
unicast_listen                  1
unicast_req_duration            60
logSyncInterval                 -5
logMinDelayReqInterval          -4
announceReceiptTimeout		2
#
# Customize the following for slave operation:
#
[unicast_master_table]
table_id                        1
logQueryInterval                2
UDPv4                           $PEER_IP_ADDRESS
[$IFNAME]
unicast_master_table            1

Substitua os seguintes espaços reservados:

  • $PEER_IP_ADDRESS: o endereço IP do nó PTP seguinte com o qual se comunicar, como o relógio mestre ou de limite que fornecerá a sincronização.

  • $IFNAME: instrui o ptp4l sobre qual interface usar para PTP.

Após a criação do arquivo, faça referência a ele, junto com o nome da interface para PTP, em /etc/sysconfig/ptp4l para que o daemon seja iniciado corretamente. Para fazer isso, altere a linha OPTIONS= para:

OPTIONS="-f /etc/ptp4l-G.8275.2.conf --message_tag ptp-8275.2"

Mais precisamente:

  • -f requer o nome do arquivo de configuração que será usado; neste caso, /etc/ptp4l-G.8275.2.conf.

  • --message_tag permite identificar melhor a saída de ptp4l nos registros do sistema e é opcional.

Após a conclusão das etapas acima, o daemon ptp4l deverá ser (re)iniciado:

# systemctl restart ptp4l

Verifique o status da sincronização observando os registros com:

# journalctl -e -u ptp4l

41.13.2.3 Configuração do phc2sys

Embora não seja obrigatório, é recomendado completar toda a configuração de ptp4l antes de passar para o phc2sys. O phc2sys não requer um arquivo de configuração, e seus parâmetros de execução podem ser controlados unicamente pela variável OPTIONS= presente em /etc/sysconfig/ptp4l, de maneira similar a ptp4l:

OPTIONS="-s $IFNAME -w"

Em que $IFNAME é o nome da interface já configurada em ptp4l, que será usada como fonte para o relógio do sistema. Isso é usado para identificar o PHC de origem.

41.13.3 Integração da Cluster API

Sempre que um cluster é implantado por cluster de gerenciamento e provisionamento de rede direcionado, tanto o arquivo de configuração quanto as duas variáveis de configuração em /etc/sysconfig podem ser implantados no host no momento do provisionamento. Veja abaixo o trecho de uma definição de cluster, com foco no objeto RKE2ControlPlane modificado que implanta o mesmo arquivo de configuração G.8275.1 em todos os hosts:

apiVersion: controlplane.cluster.x-k8s.io/v1beta1
kind: RKE2ControlPlane
metadata:
  name: single-node-cluster
  namespace: default
spec:
  infrastructureRef:
    apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
    kind: Metal3MachineTemplate
    name: single-node-cluster-controlplane
  replicas: 1
  version: ${RKE2_VERSION}
  rolloutStrategy:
    type: "RollingUpdate"
    rollingUpdate:
      maxSurge: 0
  registrationMethod: "control-plane-endpoint"
  serverConfig:
    cni: canal
  agentConfig:
    format: ignition
    cisProfile: cis
    additionalUserData:
      config: |
        variant: fcos
        version: 1.4.0
        systemd:
          units:
            - name: rke2-preinstall.service
              enabled: true
              contents: |
                [Unit]
                Description=rke2-preinstall
                Wants=network-online.target
                Before=rke2-install.service
                ConditionPathExists=!/run/cluster-api/bootstrap-success.complete
                [Service]
                Type=oneshot
                User=root
                ExecStartPre=/bin/sh -c "mount -L config-2 /mnt"
                ExecStart=/bin/sh -c "sed -i \"s/BAREMETALHOST_UUID/$(jq -r .uuid /mnt/openstack/latest/meta_data.json)/\" /etc/rancher/rke2/config.yaml"
                ExecStart=/bin/sh -c "echo \"node-name: $(jq -r .name /mnt/openstack/latest/meta_data.json)\" >> /etc/rancher/rke2/config.yaml"
                ExecStartPost=/bin/sh -c "umount /mnt"
                [Install]
                WantedBy=multi-user.target
        storage:
          files:
            - path: /etc/ptp4l-G.8275.1.conf
              overwrite: true
              contents:
                inline: |
                  # Telecom G.8275.1 example configuration
                  [global]
                  domainNumber                    24
                  priority2                       255
                  dataset_comparison              G.8275.x
                  G.8275.portDS.localPriority     128
                  G.8275.defaultDS.localPriority  128
                  maxStepsRemoved                 255
                  logAnnounceInterval             -3
                  logSyncInterval                 -4
                  logMinDelayReqInterval          -4
                  announceReceiptTimeout          3
                  serverOnly                      0
                  ptp_dst_mac                     01:80:C2:00:00:0E
                  network_transport               L2
              mode: 0644
              user:
                name: root
              group:
                name: root
            - path: /etc/sysconfig/ptp4l
              overwrite: true
              contents:
                inline: |
                  ## Path:           Network/LinuxPTP
                  ## Description:    Precision Time Protocol (PTP): ptp4l settings
                  ## Type:           string
                  ## Default:        "-i eth0 -f /etc/ptp4l.conf"
                  ## ServiceRestart: ptp4l
                  #
                  # Arguments when starting ptp4l(8).
                  #
                  OPTIONS="-f /etc/ptp4l-G.8275.1.conf -i $IFNAME --message_tag ptp-8275.1"
              mode: 0644
              user:
                name: root
              group:
                name: root
            - path: /etc/sysconfig/phc2sys
              overwrite: true
              contents:
                inline: |
                  ## Path:           Network/LinuxPTP
                  ## Description:    Precision Time Protocol (PTP): phc2sys settings
                  ## Type:           string
                  ## Default:        "-s eth0 -w"
                  ## ServiceRestart: phc2sys
                  #
                  # Arguments when starting phc2sys(8).
                  #
                  OPTIONS="-s $IFNAME -w"
              mode: 0644
              user:
                name: root
              group:
                name: root
    kubelet:
      extraArgs:
        - provider-id=metal3://BAREMETALHOST_UUID
    nodeName: "localhost.localdomain"

Além de outras variáveis, é necessário preencher a definição acima com o nome da interface e os outros objetos da Cluster API, conforme descrito no Capítulo 42, Provisionamento de rede direcionado totalmente automatizado.

Nota
Nota
  • Essa é uma abordagem prática apenas quando o hardware no cluster é uniforme e a mesma configuração é necessária em todos os hosts, inclusive o nome da interface.

  • Há outras abordagens possíveis que serão explicadas em versões futuras.

Neste ponto, os hosts devem ter uma pilha PTP em funcionamento e começarão a negociar sua função de PTP.

Documentation survey