我在 Debian 11 上把 K8s 单机搭起来了,过程没你想的那么顺(/opt 目录版)

前言

这事儿得从前天晚上说起。我手头有一台 Debian 11 的虚拟机,想着搭个 K8s 单节点环境平时做实验用。网上教程一搜一大把,可真跟着做的时候才发现,十个教程九个坑,还有一个写了一半就不见了。

踩了两天坑之后,我终于把这套流程捋顺了,而且还特意把所有数据目录都挪到了 /opt 下面------因为系统盘就剩 20G,不挪地方早晚要崩。我把整个过程记了下来,既给自己留个底,也给遇到同样问题的朋友省点时间。这篇文章不是流水账,里面夹了不少我的真实感受,比如"呵呵,这我没想到"的那种瞬间,你懂的。

强烈建议搭配AI食用(当然,也别把AI太当一回事~)


环境与思路

项目 我的实际配置
主机名 k8s-master
IP 192.168.1.10(改成你自己的)
系统 Debian 11
数据目录 /opt(所有容器、kubelet 数据都放这)
Kubernetes v1.31
容器运行时 containerd
网络插件 Flannel

重点:IP 和主机名后面会反复用到,千万别写错了。我第一遍就是 hosts 文件里多打了一个空格,结果 kubeadm init 直接报错退出了,排查了半小时,只能说"没办法,自己手贱"。


一、把主机名和 hosts 收拾干净

这是第一步,也是最容易翻车的一步。很多人上来就装软件,结果后面 service 启动不了还不知道为什么。

bash 复制代码
sudo hostnamectl set-hostname k8s-master

然后编辑 /etc/hosts

bash 复制代码
sudo nano /etc/hosts

内容改成这样(注意 IP 换成你自己的):

复制代码
127.0.0.1       localhost
127.0.1.1       k8s-master

192.168.1.10    k8s-master

验证一下解析:

bash 复制代码
hostname
hostname -i

必须返回你的真实 IP,否则 kubelet 会找不到北。


二、系统初始化,顺手装些常用工具

这步没啥好说的,纯体力活:

bash 复制代码
sudo apt update
sudo apt upgrade -y
sudo apt install -y curl vim wget net-tools gnupg lsb-release

早知道后面要频繁 curl,我第一个就该装它。


三、iptables 切换成 legacy 模式------Debian 11 绕不过的坎

Deiban 11 默认用的是 nftables,而 K8s 的一些组件(尤其是 kube-proxy)跟 legacy 模式更搭。不切换的话,后面网络会有各种莫名其妙的问题。我一开始没当回事,结果 Pod 之间死活不通,查了半天日志才发现是这里。

bash 复制代码
sudo apt install -y iptables arptables ebtables

sudo update-alternatives --set iptables /usr/sbin/iptables-legacy
sudo update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy
sudo update-alternatives --set arptables /usr/sbin/arptables-legacy
sudo update-alternatives --set ebtables /usr/sbin/ebtables-legacy

检查一下:

bash 复制代码
iptables --version

看到 (legacy) 字样就对了。呵呵,这步不做后面有你哭的时候。


四、关掉 Swap,不然 kubelet 会抗议

Kubernetes 很固执,它认为 swap 会影响性能,所以只要检测到 swap 开着,kubelet 就直接罢工。

bash 复制代码
sudo swapoff -a
sudo sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab

free -m 看一眼,Swap 那行全是 0 就对了。


五、加载内核模块

两个模块必须提前加载:overlaybr_netfilter,一个是给容器文件系统用的,一个是给网络桥接用的。

bash 复制代码
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF

sudo modprobe overlay
sudo modprobe br_netfilter

六、调整 sysctl 参数

这几个参数允许 iptables 正确看到桥接流量,并且开启内核转发。

bash 复制代码
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 --system

七、对个时

分布式系统对时间很敏感,差太多的话证书验证都会失败。装个 chrony 省心:

bash 复制代码
sudo apt install -y chrony
sudo systemctl enable chrony
sudo systemctl start chrony
timedatectl

看到 System clock synchronized: yes 就放心了。


八、创建 /opt 下的数据目录

这是为了把数据从系统盘挪出来,毕竟根分区空间金贵。

bash 复制代码
sudo mkdir -p /opt/containerd
sudo mkdir -p /opt/kubelet
sudo mkdir -p /opt/etcd
sudo mkdir -p /opt/cni
sudo mkdir -p /opt/log

其实 etcd 在单机下默认放 /var/lib/etcd,但我们可以后面通过配置改掉,或者直接软链接,这个按需来。我这里先建着,后面 kubeadm init 的时候会用到 /opt/etcd


九、安装 containerd(用 Docker 官方源)

用 Docker 的官方 containerd 包,版本比较新,兼容性也好。

bash 复制代码
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker.gpg

echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker.gpg] \
https://download.docker.com/linux/debian \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list

sudo apt update
sudo apt install -y containerd.io

然后生成默认配置并修改两个地方:数据根目录SystemdCgroup

bash 复制代码
sudo mkdir -p /etc/containerd
containerd config default | sudo tee /etc/containerd/config.toml

