Kubernetes 上手实战(2):本地搭建 K8s 学习环境
上一篇建立了 Pod、Deployment、Service 与调谐循环的心智模型。本篇把这些概念落到一套可重复创建、可彻底销毁的本地环境:用 Docker 承载节点、kind 创建集群、kubectl 操作 API,并实际部署 web 应用。
一、痛点:学习环境也需要可复现
本地 Kubernetes 方案很多。Docker Desktop 集成度高,适合希望少维护的桌面用户;minikube 支持虚拟机、容器等多种驱动,附加组件丰富;kind 把 Kubernetes 节点运行在 Docker 容器中,创建快、配置文件清晰,尤其适合教学和 CI。本系列选 kind,不代表它适合承载生产业务,而是因为环境能写成代码、失败后可低成本重建。
先安装 Docker、kind 和 kubectl,并分别检查版本。kubectl 客户端与服务器允许有限版本偏差,长期使用应参考官方版本偏差策略。不要从来路不明的网盘下载二进制;按官方安装页选择操作系统和架构,并校验校验和。Docker 需要至少约 4GB 可用内存,若镜像频繁被系统杀死,应先提高资源额度。
kubectl 通过 kubeconfig 确定集群地址、证书和身份。一个配置可保存多个 cluster、user 与 context;context 是三者加默认 namespace 的组合。多数“命令打错集群”并非 Kubernetes 故障,而是当前 context 不正确。每次执行删除或修改前,都应先打印 current-context。
二、原理:节点容器与端口映射
kind 的节点看起来像 Docker 容器,但内部运行 kubelet、容器运行时和控制平面组件。业务 Pod 由节点内的 containerd 启动,不等同于宿主机 Docker 容器。宿主机端口也不会自动进入集群;extraPortMappings 显式把宿主机 8080 映射到节点 30080,便于稍后验证 NodePort。
下面保存为 kind.yaml。一个控制平面加两个工作节点足够观察调度与节点故障;固定节点镜像版本能避免今天创建和下月创建得到不同 Kubernetes 版本。若官方已更新镜像,可替换为当前受支持的精确标签,但团队应把这个变化作为受审查的依赖升级。
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: k8s-lab
nodes:
- role: control-plane
extraPortMappings:
- containerPort: 30080
hostPort: 8080
protocol: TCP
- role: worker
labels:
learning.k8s.io/zone: a
- role: worker
labels:
learning.k8s.io/zone: b
networking:
ipFamily: ipv4
apiServerAddress: 127.0.0.1
apiServerPort: 6443
kubeadmConfigPatches:
- |
kind: ClusterConfiguration
apiServer:
extraArgs:
enable-admission-plugins: NodeRestriction
创建脚本在任何步骤失败时立即退出,并核对 context、节点和系统 Pod。它还建立独立 namespace,减少与其他实验互相污染。重复运行时若集群已存在,脚本不会默默覆盖,而会提示先决定复用还是删除。
#!/usr/bin/env bash
set -euo pipefail
cluster_name="k8s-lab"
context_name="kind-${cluster_name}"
command -v docker >/dev/null
command -v kind >/dev/null
command -v kubectl >/dev/null
docker info >/dev/null
if kind get clusters | grep -qx "${cluster_name}"; then
echo "cluster already exists: ${cluster_name}"
else
kind create cluster --config kind.yaml --wait 120s
fi
kubectl config use-context "${context_name}"
test "$(kubectl config current-context)" = "${context_name}"
kubectl wait --for=condition=Ready nodes --all --timeout=120s
kubectl get nodes -o wide
kubectl get pods -n kube-system
kubectl create namespace lab --dry-run=client -o yaml | kubectl apply -f -
kubectl config set-context --current --namespace=lab
kubectl auth can-i create deployments.apps
脚本应输出三个 Ready 节点,kube-system 中 CoreDNS、kube-proxy 等 Pod 正常,并在最后返回 yes。若节点 NotReady,先运行 docker ps 与 kind export logs ./kind-logs --name k8s-lab,不要反复重建掩盖根因。
三、实现:部署并从宿主机访问
复用上一篇的 Deployment,但为 Service 指定 type: NodePort 和 nodePort: 30080。应用清单后执行 kubectl rollout status deployment/web,再访问 http://127.0.0.1:8080。请求路径为宿主机 8080、控制平面节点 30080、Service、EndpointSlice、Pod 80;明确链路后,任一层不通都有可检查对象。
可先用 kubectl create deployment web --image=nginx:1.27-alpine --dry-run=client -o yaml 学习生成,但最终清单要写入文件。命令生成的内容通常缺少标签规范、探针与资源配置,它是脚手架而非生产答案。访问失败时依次检查 docker port k8s-lab-control-plane、Service 端口、EndpointSlice 和 Pod Ready 状态。
用 kubectl run curl --image=curlimages/curl:8.10.1 --rm -it --restart=Never -- http://web 可以从集群内部验证 DNS 和 Service。如果内部成功、宿主机失败,问题就在端口映射或 NodePort;如果 DNS 失败,检查 CoreDNS;如果有 Service 却无端点,检查标签与 readiness。这种二分法以后同样适用于云集群。
四、踩坑:版本、镜像和上下文
Apple Silicon 或 ARM Linux 应选多架构镜像,自己构建的单架构业务镜像则可能出现 exec format error。本地镜像不会自动出现在 kind 节点,需执行 kind load docker-image <image> --name k8s-lab,并使用非 latest 标签及合适的 imagePullPolicy。真实仓库镜像则由节点通过 registry 拉取。
企业代理环境常见问题是宿主机能联网而节点容器不能拉镜像。应为 Docker daemon 正确设置代理、内部 CA 与镜像仓库,而不是关闭 TLS 验证。端口 8080 或 6443 被占用时,应明确修改配置中的 hostPort 或 apiServerPort,不能随机试错后忘记记录。
删除集群是破坏操作,先确认目标:kind get clusters,再执行 kind delete cluster --name k8s-lab。它不会删除源码,但会删除该集群内所有对象和数据。学习中的重要清单必须放在宿主机 Git 仓库,而非只保留在 etcd 或临时容器里。
五、验证:形成可重建基线
一套合格环境应满足四项:工具版本可打印;当前 context 明确是 kind-k8s-lab;全部节点 Ready;删除并依据配置重建后,应用仍能由清单恢复。把创建脚本加入仓库,团队成员和 CI 才能复用同一基线。
实验结束可以导出诊断信息,再删除集群并重新运行脚本。若第二次结果一致,说明环境没有依赖隐藏的手工步骤。生产环境当然不能用 kind 代替托管集群,但这里训练出的版本固定、上下文确认、分层验证和可销毁思维会直接迁移过去。
下一篇将继续使用 lab namespace,把 web 从最小清单扩展为带滚动发布策略、资源约束和回滚验证的 Deployment,并深入 Pod 生命周期。
还可以做一次“陌生机器演练”:让同事仅依据仓库中的版本说明、kind 配置和创建脚本操作,禁止口头补充步骤。如果对方需要手工改节点、复制隐藏文件或猜测代理设置,就把缺失条件写回文档和预检。为镜像下载失败、端口冲突、资源不足分别记录症状与解决办法,并区分可自动修复和必须人工决定的情形。这个练习能提前暴露只在作者电脑成立的隐含前提。
环境基线还应保存创建时间、Kubernetes 版本、节点镜像摘要、工具版本和宿主架构。升级其中一项后重新运行节点就绪、集群内访问、宿主端口访问和销毁重建四项测试。若团队需要同时维护多个实验,应为集群名、端口和配置设明确规则,避免共享默认上下文。至此得到的不只是一个能用的集群,而是一套可移交的环境契约。
在真正创建集群前,可先用标准库检查端口和节点计划,避免把明显冲突留给 kind。第一个程序验证节点角色、主机端口范围及唯一性;修改数据即可迁移到团队自己的本地集群配置生成器。
nodes = [
{"name": "control-plane", "role": "control-plane", "host_port": 8080},
{"name": "worker-a", "role": "worker", "host_port": None},
{"name": "worker-b", "role": "worker", "host_port": None},
]
errors: list[str] = []
ports: set[int] = set()
roles = [node["role"] for node in nodes]
if roles.count("control-plane") != 1:
errors.append("exactly one control-plane is required")
if roles.count("worker") < 1:
errors.append("at least one worker is required")
for node in nodes:
port = node["host_port"]
if port is None:
continue
if not 1024 <= port <= 65535:
errors.append(f"invalid port:{port}")
if port in ports:
errors.append(f"duplicate port:{port}")
ports.add(port)
print(f"nodes={len(nodes)} workers={roles.count('worker')}")
print(f"mapped_ports={sorted(ports)}")
print("validation=" + ("PASS" if not errors else "FAIL"))
运行输出:
nodes=3 workers=2
mapped_ports=[8080]
validation=PASS
第二个程序把 context 保护写成可测试函数。生产脚本同样应在修改前比较允许值,而不是只把当前 context 打印出来后依赖操作者肉眼判断。
from dataclasses import dataclass
@dataclass(frozen=True)
class KubectlTarget:
context: str
namespace: str
def authorize(target: KubectlTarget, allowed: set[KubectlTarget]) -> str:
if target not in allowed:
return "DENY"
if target.namespace in {"default", "kube-system"}:
return "DENY"
return "ALLOW"
allowed = {
KubectlTarget("kind-k8s-lab", "lab"),
KubectlTarget("kind-k8s-lab", "debug-lab"),
}
attempts = [
KubectlTarget("kind-k8s-lab", "lab"),
KubectlTarget("company-production", "lab"),
]
for attempt in attempts:
decision = authorize(attempt, allowed)
print(f"{attempt.context}/{attempt.namespace}={decision}")
运行输出:
kind-k8s-lab/lab=ALLOW
company-production/lab=DENY
参考来源
- kind Quick Start
- kind Configuration
- Install and Set Up kubectl
- Organizing Cluster Access Using kubeconfig
👍 觉得有用就点个 赞 + 收藏,方便回头查阅;有疑问直接在评论区留言,我看到都会回。
🚀 本文属于 《Kubernetes 上手实战》 系列,持续更新,关注不迷路。
📌 文章里的代码都能直接跑。想要可直接 clone 的完整工程 + 配套部署脚本 / 踩坑清单?评论一声或发邮件到 cj2664@qq.com,我免费发你。
如果你正好在做类似系统、或有工程化难题想找人做,也欢迎邮件聊一句——我按实际情况评估,能落地的就接单或出方案。评论和邮件都能直接找到我,不用跳别的平台。
更多推荐
所有评论(0)