# k8s 集群搭建与 kubectl 实操


## 导读

本篇解决本地 K8s 学习链路上的三个核心问题：用什么工具在单机上拉起一个可用的集群、kubectl 命令体系如何覆盖日常运维动作、怎样把一个 nginx Deployment 跑起来并从本机访问到它。选型上以 kind 为主力，因为它启动快、资源占用低，且能直接复用本机已有的 Docker 环境。

## 核心概念

本地拉起 K8s 集群有三条主流路径，差异体现在运行载体、启动开销和拓扑能力上。

**minikube** 是历史最久的单机方案，默认在虚拟机或 Docker 容器里跑一个单节点集群，自带 dashboard 和 addons 体系，适合零基础入门和快速体验官方插件。它的代价是启动偏慢，多节点拓扑支持有限，资源占用相对固定。

**kind**（Kubernetes IN Docker）用 Docker 容器模拟 K8s 节点，一个容器既是 node 又内嵌 kubelet 和容器运行时。启动通常只需几秒，支持用配置文件声明多控制面、多工作节点的拓扑，是 Kubernetes 自身 CI 和社区测试的事实标准，适合需要模拟真实多节点行为的场景。

**kubeadm** 是官方集群引导工具，本身不提供运行载体，而是直接在真实机器（裸机、云主机）上初始化控制面并引导工作节点加入。它面向生产环境搭建，需要自行准备机器、网络和容器运行时，学习成本最高但最贴近真实部署。

三者并非互斥：kind 负责本地开发与测试，kubeadm 负责生产集群初始化，minikube 介于两者之间偏重教学。本篇选用 kind，完整链路如下所示。

```mermaid
flowchart LR
    A[安装 kind 与 kubectl] --> B[创建集群<br/>kind create cluster]
    B --> C[kubectl get nodes<br/>验证节点 Ready]
    C --> D[编写 nginx Deployment YAML]
    D --> E[kubectl apply<br/>部署应用]
    E --> F[暴露 Service<br/>或 port-forward]
    F --> G[curl 验证<br/>nginx 首页]
```

图 1 描绘了从工具安装到访问验证的完整步骤链路。这条链路也是后续所有 K8s 实操的基本骨架：装工具、建集群、验状态、写清单、下发、暴露、访问，每一步都对应一个可验证的产物。

## 图解原理

kubectl 本身不直接操作节点，所有读写请求都发往 kube-apiserver，节点上的 kubelet 再根据 apiserver 的期望状态驱动容器运行时。kubectl 要连到哪个 apiserver、用什么身份认证，完全由 kubeconfig 文件决定。

kubeconfig 是一个 YAML 文件，默认路径 `~/.kube/config`，内部由三组对象构成：clusters 描述要连接的 apiserver 地址和 CA 证书，users 描述认证凭证（客户端证书或 Token），contexts 把一个 cluster 和一个 user 绑定成一组可切换的连接上下文。current-context 字段记录当前激活的 context，切换 context 就等于切换"连到哪个集群、以谁的身份"，这是多集群管理的核心机制。

```mermaid
flowchart TB
    subgraph 客户端
        KUBECTL[kubectl 命令]
    end
    subgraph kubeconfig 文件
        CURRENT[current-context<br/>当前激活上下文]
        CLUSTER[cluster<br/>server 地址 + CA 证书]
        USER[user<br/>客户端证书 / Token]
        CURRENT --> CLUSTER
        CURRENT --> USER
    end
    KUBECTL -->|读取 ~/.kube/config| CURRENT
    CLUSTER -->|指定连接地址与 TLS| APISERVER[kube-apiserver]
    USER -->|携带认证凭证| APISERVER
    APISERVER -->|调度期望状态| KUBELET[kubelet<br/>工作节点]
    KUBELET -->|驱动| RUNTIME[容器运行时]
```

图 2 展示了 kubectl 经由 kubeconfig 到达 apiserver 的连接模型。context 是三要素的粘合层：它引用一个 cluster 提供连接目标，引用一个 user 提供身份凭证，kubectl 每次发请求时都从 current-context 取出这两者组装成 TLS 连接。理解这个模型后，多集群、多租户的切换就只是改一个字段的事。

## 动手实操

### 安装 kind 与 kubectl

前提是本机已装好 Docker 并能正常拉取镜像。kind 把每个节点跑成一个 Docker 容器，没有 Docker 就没有运行载体。

```bash
# macOS 通过 Homebrew 安装 kind
brew install kind

# 或下载二进制（按平台替换 url 中的 darwin-amd64）
curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.23.0/kind-darwin-amd64
chmod +x ./kind
sudo mv ./kind /usr/local/bin/

# kubectl 同理
brew install kubectl
kubectl version --client
```

### 拉起第一个集群

