Solucionar problemas de clústeres de carga de trabajo con diagnósticos de bloqueo

En este tema se explica cómo utilizar Crash Diagnostics (Crashd) para diagnosticar clústeres de carga de trabajo inestables o que no responden en función de Photon OS en Tanzu Kubernetes Grid con un clúster de administración independiente.

Para obtener información sobre cómo utilizar Crashd para diagnosticar clústeres de carga de trabajo implementados por un supervisor vSphere with Tanzu, consulte Cómo recopilar un paquete de registros de diagnóstico de un clúster invitado Tanzu Kubernetes en vSphere with Tanzu en la base de conocimientos de VMware.

Descripción general: Crashd

Crashd examina el clúster de carga de trabajo de arranque que el proceso tanzu cluster create crea localmente mediante kind antes de implementar el clúster en la infraestructura de nube.

Crashd es un proyecto de código abierto que facilita la solución de problemas con clústeres de Kubernetes.

Crashd utiliza un archivo de script escrito en Starlark, un lenguaje similar a Python, que interactúa con los clústeres de carga de trabajo o de administración para recopilar información de infraestructura y clúster.

Crashd puede recopilar diagnósticos de infraestructuras compatibles, entre las que se incluyen:

  • AWS
  • Azure
  • vSphere

Crashd toma los resultados de los comandos que ejecuta el script y los agrega a un archivo tar. A continuación, el archivo tar se guarda localmente para un análisis posterior.

Tanzu Kubernetes Grid incluye archivos binarios firmados para Crashd y un archivo de script de diagnóstico para clústeres de carga de trabajo Photon OS.

Instalar o actualizar el archivo binario Crashd

Para instalar o actualizar crashd, siga las instrucciones que aparecen a continuación.

  1. Vaya a VMware Customer Connect e inicie sesión con las credenciales de VMware Customer Connect.
  2. Vaya a Descargas de productos de Tanzu Kubernetes Grid v2.3.1.
  3. Descargue Crashd para su plataforma.

    • Linux: crashd-linux-amd64-v0.3.7+vmware.7-4-g59b239d.tar.gz
    • macOS: crashd-darwin-amd64-v0.3.7+vmware.7-4-g59b239d.tar.gz
  4. Utilice el comando tar para desempaquetar el archivo binario de la plataforma.

    • Linux:

      tar -xvf crashd-linux-amd64-v0.3.7-vmware.7.tar.gz
      
    • macOS:

      tar -xvf crashd-darwin-amd64-v0.3.7-vmware.7.tar.gz
      
  5. El paso anterior crea un directorio denominado crashd con los siguientes archivos:

    crashd
    crashd/args
    crashd/diagnostics.crsh
    crashd/crashd-PLATFORM-amd64-v0.3.7+vmware.7
    
  6. Mueva el archivo binario a la carpeta /usr/local/bin.

    • Linux:

      mv ./crashd/crashd-linux-amd64-v0.3.7+vmware.7 /usr/local/bin/crashd
      
    • macOS:

      mv ./crashd/crashd-darwin-amd64-v0.3.7+vmware.7 /usr/local/bin/crashd
      

Ejecutar Crashd en clústeres de carga de trabajo de Photon OS

Cuando se ejecuta Crashd, toma los valores de argumento de un archivo args y los envía a un archivo de script diagnostics.crsh. El script ejecuta comandos para extraer información que puede ayudar a diagnosticar problemas en clústeres de carga de trabajo de Photon OS.

Requisitos previos

Antes de ejecutar el script de Crashd diagnostics.crsh, la máquina local debe tener los siguientes programas en su ruta de ejecución:

  • kubectl
  • scp
  • ssh

    Nota

    Al investigar problemas con un clúster de arranque, necesitará el comando kind (v0.7.0 o superior) instalado localmente.

Además, antes de poder ejecutar Crashd, debe seguir estos pasos:

  • Configure Crashd con un par de claves privada/pública ssh.
  • Asegúrese de que las máquinas virtuales de Tanzu Kubernetes Grid estén configuradas para utilizar la clave pública SSH.
  • Extraiga el archivo kubeconfig para el clúster de administración mediante el comando tanzu mc kubeconfig get <management-cluster-name>.
  • Para una configuración más sencilla, asegúrese de que el archivo kubeconfig, public-key, el archivo diagnostics.crsh y el archivo args se encuentren en la misma ubicación.
  • Elimine cualquier clúster kind local que no sea el que se creó para implementar el clúster de carga de trabajo que está solucionando:

    • Ejecute docker ps para identificar el clúster de kind.
    • Elimine cualquier otro clúster kind ejecutando kind delete cluster --name CLUSTER-NAME