编辑配置文件:

bash 复制代码
sudo nano /etc/containerd/config.toml

找到 root 字段改成 /opt/containerd,找到 SystemdCgroup 改成 true

toml 复制代码
root = "/opt/containerd"
state = "/run/containerd"

[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
  SystemdCgroup = true

重启并设置开机自启:

bash 复制代码
sudo systemctl restart containerd
sudo systemctl enable containerd

十、安装 Kubernetes 组件

添加官方源,然后装 kubelet、kubeadm、kubectl,并锁定版本防止被意外升级。

bash 复制代码
sudo mkdir -p /etc/apt/keyrings

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 update
sudo apt install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl

十一、把 kubelet 的数据目录也改到 /opt

默认 kubelet 会把数据存在 /var/lib/kubelet,这跟我们的规划不符。通过 systemd 的 drop-in 文件来覆盖启动参数。

bash 复制代码
sudo mkdir -p /etc/systemd/system/kubelet.service.d

创建配置文件:

bash 复制代码
cat <<EOF | sudo tee /etc/systemd/system/kubelet.service.d/10-kubelet.conf
[Service]
Environment="KUBELET_EXTRA_ARGS=--root-dir=/opt/kubelet"
EOF

重载并重启 kubelet:

bash 复制代码
sudo systemctl daemon-reload
sudo systemctl restart kubelet

这一步在 init 之前做最合适,因为 init 的时候 kubelet 会开始写数据,如果不提前改目录,就会写到 /var/lib 下面去,后面再挪就麻烦了。早知道我第一遍就该这么干


十二、kubeadm init------最紧张的一步

终于要初始化集群了。先装几个必要的网络工具包(kubeadm 依赖它们):

bash 复制代码
sudo apt update
sudo apt install -y conntrack ebtables ethtool socat iproute2

然后执行 init。注意把 --apiserver-advertise-address 换成你自己的 IP,--pod-network-cidr 使用 10.244.0.0/16 是为了和 Flannel 默认网段匹配。

bash 复制代码
sudo kubeadm init \
  --apiserver-advertise-address=192.168.1.10 \
  --pod-network-cidr=10.244.0.0/16 \
  --cri-socket=unix:///run/containerd/containerd.sock

如果一切顺利,最后会输出一段 kubeadm join ... 的命令,一定保存下来!虽然我们是单节点用不上 join,但万一以后想加节点呢?

如果失败了怎么办?我就失败过一次,因为之前残留了一些配置。这时候需要彻底清理:

bash 复制代码
sudo kubeadm reset -f
sudo rm -rf /var/lib/etcd
sudo rm -rf /etc/kubernetes/manifests/*

然后再重新 init。这个过程就像重启电脑,没什么大不了,就是多等几分钟。


十三、配置 kubectl 让普通用户也能用

bash 复制代码
mkdir -p $HOME/.kube
sudo cp /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

测试一下:

bash 复制代码
kubectl get nodes

这时候节点状态应该是 NotReady,因为网络插件还没装。别慌,这是正常的。


十四、安装 Flannel 网络插件

bash 复制代码
kubectl apply -f https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml

等个一两分钟,再 kubectl get nodes,状态就变成 Ready 了。我第一次看到这个结果的时候长舒一口气------总算没白折腾。


十五、允许 Master 节点调度 Pod(单机必做)

默认情况下,Master 节点是打了污点(taint)的,不允许普通 Pod 运行。我们既然是单机,就得去掉它:

bash 复制代码
kubectl taint nodes --all node-role.kubernetes.io/control-plane-

验证一下污点没了:

bash 复制代码
kubectl describe node | grep -i taint

十六、跑个 Nginx 测试一下

bash 复制代码
kubectl create deployment nginx --image=nginx
kubectl expose deployment nginx --port=80 --type=NodePort
kubectl get svc nginx

记下那个 30000+ 的端口,浏览器访问 http://你的服务器IP:端口,看到 Nginx 欢迎页就说明集群完全可用了。

测试完可以把这俩资源删掉,免得占用端口:

bash 复制代码
kubectl delete deployment nginx
kubectl delete service nginx

十七、最终的数据目录结构

检查一下我们的劳动成果:

bash 复制代码
ls /opt/containerd
ls /opt/kubelet

应该能看到 containerd 的元数据、kubelet 的 pod 目录都乖乖躺在这里。没白费功夫。


十八、完整的安装顺序------很多人失败就是因为跳步

如果你是按我的步骤从头做下来的,顺序应该是这样的:

  1. 修改 hostname + hosts
  2. apt update && upgrade
  3. iptables 切换 legacy
  4. 关闭 swap
  5. 加载内核模块
  6. 设置 sysctl
  7. 时间同步
  8. 安装 containerd 并配置
  9. 安装 kubeadm/kubelet/kubectl
  10. 修改 kubelet 数据目录
  11. kubeadm init
  12. 配置 kubectl
  13. 安装 Flannel
  14. 去除 Master 污点
  15. 测试部署

Kubernetes 安装失败,90% 都是顺序错了,或者是某一步没生效就急着往下走。没办法,这玩意就是环环相扣,少一个螺丝都不行。


进阶:想让这套环境更好用?聊聊 Kite 和其他组件

你可能会问,接下来我该装什么?我见过不少新手搭完集群就迷茫了。其实 Kubernetes 本身只提供基础能力,后面要加的东西都是"选装包"。下面我结合自己的使用习惯和你提到的 Kite 说说怎么选。

这些组件到底要不要装?

组件 是否必须 我的建议
Helm 非必须,但强烈推荐 K8s 的应用商店,后面装 Kite 也会用到,先装上吧。
Ingress Nginx 非必须 如果你想用域名访问集群里的服务(比如 Kite 控制台),就需要;单机 IP 访问可以暂时不装。
Kubernetes Dashboard 非必须 Kite 完全可以替代它,界面好看功能还多,没必要装两个。
Metrics Server 非必须 想让 Kite 或 kubectl top 看到 CPU/内存曲线,就必须装,否则图表是空的。
Prometheus + Grafana 非必须 Kite 已经内置了基础监控图表,除非你需要长期存储和高级告警,否则先放一放。
StorageClass 非必须 跑无状态应用用不到,等你想玩数据库了再说。单机测试用 hostPath 就行。

Kite 能覆盖哪些?

根据官方介绍,Kite 自带:

  • 资源管理界面(替代 Dashboard)
  • 实时 CPU/内存/网络图表(需要 Metrics Server 提供数据)
  • Web 终端(直接进容器操作)
  • 日志查看、YAML 编辑
  • 多集群切换

所以我个人觉得,对于单机实验环境,装个 Kite 就够你玩的了

我推荐的后续安装顺序

1. 安装 Helm

bash 复制代码
curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3
chmod 700 get_helm.sh
./get_helm.sh

2. 安装 Metrics Server

bash 复制代码
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

注意:在某些自签证书的环境下,Metrics Server 可能需要添加 --kubelet-insecure-tls 参数才能正常工作。如果装完 Kite 看不到监控数据,记得去检查一下 Metrics Server 的日志。

3. 用 Helm 部署 Kite

bash 复制代码
helm repo add kite https://kite-org.github.io/kite/
helm repo update
helm install kite kite/kite -n kube-system

4. 访问 Kite

默认 Kite 的 Service 是 ClusterIP 类型,只能集群内部访问。最简单暴力的方式是用 kubectl port-forward

bash 复制代码
nohup kubectl port-forward -n kube-system svc/kite 8080:8080 &

然后浏览器打开 http://你的服务器IP:8080 就行。

云服务器访问不了怎么办?

如果你用的是云服务器,port-forward 默认只监听 127.0.0.1,外网是打不开的。这时候有两个办法:

办法一:改用 NodePort

bash 复制代码
kubectl patch svc kite -n kube-system -p '{"spec":{"type":"NodePort"}}'
kubectl get svc -n kube-system kite

会看到一个类似 8080:31234 的端口映射,去云控制台的安全组里开放那个 31234 端口,然后公网访问 http://公网IP:31234 即可。

办法二:配置 Nginx 反向代理

在宿主机上装个 Nginx,把域名流量转发到 127.0.0.1:8080,再加个 SSL 证书,体验和线上服务一样。具体配置不展开,网上大把教程。


写在最后

这篇文章算是我这几天折腾 Debian 11 + K8s 的一个完整记录。过程中无数次想砸键盘,但最后看到 Nginx 页面和 Kite 仪表板的那一刻,又觉得挺值的。如果你也正在啃这块硬骨头,希望这篇笔记能帮你少走弯路。

要是你在哪一步卡住了,或者发现有更好的优化方法,欢迎在评论区交流------毕竟 K8s 的世界里,谁还不是一边踩坑一边成长呢?

PS:单机版k8s纯手搓也就图一乐,市面上快速搭建k8s集群的还是蛮多的。

相关推荐
bukeyiwanshui19 小时前
20260626 k8sControllers介绍
云原生·容器·kubernetes
能有时光20 小时前
] K8s 核心组件使用指南
云原生·容器·kubernetes
云烟成雨TD1 天前
Kubernetes 系列【13】控制器:可视化操作 Deployment
云原生·容器·kubernetes
运维老郭1 天前
【K8s运维实战】Kubernetes临时存储卷实战:emptyDir核心用法与gitRepo弃用迁移指南
运维·云原生·kubernetes
gs801402 天前
记一次多 Agent 架构在单节点 K8s 触发的 PID 耗尽与 Pod 驱逐(Evicted)大摸排
容器·架构·kubernetes
bukeyiwanshui2 天前
20260701 k8s Metric Server
容器·贪心算法·kubernetes
Zhu7582 天前
在k8s集群部署ApacheHadoop单节点单数据副本
云原生·容器·kubernetes
摇滚侠2 天前
云原生 Java 架构师的第一课 K8s+Docker+KubeSphere+DevOps 51-60
java·云原生·kubernetes
bukeyiwanshui2 天前
20260703 K8s ECShop商城项目
云原生·容器·kubernetes