课程目录(第 3 章 / 共 33 章)
课程/认识 Kubernetes

搭一个本地集群:kind 与 kubectl 上手

3 章 / 共 33·18 分钟·入门环境kindkubectl

用 kind 在自己电脑上创建单节点和多节点集群,装好 kubectl,并学会验证、切换和清理集群。

学完这一章,你将能够

  • 在本地用 kind 创建并验证一个可用的 Kubernetes 集群
  • 用 kubectl 的常用命令确认集群、节点、命名空间的状态
  • 会写 kind 配置文件,创建一个带端口映射的多节点集群

这一章你会得到什么

前面两章都在讲概念,从现在开始动手。这一章结束后,你的电脑上会跑着一个真实的 Kubernetes 集群,kubectl get nodes 会返回 Ready,后面每一章的命令都能直接粘贴执行。

本地集群的价值在于容错:随便折腾、随便删,重建只要几分钟。学习 Kubernetes 最怕的就是不敢动手,而 kind(Kubernetes in Docker)就是为「把整个集群塞进几个容器里」而设计的。

kind 的原理很直白:每一个「节点」其实是一个 Docker 容器,容器里跑着 kubelet、containerd 和控制平面组件。所以它对宿主机几乎没有额外要求,只要有 Docker 就行。

text
你的电脑

 ├── 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 可用:

bash
docker version
docker info
  • macOS / Windows:装 Docker Desktop,启动后确认菜单栏图标是运行状态。
  • Linux:装 Docker Engine 即可,装完把当前用户加入 docker 组,然后重新登录:
bash
sudo usermod -aG docker $USER

docker 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:

bash
brew install kubectl

Linux 用官方软件源(以 Debian / Ubuntu 为例):

bash
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

不想用软件源,也可以直接下二进制:

bash
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

kind 有三种装法,选一种:

bash
brew install kind                          # macOS / Linux 用 Homebrew
go install sigs.k8s.io/kind@latest         # 有 Go 环境

没有 Go 又不想用 Homebrew,就下载官方二进制(把版本号和平台换成你自己的,最新版本号见 kind 发布页):

bash
# 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

装完确认两者都能执行:

bash
kubectl version --client
kind version

动手:创建第一个单节点集群

创建并进入你的第一个集群

一条命令就能创建,kind 会依次拉取节点镜像、初始化控制平面、安装 CNI:

bash
kind create cluster --name kube101

输出末尾会看到 Set kubectl context to "kind-kube101",说明 kubectl 已经自动指向这个新集群。第一次执行会下载约 1 GB 的节点镜像,慢是正常的。

想指定 Kubernetes 版本,可以加 --image

bash
kind create cluster --name kube101 --image kindest/node:v1.31.0

验证:四条命令认识你的集群

确认集群真的可用

bash
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 wideSTATUSReadyCONTAINER-RUNTIME 显示 containerd://VERSION 是你的 Kubernetes 版本。
  • kubectl get ns 至少包含 defaultkube-node-leasekube-publickube-system 四个命名空间。
  • kubectl get pods -A 里所有 Pod 都是 Running,其中包括上一章见过的 corednskube-proxy 和 CNI 插件。

最后一条命令是「全集群健康体检」:如果有 Pod 不是 Running,先别急着往下走,按本章最后的常见坑排查。

环境自检清单

每次重装系统、换了网络、或者隔几天回来继续学,先花一分钟跑一遍这张表。只要有一项不满足,后面所有章节都会跟着出错,先修环境再往下走。

检查项命令期望输出不满足怎么办
Docker 守护进程docker info打印出 Server VersionTotal Memory启动 Docker Desktop,或 sudo systemctl start docker
Docker 内存docker info 里的 Total Memory单节点 ≥ 2 GB,多节点 ≥ 6 GBDocker Desktop → Settings → Resources 调大后重启
kubectlkubectl version --client打印 Client Version: v1.3x.x回到上面的安装步骤重装
kindkind version打印 kind v0.2x.x确认 /usr/local/bin/kindPATH
集群存在kind get clusters列出 kube101kind create cluster --name kube101
当前 contextkubectl config current-contextkind-kube101kubectl config use-context kind-kube101
节点就绪kubectl get nodes每行 STATUS 都是 Ready见本章常见坑第一条
系统 Podkubectl get pods -A全部 Running见下面的速查表
DNS 可用kubectl run dns-test --image=busybox:1.36 --restart=Never -n demo -- nslookup kubernetes.default.svc.cluster.localPod 变成 Completedkube-system 里的 CoreDNS