Configurar Crashd

  1. Desplácese hasta la ubicación en la que descargó y desempaquetó el paquete Crashd.

  2. En un editor de texto, sobrescriba el archivo de argumentos existente args con el siguiente código. Este archivo contiene los pares de clave/valor que se transferirán al script CrashD:

    # ######################################################
    # Crashd script argument file
    #
    # This file defines CLI argument values that are passed
    # Crashd when running scripts for troubleshooting TKG
    # clusters.
    # ######################################################
    
    # target: specifies cluster to target.
    # Valid targets are: {bootstrap, mgmt, workload}
    target=mgmt
    
    # infra: the underlying infrastructure used by the TKG cluster.
    # Valid values are: {vsphere, aws, azure}
    infra=vsphere
    
    # workdir: a local directory where collected files are staged.
    workdir=./workdir
    
    # ssh_user: the user ID used for SSH connections to cluster nodes.
    ssh_user=capv
    
    # ssh_pk_file: the path to the private key file created to SSH
    # into cluster nodes.
    ssh_pk_file=./capv.pem
    
    # ######################################################
    # Management Cluster
    # The following arguments are used to collect information
    # from a management cluster or named workload clusters.
    # ######################################################
    
    # mgmt_cluster_config: the kubeconfig file path for the management cluster.
    mgmt_cluster_config=./tkg_cluster_config
    
    # ######################################################
    # Workload Cluster
    # The following arguments are used to collect information
    # from one or more workload clusters that are managed
    # by the management cluster configured above.
    # ######################################################
    
    # workload_clusters: a comma separated list of workload cluster names
    # [uncomment below]
    #workload_clusters=tkg-cluster-wc-498
    
    # workload_cluster_ns: the namespace where the workload cluster
    # is hosted in the management plane.
    # Note: it's actually the namespace in which the secrets/${workload_cluster_name}-kubeconfig
    # is created in the management cluster.
    # [uncomment below]
    #workload_cluster_ns=default
    
  3. Registre la ruta local en un archivo de clave privada SSH. Si aún no tiene un par de claves SSH o desea generar uno nuevo, ejecute ssh-keygen, como se describe en Crear un par de claves SSH. Por ejemplo:

    ssh-keygen -t rsa -b 4096 -C "[email protected]"

    Cuando se le solicite, introduzca una ruta local para la ubicación del archivo. Para obtener información sobre cómo crear los pares de claves SSH.

  4. Configure los siguientes argumentos en el archivo args:

    • target: Establezca este valor en:
      • bootstrap para diagnosticar un clúster de administración independiente de arranque local
      • mgmt para diagnosticar un clúster de administración independiente implementado
      • workload para diagnosticar uno o varios clústeres de carga de trabajo
    • infra: La infraestructura subyacente para el clúster: aws, azure o vsphere.
    • workdir: La ubicación en la que se recopilan los archivos.
    • ssh_user: El usuario de SSH que se utiliza para acceder a las máquinas del clúster. Para los clústeres que se ejecutan en vSphere, el nombre de usuario es capv.
    • ssh_pk_file: La ruta de acceso al archivo de clave privada SSH.
    • mgmt_cluster_config La ruta del archivo kubeconfig para el clúster de administración.
  5. Para diagnosticar los clústeres de carga de trabajo, quite la marca de comentario y establezca los siguientes argumentos además de los argumentos enumerados anteriormente:

    • workload_clusters: Una lista separada por comas de nombres de clústeres de carga de trabajo desde los que recopilar información de diagnósticos.
    • workload_cluster_ns: El espacio de nombres en el que secrets/WORKLOAD-CLUSTER-NAME-kubeconfig se crea en el clúster de administración.

Ejecutar Crashd

  1. Cree un archivo de script Crashd diagnostics.crsh que contenga el código en el Archivo de diagnóstico diagnostics.crsh a continuación.

  2. Ejecute el comando crashd desde la ubicación en la que se encuentra el archivo de script diagnostics.crsh y el archivo de argumento args.

    crashd run --args-file args diagnostics.crsh
    
  3. De forma opcional, puede supervisar la salida de Crashd. De forma predeterminada, el comando crashd se ejecuta de forma silenciosa hasta que finaliza. Sin embargo, puede usar la marca --debug para ver mensajes de registro en la pantalla similares a los siguientes:

    crashd run --debug --args-file args diagnostics.crsh
    
    DEBU[0003] creating working directory ./workdir/tkg-kind-12345
    DEBU[0003] kube_capture(what=objects)
    DEBU[0003] Searching in 20 groups
    ...
    DEBU[0015] Archiving [./workdir/tkg-kind-12345] in bootstrap.tkg-kind-12345.diagnostics.tar.gz
    DEBU[0015] Archived workdir/tkg-kind-12345/kind-logs/docker-info.txt
    DEBU[0015] Archived workdir/tkg-kind-12345/kind-logs/tkg-kind-12345-control-plane/alternatives.log
    DEBU[0015] Archived workdir/tkg-kind-12345/kind-logs/tkg-kind-12345-control-plane/containerd.log
    

