
如果你第一次接触 Kubernetes大概率会被一堆名词绕晕Node、Pod、Deployment、Service……但真正干活的时候你大部分时间面对的就是一个命令行工具——kubectl。很多人以为学会kubectl get pods就算入门了结果一遇到 Pod 一直 Pending、镜像拉不下来、日志看不了、配置改不动就卡住不动了。这次我拿“部署第一个 Pod”当主线把 Kubectl 核心命令、Pod 背后的运行逻辑、以及我没少踩过的坑一起盘一遍。内容适合刚接触容器编排的开发和运维也适合正在准备 CKA 或 K8s 面试、但缺少实操环境的朋友。1. 动手之前先把三件事想明白1.1 Pod 不是一台虚拟机很多新手最容易犯的错误是把 Pod 当虚拟机理解。虚拟机里跑的是完整 OS你 ssh 进去啥都能干Pod 是 Kubernetes 调度的最小单位里面是一个或多个容器这些容器共享同一个网络命名空间、同一个 IP、同一组存储卷。也就是说同一个 Pod 里的容器之间可以直接用localhost互相访问对外则共享一个 Pod IP。这个设计和“一台服务器上跑多个进程”有点像但它更像一个“容器组合”通常一个 Pod 里放一个主容器再可放一个或多个辅助容器sidecar比如日志转发、配置热加载、监控采集这类辅助功能。把它们放进同一个 Pod是为了让它们和主程序保持同样的生命周期——一起调度到同一台节点上一起启动一起被清理。为什么这很重要因为你用 kubectl 操作 Pod 的时候很多命令要区分“容器”和“Pod”。一个 Pod 里有多个容器时kubectl logs和kubectl exec都必须显式指定容器名否则 kubectl 会提示你需要-c参数。这个细节在第一次排障时特别容易卡住。1.2 kubectl 本身只是一个 HTTP 客户端kubectl 不是魔法也不是什么“集群管理终端”。它本质上是 Kubernetes API Server 的一个客户端通过 kubeconfig 文件里配置的 API Server 地址、证书或 token发起 HTTP 请求。你输入kubectl get pods背后是向 API Server 发送了一次 GET 请求查询 Pod 列表输入kubectl apply -f xx.yaml背后是把 YAML 内容 POST/PATCH 给 API Server让集群把它存进 etcd。控制器Controller Manager看到你声明的“期望状态”后再去创建或调整真正的资源。理解这一点之后很多现象就说得通了为什么kubectl get nodes不通可能就是 kubeconfig 地址错了为什么某个命令只有特定账号能执行可能是因为 RBAC 权限不允许。你能操作集群的范围完全取决于你手里这份 kubeconfig 对应的身份和权限。1.3 学会用“期望状态”思考Kubernetes 排障时有个很好的思维框架一切资源都是 API 对象每个对象都有spec期望状态和status当前状态。你写 YAML就是在告诉集群“我要这样的东西”控制器则会尽量让实际状态向期望状态靠拢。比如你声明 Pod 里跑 nginxkubelet 就会去节点上拉镜像、启动容器。你声明 Deployment 要 3 个副本ReplicaSet 控制器就会保证有 3 个 Pod。声明式的好处是你不需要关心节点上具体怎么执行的细节集群会自动补偿。这也是为什么生产环境不建议直接创建裸 Pod而是用 Deployment、StatefulSet 等控制器来管理 Pod。和 Pod 经常一起出现的周边对象包括 Namespace命名空间、ConfigMap配置、Secret敏感信息、Deployment工作负载、Service访问入口。它们全都是 API 对象都可以用 YAML 描述也都可以用 kubectl 查看和操作。明白了这个框架后面看命令就不会觉得零散。2. 环境准备从安装 kubectl 到成功连上集群2.1 安装 kubectl 的两种常用方式如果你本机还没装 kubectl最直接的方式是用包管理器安装# macOS brew install kubectl # Ubuntu/Debian sudo apt-get update sudo apt-get install -y kubectl # Windows choco install kubernetes-cli想装指定版本也可以下载官方二进制curl -LO https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl装完先确认一下kubectl version --client这一步能确认客户端本身可用。注意 kubectl 和集群版本最好不要太悬殊小版本差一两个通常没问题差太多可能会出现命令兼容问题。2.2 配置 kubeconfigcontext 和 namespace 是重点kubectl 的所有连接信息都放在 kubeconfig 里默认路径是~/.kube/config。这个文件核心包含三块clusters集群地址、users用户凭证、contexts某个集群某个用户的组合。context是 kubectl 日常使用里最需要搞清楚的概念。一个 kubeconfig 里可以配置很多个 context比如开发集群、测试集群、生产集群你只需要切换 context 就能在不同环境间来回操作不用反复改文件。常用命令# 查看所有 context kubectl config get-contexts # 切换 context kubectl config use-context dev-cluster # 查看当前 context kubectl config current-context还有一个很实用的技巧给当前 context 绑定默认 namespace。kubectl config set-context --current --namespacedev绑定之后所有不带-n的 kubectl 命令都会默认作用在dev这个命名空间下能省掉很多重复输入。2.3 连集群前的三行自检拿到集群地址后我一般会依次跑三条命令kubectl cluster-info kubectl get nodes kubectl auth can-i list pods --all-namespaces第一条验证 API Server 是否通第二条验证是否有权限读取节点信息第三条验证当前用户在 Pod 相关资源上的权限。如果cluster-info正常但get nodes返回 Forbidden那大概率不是网络问题而是 RBAC 权限不够。权限这块在多人共用集群时尤其常见遇到“明明集群是好的但命令就是不行”的情况先检查 context 和权限别急着怀疑环境。3. 部署第一个 Pod两条路线推荐走声明式3.1 一行命令快速起 Podkubectl run如果你想快速验证环境、跑一个临时 Pod可以用命令式方式kubectl run nginx-demo \ --imagenginx:1.25 \ --restartNever \ --port80 \ --labelsappnginx-demo,envtest这里必须强调--restartNever。不写这个参数时kubectl run会根据默认重启策略创建 Deployment 或 Job而不是裸 Pod。手动指定--restartNever才会生成一个独立的 Pod 对象。Pod 创建后查看状态kubectl get pods -o wide如果状态变成Running说明第一个 Pod 已经跑起来了。你也可以用以下命令看它到底生成了一份什么样的 YAMLkubectl run nginx-demo --imagenginx:1.25 --restartNever --port80 --labelsappnginx-demo,envtest --dry-runclient -o yaml这个命令不会真正创建资源只会把对象定义打印到终端非常适合用来学习或确认参数效果。3.2 用 YAML 声明式创建 Pod命令行方式适合临时调试但真正要复现、评审、版本化管理建议用 YAML。一个最简单的 Pod 定义长这样apiVersion: v1 kind: Pod metadata: name: nginx-demo namespace: default labels: app: nginx-demo spec: containers: - name: nginx image: nginx:1.25 ports: - containerPort: 80 protocol: TCP保存为pod-nginx.yaml然后执行kubectl apply -f pod-nginx.yaml kubectl get pods -o wide kubectl describe pod nginx-demoapply和create的区别值得提一下。create是创建对象对象已存在时会直接报错apply是声明式创建或更新以本地文件为基准做增量修改。日常使用、CI/CD 里我都习惯用apply因为它更符合“配置即代码”的思路。另外describe是排障第一利器。它会输出 Pod 的完整信息包括调度到的节点、容器状态、镜像、环境变量、挂载卷还有最下面的 Events 区域。Events 会记录“拉镜像失败”“探针失败”“节点资源不足”这类关键事件大多数问题都能在这里找到直接原因。3.3 给 Pod 加上资源和环境变量实际部署时裸跑一个容器基本不合格。至少要加上资源限制和环境变量。一个带资源的 Pod 配置片段spec: containers: - name: nginx image: nginx:1.25 resources: requests: cpu: 100m memory: 128Mi limits: cpu: 500m memory: 256Mi env: - name: APP_ENV value: dev这里的100m表示 0.1 个 CPU 核心128Mi表示 128 兆内存的二进制计算单位。requests是调度依据集群会找一台剩余资源满足 requests 的节点limits是运行时限制超过限制可能会被杀掉或限流。经验不足时最容易犯的错是只写 limits 不写 requests这会让调度器误以为容器占用资源很低节点资源紧张时反而更容易被 OOM Kill。如果配置来自 ConfigMap可以用envFrom或 volume 挂载注入后面第 6 节会具体演示。4. 拿得出手的 kubectl 核心命令清单4.1 先学会 get、describe、logs 三件套运维 Kubernetes 最常用的不是几十条命令而是反复用这三条。我习惯的排查顺序是先get看整体状态再describe看事件最后logs看应用输出。命令作用典型场景kubectl get pods列出 Pod 及状态快速看整体健康度kubectl get pods -n kube-system指定命名空间查看排查系统组件 Podkubectl describe pod name查看 Pod 完整事件和配置Pod 状态异常时定位原因kubectl logs name -n ns --tail100 -f查看容器日志并跟随输出看应用启动报错或实时日志kubectl get events --sort-by.metadata.creationTimestamp查看命名空间内事件找历史错误事件关于 logs 有个容易忽略的点Pod 里如果有多个容器必须用-c container指定容器名否则只会返回第一个容器的日志。如果容器已经崩溃想看在崩溃前的日志可以加--previous参数这个在 CrashLoopBackOff 场景里几乎是必用的。4.2 进入容器与拷贝文件exec 和 cp调试 Pod 时经常需要进入容器看文件、跑命令。最基础的是execkubectl exec -it nginx-demo -- /bin/bash它等同于在容器里执行/bin/bash。有些镜像里没有 bash只有 sh这时可以换成kubectl exec -it nginx-demo -- /bin/sh很多精简镜像连 shell 都没有那就只能在宿主机层面排查或者改用临时 debug 容器。在 Pod 和本地之间传文件用的是kubectl cp# 本地文件拷进 Pod kubectl cp ./index.html nginx-demo:/usr/share/nginx/html/index.html # 从 Pod 拷回本地 kubectl cp nginx-demo:/usr/share/nginx/html/index.html ./backup.html这里有个坑kubectl cp底层依赖容器里的tar命令。如果基础镜像非常精简没有安装 tar这命令会直接报错。遇到这种情况不要浪费时间折腾 cp改用exec配合cat或dd做手工传输。还有一个习惯建议生产环境尽量少用 cp 改运行中容器里的文件因为容器一旦重建改动就全部丢了。要改配置应该改 ConfigMap 或镜像而不是手工进容器改。4.3 临时访问集群内服务port-forwardPod 没有暴露到集群外部时想用本地浏览器或 curl 测试服务用端口转发最简单kubectl port-forward pod/nginx-demo 8080:80命令执行后会阻塞终端本地访问localhost:8080就能访问到 Pod 的 80 端口。除了 Pod也可以转发 Deployment 或 Servicekubectl port-forward deployment/nginx-demo 8080:80 kubectl port-forward svc/nginx-demo 8080:80这种方式适合临时联调和验证但别把它当成生产访问方案。生产环境应该用 Service、Ingress 这类持久稳定的入口资源。5. 部署后 Pod 起不来故障排查实录5.1 先从状态现象入手第一个 Pod 很少能一次跑通。这不是玄学而是因为 Pod 的启动链路太长调度、拉镜像、创建容器、启动进程、探针检查任何一环出问题都会卡住。排查时我一般不带任何预设先看状态kubectl get pods -w常见状态和原因状态含义最常见的直接原因Pending等待调度节点资源不足、亲和性不满足、有污点ContainerCreating容器创建中拉镜像慢、存储卷挂载失败、Secret 拉取失败Running容器已启动但还要看 ready 状态和日志CrashLoopBackOff容器启动后反复崩溃启动命令错误、配置缺失、应用抛异常ImagePullBackOff镜像拉不下来镜像 tag 不存在、仓库需要认证、网络不通状态只能定位阶段真正找原因要看describe和logs。先说 Pending这类问题一般是调度层。describe pod里如果写着0/3 nodes available后面跟着的是“Insufficient cpu”“Insufficient memory”之类说明节点资源不够如果写着节点有污点taint而 Pod 没有对应容忍toleration说明是亲和性问题。5.2 实操案例镜像拉不下来有一次我在测试环境部署一个内部镜像Pod 一直停在ContainerCreating过一会儿变成ImagePullBackOff。用describe看到的 Events 是Failed to pull image myregistry.local/nginx:1.25: image pull failed ...我第一反应是网络不通但实际检查后发现是镜像仓库需要登录而 Pod 没配置imagePullSecret。解决办法是先创建 Secretkubectl create secret docker-registry registry-auth \ --docker-servermyregistry.local \ --docker-usernamexxx \ --docker-passwordxxx然后在 Pod YAML 中声明spec: imagePullSecrets: - name: registry-auth还有一个常被忽略的点镜像 tag。image: nginx:latest这种写法在 YAML 里没问题但很多私有仓库不允许拉取不存在的 tag或者只缓存了特定版本。排查时先把镜像名和 tag 拿到本地用docker pull验证一遍能省很多时间。5.3 实操案例进程一直重启Pod 状态变成CrashLoopBackOff时kubectl get pods已经看不到有用信息了要看应用本身的输出kubectl logs nginx-demo --previoustrue--previous会显示上一次容器实例的输出。大多数启动崩溃都能在这里找到直接原因比如环境变量没配、数据库连不上、启动脚本路径不对。有一次我遇到容器一直重启日志却没有任何报错后来发现是健康检查探针写错了readinessProbe 的端口设成了 8080而应用实际监听的是 80Kubernetes 认为容器不健康就会不断杀掉并重启。所以排查 CrashLoopBackOff 时除了看日志也要用describe看探针配置。5.4 权限和命名空间造成的“假故障”还有一种情况Pod 明明好好的但命令报错。比如kubectl get pods --all-namespaces # Error from server (Forbidden): pods is forbidden这不代表集群出了问题而是当前身份没有跨命名空间查看的权限。先看当前 context 和 namespacekubectl config current-context kubectl config view --minify | grep namespace如果只是想看某个 namespace 下的 Pod直接指定kubectl get pods -n my-namespace想确认自己到底能不能执行某个操作可以这样kubectl auth can-i list pods kubectl auth can-i create deployments这个命令会直接返回 yes 或 no。排障时先确认是应用问题、集群问题还是权限问题思路会清晰很多。6. 往前走一步从裸 Pod 到 Deployment 与 ConfigMap6.1 用 Deployment 代替裸 Pod裸 Pod 最大的问题是“没有控制器管理”。节点挂了裸 Pod 不会被自动迁移Pod 被删了也不会自动重建。生产环境真正部署应用通常用 Deployment。Deployment 本身不直接管理 Pod它管理的是 ReplicaSetReplicaSet 负责维护指定数量的 Pod 副本。一个典型的 Deployment YAML 长这样apiVersion: apps/v1 kind: Deployment metadata: name: nginx-demo labels: app: nginx-demo spec: replicas: 2 selector: matchLabels: app: nginx-demo template: metadata: labels: app: nginx-demo spec: containers: - name: nginx image: nginx:1.25 ports: - containerPort: 80这里最容易漏的是selector。它决定了 Deployment 管理哪些 Pod。template.metadata.labels必须和selector.matchLabels匹配否则 Deployment 创建出来后会一直找不到对应 Pod。部署后可以用kubectl apply -f deployment-nginx.yaml kubectl get deployment,rs,pods kubectl rollout status deployment/nginx-demorollout status会实时显示“等待副本可用”或“成功完成滚动更新”是发布时最常看的命令之一。6.2 用 ConfigMap 把配置从镜像里拆出来想给不同环境配不同参数又不想反复构建镜像就会用到 ConfigMap。它本质上是一个存键值对的 API 对象数据可以是普通 key-value也可以是文件内容。用命令创建kubectl create configmap nginx-config --from-filenginx.conf./nginx.conf也可以写 YAMLapiVersion: v1 kind: ConfigMap metadata: name: nginx-config data: APP_ENV: production nginx.conf: | server { listen 80; server_name localhost; }然后在 Deployment 里注入spec: containers: - name: nginx image: nginx:1.25 envFrom: - configMapRef: name: nginx-config volumeMounts: - name: config-volume mountPath: /etc/nginx/conf.d volumes: - name: config-volume configMap: name: nginx-config环境变量和配置文件都能从 ConfigMap 来。注意一个经验ConfigMap 更新后正在运行的 Pod 不会自动重新读取环境变量也不会自动重启。如果配置变了需要滚动更新 Deployment或者重启相关 Pod新配置才会真正生效。文件和挂载的方式虽然能“热更新”但也要看应用本身是否支持动态加载。6.3 用 rollout 实现发布的升级和回滚Deployment 相比裸 Pod另一个大优势是支持滚动更新和回滚。比如升级镜像版本kubectl set image deployment/nginx-demo nginxnginx:1.26这条命令会触发一次滚动更新新 ReplicaSet 先创建新 Pod等新 Pod ready 后旧 Pod 才被逐步清理。想看进度kubectl rollout status deployment/nginx-demo想看历史版本kubectl rollout history deployment/nginx-demo如果新版有问题直接回滚到上一个版本kubectl rollout undo deployment/nginx-demo这比手工去删 Pod、改配置快得多也是生产环境发布的标准动作。还有一个我常用的技巧把roller类命令和健康检查同时使用发布前先检查新 Pod 状态避免流量打到未就绪的实例上。最后再分享一个我实际用下来的习惯不要急着背命令先学会kubectl explain。比如不确定 Pod 的某个字段该怎么写直接执行kubectl explain pod.spec.containers kubectl explain deployment.spec.strategy.rollingUpdate它会告诉你每个字段的含义、类型、可选值比查网页文档快而且版本一定和你当前集群匹配。遇到报错时多看一眼 Events多用--previous看崩溃日志你会发现大部分问题真的不是玄学只是信息还没看到位而已。