VMware용 GKE NFS 및 DataPlane v2 문제 해결

이 문서에서는 볼륨 또는 포드가 중단된 NFS 마운트에 문제가 있고 DataPlane v2가 사용 설정된 클러스터를 만든 경우 VMware용 GKE에 대한 수동 절차를 자세히 설명합니다. 이 문제에 취약한 스토리지 드라이버로 구동되는 ReadWriteMany 볼륨을 사용하는 워크로드가 있는 경우에 다음과 같은 문제가 발생할 수 있습니다(국한되지는 않음).

  • Robin.io
  • Portworx(sharedv4 서비스 볼륨)
  • csi-nfs

일부 스토리지 아키텍처의 NFS 마운트는 Kubernetes Service(ClusterIP) 및 DataPlane v2를 통해 엔드포인트에 연결되면 중단될 수 있습니다. 이 동작은 Linux 커널 소켓 코드가 Cilium의 eBPF 프로그램과 상호작용하는 방식에 제한이 있기 때문입니다. 사용되지 않는 NFS 마운트를 마운트 해제할 수 없으므로 컨테이너가 I/O에서 차단되거나 컨테이너를 종료하지 못할 수 있습니다.

Ondat, Robin.io 또는 Portworx와 같은 소프트웨어 정의 또는 하이퍼컨버지드 스토리지 솔루션을 포함하여 Kubernetes 노드에서 실행되는 NFS 서버에 호스팅되는 RWX 스토리지를 사용하는 경우에 이 문제가 발생할 수 있습니다.

기존 클러스터 구성 검토

클러스터에서 기존 구성 값 일부를 가져옵니다. 다음 섹션에서는 다음 단계의 값을 사용하여 kube-proxy 매니페스트를 만듭니다.

  1. cm/cilium-config에서 ClusterCIDR을 가져옵니다.

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

    다음 출력 예시에서는 192.168.0.0/16ClusterCIDR로 사용하는 것을 보여줍니다.

    ipv4-native-routing-cidr: 192.168.0.0/16
    native-routing-cidr: 192.168.0.0/16
    
  2. anetd DaemonSet에서 APIServerAdvertiseAddressAPIServerPort를 가져옵니다.

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

    다음 출력 예시에서는 21.1.4.119APIServerAdvertiseAddress로, 443APIServerPort로 사용하는 것을 보여줍니다.

    - name: KUBERNETES_SERVICE_HOST
      value: 21.1.4.119
    - name: KUBERNETES_SERVICE_PORT
      value: "443"
    
  3. anetd DaemonSet에서 RegistryCredentialsSecretName을 가져옵니다.

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

    다음 출력 예시에서는 private-registry-credsRegistryCredentialsSecretName으로 사용하는 것을 보여줍니다.

    imagePullSecrets:
      - name: private-registry-creds
    
  4. anetd DameonSet에서 Registry를 가져옵니다.

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

    다음 출력 예시에서는 gcr.io/gke-on-prem-releaseRegistry로 사용하는 것을 보여줍니다.

    image: gcr.io/gke-on-prem-release/cilium/cilium:v1.12.6-anthos1.15-gke4.2.7
    
  5. 관리자 클러스터의 클러스터 네임스페이스에 있는 kube-apiserver 이미지 태그에서 KubernetesVersion을 가져옵니다.

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

    ADMIN_KUBECONFIG를 관리자 클러스터의 kubeconfig 파일로, CLUSTER_NAME을 사용자 클러스터 이름으로 바꿉니다.

    다음 출력 예시에서는 v1.26.2-gke.1001KubernetesVersion으로 사용하는 것을 보여줍니다.

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

kube-proxy 매니페스트 준비

이전 섹션에서 가져온 값을 사용하여 kube-proxy를 클러스터에 배포하는 YAML 매니페스트를 만들고 적용합니다.

  1. 원하는 편집기에서 kube-proxy.yaml이라는 매니페스트를 만듭니다.

    nano kube-proxy.yaml
    
  2. 다음 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
    

    이 YAML 매니페스트에서 다음 값을 설정합니다.

    • APIServerAdvertiseAddress: KUBERNETES_SERVICE_HOST 값입니다(예: 21.1.4.119).
    • APIServerPort: KUBERNETES_SERVICE_PORT 값입니다(예: 443).
    • Registry: Cilium 이미지의 프리픽스입니다(예: gcr.io/gke-on-prem-release).
    • RegistryCredentialsSecretName: 이미지 가져오기 보안 비밀 이름입니다(예: private-registry-creds).
  3. 편집기에서 매니페스트 파일을 저장하고 닫습니다.

anetd 패치 준비

anetd의 업데이트를 만들고 준비합니다.

  1. 원하는 편집기에서 cilium-config-patch.yaml이라는 매니페스트를 만듭니다.

    nano cilium-config-patch.yaml
    
  2. 다음 YAML 정의를 복사하여 붙여넣습니다.

    data:
      kube-proxy-replacement: "disabled"
      kube-proxy-replacement-healthz-bind-address: ""
      retry-kube-proxy-healthz-binding: "false"
      enable-host-reachable-services: "false"
    
  3. 편집기에서 매니페스트 파일을 저장하고 닫습니다.

kube-proxy 배포 및 anetd 재구성

클러스터에 구성 변경사항을 적용합니다. 변경사항을 적용하기 전에 기존 구성의 백업을 만듭니다.

  1. anetdcilium-config의 현재 구성을 백업합니다.

    kubectl get ds -n kube-system anetd > anetd-original.yaml
    kubectl get cm -n kube-system cilium-config > cilium-config-original.yaml
    
  2. kubectl을 사용하여 kube-proxy.yaml을 적용합니다.

    kubectl apply -f kube-proxy.yaml
    
  3. 포드가 Running인지 확인합니다.

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

    다음 출력 예시에서는 포드가 올바르게 실행되고 있음임을 보여줍니다.

    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. kubectl을 사용하여 cilium-config ConfigMap을 패치합니다.

    kubectl patch cm -n kube-system cilium-config --patch-file cilium-config-patch.yaml
    
  5. 다음과 같이 kubectl을 사용하여 anetd를 수정합니다.

    kubectl edit ds -n kube-system anetd
    

    열린 편집기에서 anetd 사양을 수정합니다. 다음을 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
    

    Imageanetd DaemonSet의 다른 Cilium 컨테이너(예: gcr.io/gke-on-prem-release/cilium/cilium:v1.12.6-anthos1.15-gke4.2.7)에서 사용하는 이미지와 동일한 이미지로 바꿉니다.

  6. 편집기에서 매니페스트 파일을 저장하고 닫습니다.

  7. 이 변경사항을 적용하려면 클러스터의 모든 노드를 재부팅합니다. 중단을 최소화하기 위해 재부팅 전에 각 노드를 드레이닝할 수 있습니다. 하지만 드레이닝 프로세스를 차단하는 NFS 마운트가 손상되어 RWX 볼륨을 사용하는 포드가 Terminating 상태로 중단될 수 있습니다.

    차단된 포드를 강제로 삭제하고 노드가 올바르게 드레이닝되도록 허용할 수 있습니다.

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

    POD_NAME을 삭제하려는 포드로, POD_NAMESPACE를 해당 네임스페이스로 바꿉니다.

다음 단계

추가 지원이 필요한 경우 Cloud Customer Care에 문의하세요.