Dépannage des clusters de charge de travail avec diagnostics d'incident

Cette rubrique explique comment utiliser Crash Diagnostics (Crashd) pour diagnostiquer les clusters de charge de travail instables ou ne répondant pas basés sur Photon OS dans Tanzu Kubernetes Grid avec un cluster de gestion autonome.

Pour savoir comment utiliser Crashd pour diagnostiquer les clusters de charge de travail déployés par un superviseur vSphere with Tanzu, reportez-vous à la section Comment collecter un bundle de journaux de diagnostics à partir d'un cluster invité Tanzu Kubernetes sur vSphere with Tanzu dans la base de connaissances VMware.

Présentation : Crashd

Crashd examine le cluster de charge de travail de démarrage que le processus tanzu cluster create crée localement à l'aide de kind avant de déployer le cluster sur l'infrastructure du cloud.

Crashd est un projet open source qui facilite le dépannage des problèmes de clusters Kubernetes.

Crashd utilise un fichier de script écrit en Starlark, un langage de type Python, qui interagit avec vos clusters de gestion ou de charge de travail pour collecter des informations sur l'infrastructure et le cluster.

Crashd peut collecter des diagnostics à partir des infrastructures prises en charge, notamment :

  • AWS
  • Azure
  • vSphere

Crashd récupère la sortie à partir des commandes exécutées par le script et ajoute la sortie à un fichier tar. Le fichier tar est ensuite enregistré localement pour une analyse plus approfondie.

Tanzu Kubernetes Grid inclut des fichiers binaires signés pour Crashd et un fichier de script de diagnostics pour les clusters de charge de travail Photon OS.

Installer ou mettre à niveau le fichier binaire Crashd

Pour installer ou mettre à niveau crashd, suivez les instructions ci-dessous.

  1. Accédez à la page de téléchargements de Tanzu Kubernetes Grid et connectez-vous avec vos informations d'identification VMware Customer Connect.
  2. Téléchargez Crashd pour votre plate-forme.

    • 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. Utilisez la commande tar pour décompresser le fichier binaire pour votre plate-forme.

    • 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. L'étape précédente crée un répertoire nommé crashd avec les fichiers suivants :

    crashd
    crashd/args
    crashd/diagnostics.crsh
    crashd/crashd-PLATFORM-amd64-v0.3.7+vmware.6
    
  5. Déplacez le fichier binaire dans le dossier /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
      

Exécuter Crashd sur des clusters de charge de travail Photon OS

Lorsque Crashd s'exécute, il récupère les valeurs d'argument d'un fichier args et les transmet à un fichier de script, diagnostics.crsh. Le script exécute des commandes pour extraire des informations qui peuvent aider à diagnostiquer les problèmes sur les clusters de charge de travail Photon OS.

Conditions requises

Avant d'exécuter le script Crashd diagnostics.crsh, votre machine locale doit avoir les programmes suivants sur son chemin d'exécution :

  • kubectl
  • scp
  • ssh

    Remarque

    Lors de l'examen des problèmes liés à un cluster de démarrage, vous aurez besoin de la commande kind (v0.7.0 ou version ultérieure) installée localement.

De plus, avant de pouvoir exécuter Crashd, vous devez suivre les étapes suivantes :

  • Configurez Crashd avec une paire de clé privée/clé publique SSH.
  • Assurez-vous que vos machines virtuelles Tanzu Kubernetes Grid sont configurées pour utiliser votre clé publique SSH.
  • Extrayez le fichier kubeconfig pour le cluster de gestion à l'aide de la commande tanzu mc kubeconfig get <management-cluster-name>.
  • Pour une configuration plus simple, assurez-vous que kubeconfig, le fichier public-key, le fichier diagnostics.crsh et le fichier args se trouvent au même emplacement.
  • Supprimez tous les clusters kind locaux autres que celui qui a été créé pour déployer le cluster de charge de travail que vous dépannez :

    • Exécutez docker ps pour identifier le cluster kind en cours d'exécution.
    • Supprimez tout autre cluster kind en exécutant kind delete cluster --name CLUSTER-NAME.

Configurer Crashd

  1. Accédez à l'emplacement dans lequel vous avez téléchargé et décompressé le bundle Crashd.

  2. Dans un éditeur de texte, remplacez le fichier d'arguments existant args par le code ci-dessous. Ce fichier contient les paires clé/valeur à transmettre au 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. Enregistrez le chemin d'accès local à un fichier de clé privée SSH. Si vous ne disposez pas déjà d'une paire de clés SSH ou si vous souhaitez en générer une nouvelle, exécutez ssh-keygen comme décrit dans la section Créer une paire de clés SSH. Par exemple :

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

    Lorsque vous y êtes invité, entrez un chemin d'accès local pour l’emplacement du fichier. Pour plus d’informations sur la création des paires de clés SSH.

  4. Définissez les arguments suivants dans le fichier args :

    • target : Définissez cette valeur sur :
      • bootstrap pour diagnostiquer un cluster de gestion autonome de démarrage local
      • mgmt pour diagnostiquer un cluster de gestion autonome déployé
      • workload pour diagnostiquer un ou plusieurs clusters de charge de travail
    • infra : Infrastructure sous-jacente de votre cluster : aws, azure ou vsphere.
    • workdir : Emplacement de collecte des fichiers.
    • ssh_user : Utilisateur SSH utilisé pour accéder aux machines du cluster. Pour les clusters s'exécutant sur vSphere, le nom d'utilisateur est capv.
    • ssh_pk_file : Chemin d’accès à votre fichier de clé privée SSH.
    • mgmt_cluster_config Chemin du fichier kubeconfig pour le cluster de gestion.
  5. Pour diagnostiquer les clusters de charge de travail, annulez la mise en commentaire et définissez les éléments suivants en plus des arguments répertoriés ci-dessus :

    • workload_clusters : Liste séparée par des virgules de noms de cluster de charge de travail à partir desquels collecter des informations de diagnostic.
    • workload_cluster_ns : Espace de noms dans lequel secrets/WORKLOAD-CLUSTER-NAME-kubeconfig est créé dans le cluster de gestion.

Exécuter Crashd

  1. Créez un fichier de script Crashd diagnostics.crsh contenant le code dans le fichier de diagnosticsdiagnostics.crsh ci-dessous.

  2. Exécutez la commande crashd à partir de l'emplacement où se trouvent le fichier de script diagnostics.crsh et le fichier d'argument args.

    crashd run --args-file args diagnostics.crsh
    
  3. Le cas échéant, vous pouvez surveiller la sortie de Crashd. Par défaut, la commande crashd s'exécute en silence jusqu'à ce qu'elle se termine. Cependant, vous pouvez utiliser l'indicateur --debug pour afficher sur l'écran des messages de journal semblables aux messages suivants :

    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
    

Fichier de diagnostics diagnostics.crsh

Dans le téléchargement du bundle CrashD, remplacez le fichier diagnostics.crsh existant par le code suivant, en tant que script à transmettre à la commande 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