Archivo de diagnóstico diagnostics.crsh

En la descarga del paquete CrashD, sobrescriba el archivo diagnostics.crsh existente con el siguiente código, como el script para pasar al comando crashd run:

def capture_node_diagnostics(nodes):
    capture(cmd="sudo df -i", resources=nodes)
    capture(cmd="sudo crictl info", resources=nodes)
    capture(cmd="df -h /var/lib/containerd", resources=nodes)
    capture(cmd="sudo systemctl status kubelet", resources=nodes)
    capture(cmd="sudo systemctl status containerd", resources=nodes)
    capture(cmd="sudo journalctl -xeu kubelet", resources=nodes)
    capture(cmd="sudo journalctl -xeu containerd", resources=nodes)
    capture(cmd="sudo cat /var/log/cloud-init-output.log", resources=nodes)
    capture(cmd="sudo cat /var/log/cloud-init.log", resources=nodes)
def capture_windows_node_diagnostics(nodes):
    capture(cmd="Get-CimInstance -ClassName Win32_LogicalDisk", file_name="disk_info.out", resources=nodes)
    capture(cmd="(Get-ItemProperty -Path c:\\windows\\system32\\hal.dll).VersionInfo.FileVersion",file_name="windows_version_info.out", resources=nodes)
    capture(cmd="cat C:\\k\\StartKubelet.ps1 ; cat C:\\var\\lib\\kubelet\\kubeadm-flags.env", resources=nodes)
    capture(cmd="Get-Service Kubelet | select * ", resources=nodes)
    capture(cmd="Get-Service Containerd | select * ", resources=nodes)
    capture(cmd="Get-Service ovs* | select * ", resources=nodes)
    capture(cmd="Get-Service antrea-agent | select * ", resources=nodes)
    capture(cmd="Get-Service kube-proxy | select * ", resources=nodes)
    capture(cmd="Get-Service Kubelet | select * ", resources=nodes)
    capture(cmd="Get-HNSNetwork", resources=nodes)
    capture(cmd="& 'c:\\Program Files\\containerd\\crictl.exe' -r 'npipe:////./pipe/containerd-containerd' info", resources=nodes)
    capture(cmd="Get-MpPreference | select ExclusionProcess", resources=nodes)
    capture(cmd="cat c:\\var\\log\\kubelet\\kubelet.exe.INFO", resources=nodes)
    capture(cmd="cat c:\\var\\log\\antrea\\antrea-agent.exe.INFO", resources=nodes)
    capture(cmd="cat c:\\var\\log\\kube-proxy\\kube-proxy.exe.INFO", resources=nodes)
    capture(cmd="cat 'c:\\Program Files\\Cloudbase Solutions\\Cloudbase-Init\\log\\cloudbase-init-unattend.log'", resources=nodes)
    capture(cmd="cat 'c:\\Program Files\\Cloudbase Solutions\\Cloudbase-Init\\log\\cloudbase-init.log'", resources=nodes)
    copy_from(path="C:\\Windows\\System32\\Winevt\\Logs\\System.evtx", resources=nodes)
    copy_from(path="C:\\Windows\\System32\\Winevt\\Logs\\Application.evtx", resources=nodes)
    copy_from(path="c:\\openvswitch\\var\\log\\openvswitch\\ovs-vswitchd.log", resources=nodes)
    copy_from(path="c:\\openvswitch\\var\\log\\openvswitch\\ovsdb-server.log", resources=nodes)
# fetches a suitable capi provider, for either capa or others (capv/capz),
# to be used for enumerating cluster machines
def fetch_provider(iaas, workload_cluster_name, ssh_cfg, kube_cfg, namespace, filter_labels):
    # workaround: vsphere and azure use same provider as they work similarly (see issue #162)
    if iaas == "vsphere" or iaas == "azure":
        provider = capv_provider(
            workload_cluster=workload_cluster_name,
            namespace=namespace,
            ssh_config=ssh_cfg,
            mgmt_kube_config=kube_cfg,
            labels=filter_labels
        )
    else:
        provider = capa_provider(
            workload_cluster=workload_cluster_name,
            namespace=namespace,
            ssh_config=ssh_cfg,
            mgmt_kube_config=kube_cfg,
            labels=filter_labels
        )
    return provider
