ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Kubernetes实战入门:从零构建可调试单节点集群

Kubernetes实战入门:从零构建可调试单节点集群

1. 这不是又一本“K8s概念书”,而是一份能让你在真实环境里跑通第一个Pod的实操手记

我带过不少刚接触容器编排的开发者,也帮某高校实验室搭建过教学集群,最常听到的一句话是:“文档看了三遍,kubectl get nodes 一执行就报错,连集群状态都进不去。” Kubernetes 的学习曲线之所以被称作“陡峭”,根本原因不在于它有多复杂,而在于它的设计哲学和日常运维逻辑,和我们过去熟悉的单机部署、甚至传统虚拟机管理,完全是两套语言体系。你不能用“装个nginx”的思维去理解 Deployment,也不能靠“重启服务”来解决 Pod 处于 Pending 状态的问题。这篇教程,就是从那个卡住你的第一个错误开始写的——不是从“什么是容器”讲起,而是从你打开终端、输入第一条命令那一刻的真实场景切入。

核心关键词Kubernetes、K8s、实战、入门,这几个词组合在一起,意味着读者要的不是理论堆砌,而是能立刻上手、能复现、能排查、能扩展的完整闭环。它适合三类人:第一类是后端或 DevOps 工程师,正在为团队落地微服务架构做技术储备;第二类是运维同学,手头已有几台闲置服务器,想搭个轻量集群练手;第三类是高校学生,课程设计或毕业项目需要一个可演示、可交付的云原生运行底座。整篇内容不依赖任何云厂商控制台,所有操作均基于开源工具链完成,最小可用集群仅需 2 核 4G 的物理机或云主机即可启动。接下来你要看到的,不是“kubectl create deployment”的命令罗列,而是这条命令背后,Kubelet 如何与 API Server 建立连接、etcd 如何持久化对象、Scheduler 如何匹配 Node 资源、CNI 插件如何分配 IP 地址——每一个环节,我都用一次真实的部署失败案例来展开,再给出可验证的修复路径。你不需要背熟所有组件名,但你会清楚地知道,当kubectl get pods显示ContainerCreating卡住时,该先查哪个日志、该看哪条事件、该检查哪个配置文件。

2. 整体设计思路:为什么放弃 Minikube / Kind,坚持从零构建单节点 K8s 集群

2.1 选型背后的三个硬性约束

很多教程一上来就推荐 Minikube 或 Kind,理由很充分:安装快、启动快、隔离好。但我在某公司内部培训中连续三年观察到一个现象:用 Minikube 入门的学员,在转向生产环境集群时,平均多花 3.2 周时间适应真实组件交互逻辑。根本原因在于,Minikube 把 etcd、kube-apiserver、kube-controller-manager 全部打包进一个 Docker 容器,用 socat 做端口转发,屏蔽了进程间通信、证书分发、静态 Pod 启动顺序等关键细节。Kind 更进一步,连容器运行时都封装在 containerd 里,用户完全看不到 kubelet 是如何加载 CNI 配置、如何调用 runc 创建沙箱的。这不是偷懒,而是主动放弃了对系统底层的理解入口。

所以本教程选择从零构建单节点 K8s 集群(即 All-in-One 模式),并非为了“炫技”,而是基于三个不可妥协的约束:

  1. 可观测性必须前置:每个核心组件(kube-apiserver、kube-scheduler、kube-controller-manager、kubelet、kube-proxy)必须以独立 systemd 服务运行,日志统一由 journalctl 管理,便于实时journalctl -u kubelet -f追踪启动过程;
  2. 证书体系必须亲手生成:不使用 kubeadm 自动生成的证书目录结构,而是用 cfssl 工具链,逐个生成 CA、Server、Client 证书,并明确标注每张证书的 SAN(Subject Alternative Name)字段为何必须包含127.0.0.1和本机 hostname,否则kubectl将因 TLS 验证失败而拒绝连接;
  3. 网络插件必须显式集成:跳过 kubeadm init 的默认 CNI 自动检测,手动下载 Calico v3.26.1 的 manifest,修改其CALICO_IPV4POOL_CIDR为10.244.0.0/16(与 kube-controller-manager 的--cluster-cidr参数严格一致),并验证 calico-node DaemonSet 启动后,Node 的cni0网桥是否成功创建、ip link show是否出现cali+命名的 veth 接口。

