이 항목에서는 충돌 진단(Crashd)을 사용하여 독립형 관리 클러스터가 있는 Tanzu Kubernetes Grid의 Photon OS 기반으로 불안정하거나 응답하지 않는 워크로드 클러스터를 진단하는 방법을 설명합니다.
Crashd를 사용하여 vSphere with Tanzu Supervisor에서 배포된 워크로드 클러스터를 진단하는 방법은 VMware 기술 자료의 vSphere with TanzuvSphere with Tanzu의 Tanzu Kubernetes 게스트 클러스터에서 진단 로그 번들을 수집하는 방법을 참조하십시오.
Crashd는 클러스터를 클라우드 인프라에 배포하기 전에 tanzu cluster create
프로세스에서 kind
를 사용하여 로컬로 생성하는 부트스트랩 워크로드 클러스터를 검토합니다.
Crashd는 Kubernetes 클러스터의 문제를 쉽게 해결할 수 있는 오픈 소스 프로젝트입니다.
Crashd는 Python과 같은 언어인 Starlark로 작성된 스크립트 파일을 사용하여 관리 또는 워크로드 클러스터와 상호 작용하여 인프라와 클러스터 정보를 수집합니다.
Crashd는 다음을 포함하여 지원되는 인프라에서 진단을 수집할 수 있습니다.
Crashd는 스크립트가 실행하는 명령에서 출력을 가져와 출력을 tar
파일에 추가합니다. 그런 다음 추가 분석을 위해 tar
파일이 로컬로 저장됩니다.
Tanzu Kubernetes Grid에는 Crashd를 위한 서명된 바이너리 및 Photon OS 워크로드 클러스터용 진단 스크립트 파일이 포함되어 있습니다.
crashd
를 설치하거나 업그레이드하려면 아래 지침을 따르십시오.
해당 플랫폼용 Crashd를 다운로드합니다.
tar
명령을 사용하여 플랫폼의 바이너리 압축을 풉니다.
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
이전 단계에서 다음 파일이 있는 crashd
디렉토리가 생성됩니다.
crashd
crashd/args
crashd/diagnostics.crsh
crashd/crashd-PLATFORM-amd64-v0.3.7+vmware.6
바이너리를 /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
Crashd가 실행되면 args
파일에서 인수 값을 가져와 스크립트 파일(diagnostics.crsh
)에 전달합니다. 스크립트는 명령을 실행하여 Photon OS 워크로드 클러스터의 문제를 진단하는 데 도움이 되는 정보를 추출합니다.
Crashd 스크립트 diagnostics.crsh
를 실행하기 전에 로컬 시스템의 실행 경로에 다음과 같은 프로그램이 있어야 합니다.
kubectl
scp
ssh
참고부트스트랩 클러스터의 문제를 조사할 때 로컬에
kind
(v0.7.0 이상) 명령이 설치되어 있어야 합니다.
또한 Crashd를 실행하기 전에 다음 단계를 수행해야 합니다.
kubeconfig
명령을 사용하여 관리 클러스터의 tanzu mc kubeconfig get <management-cluster-name>
파일을 추출합니다.kubeconfig
, public-key
파일, diagnostics.crsh
파일, args
파일이 동일한 위치에 있는지 확인합니다.문제 해결 중인 워크로드 클러스터를 배포하기 위해 생성된 클러스터 이외의 로컬 kind
클러스터를 삭제합니다.
docker ps
를 실행하여 현재 실행 중인 kind
클러스터를 식별합니다.kind
를 실행하여 다른 kind delete cluster --name CLUSTER-NAME
클러스터를 삭제합니다.Crashd 번들을 다운로드하고 압축을 푼 위치로 이동합니다.
텍스트 편집기에서 기존 인수 파일 args
를 아래 코드로 덮어씁니다. 이 파일에는 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
SSH 개인 키 파일의 로컬 경로를 기록합니다. SSH 키 쌍이 아직 없거나 새 키 쌍을 생성하려면 SSH 키 쌍 생성에 설명된 대로 ssh-keygen
을 실행합니다. 예:
ssh-keygen -t rsa -b 4096 -C "[email protected]"
메시지가 표시되면 파일 위치의 로컬 경로를 입력합니다. SSH 키 쌍 생성에 대한 자세한 내용.
args
파일에 다음 인수를 설정합니다.
target
: 이 값을 다음으로 설정합니다.
bootstrap
mgmt
workload
infra
: 클러스터의 기본 인프라: aws
, azure
또는 vsphere
.workdir
: 파일이 수집되는 위치입니다.ssh_user
: 클러스터 시스템에 액세스하는 데 사용되는 SSH 사용자입니다. vSphere에서 실행 중인 클러스터의 경우 사용자 이름은 capv
입니다.ssh_pk_file
: SSH 개인 키 파일의 경로입니다.mgmt_cluster_config
관리 클러스터의 kubeconfig 파일 경로입니다.워크로드 클러스터를 진단하려면 위에 있는 인수 외에 주석 처리를 제거하고 다음을 설정합니다.
workload_clusters
: 진단 정보를 수집할 워크로드 클러스터 이름의 쉼표로 구분된 목록입니다.workload_cluster_ns
: secrets/WORKLOAD-CLUSTER-NAME-kubeconfig
가 관리 클러스터에서 생성되는 네임스페이스입니다.아래의 진단 파일 diagnostics.crsh
에 있는 코드가 포함된 충돌 스크립트 파일 diagnostics.crsh
를 생성합니다.
스크립트 파일 diagnostics.crsh
와 인수 파일 args
가 있는 위치에서 crashd
명령을 실행합니다.
crashd run --args-file args diagnostics.crsh
필요한 경우 Crashd 출력을 모니터링합니다. 기본적으로 crashd
명령은 완료될 때까지 자동으로 실행됩니다. 따라서 --debug
플래그를 사용하여 화면에 다음과 유사한 로그 메시지를 볼 수 있습니다.
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
CrashD 번들 다운로드에서 crashd run
명령에 전달할 스크립트로 기존 diagnostics.crsh
파일을 다음 코드로 덮어씁니다.
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)