当你成功部署了应用程序之后,接下来的关键就是确保它们稳定运行并能够被外界访问。
本章将详细介绍如何利用 kubectl
命令行工具来监控 K8s 资源的状态、查看容器日志以排查问题,并进一步探讨将应用程序暴露给集群外部用户访问的各种 Service 类型。
在 Kubernetes 中,一切皆资源。理解如何查看这些资源的状态是日常运维的基础。kubectl get
和 kubectl describe
是你最常用的两个命令,它们能帮助你快速了解集群的健康状况和资源的详细信息。
kubectl get
的深入使用:快速概览kubectl get
命令用于列出指定类型资源的简要信息。通过它,你可以快速查看 Pod、Deployment、Service、PVC 等对象的当前状态。
查看所有 Pod 的状态:
kubectl get pods
------------
NAME READY STATUS RESTARTS AGE
postgres-deployment-5596fb49f-kzdnw 1/1 Running 0 2d22h
redis-0 1/1 Running 0 3d
redis-1 1/1 Running 0 3d
redis-2 1/1 Running 0 3d
redis-3 1/1 Running 0 3d
redis-4 1/1 Running 0 3d
redis-5 1/1 Running 0 3d
你将看到一个列表,包含 Pod 的名称(NAME)、就绪状态(READY)、当前状态(STATUS)、重启次数(RESTARTS)和运行时间(AGE)。
READY:N/M
表示 Pod 中 M
个容器里有 N
个通过了就绪探针(Readiness Probe),表明这些容器已准备好接收流量。1/1
表示唯一容器已就绪。
STATUS:
Running
:Pod 中的所有容器都在运行中。Pending
:Pod 已经被 K8s 接受,但至少有一个容器镜像还未被创建。这可能是因为镜像正在下载,或者 Pod 正在等待调度到合适的 Node。ContainerCreating
:Pod 已经被调度到 Node 上,容器正在创建中。CrashLoopBackOff
:容器启动后又崩溃,并正在不断重启。这通常表示应用程序内部有错误。Error
:容器在启动时遇到错误。ImagePullBackOff
:K8s 尝试拉取容器镜像失败,例如镜像名称错误或私有仓库认证失败。Terminating
:Pod 正在被删除。Completed
:Pod 中的所有容器已成功完成(通常用于一次性任务的 Job)。查看特定应用(如 Redis)的 Pod 状态:
为了方便过滤,我们经常使用 -l
(label selector) 选项,只显示带有特定标签的 Pod。
kubectl get pods -l app=redis-cluster
kubectl get pods -l app=postgres
查看所有 Deployment 和 StatefulSet:
kubectl get deploy
----------
NAME READY UP-TO-DATE AVAILABLE AGE
postgres-deployment 1/1 1 1 2d22h
kubectl get sts
----------
NAME READY AGE
redis 6/6 3d
它们会显示 Deployment/StatefulSet 的名称、期望的 Pod 数量、当前运行的 Pod 数量、就绪的 Pod 数量、最新的 Pod 数量以及可用时间。
查看所有 Service:
kubectl get svc
----------
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 21d
postgres-service NodePort 10.99.206.59 <none> 5432:30001/TCP 2d22h
redis-service ClusterIP None <none> 6379/TCP,16379/TCP 3d
你会看到 Service 的名称、类型(ClusterIP, NodePort, LoadBalancer 等)、集群 IP (CLUSTER-IP)、外部 IP (EXTERNAL-IP,如果有的话)、端口(PORT(S)表示 Service 监听的端口及其映射的目标端口,如 5432:30001/TCP
表示 Service 对外暴露 30001,转发到 Pod 的 5432 端口)。
查看所有 PersistentVolumeClaim (PVC):
kubectl get pvc
----------
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
postgres-pvc Bound pvc-17f757ef-d278-4391-9533-dabbcd00dcc7 5Gi RWO hostpath <unset> 2d22h
redis-data-redis-0 Bound pvc-7b45ea67-7fa9-43ff-9dfb-f06f15920242 1Gi RWO hostpath <unset> 3d
redis-data-redis-1 Bound pvc-fb2d99d3-4403-4973-be5f-44694eb46032 1Gi RWO hostpath <unset> 3d
redis-data-redis-2 Bound pvc-1d6ca325-35a9-430e-8510-d2d634749b5d 1Gi RWO hostpath <unset> 3d
redis-data-redis-3 Bound pvc-691001f2-a223-4055-b305-56341d9e0c70 1Gi RWO hostpath <unset> 3d
redis-data-redis-4 Bound pvc-5c9d2518-8ff2-42a1-bb51-f94d02c7231d 1Gi RWO hostpath <unset> 3d
redis-data-redis-5 Bound pvc-a00ce548-e957-4ff4-9248-ad0b162825f5 1Gi RWO hostpath <unset> 3d
显示 PVC 的名称、状态(Bound
表示已绑定到 PV,Pending
表示正在等待绑定)、存储卷名称、容量等。
查看所有 Node 的状态:
kubectl get nodes
----------
NAME STATUS ROLES AGE VERSION
docker-desktop Ready control-plane 21d v1.32.2
显示 Node 的名称、状态(Ready
表示可用于运行 Pod)、角色、版本以及运行时间。
kubectl describe
:获取资源详细信息当 kubectl get
提供的信息不足以诊断问题时,kubectl describe
命令能提供某个特定 K8s 资源的更详细、更丰富的事件和状态信息。这是排查问题时极其有用的工具。
详细查看某个 Pod 的信息:
kubectl describe pod <pod-name>
----------
Name: redis-0
Namespace: default
Priority: 0
Service Account: default
Node: docker-desktop/192.168.65.3
Start Time: Fri, 06 Jun 2025 17:35:07 +0800
Labels: app=redis-cluster
apps.kubernetes.io/pod-index=0
controller-revision-hash=redis-5459fb65c
statefulset.kubernetes.io/pod-name=redis-0
Annotations: <none>
Status: Running
IP: 10.1.0.18
IPs:
IP: 10.1.0.18
Controlled By: StatefulSet/redis
Containers:
redis:
Container ID: docker://94de1c78c6912e68424c5185a563b8ebbc95cbaa8a7b5939ed0e7d872ae03d15
Image: redis:6.2.7-alpine
Image ID: docker-pullable://redis@sha256:fe7cce7c87019e13a14a04eddf96d74c72ae64d503ebf6f8d38ad0393d13ead1
Ports: 6379/TCP, 16379/TCP
Host Ports: 0/TCP, 0/TCP
Command:
redis-server
Args:
/etc/redis/redis.conf
State: Running
Started: Fri, 06 Jun 2025 17:36:20 +0800
Ready: True
Restart Count: 0
Environment:
POD_NAME: redis-0 (v1:metadata.name)
Mounts:
/data from redis-data (rw)
/etc/redis from redis-config (rw)
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-7m9gj (ro)
Conditions:
Type Status
PodReadyToStartContainers True
Initialized True
Ready True
ContainersReady True
PodScheduled True
Volumes:
redis-data:
Type: PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)
ClaimName: redis-data-redis-0
ReadOnly: false
redis-config:
Type: ConfigMap (a volume populated by a ConfigMap)
Name: redis-cluster-config
Optional: false
kube-api-access-7m9gj:
Type: Projected (a volume that contains injected data from multiple sources)
TokenExpirationSeconds: 3607
ConfigMapName: kube-root-ca.crt
ConfigMapOptional: <nil>
DownwardAPI: true
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events: <none>
输出会非常详细,包含:
详细查看 Deployment 或 StatefulSet 状态:
kubectl describe deploy <deployment-name>
kubectl describe sts <statefulset-name>
# 例如:
kubectl describe deploy postgres-deployment
kubectl describe sts redis
你可以看到控制器对 Pod 的管理情况,包括:
kubectl describe sts redis
----------
Name: redis
Namespace: default
CreationTimestamp: Fri, 06 Jun 2025 17:35:06 +0800
Selector: app=redis-cluster
Labels: app=redis-cluster
Annotations: <none>
Replicas: 6 desired | 6 total
Update Strategy: RollingUpdate
Partition: 0
Pods Status: 6 Running / 0 Waiting / 0 Succeeded / 0 Failed
Pod Template:
Labels: app=redis-cluster
Containers:
redis:
Image: redis:6.2.7-alpine
Ports: 6379/TCP, 16379/TCP
Host Ports: 0/TCP, 0/TCP
Command:
redis-server
Args:
/etc/redis/redis.conf
Environment:
POD_NAME: (v1:metadata.name)
Mounts:
/data from redis-data (rw)
/etc/redis from redis-config (rw)
Volumes:
redis-config:
Type: ConfigMap (a volume populated by a ConfigMap)
Name: redis-cluster-config
Optional: false
Node-Selectors: <none>
Tolerations: <none>
Volume Claims:
Name: redis-data
StorageClass:
Labels: <none>
Annotations: <none>
Capacity: 1Gi
Access Modes: [ReadWriteOnce]
Events: <none>
kubectl describe deploy postgres-deployment
----------
Name: postgres-deployment
Namespace: default
CreationTimestamp: Fri, 06 Jun 2025 19:43:46 +0800
Labels: app=postgres
Annotations: deployment.kubernetes.io/revision: 1
Selector: app=postgres
Replicas: 1 desired | 1 updated | 1 total | 1 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 25% max unavailable, 25% max surge
Pod Template:
Labels: app=postgres
Containers:
postgres:
Image: postgres:13
Port: 5432/TCP
Host Port: 0/TCP
Environment:
POSTGRES_DB: mydatabase
POSTGRES_USER: myuser
POSTGRES_PASSWORD: <set to the key 'postgres-password'in secret 'postgres-secret'> Optional: false
Mounts:
/var/lib/postgresql/data from postgres-storage (rw)
Volumes:
postgres-storage:
Type: PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)
ClaimName: postgres-pvc
ReadOnly: false
Node-Selectors: <none>
Tolerations: <none>
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing True NewReplicaSetAvailable
OldReplicaSets: <none>
NewReplicaSet: postgres-deployment-5596fb49f (1/1 replicas created)
Events: <none>
详细查看 Service 信息:
kubectl describe svc <service-name>
# 例如:
kubectl describe svc postgres-service
kubectl describe svc redis-service
你会看到 Service 的类型、IP 地址、端口映射、关联的 Pod 标签选择器、以及重要的 Endpoints(指向后端 Pod 的实际 IP 地址和端口)。如果 Endpoints 列表为空或不正确,通常意味着 Service 没有正确地选中任何 Pod。
kubectl describe svc redis-service
----------
Name: redis-service
Namespace: default
Labels: app=redis-cluster
Annotations: <none>
Selector: app=redis-cluster
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: None
IPs: None
Port: client 6379/TCP
TargetPort: 6379/TCP
Endpoints: 10.1.0.18:6379,10.1.0.19:6379,10.1.0.20:6379 + 3 more...
Port: cluster 16379/TCP
TargetPort: 16379/TCP
Endpoints: 10.1.0.18:16379,10.1.0.19:16379,10.1.0.20:16379 + 3 more...
Session Affinity: None
Internal Traffic Policy: Cluster
Events: <none>
详细查看 PersistentVolumeClaim (PVC) 信息:
kubectl describe pvc <pvc-name>
# 例如:
kubectl describe pvc postgres-pvc
kubectl describe pvc redis-data-redis-0 # 注意这里是 StatefulSet 自动生成的 PVC 名称
你会看到 PVC 的状态(Bound
)、绑定的 PV 名称、容量、访问模式以及重要的事件,这些事件能帮助你诊断 PVC 是否成功绑定,例如是否找到合适的 PV,或者 StorageClass 是否正确。
kubectl describe pvc postgres-pvc
----------
Name: postgres-pvc
Namespace: default
StorageClass: hostpath
Status: Bound
Volume: pvc-17f757ef-d278-4391-9533-dabbcd00dcc7
Labels: app=postgres
Annotations: pv.kubernetes.io/bind-completed: yes
pv.kubernetes.io/bound-by-controller: yes
volume.beta.kubernetes.io/storage-provisioner: docker.io/hostpath
volume.kubernetes.io/storage-provisioner: docker.io/hostpath
Finalizers: [kubernetes.io/pvc-protection]
Capacity: 5Gi
Access Modes: RWO
VolumeMode: Filesystem
Used By: postgres-deployment-5596fb49f-kzdnw
Events: <none>
通过熟练使用 kubectl get
进行快速概览和 kubectl describe
进行深度诊断,你将能够有效地监控和排查 Kubernetes 集群中应用程序的运行状态。
日志是排查应用程序问题的生命线。当你的 K8s Pod 出现异常(例如 CrashLoopBackOff
、Error
),或者应用程序行为不符合预期时,查看容器的日志输出通常是定位问题的首要步骤。
K8s 提供了一个简单而强大的命令:kubectl logs
。
kubectl logs
的基本与高级用法kubectl logs
命令用于获取 Pod 中容器的标准输出和标准错误流。
查看单个 Pod 的日志:
这是最基本的用法。如果 Pod 只有一个容器,它会直接显示该容器的日志。如果 Pod 中包含多个容器,你必须使用 -c <container-name>
参数来指定要查看哪个容器的日志,否则 kubectl 会返回一个错误。
kubectl logs <pod-name>
# 例如,查看之前部署的 Redis Pod 的日志:
kubectl logs redis-0
# 查看 PostgreSQL Pod 的日志 (替换为你的实际 Pod 名称,通常是 postgres-deployment-xxxx-xxxx):
kubectl logs postgres-deployment-5c4d6f8d7-abcde
kubectl logs redis-0
----------
1:C 06 Jun 2025 09:36:20.977 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
1:C 06 Jun 2025 09:36:20.977 # Redis version=6.2.7, bits=64, commit=00000000, modified=0, pid=1, just started
1:C 06 Jun 2025 09:36:20.977 # Configuration loaded
1:M 06 Jun 2025 09:36:20.978 * monotonic clock: POSIX clock_gettime
1:M 06 Jun 2025 09:36:20.978 * No cluster configuration found, I'm 1eb3dd6f7712c7661f934ec1c65d36ac4daa9ac3
1:M 06 Jun 2025 09:36:20.980 # A key '__redis__compare_helper' was added to Lua globals which is not on the globals allow list nor listed on the deny list.
1:M 06 Jun 2025 09:36:20.980 * Running mode=cluster, port=6379.
1:M 06 Jun 2025 09:36:20.980 # Server initialized
1:M 06 Jun 2025 09:36:20.981 * Ready to accept connections
1:M 06 Jun 2025 09:49:40.061 # configEpoch set to 1 via CLUSTER SET-CONFIG-EPOCH
1:M 06 Jun 2025 09:50:49.209 # IP address for this node updated to 10.1.0.18
1:M 06 Jun 2025 09:50:50.270 # Cluster state changed: ok
1:M 06 Jun 2025 09:50:51.209 * Replica 10.1.0.22:6379 asks for synchronization
1:M 06 Jun 2025 09:50:51.209 * Partial resynchronization not accepted: Replication ID mismatch (Replica asked for '49e21a5711c30accd0ca34949639b5989aac6f7c', my replication IDs are '31d18caf7fabcf337842d998f317a24c71cebdd1' and '0000000000000000000000000000000000000000')
1:M 06 Jun 2025 09:50:51.209 * Replication backlog created, my new replication IDs are 'de56b675185ab492544ae85b097d1c2ed472a36a' and '0000000000000000000000000000000000000000'
1:M 06 Jun 2025 09:50:51.209 * Starting BGSAVE for SYNC with target: disk
1:M 06 Jun 2025 09:50:51.210 * Background saving started by pid 36
36:C 06 Jun 2025 09:50:51.214 * DB saved on disk
36:C 06 Jun 2025 09:50:51.214 * RDB: 0 MB of memory used by copy-on-write
1:M 06 Jun 2025 09:50:51.267 * Background saving terminated with success
1:M 06 Jun 2025 09:50:51.267 * Synchronization with replica 10.1.0.22:6379 succeeded
查看特定 Pod 中某个容器的日志:
如果你的 Pod 中运行了多个容器(比如一个主应用容器和一个 Sidecar 日志收集容器),你需要指定容器名称才能查看特定容器的日志。
kubectl logs <pod-name> -c <container-name>
# 例如,如果你的 Pod 中有名为 'my-app' 和 'log-collector' 两个容器:
kubectl logs my-app-pod-xxxx -c my-app
对于我们部署的 Redis 和 PostgreSQL,每个 Pod 通常只有一个主容器,所以不需要指定 -c
。
实时跟踪 Pod 日志 (类似 tail -f
):
在开发或调试时,你可能希望实时查看日志输出。kubectl logs -f
(-f
是 follow 的缩写)可以满足这个需求。
kubectl logs -f <pod-name>
# 例如:
kubectl logs -f redis-0
这会持续输出最新的日志行,直到你按下 Control+C
停止。
查看 Pod 过去一段时间的日志:
如果你只想查看最近的日志,可以使用 --since
选项。
kubectl logs <pod-name> --since=1h # 查看过去 1 小时的日志
kubectl logs <pod-name> --since=5m # 查看过去 5 分钟的日志
kubectl logs <pod-name> --since-time="2025-06-01T10:00:00Z" # 查看某个时间点之后的日志
查看 Pod 重启前的日志:
如果 Pod 经历了多次重启,你可能需要查看上一次容器实例的日志。
kubectl logs <pod-name> --previous # 或 -p
# 例如:
kubectl logs redis-0 --previous
这在排查 CrashLoopBackOff
状态的 Pod 时特别有用,因为它可以让你看到导致上次崩溃的原因。
kubectl logs
是排查实时问题的利器,但它无法提供历史日志的持久化存储。在生产环境中,通常会部署一套集中式日志系统(如 EFK/ELK Stack、Loki+Promtail 等),将所有容器的日志统一收集、存储和检索,以便进行长期的监控和审计。
当应用程序在 K8s 中出现问题时,你可以遵循以下基本步骤进行排查:
kubectl get pods
):kubectl logs <pod-name>
查看日志。 应用的错误信息(例如代码语法错误、配置错误、数据库连接失败等)通常会直接打印在日志中。kubectl logs <pod-name> --previous
查看上一次崩溃前的日志。kubectl exec -it <pod-name> -- bash
进入容器内部进行调试(请注意,kubectl exec
命令要求目标容器的镜像中包含一个可用的 shell 程序(如 /bin/bash
或 /bin/sh
)。对于一些极简的 distroless
或 scratch
基础镜像,可能无法进入)。kubectl describe pod <pod-name>
中的 Events 会给出具体拉取失败的原因。kubectl describe pod <pod-name>
查看 Events 部分。可能的原因包括:没有足够的资源(CPU/内存),没有合适的 Node,或者 PersistentVolumeClaim 处于 Pending
状态。ImagePullBackOff
或 ErrImagePull
:K8s 无法拉取容器镜像。CrashLoopBackOff
:应用程序启动后立即崩溃并不断重启。这是最常见的应用层错误。CrashLoopBackOff
类似,也需要查看日志。kubectl get deploy/sts
& kubectl describe deploy/sts
):DESIRED
)、当前副本数 (CURRENT
) 和就绪副本数 (READY
) 是否匹配。kubectl describe
的 Events 部分会显示控制器尝试调整副本时的信息。kubectl get svc
& kubectl describe svc
):selector
是否与 Pod 的 labels
完全匹配。这是最常见的 Service 访问问题。Running
和 Ready
状态。CLUSTER-IP
或 EXTERNAL-IP
(对于 LoadBalancer 类型)。Endpoints
列表。
这是至关重要的一项!如果Endpoints
列表为空或没有包含你期望的 Pod IP,说明 Service 没有正确地选中任何后端 Pod。port
和 targetPort
映射是否正确。kubectl get pvc
& kubectl describe pvc
):Pending
状态,并且 Pod 无法启动,说明存储卷没有成功绑定。kubectl describe pvc <pvc-name>
的 Events 部分会显示 PVC 为什么无法绑定,例如没有可用的 PV,或者 StorageClass 配置有问题。通过系统性地检查这些 K8s 资源的状态和日志,你将能够高效地诊断和解决大部分应用程序在 Kubernetes 中遇到的问题。