上一篇建立了 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 pskind export logs ./kind-logs --name k8s-lab,不要反复重建掩盖根因。

三、实现:部署并从宿主机访问

复用上一篇的 Deployment,但为 Service 指定 type: NodePortnodePort: 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

参考来源


👍 觉得有用就点个 赞 + 收藏,方便回头查阅;有疑问直接在评论区留言,我看到都会回。

🚀 本文属于 《Kubernetes 上手实战》 系列,持续更新,关注不迷路。

📌 文章里的代码都能直接跑。想要可直接 clone 的完整工程 + 配套部署脚本 / 踩坑清单?评论一声或发邮件到 cj2664@qq.com,我免费发你。
如果你正好在做类似系统、或有工程化难题想找人做,也欢迎邮件聊一句——我按实际情况评估,能落地的就接单或出方案。评论和邮件都能直接找到我,不用跳别的平台。

更多推荐