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.
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:
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.
Gehen Sie nach den folgenden Anweisungen vor, um crashd
zu installieren oder ein Upgrade durchzuführen.
Laden Sie Crashd für Ihre Plattform herunter.
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.7.tar.gz
macOS:
tar -xvf crashd-darwin-amd64-v0.3.7-vmware.7.tar.gz
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.7
Verschieben Sie die Binärdatei in den Ordner /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
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.
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
HinweisZur 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:
kubeconfig
für den Verwaltungscluster mithilfe des Befehls tanzu mc kubeconfig get <management-cluster-name>
.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:
docker ps
aus, um den aktuell ausgeführten kind
-Cluster zu ermitteln.kind
-Cluster, indem Sie kind delete cluster --name CLUSTER-NAME
ausführen.Navigieren Sie zu dem Speicherort, an dem Sie das Crashd-Paket heruntergeladen und entpackt haben.
Ü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
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 protected]"
Geben Sie bei entsprechender Aufforderung einen lokalen Pfad für den Dateispeicherort ein. Informationen zum Erstellen der SSH-Schlüsselpaare.
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 diagnostizierenmgmt
, um einen bereitgestellten eigenständigen Verwaltungscluster zu diagnostizierenworkload
, um einen oder mehrere Arbeitslastcluster zu diagnostiziereninfra
: 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.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.Erstellen Sie eine Crashd-Skriptdatei diagnostics.crsh
, die den Code in der Diagnosedatei diagnostics.crsh
unten enthält.
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
Ü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
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)