Kubernetes 集群搭建指南
基于 Dell R730 PVE 虚拟化环境,使用 kubeadm 部署 K8s 集群
版本:K8s v1.34 | 容器运行时:containerd | CNI:Calico | 存储:Local Path Provisioner
一、准备工作
1.1 环境概述
| 项目 | 说明 |
|---|---|
| 硬件平台 | Dell R730 (PVE 9.1) |
| K8s 版本 | v1.34 |
| 节点数量 | 1 Master + 2 Worker |
| 网络插件 | Calico |
| 容器运行时 | containerd |
| 存储类型 | NFS (nfs-subdir-external-provisioner) |
1.1.1 整体架构图
1.2 节点信息
| 节点角色 | 主机名 | vCPU | 内存 | 系统盘 | 数据盘 | 内网 IP | 用途 |
|---|---|---|---|---|---|---|---|
| Master | k8s-master | 6 | 4GB | 32GB | 64GB | 192.168.1.165 | 控制平面 |
| Worker-1 | k8s-worker-1 | 8 | 6GB | 32GB | 100GB | 192.168.1.166 | 工作节点 |
1.3 前置要求
操作系统要求:
- Ubuntu 24.04 LTS ✅ (当前环境)
- Ubuntu 22.04 LTS
- 最低配置:2核 2GB
系统配置:
bash
# 关闭 swap
sudo swapoff -a && sudo sed -i '/ swap / s/^\(.*\)$/#\1/' /etc/fstab
# 加载内核模块
sudo modprobe overlay && sudo modprobe br_netfilter
# 配置 sysctl
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
sudo sysctl --system1.4 网络规划
物理网络 (PVE 网桥 vmbr0)
| IP 地址 | 主机名 | 用途 |
|---|---|---|
| 192.168.1.161 | easytier-gateway | EasyTier 网关 |
| 192.168.1.162 | monitor-storage | 监控 + NFS 存储 |
| 192.168.1.165 | k8s-master | 控制平面 / API Server |
| 192.168.1.166 | k8s-worker-1 | 工作节点 |
网段:192.168.1.0/24
Kubernetes Pod 网络 (Calico CNI)
| 网段 | 用途 | 分配给 |
|---|---|---|
| 10.244.0.0/24 | Master 节点 Pod | k8s-master |
| 10.244.1.0/24 | Worker-1 Pod | k8s-worker-1 |
网段:10.244.0.0/16 (总计 256 个 /24 子网)
Kubernetes Service 网络
| 网段 | 用途 |
|---|---|
| 10.96.0.0/16 | ClusterIP、NodePort、LoadBalancer、DNS |
二、安装容器运行时 (containerd)
为什么选 containerd? kubeadm 默认推荐、轻量稳定、Ubuntu 24.04 默认集成
2.1 安装步骤
所有节点执行:
bash
# 1. 安装 containerd
sudo apt-get update && sudo apt-get install -y containerd
# 2. 生成默认配置
sudo mkdir -p /etc/containerd
sudo containerd config default | sudo tee /etc/containerd/config.toml
# 3. 配置 systemd cgroup 驱动 (必须!)
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml
# 4. 重启并启用
sudo systemctl restart containerd && sudo systemctl enable containerd
# 5. 验证
sudo ctr version2.2 关键配置说明
| 配置 | 作用 |
|---|---|
SystemdCgroup = true | 与 kubelet cgroupDriver 保持一致 |
三、安装 kubeadm、kubelet、kubectl
3.1 添加 Kubernetes 仓库 (阿里云镜像源)
所有节点执行:
bash
# 1. Ubuntu 24.04 需要创建密钥目录
sudo mkdir -p -m 755 /etc/apt/keyrings
# 2. 获取 GPG 密钥
curl -fsSL https://mirrors.aliyun.com/kubernetes-new/core/stable/v1.34/deb/Release.key |
gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
# 3. 添加阿里云镜像源 (传统 deb 格式)
echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://mirrors.aliyun.com/kubernetes-new/core/stable/v1.34/deb/ /" | \
sudo tee /etc/apt/sources.list.d/kubernetes.list
# 4. 安装并锁定版本
sudo apt-get update && sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl
# 5. 启用 kubelet
sudo systemctl enable --now kubelet镜像源:
https://mirrors.aliyun.com/kubernetes-new/(国内快速)
3.2 版本验证
bash
# 查看已安装版本
kubeadm version
kubelet --version
kubectl version --client四、节点数据盘配置
重要:数据盘配置必须在初始化集群前完成,否则容器数据会占满系统盘导致 Pod 被驱逐。
4.1 为什么需要独立数据盘
┌─────────────────────────────────────────────────────────────┐
│ kubelet evictionHard 只监控根分区 │
├─────────────────────────────────────────────────────────────┤
│ │
│ /dev/sda (系统盘 32GB) │
│ └─ / (根分区) ← kubelet 只监控这里 │
│ /var/lib/kubelet ← 容器数据全在这里 │
│ │
│ 问题:容器镜像占用系统盘,根分区满了 │
│ → kubelet 驱逐所有 Pod │
│ → 即使数据盘还有 90GB │
│ │
└─────────────────────────────────────────────────────────────┘
解决方案:软链接 /var/lib/kubelet → /data/kubelet4.2 初始化 LVM 数据卷
bash
# 1. 清空签名 → 2. 创建 PV → 3. 创建 VG → 4. 创建 LV → 5. 格式化
sudo wipefs -a /dev/sdb
sudo pvcreate /dev/sdb
sudo vgcreate data-vg /dev/sdb
sudo lvcreate -l 100%FREE -n data-lv data-vg
sudo mkfs.ext4 /dev/data-vg/data-lv
# 6. 挂载到 /data
sudo mkdir -p /data
sudo mount /dev/data-vg/data-lv /data
# 7. 永久挂载(重启后生效)
echo '/dev/mapper/data--vg-data--lv /data ext4 defaults 0 0' | sudo tee -a /etc/fstab
# 8. 验证
df -h /data4.3 迁移 kubelet 到数据盘
bash
# 1. 停止服务
sudo systemctl stop kubelet
# 2. 迁移数据到数据盘
sudo mkdir -p /data/kubelet
sudo rsync -av /var/lib/kubelet/ /data/kubelet/
# 3. 创建软链接替换原目录
sudo rm -rf /var/lib/kubelet
sudo ln -s /data/kubelet /var/lib/kubelet
# 4. 重启服务
sudo systemctl start kubelet
# 5. 验证
ls -la /var/lib/kubelet4.4 迁移 containerd 到数据盘
bash
# 1. 停止服务
sudo systemctl stop kubelet
sudo systemctl stop containerd
# 2. 迁移数据到数据盘
sudo mkdir -p /data/containerd
sudo rsync -av /var/lib/containerd/ /data/containerd/
# 3. 创建软链接替换原目录
sudo rm -rf /var/lib/containerd
sudo ln -s /data/containerd /var/lib/containerd
# 4. 启动服务(containerd 必须先启动)
sudo systemctl start containerd
sudo systemctl start kubelet
# 5. 验证
ls -la /var/lib/containerd
sudo ctr version五、初始化 Master 节点
5.1 创建初始化配置文件
仅在 Master 节点执行:
bash
cat > kubeadm-config.yaml << 'EOF'
apiVersion: kubeadm.k8s.io/v1beta3
kind: InitConfiguration
localAPIEndpoint:
advertiseAddress: "192.168.1.165"
bindPort: 6443
nodeRegistration:
criSocket: unix:///var/run/containerd/containerd.sock
name: k8s-master
taints: nil
---
apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
kubernetesVersion: "v1.34.0"
clusterName: "homelab-k8s"
controlPlaneEndpoint: "192.168.1.165:6443"
networking:
dnsDomain: "cluster.local"
podSubnet: "10.244.0.0/16"
serviceSubnet: "10.96.0.0/16"
apiServer:
extraArgs:
service-node-port-range: "30000-32767"
---
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
cgroupDriver: systemd
EOF5.2 执行初始化
bash
# 初始化 Master 节点
sudo kubeadm init --config=kubeadm-config.yaml --upload-certs5.3 配置 kubectl
bash
# 当前用户配置
mkdir -p ~/.kube
sudo cp /etc/kubernetes/admin.conf ~/.kube/config
sudo chown $(id -u):$(id -g) ~/.kube/config
# 验证集群
kubectl get nodes
kubectl get pods -A六、安装 Calico CNI
6.1 为什么选 Calico?
| 特性 | Calico | Flannel | 说明 |
|---|---|---|---|
| 网络策略 | ✅ 支持 | ❌ 不支持 | 网络安全必需 |
| 性能 | 高 | 中 | eBPF 加速 |
| 复杂度 | 中 | 低 | Flannel 简单 |
| 社区活跃 | ✅ | ✅ | 都很活跃 |
6.2 安装 Calico
bash
# 使用 manifest (推荐)
kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.28.2/manifests/calico.yamlCalico 版本:v3.28.2 是 K8s 1.34 的推荐版本
6.3 验证 Calico
bash
# 检查 Pod 运行状态
kubectl get pods -n kube-system -l k8s-app=calico-node
# 检查节点网络
kubectl get nodes -o wide七、Worker 节点加入集群
7.1 使用 Token 加入
bash
# 查看现有 Token
kubeadm token list
# 创建新 Token (如果过期)
kubeadm token create --print-join-command7.2 加入 Worker 节点
bash
# 在 Worker 节点上执行
sudo kubeadm join 192.168.1.165:6443 \
--token <your-token> \
--discovery-token-ca-cert-hash sha256:<your-hash>7.3 验证节点状态
bash
# 在 Master 上执行
kubectl get nodes
# 状态应为 Ready
NAME STATUS ROLES AGE VERSION
k8s-master Ready control-plane 10m v1.34.0
k8s-worker-1 Ready <none> 2m v1.34.0八、集群基础配置
8.1 安装核心插件
bash
# Metrics Server (提供 kubectl top 命令)
kubectl apply -f - <<'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
name: metrics-server
namespace: kube-system
spec:
selector:
matchLabels:
k8s-app: metrics-server
template:
metadata:
labels:
k8s-app: metrics-server
spec:
containers:
- name: metrics-server
image: registry.k8s.io/metrics-server/metrics-server:v0.7.2
args:
- --kubelet-insecure-tls
- --kubelet-preferred-address-types=InternalIP
resources:
requests:
cpu: 100m
memory: 200Mi
tolerations:
- key: "node-role.kubernetes.io/control-plane"
effect: "NoSchedule"
EOF8.2 创建命名空间
bash
# 创建业务命名空间
kubectl create namespace network # 网络组件 (Ingress)
kubectl create namespace ci-cd # CI/CD 平台
kubectl create namespace monitoring # 监控系统
kubectl create namespace storage # 存储组件
kubectl create namespace infra # 基础设施
kubectl create namespace apps # 业务应用
# 验证
kubectl get namespaces九、存储配置 (Local Path Provisioner)
9.1 为什么用 Local Path Provisioner
| 存储方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| Local Path | 性能高,配置简单 | 不支持跨节点共享 | 内网 homelab 场景 |
| NFS | 支持多节点共享 | 需要额外 NFS 服务器 | 跨节点有状态应用 |
9.2 安装 Local Path Provisioner
bash
# 部署 Local Path Provisioner
kubectl apply -f https://raw.githubusercontent.com/rancher/local-path-provisioner/v0.0.26/deploy/local-path-provisioner.yaml
# 设置为默认 StorageClass
kubectl patch storageclass local-path -p '{"metadata":{"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'9.3 验证存储配置
bash
# 检查 StorageClass
kubectl get storageclass
# 测试 PVC 动态创建
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: test-pvc
namespace: storage
spec:
accessModes:
- ReadWriteOnce
storageClassName: local-path
resources:
requests:
storage: 1Gi
EOF
# 检查 PVC 状态
kubectl get pvc -n storage
# 清理测试资源
kubectl delete pvc test-pvc -n storage十、Ingress Controller 部署
10.1 部署 NGINX Ingress Controller
为什么用 hostNetwork? 当前资源有限,使用 hostNetwork 简化部署。
bash
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: Namespace
metadata:
name: network
---
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
name: nginx
annotations:
ingressclass.kubernetes.io/is-default-class: "true"
spec:
controller: k8s.io/ingress-nginx
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: ingress-nginx
namespace: network
spec:
selector:
matchLabels:
app: ingress-nginx
template:
metadata:
labels:
app: ingress-nginx
spec:
hostNetwork: true
dnsPolicy: ClusterFirstWithHostNet
containers:
- name: controller
image: registry.k8s.io/ingress-nginx/controller:v1.12.0
args:
- /nginx-ingress-controller
- --controller-class=k8s.io/ingress-nginx
- --ingress-class=nginx
ports:
- name: http
containerPort: 80
hostPort: 80
- name: https
containerPort: 443
hostPort: 443
resources:
requests:
cpu: 100m
memory: 90Mi
livenessProbe:
failureThreshold: 5
httpGet:
path: /healthz
port: 10254
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
failureThreshold: 3
httpGet:
path: /healthz
port: 10254
initialDelaySeconds: 10
periodSeconds: 10
EOF10.2 验证 Ingress Controller
bash
# 检查 Pod 运行状态
kubectl get pods -n network
# 测试访问
curl -I http://192.168.1.165十一、集群验证清单
11.1 节点状态
bash
kubectl get nodes -o wide预期输出:
NAME STATUS ROLES AGE VERSION INTERNAL-IP
k8s-master Ready control-plane 1h v1.34.0 192.168.1.165
k8s-worker-1 Ready <none> 45m v1.34.0 192.168.1.16611.2 系统组件
bash
# 检查所有系统 Pod
kubectl get pods -n kube-system十二、集群管理
12.1 常用管理命令
bash
# 集群状态
kubectl cluster-info
# 节点维护
kubectl drain k8s-worker-1 --ignore-daemonsets --delete-emptydir-data # 驱逐 Pod
kubectl uncordon k8s-worker-1 # 恢复调度
# 资源查看
kubectl top nodes
kubectl top pods -A12.2 etcd 备份与恢复
bash
# 备份 etcd
sudo ETCDCTL_API=3 etcdctl snapshot save /tmp/etcd-snapshot.db \
--endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key
# 验证备份
sudo ETCDCTL_API=3 etcdctl --write-out=table snapshot status /tmp/etcd-snapshot.db十三、常见问题排查
13.1 节点 NotReady
bash
# 检查 kubelet 状态
sudo systemctl status kubelet
sudo journalctl -u kubelet -n 100
# 检查网络插件
kubectl logs -n kube-system -l k8s-app=calico-node --tail=5013.2 Pod 无法启动
bash
# 检查 Events
kubectl describe pod <pod-name> -n <namespace>
# 检查调度
kubectl get events -n <namespace> --sort-by='.lastTimestamp'13.3 PVC 无法绑定
bash
# 检查 StorageClass
kubectl get storageclass
# 检查 Local Path Provisioner
kubectl get pods -n local-path-storage十四、后续步骤
完成集群搭建后,推荐按以下顺序部署:
| 顺序 | 内容 | 说明 |
|---|---|---|
| 1 | 监控 | Prometheus 接入 K8s 指标采集 |
| 2 | 日志系统 | Loki + Promtail |
| 3 | CI/CD 平台 | Gitea + Harbor |
| 4 | 业务应用 | VitePress 博客等 |