我在Debian 11上把K8s单机搭起来了,过程难道不是你想象中的那么顺利吗?

摘要:前言 这事儿得从前天晚上说起。我手头有一台 Debian 11 的虚拟机,想着搭个 K8s 单节点环境平时做实验用。网上教程一搜一大把,可真跟着做的时候才发现,十个教程九个坑,还有一个写了一半就不见了。 踩了两天坑之后,我终于把这套流程捋顺
前言 这事儿得从前天晚上说起。我手头有一台 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 启动不了还不知道为什么。 sudo hostnamectl set-hostname k8s-master 然后编辑 /etc/hosts: sudo nano /etc/hosts 内容改成这样(注意 IP 换成你自己的): 127.0.0.1 localhost 127.0.1.1 k8s-master 192.168.1.10 k8s-master 验证一下解析: hostname hostname -i 必须返回你的真实 IP,否则 kubelet 会找不到北。 二、系统初始化,顺手装些常用工具 这步没啥好说的,纯体力活: 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 之间死活不通,查了半天日志才发现是这里。 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 检查一下: iptables --version 看到 (legacy) 字样就对了。呵呵,这步不做后面有你哭的时候。 四、关掉 Swap,不然 kubelet 会抗议 Kubernetes 很固执,它认为 swap 会影响性能,所以只要检测到 swap 开着,kubelet 就直接罢工。 sudo swapoff -a sudo sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab 用 free -m 看一眼,Swap 那行全是 0 就对了。 五、加载内核模块 两个模块必须提前加载:overlay 和 br_netfilter,一个是给容器文件系统用的,一个是给网络桥接用的。 cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf overlay br_netfilter EOF sudo modprobe overlay sudo modprobe br_netfilter 六、调整 sysctl 参数 这几个参数允许 iptables 正确看到桥接流量,并且开启内核转发。 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 省心: sudo apt install -y chrony sudo systemctl enable chrony sudo systemctl start chrony timedatectl 看到 System clock synchronized: yes 就放心了。 八、创建 /opt 下的数据目录 这是为了把数据从系统盘挪出来,毕竟根分区空间金贵。 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 包,版本比较新,兼容性也好。 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。 sudo mkdir -p /etc/containerd containerd config default | sudo tee /etc/containerd/config.toml 编辑配置文件: sudo nano /etc/containerd/config.toml 找到 root 字段改成 /opt/containerd,找到 SystemdCgroup 改成 true: root = "/opt/containerd" state = "/run/containerd" [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options] SystemdCgroup = true 重启并设置开机自启: sudo systemctl restart containerd sudo systemctl enable containerd 十、安装 Kubernetes 组件 添加官方源,然后装 kubelet、kubeadm、kubectl,并锁定版本防止被意外升级。 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 文件来覆盖启动参数。 sudo mkdir -p /etc/systemd/system/kubelet.service.d 创建配置文件: cat <<EOF | sudo tee /etc/systemd/system/kubelet.service.d/10-kubelet.conf [Service] Environment="KUBELET_EXTRA_ARGS=--root-dir=/opt/kubelet" EOF 重载并重启 kubelet: sudo systemctl daemon-reload sudo systemctl restart kubelet 这一步在 init 之前做最合适,因为 init 的时候 kubelet 会开始写数据,如果不提前改目录,就会写到 /var/lib 下面去,后面再挪就麻烦了。早知道我第一遍就该这么干。 十二、kubeadm init——最紧张的一步 终于要初始化集群了。先装几个必要的网络工具包(kubeadm 依赖它们): 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 默认网段匹配。 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,但万一以后想加节点呢? 如果失败了怎么办?我就失败过一次,因为之前残留了一些配置。这时候需要彻底清理: sudo kubeadm reset -f sudo rm -rf /var/lib/etcd sudo rm -rf /etc/kubernetes/manifests/* 然后再重新 init。这个过程就像重启电脑,没什么大不了,就是多等几分钟。 十三、配置 kubectl 让普通用户也能用 mkdir -p $HOME/.kube sudo cp /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config 测试一下: kubectl get nodes 这时候节点状态应该是 NotReady,因为网络插件还没装。别慌,这是正常的。 十四、安装 Flannel 网络插件 kubectl apply -f https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml 等个一两分钟,再 kubectl get nodes,状态就变成 Ready 了。我第一次看到这个结果的时候长舒一口气——总算没白折腾。 十五、允许 Master 节点调度 Pod(单机必做) 默认情况下,Master 节点是打了污点(taint)的,不允许普通 Pod 运行。我们既然是单机,就得去掉它: kubectl taint nodes --all node-role.kubernetes.io/control-plane- 验证一下污点没了: kubectl describe node | grep -i taint 十六、跑个 Nginx 测试一下 kubectl create deployment nginx --image=nginx kubectl expose deployment nginx --port=80 --type=NodePort kubectl get svc nginx 记下那个 30000+ 的端口,浏览器访问 http://你的服务器IP:端口,看到 Nginx 欢迎页就说明集群完全可用了。 测试完可以把这俩资源删掉,免得占用端口: kubectl delete deployment nginx kubectl delete service nginx 十七、最终的数据目录结构 检查一下我们的劳动成果: ls /opt/containerd ls /opt/kubelet 应该能看到 containerd 的元数据、kubelet 的 pod 目录都乖乖躺在这里。没白费功夫。 十八、完整的安装顺序——很多人失败就是因为跳步 如果你是按我的步骤从头做下来的,顺序应该是这样的: 修改 hostname + hosts apt update && upgrade iptables 切换 legacy 关闭 swap 加载内核模块 设置 sysctl 时间同步 安装 containerd 并配置 安装 kubeadm/kubelet/kubectl 修改 kubelet 数据目录 kubeadm init 配置 kubectl 安装 Flannel 去除 Master 污点 测试部署 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 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 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 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: nohup kubectl port-forward -n kube-system svc/kite 8080:8080 & 然后浏览器打开 http://你的服务器IP:8080 就行。 云服务器访问不了怎么办? 如果你用的是云服务器,port-forward 默认只监听 127.0.0.1,外网是打不开的。这时候有两个办法: 办法一:改用 NodePort 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集群的还是蛮多的。