课程目录(第 3 章 / 共 33 章)
搭一个本地集群:kind 与 kubectl 上手
用 kind 在自己电脑上创建单节点和多节点集群,装好 kubectl,并学会验证、切换和清理集群。
学完这一章,你将能够
- ✓在本地用 kind 创建并验证一个可用的 Kubernetes 集群
- ✓用 kubectl 的常用命令确认集群、节点、命名空间的状态
- ✓会写 kind 配置文件,创建一个带端口映射的多节点集群
这一章你会得到什么
前面两章都在讲概念,从现在开始动手。这一章结束后,你的电脑上会跑着一个真实的 Kubernetes 集群,kubectl get nodes 会返回 Ready,后面每一章的命令都能直接粘贴执行。
本地集群的价值在于容错:随便折腾、随便删,重建只要几分钟。学习 Kubernetes 最怕的就是不敢动手,而 kind(Kubernetes in Docker)就是为「把整个集群塞进几个容器里」而设计的。
kind 的原理很直白:每一个「节点」其实是一个 Docker 容器,容器里跑着 kubelet、containerd 和控制平面组件。所以它对宿主机几乎没有额外要求,只要有 Docker 就行。
你的电脑
│
├── Docker(容器运行时)
│ ├── kind-kube101-control-plane ← 一个容器 = 一个「节点」
│ ├── kind-kube101-worker
│ └── kind-kube101-worker2
│
└── kubectl
│ 读取 ~/.kube/config 里的 context
▼
https://127.0.0.1:<随机端口> ──► 控制平面(apiserver)理清这张图,后面所有命令都能对上号:kind 管的是左边那些容器,kubectl 走右边那条线跟 apiserver 说话。
前置条件:先让 Docker 跑起来
kind 需要容器运行时来承载节点容器,所以第一步是确认 Docker 可用:
docker version
docker info- macOS / Windows:装 Docker Desktop,启动后确认菜单栏图标是运行状态。
- Linux:装 Docker Engine 即可,装完把当前用户加入
docker组,然后重新登录:
sudo usermod -aG docker $USERdocker info 能打印出 Server 版本和资源信息就说明就绪了。如果报 Cannot connect to the Docker daemon,说明 Docker 没启动,先解决这一步,不要继续往下装。
给 Docker 分多少内存
一个单节点 kind 集群大约需要 2 GB,多节点建议给 Docker 至少 6 GB。Docker Desktop 默认可能只有 2 GB,后面节点 NotReady 十有八九是这个原因。
安装 kubectl 和 kind
kubectl 是操作集群的命令行客户端,它和集群版本要大致匹配(前后差一个小版本没问题)。
macOS 用 Homebrew:
brew install kubectlLinux 用官方软件源(以 Debian / Ubuntu 为例):
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.31/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.31/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update
sudo apt-get install -y kubectl不想用软件源,也可以直接下二进制:
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/kubectlkind 有三种装法,选一种:
brew install kind # macOS / Linux 用 Homebrew
go install sigs.k8s.io/kind@latest # 有 Go 环境没有 Go 又不想用 Homebrew,就下载官方二进制(把版本号和平台换成你自己的,最新版本号见 kind 发布页):
# Linux x86_64
curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.24.0/kind-linux-amd64
# macOS Apple Silicon
curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.24.0/kind-darwin-arm64
chmod +x ./kind
sudo mkdir -p /usr/local/bin
sudo mv ./kind /usr/local/bin/kind装完确认两者都能执行:
kubectl version --client
kind version动手:创建第一个单节点集群
创建并进入你的第一个集群
一条命令就能创建,kind 会依次拉取节点镜像、初始化控制平面、安装 CNI:
kind create cluster --name kube101输出末尾会看到 Set kubectl context to "kind-kube101",说明 kubectl 已经自动指向这个新集群。第一次执行会下载约 1 GB 的节点镜像,慢是正常的。
想指定 Kubernetes 版本,可以加 --image:
kind create cluster --name kube101 --image kindest/node:v1.31.0验证:四条命令认识你的集群
确认集群真的可用
kubectl cluster-info
kubectl get nodes -o wide
kubectl get ns
kubectl get pods -A怎么判断成功:
kubectl cluster-info打印出Kubernetes control plane is running at https://127.0.0.1:<端口>和CoreDNS is running at ...。kubectl get nodes -o wide的STATUS是Ready,CONTAINER-RUNTIME显示containerd://,VERSION是你的 Kubernetes 版本。kubectl get ns至少包含default、kube-node-lease、kube-public、kube-system四个命名空间。kubectl get pods -A里所有 Pod 都是Running,其中包括上一章见过的coredns、kube-proxy和 CNI 插件。
最后一条命令是「全集群健康体检」:如果有 Pod 不是 Running,先别急着往下走,按本章最后的常见坑排查。
环境自检清单
每次重装系统、换了网络、或者隔几天回来继续学,先花一分钟跑一遍这张表。只要有一项不满足,后面所有章节都会跟着出错,先修环境再往下走。
| 检查项 | 命令 | 期望输出 | 不满足怎么办 |
|---|---|---|---|
| Docker 守护进程 | docker info | 打印出 Server Version 与 Total Memory | 启动 Docker Desktop,或 sudo systemctl start docker |
| Docker 内存 | docker info 里的 Total Memory | 单节点 ≥ 2 GB,多节点 ≥ 6 GB | Docker Desktop → Settings → Resources 调大后重启 |
| kubectl | kubectl version --client | 打印 Client Version: v1.3x.x | 回到上面的安装步骤重装 |
| kind | kind version | 打印 kind v0.2x.x | 确认 /usr/local/bin/kind 在 PATH 里 |
| 集群存在 | kind get clusters | 列出 kube101 | kind create cluster --name kube101 |
| 当前 context | kubectl config current-context | kind-kube101 | kubectl config use-context kind-kube101 |
| 节点就绪 | kubectl get nodes | 每行 STATUS 都是 Ready | 见本章常见坑第一条 |
| 系统 Pod | kubectl get pods -A | 全部 Running | 见下面的速查表 |
| DNS 可用 | kubectl run dns-test --image=busybox:1.36 --restart=Never -n demo -- nslookup kubernetes.default.svc.cluster.local | Pod 变成 Completed | 查 kube-system 里的 CoreDNS |
最后一条是「端到端验证」:它创建一个临时 Pod,在集群内部解析 kubernetes.default.svc.cluster.local 这个 Service 的完整域名。这里刻意写全名,避免镜像里 nslookup 对搜索域的处理差异带来误判。跑完记得清理:
kubectl get pods -n demo
kubectl delete pod dns-test -n demo如果 demo 命名空间还不存在,这条命令会报 namespaces "demo" is not found,用 kubectl create namespace demo 建一个即可(第 4 章 第一个 Pod 也会做这件事)。
换成多节点集群
单节点够学 Pod 和 Deployment,但学不到「Pod 落在哪台机器上」这件事。而且 第 8 章 Ingress 需要把宿主机的 80/443 端口映射进集群,这要在创建集群时就配好。
新建一个 kind-config.yaml:
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: kube101
nodes:
- role: control-plane
kubeadmConfigPatches:
- |
kind: InitConfiguration
nodeRegistration:
kubeletExtraArgs:
node-labels: "ingress-ready=true"
extraPortMappings:
- containerPort: 80
hostPort: 80
protocol: TCP
- containerPort: 443
hostPort: 443
protocol: TCP
- role: worker
- role: worker三个关键点:
nodes里第一个是控制平面,后面两个是工作节点,一共 1 控制面 + 2 工作节点。extraPortMappings把宿主机的 80 和 443 映射到控制平面节点容器里,第 8 章 Ingress 的 Ingress Controller 会监听这两个端口。node-labels给控制平面节点打上ingress-ready=true,方便按节点选择器部署 Ingress Controller。
先删掉旧集群,再用配置文件重建:
kind delete cluster --name kube101
kind create cluster --config kind-config.yaml
kubectl get nodes这次应该看到三行,其中一行的 ROLES 是 control-plane,另外两行是 <none>。
清理与重建集群
kind 的集群就是几个容器,删起来很干净:
kind get clusters
kind delete cluster --name kube101kind delete cluster 会把节点容器和 kubeconfig 里的 context 一起删掉。想彻底重来,删完直接重新 kind create cluster 即可。
kubeconfig 与 context
kubectl 靠 ~/.kube/config 里的 context 决定连哪个集群。kind 创建集群时会自动添加并切换到 kind-<集群名> 这个 context。
kubectl config get-contexts
kubectl config current-context
kubectl config use-context kind-kube101如果你同时有多个集群(比如还连着一个测试环境的集群),每次执行命令前确认当前 context,否则很可能把实验操作打到不该碰的集群上。
本课程为什么统一用 kind
本课程统一用 kind:它更接近真实集群的多节点形态,后面讲调度和 Ingress 时不会受限于单节点。已经装过 minikube 的同学不用卸载,两者可以共存,只要注意 context 别选错。下一节我们把常见轻量发行版放在一起对比,并快速体验一次 k3s。
轻量发行版怎么选:kind / minikube / k3s / k0s / RKE2
本课程用 kind,但真实项目里你会遇到别的选择。「发行版」的差别不在 API——它们跑的都是标准 Kubernetes——而在控制面怎么打包、默认装了什么、能不能满足合规要求。
| 发行版 | 定位 | 控制面形态 | 默认组件与特点 | 适合场景 |
|---|---|---|---|---|
| kind | 学习与 CI | 每个节点是一个容器 | 接近原生 kubeadm,多节点一个配置文件搞定 | 学多节点、跑 CI、做 Ingress 实验 |
| minikube | 单机体验 | 单节点,Docker / 虚拟机驱动 | 内置 minikube addons,开箱省事 | 快速体验、没有 Docker 时用虚拟机 |
| k3s | 边缘与轻量 | 单个二进制进程内嵌控制面 | 自带 containerd、Flannel、Traefik、ServiceLB、local-path,默认 SQLite / 可选 embedded etcd | 边缘、IoT、单机、资源受限、想少运维 |
| k0s | 极简与可嵌入 | 单二进制,控制面与 worker 可同进程 | 外部依赖少,默认 Calico,内置 konnectivity,可换自定义 CNI | 想自己组装数据面、在容器里跑集群 |
| RKE2 | 安全加固 | 系统服务形态,控制面与 agent 分离 | 默认 Canal CNI、CIS 基线加固、SELinux / FIPS 友好 | 等保、合规、政府与金融类环境 |
怎么选,一句话版本:学概念和跑 CI 用 kind;只是想快速体验用 minikube 或 k3s;边缘与资源受限用 k3s;要过安全基线用 RKE2;想把网络和入口完全自己掌控用 k0s。 它们的 kubeconfig、kubectl 命令完全一致,所以换发行版的成本主要在运维方式,而不是应用清单。
参考:reference/k8s-in-action/.arch/k3s/README.md
k3s 快速体验:十分钟换一套发行版
想亲手摸一下非 kind 的集群,k3s 是最省事的一条路——一条命令装完,kubectl 直接可用:
curl -sfL https://get.k3s.io | sh -
sudo k3s kubectl get nodes
sudo k3s kubectl get pods -A
# 把 kubeconfig 挪到常用位置(k3s 默认只给 root 用)
mkdir -p ~/.kube
sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
sudo chown $(id -u):$(id -g) ~/.kube/config
kubectl get nodes
# 体验完清理
sudo /usr/local/bin/k3s-uninstall.shkubectl get nodes 出现一个 Ready 节点、kubectl get pods -A 里 Traefik 与 CoreDNS 都在跑,就说明集群可用。注意它和 kind 的 context 会同时存在,切回来记得 kubectl config use-context kind-kube101。
k3s 单进程形态(每个 server 节点)
┌──────────────────────────────────────┐
│ k3s server 进程 │
│ ├── kube-apiserver │
│ ├── kube-controller-manager │
│ ├── kube-scheduler │
│ └── embedded etcd(--cluster-init)│
└──────────────────────────────────────┘
▲ kubeconfig / agent 都指向 VIP:6443
│ kube-vip 必须先在集群里跑起来有三处差异在生产上特别关键,和 kubespray 集群不一样:
- 控制面是一个进程,HA 靠 `--cluster-init` + VIP。 第一台用
--cluster-init初始化 embedded etcd,其余节点用--server https://<VIP>:<apiserver 端口>加入(k3s 默认 6443,若入口经 haproxy/keepalived 转发在别的端口,就写那个端口)。kube-vip 必须在第二台 join 之前就部署好,因为 join 时指定的--server地址必须已经存在;ARP 模式下ssh <VIP>就能看到它漂在哪台机器上。参考:reference/k8s-in-action/.arch/k3s/kube-vip/README.md - etcd 快照是自带功能。 默认每天 00:00 与 12:00 各打一次快照,只保留最近 5 份自动快照,目录是
/var/lib/rancher/k3s/server/db/snapshots;sudo k3s etcd-snapshot save手工补一份(on-demand-*前缀,不会被自动清理)。恢复的顺序不能乱:先在所有控制面systemctl stop k3s,在其中一台上执行k3s server --cluster-reset --cluster-reset-restore-path=<快照文件>,再把db目录改名移走,然后启动并让其它节点重新加入。参考:reference/k8s-in-action/.arch/k3s/etcd.md - 打包组件会被回退。 Traefik、ServiceLB、local-path 这些默认组件由
/var/lib/rancher/k3s/server/manifests托管,直接kubectl patch改它们的 Deployment,k3s 重启时会被覆盖回默认版本;要持久化改动,就在该目录下放一个同名的.skip空文件,把这份清单「接管」出来。参考:reference/k8s-in-action/.arch/k3s/patch/README.md
轻量发行版不等于生产就绪
k3s 的默认存储是 SQLite(单节点)或 embedded etcd(HA),默认组件、证书有效期、备份策略都和你熟悉的 kubeadm 集群不同。用它做边缘集群完全没问题,但上线前一定要确认:备份是否真的能恢复、证书到期时间、以及默认 CNI 与 Ingress 是否满足需求。
常见坑
四个新手必踩的坑
- 节点一直 NotReady:多半是 Docker 分配的内存不够。检查
docker info里的Total Memory,Docker Desktop 到 Settings → Resources 调到 6 GB 以上,然后删掉集群重建。如果kubectl get pods -A里kube-proxy在CrashLoopBackOff,也先怀疑资源不足。 - 端口 80 或 443 被占用:
kind create cluster会报bind: address already in use。用lsof -i :80找出占用进程(macOS 常见是本地 Nginx 或 AirPlay),或者把配置里的hostPort改成 8080 / 8443,同时记住后续访问要用新端口。 - context 选错:命令报
connection refused或行为诡异时,先跑kubectl config current-context。切换用kubectl config use-context kind-kube101。 - 镜像拉取慢或超时:
kind create cluster卡在Pulling image属于正常,国内网络可以给 Docker 配镜像加速器;应用镜像拉不动时,可以先用docker pull拉到本地,再kind load docker-image nginx:1.27 --name kube101送进节点。
排错速查表
上面几条按「现象」整理成一张表,出问题时对着找,比从头回忆快得多。
| 现象 | 原因 | 怎么确认 | 怎么办 |
|---|---|---|---|
kind create cluster 报 bind: address already in use | 宿主机 80/443 已被别的进程占用 | lsof -i :80、lsof -i :443 | 关掉占用进程,或把 hostPort 改成 8080/8443 |
节点一直 NotReady | Docker 内存不足,或 CNI 没起来 | docker info 看 Total Memory,再看 kubectl get pods -n kube-system | 调大 Docker 内存后删掉集群重建 |
kube-proxy 反复 CrashLoopBackOff | 多半也是资源不足,少数是内核模块或权限问题 | kubectl logs -n kube-system -l k8s-app=kube-proxy | 先加内存重建;仍失败就换 Docker 驱动重装 |
kubectl 报 connection refused | 当前 context 指向了已删除的集群 | kubectl config current-context 配合 kind get clusters | 切回正确 context,或重新创建集群 |
Pod 卡在 ContainerCreating | CNI 插件没装好或没就绪 | kubectl get pods -n kube-system 里有没有 CNI 的 Pod | 重建集群,或检查 CNI 清单是否安装成功 |
| 镜像拉取超时 | 网络不通或镜像源太慢 | 在宿主机执行 docker pull nginx:1.27 能否成功 | 配镜像加速器,或用 kind load docker-image 导入 |
kubectl get pods -A 里 DNS 相关 Pod 不是 Running | CoreDNS 起不来,常见于内存不足或 CNI 未就绪 | kubectl describe pod -n kube-system -l k8s-app=kube-dns | 先解决 CNI 与内存,再重启 CoreDNS Pod |
自测题
自测:kind 的「节点」明明是一个 Docker 容器,为什么 Kubernetes 承认它是节点?(点击展开答案)
因为 Kubernetes 判断节点只有一个标准:有没有 kubelet 连上 apiserver 并持续上报心跳。kind 在容器里跑起了 kubelet 和 containerd,kubelet 通过 TLS 向 apiserver 注册自己,集群就多了一个 Node 对象——它并不关心底层是物理机、虚拟机还是容器。
代价是这层抽象会漏:容器里看不到宿主机内核的全部特性,节点之间共享宿主机的 CPU 和内存,所以 kind 适合学习与 CI,不适合压测真实性能。
自测:为什么 `kubectl get nodes` 已经是 Ready,Pod 却仍然起不来?(点击展开答案)
Ready 只代表 kubelet 心跳正常、节点没有报告致命的 MemoryPressure 或 DiskPressure。它不保证网络插件、DNS、镜像仓库这些「Pod 跑起来之后才需要」的东西可用。
所以判断顺序要固定:先 kubectl get nodes 确认节点活着,再 kubectl get pods -n kube-system 确认 CNI 与 CoreDNS 就绪,最后才看业务 Pod。CNI 没就绪的典型表现就是业务 Pod 卡在 ContainerCreating,而节点依然 Ready。
自测:k3s 自带 etcd 快照,为什么还不能省掉备份演练?(点击展开答案)
因为「有快照文件」和「能在故障时恢复」是两件事。k3s 默认每 12 小时打一次快照、只保留最近 5 份,也就是说最坏情况下你会丢掉 12 小时的数据;恢复还要按固定顺序执行(停所有控制面 → --cluster-reset 指定快照 → 移走 db 目录 → 重启 → 其它节点重新加入),顺序错了可能连集群都起不来。生产做法是:把快照同步到集群外的对象存储,并至少演练一次恢复,确认快照能用、恢复耗时能接受。
自测:为什么切换集群只需要改一个 context?(点击展开答案)
kubectl 本身不保存任何集群状态,它每次执行都从 ~/.kube/config 里读出「当前 context 对应的 cluster(apiserver 地址 + CA 证书)」和「user(客户端证书或 token)」,然后用这套凭据发起 HTTPS 请求。context 就是这两者的一个命名组合。
正因为凭据来自本地文件,切错 context 不会有任何提示:你的命令会被原样发到另一个集群,包括 delete。养成先 kubectl config current-context 的习惯,成本一秒,收益是少一次事故。
小结
- kind 把每个节点做成一个 Docker 容器,因此只需要 Docker 就能在本地跑出真实集群。
- 创建集群是
kind create cluster --name kube101,验证靠kubectl cluster-info、kubectl get nodes -o wide、kubectl get ns、kubectl get pods -A四条命令。 - 多节点集群用
kind-config.yaml描述:1 个 control-plane + 2 个 worker,并预留 80 / 443 端口给后面的 Ingress。 kind delete cluster可以随时推倒重来;学习期重建往往比修更快。- 轻量发行版各有定位:kind 学多节点、minikube 快速体验、k3s 边缘与单机、k0s 极简、RKE2 安全合规;API 一致,差别在运维方式。
- 永远先确认
kubectl config current-context,再执行会改动集群的命令。
相关章节:组件分工看不懂时回到 第 2 章 集群架构;集群起来之后从 第 4 章 第一个 Pod 开始练命令。
练习
- 用
kind create cluster --name scratch再建一个集群,然后用kubectl config get-contexts观察两个 context,最后把它删掉。 - 给
kind-config.yaml增加一个工作节点,重建集群,用kubectl get nodes -o wide确认新节点加入。 - 跑
kubectl get pods -n kube-system -o wide,对照 第 2 章 集群架构 里的组件表,找出哪些 Pod 跑在哪个节点上。
集群有了,下一章我们创建第一个 Pod,把 kubectl 最常用的几条命令一次练熟。