# retrieves linux management provider for linux nodes
def fetch_mgmt_provider_linux(infra, ssh_cfg, kube_cfg, ns):
    return fetch_provider(infra, '', ssh_cfg, kube_cfg, ns, ["kubernetes.io/os=linux"])
# retrieves windows mgmt provider for windows nodes
def fetch_mgmt_provider_windows(infra, ssh_cfg, kube_cfg, ns):
    return fetch_provider(infra, '', ssh_cfg, kube_cfg, ns, ["kubernetes.io/os=windows"])
# retrieves linux workload provider for linux nodes
def fetch_workload_provider_linux(infra, wc_cluster, ssh_cfg, kube_cfg, ns):
    return fetch_provider(infra, wc_cluster, ssh_cfg, kube_cfg, ns, ["kubernetes.io/os=linux"])
# retrieves windows workload provider for windodws nodes
def fetch_workload_provider_windows(infra, wc_cluster, ssh_cfg, kube_cfg, ns):
    return fetch_provider(infra, wc_cluster, ssh_cfg, kube_cfg, ns, ["kubernetes.io/os=windows"])
def diagnose_mgmt_cluster(infra):
    # validation
    args.ssh_user
    args.ssh_pk_file
    args.mgmt_cluster_config
    if len(infra) == 0:
        print("Infra argument not provided")
        return
    wd = "{}/tkg-mgmt-cluster".format(args.workdir)
    conf = crashd_config(workdir=wd)
    ssh_conf = ssh_config(username=args.ssh_user, private_key_path=args.ssh_pk_file)
    kube_conf = kube_config(path=args.mgmt_cluster_config)
    # fetch linux mgmt node diagnostics
    mgmt_provider_linux = fetch_mgmt_provider_linux(infra, ssh_conf, kube_conf, '')
    lin_nodes = resources(provider=mgmt_provider_linux)
    capture_node_diagnostics(lin_nodes)
    # fetch win mgmt node diagnostics
    mgmt_provider_win = fetch_mgmt_provider_windows(infra, ssh_conf, kube_conf, '')
    win_nodes = resources(provider=mgmt_provider_win)
    if len(win_nodes) > 0:
        capture_windows_node_diagnostics(win_nodes)
    #add code to collect pod info from cluster
    set_defaults(kube_config(capi_provider = mgmt_provider_linux))
    pods_ns=[
        "capi-kubeadm-bootstrap-system",
        "capi-kubeadm-control-plane-system",
        "capi-system",
        "capi-webhook-system",
        "cert-manager",
        "tkg-system",
        "kube-system",
        "tkr-system",
        "capa-system",
        "capv-system",
        "capz-system",
    ]
    if infra == "vsphere":
        pods_ns.append("tkg-system-networking")
        pods_ns.append("avi-system")
    kube_capture(what="logs", namespaces=pods_ns)
    kube_capture(what="objects", kinds=["pods", "services"], namespaces=pods_ns)
    kube_capture(what="objects", kinds=["deployments", "replicasets"], groups=["apps"], namespaces=pods_ns)
    kube_capture(what="objects", kinds=["apps"], groups=["kappctrl.k14s.io"], namespaces=["tkg-system"])
    kube_capture(what="objects", kinds=["tanzukubernetesreleases"], groups=["run.tanzu.vmware.com"])
    kube_capture(what="objects", kinds=["configmaps"], namespaces=["tkr-system"])
    kube_capture(what="objects", categories=["cluster-api"])
    kube_capture(what="objects", groups=["ipam.cluster.x-k8s.io"])
    if infra == "vsphere":
        kube_capture(what="objects", kinds=["akodeploymentconfigs"])
    archive(output_file="tkg-mgmt.diagnostics.tar.gz", source_paths=[conf.workdir])
