Fehlerbehebung bei Arbeitslastclustern mit Absturzdiagnose

In diesem Thema wird die Verwendung von Crash-Diagnostics (Crashd) zur Diagnose von instabilen oder nicht reagierenden Arbeitslastclustern unter Photon OS in Tanzu Kubernetes Grid mit einem eigenständigen Verwaltungscluster erläutert.

Informationen zur Verwendung von Crashd zur Diagnose von Arbeitslastclustern, die von einem vSphere with Tanzu-Supervisor bereitgestellt wurden, finden Sie unter Erfassen eines Diagnoseprotokollpakets aus einem Tanzu Kubernetes-Gastcluster in vSphere with Tanzu (How to collect a diagnostic log bundle from a Tanzu Kubernetes Guest Cluster on vSphere with Tanzu) in der VMware-Knowledge Base.

Überblick: Crashd

Crashd untersucht den Bootstrap-Arbeitslastcluster, den der tanzu cluster create-Prozess lokal mithilfe von kind erstellt, bevor der Cluster in der Cloud-Infrastruktur bereitgestellt wird.

Crashd ist ein Open Source-Projekt, das die Fehlerbehebung bei Kubernetes-Clustern vereinfacht.

Crashd verwendet eine in Starlark, einer Python-ähnlichen Sprache, geschriebene Skriptdatei, die mit Ihren Verwaltungs- oder Arbeitslastclustern interagiert, um Infrastruktur- und Clusterinformationen zu erfassen.

Crashd kann Diagnosen aus unterstützten Infrastrukturen erfassen, einschließlich:

  • AWS
  • Azure
  • vSphere

Crashd zieht die Ausgabe der Befehle, die vom Skript ausgeführt werden, heran und fügt sie zu einer tar-Datei hinzu. Die tar-Datei wird dann zur weiteren Analyse lokal gespeichert.

Tanzu Kubernetes Grid enthält signierte Binärdateien für Crashd und eine Diagnoseskriptdatei für Arbeitslastcluster, die auf Photon OS basieren.

Installieren oder Upgrade der Crashd-Binärdatei

Gehen Sie nach den folgenden Anweisungen vor, um crashd zu installieren oder ein Upgrade durchzuführen.

  1. Öffnen Sie die Download-Seite von Tanzu Kubernetes Grid und melden Sie sich mit Ihren Anmeldedaten für VMware Customer Connect an.
  2. Laden Sie Crashd für Ihre Plattform herunter.

    • Linux: crashd-linux-amd64-v0.3.7+vmware.5-4-g59b239d.tar.gz
    • macOS: crashd-darwin-amd64-v0.3.7+vmware.5-4-g59b239d.tar.gz
  3. Verwenden Sie den Befehl tar, um die Binärdatei für Ihre Plattform zu entpacken.

    • Linux:

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

      tar -xvf crashd-darwin-amd64-v0.3.7-vmware.6.tar.gz
      
  4. Im vorherigen Schritt wurde ein Verzeichnis mit dem Namen crashd und folgenden Dateien erstellt:

    crashd
    crashd/args
    crashd/diagnostics.crsh
    crashd/crashd-PLATFORM-amd64-v0.3.7+vmware.6
    
  5. Verschieben Sie die Binärdatei in den Ordner /usr/local/bin.

    • Linux:

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

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

Ausführen von Crashd auf Arbeitslastclustern mit Photon OS

Wenn Crashd ausgeführt wird, werden Argumentwerte aus einer Datei args an eine Skriptdatei diagnostics.crsh übergeben. Das Skript führt Befehle zum Extrahieren von Informationen aus, die bei der Diagnose von Problemen in Arbeitslastclustern unter Photon OS hilfreich sein können.

Voraussetzungen

Vor der Ausführung des Crashd-Skripts diagnostics.crsh muss Ihr lokaler Computer über die folgenden Programme im Ausführungspfad verfügen:

  • kubectl
  • scp
  • ssh

    Hinweis

    Zur Untersuchung von Problemen mit einem Bootstrap-Cluster benötigen Sie den lokal installierten Befehl kind (v0.7.0 oder höher).