单节点集群只需一条命令。kind 会自动拉取节点镜像、初始化控制面，并把 kubeconfig 写入 `~/.kube/config`。

```bash
kind create cluster --name k8s-learning
```

若要模拟多节点拓扑，先写一份配置文件再创建。下面的配置声明一个控制面加两个工作节点，适合后续观察 Pod 调度分布。

```yaml
# kind-config.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
- role: worker
```

```bash
kind create cluster --name k8s-learning --config kind-config.yaml
```

### kubectl 验证集群状态

集群创建完成后，用 get 系列命令确认节点就绪。

```bash
kubectl get nodes
kubectl cluster-info
```

正常输出里每个节点 STATUS 应为 Ready，control-plane 和 worker 角色分明。若看到 NotReady，通常是节点容器还在初始化网络插件，等待十几秒后重新查询即可。

### kubeconfig 与 context 切换

kind 创建集群时已自动生成并切换到对应 context。查看当前所有上下文与激活项，能帮你避免误操作到别的集群。

```bash
kubectl config get-contexts
kubectl config current-context
kubectl config use-context kind-k8s-learning
```

`get-contexts` 列出所有 context 及其绑定的 cluster、user，星号标记当前激活项。`use-context` 切换激活 context，之后的 kubectl 命令都会指向该集群。

### kubectl 核心命令体系

掌握下面七条命令，就能覆盖日常 80% 的运维动作。先用系统自带资源练手，再处理自定义应用。

```bash
# get：列出资源，-A 跨所有命名空间
kubectl get pods -A
kubectl get nodes -o wide

# describe：查看资源详细事件与状态，排错首选
kubectl describe node k8s-learning-worker

# apply：声明式下发，幂等创建或更新
kubectl apply -f nginx-deployment.yaml

# delete：按文件或按名称删除
kubectl delete -f nginx-deployment.yaml
kubectl delete pod <pod-name>

# logs：查看容器日志，--tail 限制行数
kubectl logs -l app=nginx --tail=20

# exec：进入容器执行命令
kubectl exec -it <pod-name> -- nginx -v

# port-forward：把本地端口转发到 Pod，临时访问用
kubectl port-forward deployment/nginx-deploy 8080:80
```

### 部署 nginx Deployment

写一份 Deployment 清单，声明两个副本。YAML 里的 selector 与 template.labels 必须一致，否则 apiserver 会拒绝创建。

```yaml
# nginx-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deploy
  labels:
    app: nginx
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.27
        ports:
        - containerPort: 80
```

```bash
kubectl apply -f nginx-deployment.yaml
kubectl get pods -l app=nginx -o wide
kubectl describe deployment nginx-deploy
```

`-o wide` 会额外显示 Pod 调度到哪个节点、内部 IP，多节点集群下能直观看到副本分布。

### 验证访问

临时访问用 port-forward，它把本地 8080 端口直接接到 Deployment 的 80 端口，不需要 Service。

```bash
# 前台运行，另开终端执行 curl
kubectl port-forward deployment/nginx-deploy 8080:80
curl http://localhost:8080
```

看到 nginx 默认欢迎页即说明 Pod 真正起来了。更持久的方式是创建 Service，用 NodePort 在节点上暴露固定端口。

```yaml
# nginx-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: nginx-svc
spec:
  type: NodePort
  selector:
    app: nginx
  ports:
  - port: 80
    targetPort: 80
    nodePort: 30080
```

```bash
kubectl apply -f nginx-service.yaml
kubectl get svc nginx-svc
```

kind 集群的节点是 Docker 容器，NodePort 无法直接从宿主机访问。需要通过 port-forward 转发到 Service，或用 `kubectl port-forward svc/nginx-svc 8080:80` 绕过这一限制。

## 常见问题与避坑

**kind 节点镜像拉取慢或超时**。kind 依赖的 `kindest/node` 镜像从 docker.io 拉取，国内网络容易超时。解决办法是提前手动拉取并改用本地镜像，或在 create 时通过 `--image` 指定已加速的镜像源标签。

**port-forward 长时间空闲自动断开**。kubectl 的 port-forward 默认有空闲超时，长时间不产生流量会被内核回收连接。需要持续访问时，用 Service 的 NodePort 或 LoadBalancer 替代，或加 `--keepalive` 参数保活。

**context 指向错误集群导致误删**。多集群环境下最容易踩的坑是在生产 context 下执行了 delete。每次执行写操作前，先用 `kubectl config current-context` 核对当前指向，或在 delete 命令前加 `--dry-run=server` 预演。

## 小结与进阶

kind 拉起集群、kubectl 操控资源、kubeconfig 管理连接，这三者构成了 K8s 日常操作的完整闭环。下一篇将深入 Pod 与 Deployment 的调度原理，拆解副本控制器如何维持期望状态、节点如何被选中承载 Pod。