def diagnose_workload_cluster(infra, name):
    # validation
    args.infra
    args.ssh_user
    args.ssh_pk_file
    args.mgmt_cluster_config
    workload_ns = args.workload_cluster_ns
    if len(infra) == 0:
        print("Infra argument not provided")
        return
    wd = "{}/{}".format(args.workdir, name)
    conf = crashd_config(workdir=wd)
    ssh_conf = ssh_config(username=args.ssh_user, private_key_path=args.ssh_pk_file)
    kube_conf = kube_config(path=args.mgmt_cluster_config)
    # fetch linux workload node diagnostics
    wc_provider_linux = fetch_workload_provider_linux(infra, name, ssh_conf, kube_conf, workload_ns)
    lin_nodes = resources(provider=wc_provider_linux)
    capture_node_diagnostics(lin_nodes)
    # fetch win workload node diagnostics
    wc_provider_win = fetch_workload_provider_windows(infra, name, ssh_conf, kube_conf, workload_ns)
    win_nodes = resources(provider=wc_provider_win)
    if len(win_nodes) > 0:
        capture_windows_node_diagnostics(win_nodes)
    #add code to collect pod info from cluster
    set_defaults(kube_config(capi_provider = wc_provider_linux))
    pods_ns=["default", "kube-system", "tkg-system"]
    if infra == "vsphere":
        pods_ns.append("tkg-system-networking")
        pods_ns.append("avi-system")
    kube_capture(what="logs", namespaces=pods_ns)
    kube_capture(what="objects", kinds=["pods", "services"], namespaces=pods_ns)
    kube_capture(what="objects", kinds=["deployments", "replicasets"], groups=["apps"], namespaces=pods_ns)
    kube_capture(what="objects", kinds=["apps"], groups=["kappctrl.k14s.io"], namespaces=["tkg-system"])
    if infra == "vsphere":
        kube_capture(what="objects", kinds=["akodeploymentconfigs"])
    archive(output_file="{}.diagnostics.tar.gz".format(name), source_paths=[conf.workdir])
# extract diagnostic info from local kind boostrap cluster
def diagnose_bootstrap_cluster():
    p = prog_avail_local("kind")
    if p == "":
        print("Error: kind is not available")
        return
    clusters=get_tkg_bootstrap_clusters()
    if len(clusters) == 0:
        print("No tkg-kind bootstrap cluster found")
        return
    pod_ns=[
        "caip-in-cluster-system",
        "capi-kubeadm-bootstrap-system",
        "capi-kubeadm-control-plane-system",
        "capi-system",
        "capi-webhook-system",
        "capv-system",
        "capa-system",
        "capz-system",
        "cert-manager",
        "tkg-system",
        "tkg-system-networking",
        "avi-system",
    ]
    # for each tkg-kind cluster:
    #  - capture kind logs, export kubecfg, and api objects
    for kind_cluster in clusters:
        wd = "{}/{}".format(args.workdir, kind_cluster)
        run_local("kind export logs --name {} {}/kind-logs".format(kind_cluster, wd))
        kind_cfg = capture_local(
            cmd="kind get kubeconfig --name {0}".format(kind_cluster),
            workdir="./",
            file_name="{}.kubecfg".format(kind_cluster)
        )

        conf = crashd_config(workdir=wd)
        set_defaults(kube_config(path=kind_cfg))
        kube_capture(what="objects", kinds=["pods", "services"], namespaces=pod_ns)
        kube_capture(what="objects", kinds=["deployments", "replicasets"], groups=["apps"], namespaces=pod_ns)
        kube_capture(what="objects", categories=["cluster-api"])
        kube_capture(what="objects", kinds=["akodeploymentconfigs"])
        archive(output_file="bootstrap.{}.diagnostics.tar.gz".format(kind_cluster), source_paths=[conf.workdir])
# return tkg clusters in kind (tkg-kind-xxxx)
def get_tkg_bootstrap_clusters():
    clusters = run_local("kind get clusters").split('\n')
    result = []
    for cluster in clusters:
        if cluster.startswith("tkg-kind"):
            result.append(cluster)

    return result
def check_prereqs():
    # validate args
    args.workdir
    p = prog_avail_local("ssh")
    if p == "":
        print("Error: ssh is not available")
        return False

    p = prog_avail_local("scp")
    if p == "":
        print("Error: scp is not available")
        return False

    p = prog_avail_local("kubectl")
    if p == "":
        print("Error: kubectl is not available")
        return False

    return True

def diagnose(target, infra):
    # validation
    if not check_prereqs():
        print("Error: One or more prerequisites are missing")
        return
    # run diagnostics
    if target == "bootstrap":
        diagnose_bootstrap_cluster()
    elif target == "mgmt":
        diagnose_mgmt_cluster(infra)
    elif target == "workload":
        for name in args.workload_clusters.split(","):
            diagnose_workload_cluster(infra, name)
    else:
        print("Error: unknown target {}".format(target))
diagnose(args.target, args.infra)
check-circle-line exclamation-circle-line close-line
Scroll to top icon