Resolver problemas de NFS e DataPlane v2 do GKE no VMware

Neste documento, detalhamos um procedimento manual para o GKE no VMware se você tiver problemas com as montagens NFS com um volume ou pod travado e tiver criado o cluster com o DataPlane v2 ativado. Talvez você encontre problemas se tiver cargas de trabalho usando volumes ReadWriteMany com drivers de armazenamento suscetíveis a esse problema, como, entre outros:

  • Robin.io
  • Portworx (sharedv4 de volumes de serviço)
  • csi-nfs

As montagens NFS em algumas arquiteturas de armazenamento podem ficar travadas quando estão conectadas a um endpoint usando um Serviço do Kubernetes (ClusterIP) e o DataPlane v2. Esse comportamento se deve às limitações na forma como o código de soquete do kernel do Linux interage com o programa eBPF do Cillium. Os contêineres podem ficar bloqueados na E/S ou até mesmo não poderem ser eliminados, já que a montagem NFS extinta não pode ser desmontada.

Você poderá enfrentar esse problema se usar o armazenamento RWX hospedado em servidores NFS que são executados em um nó do Kubernetes, incluindo soluções de armazenamento definidas por software ou hiperconvergentes, como Ondat, Robin.io ou Portworx.

Revisar a configuração do cluster atual

Receba alguns valores de configuração atuais do cluster. Use os valores dessas etapas para criar um manifesto kube-proxy na próxima seção.

  1. Acesse o ClusterCIDR de cm/cilium-config:

    kubectl get cm -n kube-system cilium-config -o yaml | grep native-routing-cidr
    

    O exemplo de saída a seguir mostra que você usaria 192.168.0.0/16 como ClusterCIDR:

    ipv4-native-routing-cidr: 192.168.0.0/16
    native-routing-cidr: 192.168.0.0/16
    
  2. Receba APIServerAdvertiseAddress e APIServerPort do DaemonSet anetd:

    kubectl get ds -n kube-system  anetd -o yaml | grep KUBERNETES -A 1
    

    O exemplo de saída a seguir mostra que você usaria 21.1.4.119 como APIServerAdvertiseAddress e 443 como APIServerPort:

    - name: KUBERNETES_SERVICE_HOST
      value: 21.1.4.119
    - name: KUBERNETES_SERVICE_PORT
      value: "443"
    
  3. Receba o RegistryCredentialsSecretName do DaemonSet anetd:

    kubectl get ds -n kube-system  anetd -o yaml | grep imagePullSecrets -A 1
    

    O exemplo de saída a seguir mostra que você usaria private-registry-creds como RegistryCredentialsSecretName:

    imagePullSecrets:
      - name: private-registry-creds
    
  4. Receba o Registry do DameonSet anetd:

    kubectl get ds -n kube-system  anetd -o yaml | grep image
    

    O exemplo de saída a seguir mostra que você usaria gcr.io/gke-on-prem-release como Registry:

    image: gcr.io/gke-on-prem-release/cilium/cilium:v1.12.6-anthos1.15-gke4.2.7
    
  5. Receba o KubernetesVersion da tag de imagem para kube-apiserver no namespace do cluster de administrador:

    KUBECONFIG=ADMIN_KUBECONFIG
    kubectl get sts -n CLUSTER_NAME kube-apiserver -o yaml | grep image
    

    Substitua ADMIN_KUBECONFIG pelo arquivo kubeconfig do cluster de administrador e CLUSTER_NAME pelo nome do cluster de usuário.

    O exemplo de saída a seguir mostra que você usaria v1.26.2-gke.1001 como KubernetesVersion:

    image: gcr.io/gke-on-prem-release/kube-apiserver-amd64:v1.26.2-gke.1001
    imagePullPolicy: IfNotPresent
    

Preparar manifestos kube-proxy