最后一条是「端到端验证」:它创建一个临时 Pod,在集群内部解析 kubernetes.default.svc.cluster.local 这个 Service 的完整域名。这里刻意写全名,避免镜像里 nslookup 对搜索域的处理差异带来误判。跑完记得清理:

bash
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

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。

先删掉旧集群,再用配置文件重建:

bash
kind delete cluster --name kube101
kind create cluster --config kind-config.yaml
kubectl get nodes

这次应该看到三行,其中一行的 ROLEScontrol-plane,另外两行是 <none>

清理与重建集群

kind 的集群就是几个容器,删起来很干净:

bash
kind get clusters
kind delete cluster --name kube101

kind delete cluster 会把节点容器和 kubeconfig 里的 context 一起删掉。想彻底重来,删完直接重新 kind create cluster 即可。

kubeconfig 与 context

kubectl 靠 ~/.kube/config 里的 context 决定连哪个集群。kind 创建集群时会自动添加并切换到 kind-<集群名> 这个 context。

bash
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 直接可用:

bash
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.sh

kubectl get nodes 出现一个 Ready 节点、kubectl get pods -A 里 Traefik 与 CoreDNS 都在跑,就说明集群可用。注意它和 kind 的 context 会同时存在,切回来记得 kubectl config use-context kind-kube101

text
  k3s 单进程形态(每个 server 节点)
  ┌──────────────────────────────────────┐
  │ k3s server 进程                       │
  │   ├── kube-apiserver                 │
  │   ├── kube-controller-manager        │
  │   ├── kube-scheduler                 │
  │   └── embedded etcd(--cluster-init)│
  └──────────────────────────────────────┘
        ▲  kubeconfig / agent 都指向 VIP:6443
        │  kube-vip 必须先在集群里跑起来

有三处差异在生产上特别关键,和 kubespray 集群不一样:

  1. 控制面是一个进程,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
  2. etcd 快照是自带功能。 默认每天 00:00 与 12:00 各打一次快照,只保留最近 5 份自动快照,目录是 /var/lib/rancher/k3s/server/db/snapshotssudo 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
  3. 打包组件会被回退。 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 -Akube-proxyCrashLoopBackOff,也先怀疑资源不足。
  • 端口 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 clusterbind: address already in use宿主机 80/443 已被别的进程占用lsof -i :80lsof -i :443关掉占用进程,或把 hostPort 改成 8080/8443
节点一直 NotReadyDocker 内存不足,或 CNI 没起来docker infoTotal Memory,再看 kubectl get pods -n kube-system调大 Docker 内存后删掉集群重建
kube-proxy 反复 CrashLoopBackOff多半也是资源不足,少数是内核模块或权限问题kubectl logs -n kube-system -l k8s-app=kube-proxy先加内存重建;仍失败就换 Docker 驱动重装
kubectlconnection refused当前 context 指向了已删除的集群kubectl config current-context 配合 kind get clusters切回正确 context,或重新创建集群
Pod 卡在 ContainerCreatingCNI 插件没装好或没就绪kubectl get pods -n kube-system 里有没有 CNI 的 Pod重建集群,或检查 CNI 清单是否安装成功
镜像拉取超时网络不通或镜像源太慢在宿主机执行 docker pull nginx:1.27 能否成功配镜像加速器,或用 kind load docker-image 导入
kubectl get pods -A 里 DNS 相关 Pod 不是 RunningCoreDNS 起不来,常见于内存不足或 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 心跳正常、节点没有报告致命的 MemoryPressureDiskPressure。它不保证网络插件、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-infokubectl get nodes -o widekubectl get nskubectl 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 开始练命令。

练习

  1. kind create cluster --name scratch 再建一个集群,然后用 kubectl config get-contexts 观察两个 context,最后把它删掉。
  2. kind-config.yaml 增加一个工作节点,重建集群,用 kubectl get nodes -o wide 确认新节点加入。
  3. kubectl get pods -n kube-system -o wide,对照 第 2 章 集群架构 里的组件表,找出哪些 Pod 跑在哪个节点上。

集群有了,下一章我们创建第一个 Pod,把 kubectl 最常用的几条命令一次练熟。