Darüber hinaus müssen Sie die folgenden Schritte durchführen, damit Crashd ausgeführt werden kann:

  • Konfigurieren Sie Crashd mit einem privaten/öffentlichen SSH-Schlüsselpaar.
  • Stellen Sie sicher, dass Ihre Tanzu Kubernetes Grid-VMs für die Verwendung des öffentlichen SSH-Schlüssels konfiguriert sind.
  • Extrahieren Sie die Datei kubeconfig für den Verwaltungscluster mithilfe des Befehls tanzu mc kubeconfig get <management-cluster-name>.
  • Stellen Sie für ein einfacheres Setup sicher, dass sich die Datei kubeconfig, die Datei public-key, die Datei diagnostics.crsh und die Datei args im selben Speicherort befinden.
  • Löschen Sie alle lokalen kind-Cluster, mit Ausnahme des Clusters, der für die Bereitstellung des Arbeitslastclusters erstellt wurde, für den Sie eine Fehlerbehebung durchführen:

    • Führen Sie docker ps aus, um den aktuell ausgeführten kind-Cluster zu ermitteln.
    • Löschen Sie alle anderen kind-Cluster, indem Sie kind delete cluster --name CLUSTER-NAME ausführen.

Konfigurieren von Crashd

  1. Navigieren Sie zu dem Speicherort, an dem Sie das Crashd-Paket heruntergeladen und entpackt haben.

  2. Überschreiben Sie in einem Texteditor die vorhandene Argumentdatei args mit dem folgenden Code. Diese Datei enthält die Schlüssel/Wert-Paare, die an das CrashD-Skript übergeben werden sollen:

    # ######################################################
    # 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. Zeichnen Sie den lokalen Pfad der privaten SSH-Schlüsseldatei auf. Wenn Sie nicht bereits über ein SSH-Schlüsselpaar verfügen oder ein neues Schlüsselpaar generieren möchten, führen Sie ssh-keygen wie unter Erstellen eines SSH-Schlüsselpaars beschrieben aus. Beispiel:

    ssh-keygen -t rsa -b 4096 -C "email@example.com"

    Geben Sie bei entsprechender Aufforderung einen lokalen Pfad für den Dateispeicherort ein. Informationen zum Erstellen der SSH-Schlüsselpaare.

  4. Legen Sie die folgenden Argumente in der Datei args fest:

    • target: Setzen Sie diesen Wert auf:
      • bootstrap, um einen lokalen eigenständigen Bootstrap-Verwaltungscluster zu diagnostizieren
      • mgmt, um einen bereitgestellten eigenständigen Verwaltungscluster zu diagnostizieren
      • workload, um einen oder mehrere Arbeitslastcluster zu diagnostizieren
    • infra: Die zugrunde liegende Infrastruktur für Ihren Cluster: aws, azure oder vsphere.
    • workdir: Der Speicherort, an dem Dateien erfasst werden.
    • ssh_user: Der SSH-Benutzer, der für den Zugriff auf Clustermaschinen verwendet wird. Bei Clustern, die auf vSphere ausgeführt werden, lautet der Benutzername capv.
    • ssh_pk_file: Der Pfad Ihrer privaten SSH-Schlüsseldatei.
    • mgmt_cluster_config Der Pfad der kubeconfig-Datei für den Verwaltungscluster.
  5. Um Arbeitslastcluster zu diagnostizieren, heben Sie die Auskommentierung auf und legen Sie zusätzlich zu den oben aufgeführten Argumenten Folgendes fest:

    • workload_clusters: Eine kommagetrennte Liste mit Namen von Arbeitslastclustern, aus denen Diagnoseinformationen erfasst werden sollen.
    • workload_cluster_ns: Der Namespace, in dem die secrets/WORKLOAD-CLUSTER-NAME-kubeconfig im Verwaltungscluster erstellt wird.

Ausführen von Crashd

  1. Erstellen Sie eine Crashd-Skriptdatei diagnostics.crsh, die den Code in der Diagnosedatei diagnostics.crsh unten enthält.

  2. Führen Sie den Befehl crashd in dem Speicherort aus, in dem sich die Skriptdatei diagnostics.crsh und die Argumentdatei args befinden.

    crashd run --args-file args diagnostics.crsh
    
  3. Überwachen Sie optional die Crashd-Ausgabe. Standardmäßig wird der Befehl crashd bis zum Abschluss im Hintergrund ausgeführt. Sie können jedoch das Flag --debug verwenden, um Protokollmeldungen ähnlich den folgenden auf dem Bildschirm anzuzeigen:

    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
    

Diagnosedatei diagnostics.crsh

Überschreiben Sie im Download des CrashD-Pakets die vorhandene Datei diagnostics.crsh mit dem folgenden Code als Skript, das an den Befehl crashd run übergeben werden soll:

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