Use os valores da seção anterior para criar e aplicar um manifesto YAML que implantará kube-proxy no cluster.

  1. Crie um manifesto chamado kube-proxy.yaml no editor que preferir:

    nano kube-proxy.yaml
    
  2. Copie e cole a seguinte definição de YAML:

    apiVersion: apps/v1
    kind: DaemonSet
    metadata:
      labels:
        k8s-app: kube-proxy
      name: kube-proxy
      namespace: kube-system
    spec:
      selector:
        matchLabels:
          k8s-app: kube-proxy
      template:
        metadata:
          annotations:
            scheduler.alpha.kubernetes.io/critical-pod: ""
          labels:
            k8s-app: kube-proxy
        spec:
          containers:
          - command:
            - kube-proxy
            - --v=2
            - --profiling=false
            - --iptables-min-sync-period=10s
            - --iptables-sync-period=1m
            - --oom-score-adj=-998
            - --ipvs-sync-period=1m
            - --ipvs-min-sync-period=10s
            - --cluster-cidr=ClusterCIDR
            env:
            - name: KUBERNETES_SERVICE_HOST
              value:APIServerAdvertiseAddress
            - name: KUBERNETES_SERVICE_PORT
              value: "APIServerPort"
            image: Registry/kube-proxy-amd64:KubernetesVersion
            imagePullPolicy: IfNotPresent
            name: kube-proxy
            resources:
              requests:
                cpu: 100m
                memory: 15Mi
            securityContext:
              privileged: true
            volumeMounts:
            - mountPath: /run/xtables.lock
              name: xtables-lock
            - mountPath: /lib/modules
              name: lib-modules
          imagePullSecrets:
          - name: RegistryCredentialsSecretName
          nodeSelector:
            kubernetes.io/os: linux
          hostNetwork: true
          priorityClassName: system-node-critical
          serviceAccount: kube-proxy
          serviceAccountName: kube-proxy
          tolerations:
          - effect: NoExecute
            operator: Exists
          - effect: NoSchedule
            operator: Exists
          volumes:
          - hostPath:
              path: /run/xtables.lock
              type: FileOrCreate
            name: xtables-lock
          - hostPath:
              path: /lib/modules
              type: DirectoryOrCreate
            name: lib-modules
      ---
      apiVersion: rbac.authorization.k8s.io/v1
      kind: ClusterRoleBinding
      metadata:
        name: system:kube-proxy
      roleRef:
        apiGroup: rbac.authorization.k8s.io
        kind: ClusterRole
        name: system:node-proxier
      subjects:
        - kind: ServiceAccount
          name: kube-proxy
          namespace: kube-system
      ---
      apiVersion: v1
      kind: ServiceAccount
      metadata:
        name: kube-proxy
        namespace: kube-system
    

    Nesse manifesto YAML, defina os seguintes valores:

    • APIServerAdvertiseAddress: o valor de KUBERNETES_SERVICE_HOST, como 21.1.4.119.
    • APIServerPort: o valor de KUBERNETES_SERVICE_PORT, como 443.
    • Registry: o prefixo da imagem Cilium, como gcr.io/gke-on-prem-release.
    • RegistryCredentialsSecretName: o nome do secret de extração da imagem, como private-registry-creds.
  3. Salve e feche o arquivo de manifesto no editor.

Preparar patch anetd

Crie e prepare uma atualização para anetd:

  1. Crie um manifesto chamado cilium-config-patch.yaml no editor que preferir:

    nano cilium-config-patch.yaml
    
  2. Copie e cole a seguinte definição de YAML:

    data:
      kube-proxy-replacement: "disabled"
      kube-proxy-replacement-healthz-bind-address: ""
      retry-kube-proxy-healthz-binding: "false"
      enable-host-reachable-services: "false"
    
  3. Salve e feche o arquivo de manifesto no editor.

Implantar kube-proxy e reconfigurar anetd

Aplique as alterações de configuração ao cluster. Crie backups da configuração atual antes de aplicar as alterações.

  1. Faça backup da configuração atual do anetd e do cilium-config:

    kubectl get ds -n kube-system anetd > anetd-original.yaml
    kubectl get cm -n kube-system cilium-config > cilium-config-original.yaml
    
  2. Aplique kube-proxy.yaml usando kubectl:

    kubectl apply -f kube-proxy.yaml
    
  3. Verifique se os pods estão Running:

    kubectl get pods -n kube-system -o wide | grep kube-proxy
    

    O exemplo de saída condensado a seguir mostra que os pods estão sendo executados corretamente:

    kube-proxy-f8mp9    1/1    Running   1 (4m ago)    [...]
    kube-proxy-kndhv    1/1    Running   1 (5m ago)    [...]
    kube-proxy-sjnwl    1/1    Running   1 (4m ago)    [...]
    
  4. Aplique um patch ao ConfigMap cilium-config usando kubectl:

    kubectl patch cm -n kube-system cilium-config --patch-file cilium-config-patch.yaml
    
  5. Editar anetd usando kubectl:

    kubectl edit ds -n kube-system anetd
    

    No editor que é aberto, edite a especificação de anetd. Insira o seguinte como o primeiro item em initContainers:

    - name: check-kube-proxy-rules
      image: Image
      imagePullPolicy: IfNotPresent
      command:
      - sh
      - -ec
      - |
        if [ "$KUBE_PROXY_REPLACEMENT" != "strict" ]; then
          kube_proxy_forward() { iptables -L KUBE-FORWARD; }
          until kube_proxy_forward; do sleep 2; done
        fi;
      env:
      - name: KUBE_PROXY_REPLACEMENT
        valueFrom:
          configMapKeyRef:
            key: kube-proxy-replacement
            name: cilium-config
            optional: true
      securityContext:
        privileged: true
    

    Substitua Image pela mesma imagem usada nos outros contêineres Cilium no DaemonSet anetd, como gcr.io/gke-on-prem-release/cilium/cilium:v1.12.6-anthos1.15-gke4.2.7.

  6. Salve e feche o arquivo de manifesto no editor.

  7. Para aplicar essas alterações, reinicie todos os nós do cluster. Para minimizar a interrupção, tente drenar cada nó antes da reinicialização. No entanto, os pods que usam volumes RWX podem ficar travados em um estado Terminating devido a montagens NFS interrompidas que bloqueiam o processo de drenagem.

    É possível forçar a exclusão de pods bloqueados e permitir que o nó seja drenado corretamente:

    kubectl delete pods -–force -–grace-period=0 --namespace POD_NAMESPACE POD_NAME
    

    Substitua POD_NAME pelo pod que você está tentando excluir e POD_NAMESPACE pelo namespace dele.

A seguir

Se precisar de mais ajuda, entre em contato com o Cloud Customer Care.