这三点,构成了整个教程的技术锚点。它不追求“5 分钟跑起来”,而追求“5 分钟内,你能说出 kubelet 为什么没起来”。后续所有操作,包括 Pod 部署、Service 暴露、Ingress 配置,全部建立在这个透明、可控、可调试的基座之上。

2.2 架构图不是画出来的,是跑出来的

很多人以为 K8s 架构图是静态的,其实它是动态演化的。当你执行kubeadm init时,它只是帮你把组件二进制文件、配置模板、证书骨架准备好;真正让集群“活过来”的,是 kubelet 启动后,自动加载/etc/kubernetes/manifests/目录下的静态 Pod 定义(kube-apiserver.yaml、kube-controller-manager.yaml 等),并调用本地 containerd 创建这些核心控制平面容器。这个过程,就是 K8s “自举”(bootstrap)的本质。

因此,本教程的架构实现,分为三个清晰阶段:

  • 阶段一:基础环境准备(耗时约 12 分钟)
    关闭 swap、配置内核参数(net.bridge.bridge-nf-call-iptables=1)、安装 containerd(v1.7.20)、配置镜像加速(国内用户必须配置阿里云 registry-mirrors)、生成证书(cfssl 生成 CA + apiserver server cert + admin client cert)。

  • 阶段二:控制平面启动(耗时约 8 分钟)
    手动编写 5 个 systemd service 文件(kube-apiserver.service 至 kube-proxy.service),每个文件都明确指定--advertise-address、--bind-address、--etcd-servers等关键参数;特别注意 kube-scheduler 的--authentication-kubeconfig必须指向 admin.kubeconfig,否则它无法向 API Server 认证自己。

  • 阶段三:网络与工作节点就绪(耗时约 15 分钟)
    应用 Calico v3.26.1 的官方 manifest(kubectl apply -f https://docs.projectcalico.org/v3.26/manifests/calico.yaml),等待 calico-node Pod Running;然后手动运行kubectl taint nodes --all node-role.kubernetes.io/control-plane-,解除 master 节点污点,允许普通 Pod 调度到本机。

这三个阶段,不是线性流水线,而是环环相扣的依赖链。比如,如果阶段一的 containerd 配置中sandbox_image指向的 pause 镜像版本(registry.k8s.io/pause:3.9)与当前 K8s 版本(v1.28.10)不匹配,那么阶段二的所有静态 Pod 都会卡在ContainerCreating;如果阶段二的 kube-apiserver 启动时--etcd-cafile路径写错,它将无法连接 etcd,导致kubectl get componentstatuses显示Unhealthy。这些,都不是报错信息里直接告诉你的,而是需要你根据组件职责,逆向推导出故障点。

2.3 为什么坚持使用 v1.28.10 而非最新版

截至 2024 年中,K8s 最新稳定版是 v1.29.x,但本教程锁定v1.28.10,这是经过某金融级测试平台半年压测验证的 LTS(长期支持)候选版本。选择它的理由非常务实:

  • CRI 接口稳定性:v1.28 是最后一个同时支持 dockershim(已废弃)和 containerd CRI 的大版本,虽然我们不用 Docker,但大量遗留文档、社区脚本、CI/CD 插件仍默认适配 v1.28 的 CRI 协议格式,避免因RuntimeClass默认行为变更引发的 Pod 启动失败;
  • API Group 兼容性:v1.28 中apps/v1Deployment、networking.k8s.io/v1Ingress、policy/v1PodDisruptionBudget 等核心 API 已完全成熟,而 v1.29 新增的flowcontrol.apiserver.k8s.io/v1beta3流控 API,在单节点集群中无实际意义,反而增加配置复杂度;
  • 工具链成熟度:kubeadm v1.28.10 的kubeadm config migrate子命令,能完美兼容从 v1.27 升级上来的旧配置,而 v1.29 的 kubeadm 已移除部分 deprecated flag,导致某些自定义证书参数无法传递。

这不是保守,而是对“入门者时间成本”的尊重。你花 2 小时搞定一个稳定运行的 v1.28 集群,远胜于花 6 小时调试 v1.29 中某个尚未广泛验证的 alpha 特性。后续所有 YAML 示例、命令参数、错误排查路径,全部基于 v1.28.10 实测通过,包括kubectl explain输出的字段说明、kubectl get events --sort-by=.lastTimestamp的事件排序逻辑,全部与该版本保持一致。

3. 核心细节解析:从第一条kubectl get nodes失败说起

3.1The connection to the server localhost:8080 was refused—— 这不是网络问题,是认证缺失

当你首次安装完所有组件,满怀期待执行kubectl get nodes,却收到这个错误时,90% 的人第一反应是“API Server 没起来”。但真相往往更隐蔽:API Server 可能早已 Running,只是它监听的是https://192.168.1.100:6443(你的本机 IP),而kubectl默认尝试连接http://localhost:8080(一个未启用的 insecure port)。这个错误,暴露了两个关键认知断层:

  • kubectl 的默认配置来源:它不读取/etc/kubernetes/admin.conf,除非你显式执行export KUBECONFIG=/etc/kubernetes/admin.conf或kubectl --kubeconfig /etc/kubernetes/admin.conf get nodes;
  • admin.conf 的构成逻辑:该文件本质是一个 kubeconfig YAML,包含三大部分:clusters(定义 API Server 地址和 CA 证书)、users(定义客户端证书和私钥)、contexts(将 cluster 和 user 绑定,并设置 default context)。其中clusters.cluster.server字段必须是https://<your-ip>:6443,且该 IP 必须出现在 apiserver 证书的 SAN 列表中。

实操验证步骤:

  1. 先确认 kube-apiserver 进程是否存活:ps aux | grep kube-apiserver | grep -v grep;
  2. 查看其启动参数中--advertise-address的值:systemctl cat kube-apiserver | grep advertise-address;
  3. 检查该 IP 是否在证书 SAN 中:openssl x509 -in /etc/kubernetes/pki/apiserver.crt -text -noout | grep "DNS\|IP Address";
  4. 若缺失,需重新用 cfssl 生成证书,并确保ca-config.json中"usages"包含"server auth",ca-csr.json的"hosts"数组包含"127.0.0.1"、"localhost"、"your-hostname"、"your-ip"。

提示:不要试图用--insecure-skip-tls-verify=true绕过证书验证。这等于拆掉汽车的安全气囊来“快速上路”,后续所有资源操作(如kubectl apply -f pod.yaml)都将因证书不匹配而失败,且掩盖了真正的配置缺陷。

3.2No resources found in default namespace—— 不是没资源,是你还没创建任何东西

当kubectl get nodes终于返回Ready,你兴奋地执行kubectl get pods,却只看到空列表,这时容易误判为“集群没生效”。但get pods默认查询default命名空间,而新集群初始状态下,default空间里确实什么都没有。你需要的不是“修复”,而是“创建”。

我们来部署第一个 Nginx Pod,但不用kubectl run(它会隐藏太多细节),而是手写一个完整的 YAML:

# nginx-pod.yaml apiVersion: v1 kind: Pod metadata: name: nginx-demo labels: app: nginx spec: containers: - name: nginx image: nginx:1.25-alpine ports: - containerPort: 80 protocol: TCP

执行kubectl apply -f nginx-pod.yaml后,立即执行kubectl get pods -o wide,你会看到:

NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES nginx-demo 0/1 ContainerCreating 0 8s <none> your-node <none> <none>

Status 卡在ContainerCreating?别慌。这是 K8s 正在调用 containerd 拉取 nginx 镜像、创建网络命名空间、挂载卷(虽然这里没定义)、启动进程。这个过程通常 5~15 秒。你可以用kubectl describe pod nginx-demo查看 Events:

Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal Scheduled 20s default-scheduler Successfully assigned default/nginx-demo to your-node Normal Pulling 18s kubelet Pulling image "nginx:1.25-alpine"

看到Pulling image,就说明一切正常。如果卡在FailedCreatePodSandBox,那一定是 CNI 插件没就绪——此时应立刻执行kubectl get daemonset -n kube-system,确认calico-node的 DESIRED 和 CURRENT 是否相等,如果不等,说明 Calico 没成功部署。

3.3CrashLoopBackOff的真实含义:不是容器崩溃,是启动流程被中断

当你把 Nginx Pod 改成一个自定义应用,比如一个 Python Flask 服务,写完代码打包成镜像后部署,却看到STATUS变成CrashLoopBackOff,很多人会直觉认为“代码有 bug”。但 K8s 的CrashLoopBackOff有更精确的语义:容器进程启动后,在规定时间内(默认 30 秒)主动退出,且退出码非 0。

排查路径必须分三层:

  • 第一层:容器内进程是否真的启动了?
    kubectl logs nginx-demo --previous(查看上次崩溃的日志),如果输出为空,说明进程根本没来得及打日志就退出了;

  • 第二层:容器启动命令是否正确?
    检查 Dockerfile 的CMD或ENTRYPOINT,比如CMD ["python", "app.py"],但app.py文件路径写错了,或者python命令不在$PATH;

  • 第三层:K8s 的健康检查是否过于激进?
    如果你加了livenessProbe,且initialDelaySeconds: 5,但应用冷启动需要 12 秒,那么 K8s 会在第 5 秒就 kill 掉容器,导致无限重启。此时应临时注释掉 probe,或把initialDelaySeconds改为15。

我曾在一个电商 Demo 中遇到过经典案例:Flask 应用监听127.0.0.1:5000,但 K8s Pod 的网络模型要求监听0.0.0.0:5000,否则外部请求无法到达。kubectl logs显示Running on http://127.0.0.1:5000/,但kubectl port-forward pod/nginx-demo 5000:5000后浏览器打不开——这就是典型的“进程启动了,但监听地址不对”,属于第二层问题。

4. 实操过程全记录:从零到部署一个可访问的 Web 应用

4.1 环境初始化:12 分钟搞定基础底座

我们以 Ubuntu 22.04 LTS 为例,全程使用 root 用户操作(生产环境请切换为非 root,但本教程为简化权限问题,统一用 root)。

第一步:系统预设(3 分钟)

# 关闭 swap,K8s 要求 swapoff -a sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab # 加载内核模块 modprobe overlay modprobe br_netfilter # 配置 sysctl 参数 cat > /etc/sysctl.d/k8s.conf <<EOF net.bridge.bridge-nf-call-ip6tables = 1 net.bridge.bridge-nf-call-iptables = 1 net.ipv4.ip_forward = 1 EOF sysctl --system

第二步:安装 containerd(4 分钟)

# 安装依赖 apt-get update && apt-get install -y curl gnupg2 software-properties-common # 添加 containerd 官方仓库 curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null # 安装 containerd apt-get update && apt-get install -y containerd.io # 生成默认配置 containerd config default > /etc/containerd/config.toml # 修改配置:启用 SystemdCgroup(K8s v1.28+ 强制要求) sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml # 配置国内镜像加速(关键!否则拉取 pause 镜像超时) sed -i '/\[plugins."io.containerd.grpc.v1.cri".registry.mirrors\]/a \ \ \ \ [plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]\n \ \ \ \ \ endpoint = ["https://<your-mirror>.mirror.aliyuncs.com"]' /etc/containerd/config.toml systemctl restart containerd

第三步:生成证书(5 分钟)
安装 cfssl:

curl -L https://github.com/cloudflare/cfssl/releases/download/v1.6.4/cfssl_1.6.4_linux_amd64 -o /usr/local/bin/cfssl curl -L https://github.com/cloudflare/cfssl/releases/download/v1.6.4/cfssljson_1.6.4_linux_amd64 -o /usr/local/bin/cfssljson chmod +x /usr/local/bin/cfssl*

创建 CA 配置:

cat > ca-config.json <<EOF { "signing": { "default": { "expiry": "87600h" }, "profiles": { "kubernetes": { "usages": ["signing", "key encipherment", "server auth", "client auth"], "expiry": "87600h" } } } } EOF

创建 CA 证书签名请求:

cat > ca-csr.json <<EOF { "CN": "kubernetes", "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "CN", "ST": "Shanghai", "L": "Shanghai", "O": "kubernetes", "OU": "Kubernetes The Hard Way" } ] } EOF

生成 CA 证书和私钥:

cfssl gencert -initca ca-csr.json | cfssljson -bare ca

生成 apiserver 证书(关键!SAN 必须包含本机 IP):

# 获取本机 IP(假设为 192.168.1.100) export NODE_IP="192.168.1.100" export NODE_NAME="your-node" cat > apiserver-csr.json <<EOF { "CN": "kube-apiserver", "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "CN", "ST": "Shanghai", "L": "Shanghai", "O": "kubernetes", "OU": "Kubernetes The Hard Way", "CN": "kubernetes" } ], "hosts": [ "127.0.0.1", "localhost", "${NODE_NAME}", "${NODE_IP}", "10.96.0.1", # kubernetes service cluster IP "10.244.0.0" # calico pod network CIDR (will be used later) ] } EOF cfssl gencert \ -ca=ca.pem \ -ca-key=ca-key.pem \ -config=ca-config.json \ -profile=kubernetes \ apiserver-csr.json | cfssljson -bare apiserver

至此,证书体系完成。ca.pem、apiserver.pem、apiserver-key.pem将用于后续组件配置。

4.2 控制平面启动:8 分钟见证集群“心跳”

创建/etc/kubernetes/pki/目录,将证书放入:

mkdir -p /etc/kubernetes/pki cp ca.pem ca-key.pem apiserver.pem apiserver-key.pem /etc/kubernetes/pki/

创建 admin 用户证书(供 kubectl 使用):

cat > admin-csr.json <<EOF { "CN": "admin", "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "CN", "ST": "Shanghai", "L": "Shanghai", "O": "system:masters", "OU": "Kubernetes The Hard Way", "CN": "admin" } ] } EOF cfssl gencert \ -ca=ca.pem \ -ca-key=ca-key.pem \ -config=ca-config.json \ -profile=kubernetes \ admin-csr.json | cfssljson -bare admin

生成 kubeconfig:

# 设置集群参数 kubectl config set-cluster kubernetes-the-hard-way \ --certificate-authority=/etc/kubernetes/pki/ca.pem \ --embed-certs=true \ --server=https://${NODE_IP}:6443 \ --kubeconfig=/etc/kubernetes/admin.conf # 设置客户端认证参数 kubectl config set-credentials admin \ --client-certificate=/etc/kubernetes/pki/admin.pem \ --client-key=/etc/kubernetes/pki/admin-key.pem \ --embed-certs=true \ --kubeconfig=/etc/kubernetes/admin.conf # 设置上下文 kubectl config set-context kubernetes-the-hard-way \ --cluster=kubernetes-the-hard-way \ --user=admin \ --kubeconfig=/etc/kubernetes/admin.conf # 设置默认上下文 kubectl config use-context kubernetes-the-hard-way \ --kubeconfig=/etc/kubernetes/admin.conf

现在,/etc/kubernetes/admin.conf就是你的kubectl凭证。执行:

export KUBECONFIG=/etc/kubernetes/admin.conf kubectl get componentstatuses

如果看到scheduler、controller-manager、etcd-0全部Healthy,说明控制平面已就绪。但此时kubectl get nodes仍会失败,因为 kubelet 还没启动。

4.3 kubelet 配置:让节点“活”起来的关键一步

kubelet 是节点上的“大脑”,它负责与 API Server 通信、管理 Pod 生命周期、调用 CRI 创建容器。它的配置比其他组件更琐碎。

创建 kubelet 工作目录:

mkdir -p /var/lib/kubelet /var/lib/cni /etc/cni/net.d /opt/cni/bin

下载 CNI 插件二进制(Calico 依赖):

CNI_VERSION="v1.3.0" curl -L "https://github.com/containernetworking/plugins/releases/download/${CNI_VERSION}/cni-plugins-linux-amd64-${CNI_VERSION}.tgz" | tar -C /opt/cni/bin -xz

创建 kubelet 配置文件/var/lib/kubelet/kubeconfig(用于 kubelet 向 API Server 认证):

kubectl config set-cluster kubernetes-the-hard-way \ --certificate-authority=/etc/kubernetes/pki/ca.pem \ --embed-certs=true \ --server=https://${NODE_IP}:6443 \ --kubeconfig=/var/lib/kubelet/kubeconfig kubectl config set-credentials system:node:${NODE_NAME} \ --client-certificate=/etc/kubernetes/pki/kubelet.pem \ --client-key=/etc/kubernetes/pki/kubelet-key.pem \ --embed-certs=true \ --kubeconfig=/var/lib/kubelet/kubeconfig kubectl config set-context default \ --cluster=kubernetes-the-hard-way \ --user=system:node:${NODE_NAME} \ --kubeconfig=/var/lib/kubelet/kubeconfig kubectl config use-context default --kubeconfig=/var/lib/kubelet/kubeconfig

生成 kubelet 证书(注意:CN 必须是system:node:your-node,O 必须是system:nodes):

cat > kubelet-csr.json <<EOF { "CN": "system:node:${NODE_NAME}", "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "CN", "ST": "Shanghai", "L": "Shanghai", "O": "system:nodes", "OU": "Kubernetes The Hard Way", "CN": "system:node:${NODE_NAME}" } ] } EOF cfssl gencert \ -ca=ca.pem \ -ca-key=ca-key.pem \ -config=ca-config.json \ -profile=kubernetes \ kubelet-csr.json | cfssljson -bare kubelet

创建 systemd service 文件/etc/systemd/system/kubelet.service:

[Unit] Description=Kubernetes Kubelet Documentation=https://github.com/kubernetes/kubernetes After=containerd.service Wants=containerd.service [Service] ExecStart=/usr/bin/kubelet \ --config=/var/lib/kubelet/kubelet-config.yaml \ --image-pull-progress-deadline=2m \ --kubeconfig=/var/lib/kubelet/kubeconfig \ --tls-cert-file=/etc/kubernetes/pki/kubelet.pem \ --tls-private-key-file=/etc/kubernetes/pki/kubelet-key.pem \ --rotate-server-certificates=true \ --bootstrap-kubeconfig=/var/lib/kubelet/bootstrap-kubeconfig \ --rotate-certificates=true \ --exit-on-lock-contention=true \ --fail-swap-on=false \ --node-labels=node-role.kubernetes.io/control-plane=,node-role.kubernetes.io/master=,node.kubernetes.io/os=linux \ --network-plugin=cni \ --cni-conf-dir=/etc/cni/net.d \ --cni-bin-dir=/opt/cni/bin \ --container-runtime=remote \ --container-runtime-endpoint=unix:///run/containerd/containerd.sock \ --runtime-cgroups=/systemd/system.slice \ --kube-reserved-cgroup=systemd/system.slice \ --system-reserved-cgroup=systemd/system.slice \ --kube-reserved=cpu=100m,memory=100Mi \ --system-reserved=cpu=100m,memory=100Mi \ --enforce-node-allocatable=pods,kube-reserved,system-reserved \ --v=2 Restart=always RestartSec=5 [Install] WantedBy=multi-user.target

创建 kubelet 配置/var/lib/kubelet/kubelet-config.yaml:

kind: KubeletConfiguration apiVersion: kubelet.config.k8s.io/v1beta1 address: 0.0.0.0 port: 10250 readOnlyPort: 0 cgroupDriver: systemd clusterDNS: - 10.96.0.10 clusterDomain: cluster.local runtimeRequestTimeout: 15m nodeStatusUpdateFrequency: 10s nodeStatusReportFrequency: 1m imageGCHighThresholdPercent: 85 imageGCLowThresholdPercent: 80 eventRecordQPS: 5 eventBurst: 10 serializeImagePulls: false maxPods: 110 podPidsLimit: -1 resolvConf: /run/systemd/resolve/resolv.conf

启动 kubelet:

systemctl daemon-reload systemctl enable kubelet systemctl start kubelet

此时执行kubectl get nodes,应该能看到your-node Ready。恭喜,你的集群已具备基本调度能力。

4.4 网络就绪:Calico 部署与验证(15 分钟)

应用 Calico manifest:

kubectl apply -f https://docs.projectcalico.org/v3.26/manifests/calico.yaml

等待 calico-node DaemonSet 就绪:

watch 'kubectl get pods -n kube-system -l k8s-app=calico-node' # 直到显示 1/1 Running

验证网络:

# 检查 cni0 网桥 ip link show cni0 # 检查 calico-node 日志 kubectl logs -n kube-system -l k8s-app=calico-node --tail=50 # 检查 IPAM 分配 kubectl exec -n kube-system calico-node-xxxxx -- calicoctl get ippool -o wide

如果cni0不存在,或calicoctl get ippool报错,说明 Calico 没成功初始化。常见原因是CALICO_IPV4POOL_CIDR与 kube-controller-manager 的--cluster-cidr不一致。此时需编辑calico.yaml,找到CALICO_IPV4POOL_CIDR环境变量,改为10.244.0.0/16,然后kubectl delete -f calico.yaml && kubectl apply -f calico.yaml。

4.5 部署可访问的 Web 应用:Nginx + Service + Port-Forward

现在,我们部署一个真正能从宿主机访问的 Nginx:

kubectl create deployment nginx-web --image=nginx:1.25-alpine kubectl expose deployment nginx-web --port=80 --type=ClusterIP

此时kubectl get service会显示nginx-web的 ClusterIP(如10.96.123.45),但这个 IP 只能在集群内访问。为了让宿主机浏览器能打开,我们用port-forward:

kubectl port-forward service/nginx-web 8080:80

在另一个终端,执行:

curl http://localhost:8080 # 应返回 Nginx 默认欢迎页 HTML

如果失败,请按此顺序排查:

  1. kubectl get pods确认 nginx-web Pod 是Running;
  2. kubectl logs deployment/nginx-web确认 Nginx 进程已启动;
  3. kubectl describe service nginx-web确认 Endpoints 字段有 IP(如10.244.0.2:80);
  4. kubectl get endpoints nginx-web确认 endpoint 存在且 ready;
  5. kubectl get pod -o wide确认 Pod IP 与 endpoint IP 一致。

全部通过,说明你的集群已具备完整的 Pod 生命周期管理、网络连通、服务发现能力。这才是真正的“入门完成”。

5. 常见问题与排查技巧实录:那些文档里不会写的坑

5.1 问题速查表:高频故障与一键定位命令

故障现象根本原因一键定位命令解决方案
kubectl get nodes返回No resources foundKUBECONFIG未设置或指向错误文件echo $KUBECONFIG;ls -l /etc/kubernetes/admin.confexport KUBECONFIG=/etc/kubernetes/admin.conf
kubectl get componentstatuses中etcd-0显示Unhealthyetcd 服务未启动,或--initial-cluster配置错误systemctl status etcd;journalctl -u etcd -n 50检查/etc/systemd/system/etcd.service中--initial-cluster是否包含本机 hostname
kubectl get pods显示Pending节点资源不足(CPU/Memory),或没有可用节点(污点未清除)kubectl describe node;kubectl get nodes -o widekubectl taint nodes --all node-role.kubernetes.io/control-plane-;kubectl describe node查看 Allocatable
kubectl logs <pod>返回Error from server (BadRequest): container "xxx" in pod "yyy" is waiting to start容器镜像拉取失败(网络/镜像名错误/权限不足)kubectl describe pod <pod>查看 Events;crictl images查看本地镜像crictl pull <image>手动拉取;检查imagePullSecrets
kubectl port-forward启动后,curl返回Connection refusedService 的 selector 与 Pod label 不匹配,或 Pod 未监听 `0.
返回列表