Kubernetes离线手动搭建实战:从环境准备到Calico网络联调

1. 整体思路与部署架构选型

1.1 为什么选择离线手动搭建

先聊一个大家都会遇到的问题:学习K8S到底应该从哪里入手?很多人第一反应是装个minikube或者kind,一条命令拉起来,kubectl get nodes能看到一个Ready的节点,就觉得"我会K8S了"。但等真正上了生产,出了问题才发现自己完全不知道集群内部是怎么运转的。这就跟开车一样,你在驾校开自动挡和上了路开手动挡完全是两码事。K8S这个东西,如果不去手动搭建一次,你就永远停留在"会用工具"的层面,没有真正理解"工具"本身。

我这次选择离线手动搭建,其实是被环境"逼"出来的。某公司的内网服务器完全不能访问外网,但又需要一套K8S环境来做容器化部署的测试和验证。最开始我也想过曲线救国:在一台能上网的机器上装好所有镜像,然后打包带进内网。后来发现这个思路本身就是K8S离线部署的标配玩法。索性就把整个过程完整走了一遍,顺便把之前的很多知识盲区补上了。

手动搭建的好处在哪里?首先是你能真正搞清楚每个组件是干什么的。etcd存什么数据、kube-apiserver怎么和etcd交互、kube-scheduler怎么调度Pod、kubelet怎么和容器运行时通信。这些东西在自动化的安装工具里全都是黑盒,你只知道装完就能用,但出了问题就抓瞎。其次是手动搭建的过程中,你会被迫理解证书体系、网络插件的选择、Pod网段和Service网段的规划,这些才是K8S运维的核心能力。

1.2 核心组件与架构设计

在动手之前,先把K8S的架构理清楚。我这次搭建的是最经典的单Master双Worker架构,三个节点分别是master01、worker01、worker02。为什么不用高可用?因为这是做学习和验证用的环境,单Master足够覆盖绝大部分测试场景。如果以后要上生产,再做HA,原理是一样的,只是多了负载均衡这一层。

K8S的核心组件可以分成两个部分:控制平面和节点平面。控制平面包含kube-apiserver、kube-controller-manager、kube-scheduler、etcd四个组件,节点平面包含kubelet、kube-proxy和容器运行时。

组件角色一览:

组件职责关键点
etcd集群状态存储所有数据的唯一来源,坏了集群就瞎了
kube-apiserver所有请求的入口唯一能直接访问etcd的组件
kube-controller-manager维持期望状态节点控制器、副本控制器等一堆控制器
kube-scheduler决定Pod跑在哪个节点根据资源和约束做调度决策
kubelet节点上的“眼睛和手”负责Pod的生命周期管理
kube-proxyService流量转发实现负载均衡和服务发现

这里最需要理解的是kube-apiserver的地位。所有的组件——kubelet、kube-scheduler、kube-controller-manager、kubectl——要读写集群状态,都必须经过kube-apiserver。它就像整个集群的“总闸门”,所有请求进来先经过认证、授权、准入控制三道关卡,然后才会真正写入etcd。理解了这一点,你就知道为什么在排查问题的时候,第一步总是看kube-apiserver是否正常。

1.3 版本选型与配套组件

版本选型这个问题,网上说法很多,但我给的建议很简单:选一个经过时间验证的稳定版本。我这次用的是1.23.17,为什么选这个版本?因为1.24开始,K8S把Dockershim移除了,如果你还习惯用Docker作为容器运行时,就需要额外安装CRI-Dockerd这个适配层。而1.23及之前的版本,Docker还是"原生支持"的,对初学者来说少了一道折腾。当然如果你已经适应containerd作为运行时,用更新的版本也没问题,只是我这次为了减少变量,选择了更稳妥的1.23。

配套组件版本也是一个萝卜一个坑:

组件版本说明
Kubernetesv1.23.17主版本
etcdv3.5.6存储集群数据
containerdv1.6.19容器运行时,替代Docker
crictlv1.24.0命令行调试工具
calicov3.25.0CNI网络插件

这里多说一句为什么要用containerd而不是Docker。刚开始我其实想用Docker,毕竟熟悉嘛。但研究了一下发现,在当前这个阶段,containerd才是更"正统"的容器运行时。K8S通过CRI(Container Runtime Interface)和containerd直接交互,中间少了一个Docker的转换层,更稳定也更高效。而且后面如果想把集群升级到更新版本,用containerd可以省掉很多麻烦。

2. 离线环境的准备与前置条件

2.1 离线安装包的准备策略

离线安装最大的难点不是"装",而是"怎么把需要的东西都提前准备好"。

首先明确需要准备的几个包:K8S的三个核心组件包——kubelet、kubeadm、kubectl,而且必须指定版本。这三个东西在K8S官方的yum仓库或者apt仓库里都有,关键是要在能联网的机器上先下载好。我用的是yum方式,所以需要下载kubelet-1.23.17、kubeadm-1.23.17、kubectl-1.23.17这三个rpm包,以及它们的一堆依赖。

然后就是容器运行时。我选了containerd,需要下载containerd.io这个包。这里必须提醒一下:containerd的版本和K8S版本要兼容,1.6.x的containerd配合1.23.x的K8S是经过验证的组合。

接下来是镜像。K8S集群跑起来需要一堆镜像:kube-apiserver、kube-controller-manager、kube-scheduler、kube-proxy、etcd、pause、coredns,还有网络插件calico需要的几个镜像。这一块是离线部署的重头戏,也是最容易出问题的地方。

提示:镜像的获取有一个小技巧,通过kubeadm config images list可以列出指定版本K8S需要的全部镜像清单,然后逐个拉取、打包、拷贝。这一步必须在能联网的机器上提前完成。

2.2 服务器基础环境配置

三台服务器的操作系统我用的都是CentOS 7.9,配置是4核8G、100G磁盘。这个配置跑一个测试集群完全够了,但如果想让集群运行得更从容,建议Master节点给到8核16G,毕竟etcd和kube-apiserver都是吃内存的主儿。

系统层面的准备工作集中在三个方面:主机名、IP和防火墙。

主机规划:

主机名IP地址角色
master01192.168.1.101控制平面节点
worker01192.168.1.102工作节点
worker02192.168.1.103工作节点

设置主机名很简单,hostnamectl set-hostname master01,然后三台机器的/etc/hosts都要加上三行映射关系。这一步很多人会忽略,但非常重要——K8S组件之间通信依赖主机名解析,如果你不配hosts,后面kubeadm init的时候就会报"hostname not found"的错误。

然后就是关闭防火墙。很多人对"关闭防火墙"这件事有心理障碍,觉得不安全。这里要说明一下:在K8S集群内部,组件之间需要大量端口互通,你要是开着firewalld,就得额外配置一堆端口放行规则,把K8S的端口清单一个个加进去。对于学习和测试环境,直接systemctl stop firewalld加systemctl disable firewalld是最省事的。生产环境当然不能这么干,需要按端口白名单严格配置,这是后话。

SELinux也要关掉。这个说法可能有人不认同,但我的经验是:在CentOS 7上用K8S,关SELinux能省掉80%的疑难杂症。设置setenforce 0,然后修改/etc/selinux/config把SELINUX=enforcing改成permissive。网上也有不关SELinux硬上的,但那是给追求极致安全的强迫症准备的,不适合我们这种要快速落地场景。

2.3 内核参数与系统调优

这一块是很多人容易忽略的。K8S对系统的要求不只是"能跑",还需要一些内核参数的配合。核心是开启iptables对桥接流量的处理能力。

cat > /etc/sysctl.d/k8s.conf <<EOF net.bridge.bridge-nf-call-iptables = 1 net.bridge.bridge-nf-call-ip6tables = 1 net.ipv4.ip_forward = 1 vm.swappiness = 0 EOF sysctl --system

逐条解释一下。bridge-nf-call-iptables表示让iptables能够处理经过Linux网桥的流量。这是K8S网络能不能正常工作的关键:Pod之间的通信要经过CNI网桥,如果这个参数没开,流量就无法被netfilter正确过滤和转发。ip_forward是开启IP转发,这个几乎是所有容器网络方案的硬性前提。vm.swappiness=0说得直白点就是尽量别用swap,因为容器场景下内存被swap出去会导致性能急剧下降,而且K8S在计算节点内存资源时,默认是不考虑swap的。

还有一个容易被忽视的点:需要加载br_netfilter模块。虽然在CentOS 7.9上默认会加载,但稳妥起见还是提前确认一下:

modprobe br_netfilter lsmod | grep br_netfilter

3. 容器运行时与K8S组件安装

3.1 手动安装containerd

containerd的安装方式其实很简单,就是一个rpm包。但在装完之后,有两件重要的事情要做:一是配置cgroup驱动,二是配置系统服务。

containerd默认的cgroup驱动是systemd还是cgroupfs?这里有一个历史问题。K8S从1.21版本开始,kubelet默认使用systemd作为cgroup驱动,而containerd默认用cgroupfs。这俩如果不一致,就会出现一个让人很困惑的现象:kubelet起来之后报"failed to run Kubelet: failed to validate kubelet flags"或者cgroup相关的错误。解决办法是修改containerd的配置文件,让它也用systemd。

containerd的默认配置文件可以通过containerd config default > /etc/containerd/config.toml生成,然后修改两个地方。一个是SystemdCgroup,把它从false改成true;另一个是sandbox_image,这是Pause镜像的地址。

sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml sed -i 's#sandbox_image = "registry.k8s.io/pause.*#sandbox_image = "registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.6"#' /etc/containerd/config.toml

这里要补充说一下sandbox_image这个参数的含义。Pause容器是每个Pod里的"根容器",它不干任何正事,就是占个位置,负责创建和维护Pod的网络命名空间,Pod里其他容器才能共享网络。如果Pause镜像拉不下来,整个Pod就创建不起来。所以在离线环境里,这一步提前在配置里指定好镜像源位置,能避免很多麻烦。

3.2 K8S核心组件包的安装

在安装之前,先把chache的/etc/yum.repos.d/kubernetes.repo配置好。但离线环境是访问不了这个仓库的,所以正确姿势是:在联网机器上把rpm包下载好,然后拷贝到三台服务器上,用yum install -y ./kubelet-1.23.17.rpm ./kubeadm-1.23.17.rpm ./kubectl-1.23.17.rpm本地安装。

如果遇到依赖问题,比如某个so文件缺失,我的建议是直接使用yum install --downloadonly方式把依赖也一并拉下来。

装完之后把kubelet、kubeadm、kubectl加开机自启并启动:

systemctl enable kubelet && systemctl start kubelet

注意一点:现在kubelet会起得很早,它会因为找不到集群而疯狂报错,刷一堆"failed to connect to API server"的日志。这是正常的,不用慌。kubelet本身就是个不断重试的"打不死的小强",等kubeadm init完成之后它自然就正常了。

3.3 镜像的导入与校验

离线环境最大的坎儿就是镜像。K8S核心组件加上Calico网络插件,总共需要大概十四五个镜像,每一个都必须提前准备好。

具体流程是这样:在联网机器上,先拉取所有需要的镜像,然后通过docker save -o k8s-images.tar或者ctr -n k8s.io images export把镜像打包成一个tar文件,拷贝到内网服务器后再导入。

如果你用的是containerd,导入命令是:

ctr -n k8s.io images import k8s-images.tar

注意-n k8s.io这个命名空间参数,这是K8S专用的镜像命名空间,如果不指定默认是default,导进去的镜像在K8S里根本看不到。

之前提到的kubeadm config images list是获取镜像清单的快捷方式。列出后逐个拉取即可,如下是1.23.17核心组件需要的镜像列表:

registry.cn-hangzhou.aliyuncs.com/google_containers/kube-apiserver:v1.23.17 registry.cn-hangzhou.aliyuncs.com/google_containers/kube-controller-manager:v1.23.17 registry.cn-hangzhou.aliyuncs.com/google_containers/kube-scheduler:v1.23.17 registry.cn-hangzhou.aliyuncs.com/google_containers/kube-proxy:v1.23.17 registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.6 registry.cn-hangzhou.aliyuncs.com/google_containers/etcd:3.5.6-0 registry.cn-hangzhou.aliyuncs.com/google_containers/coredns:v1.8.6

Calico需要的镜像相对独立,后面网络配置部分再单独说。

4. 证书体系与控制平面初始化

4.1 手动生成PKI证书

K8S的证书体系是整个集群安全的基础,众多组件之间通信全靠TLS双向认证。手动搭建的好处是在这个环节你会被迫搞清楚谁给谁签发证书,以及每个证书的用途。

大体的证书逻辑是:有一个集群的CA根证书,然后给apiserver签发一个服务端证书,给etcd签发证书,给每个节点的kubelet签发客户端证书。K8S集群里的所有组件都信任这个根CA,只要持有根CA签发的证书,就能互相认证。

证书要放在/etc/kubernetes/pki目录下。核心的证书文件如下:

证书文件用途说明
ca.crt, ca.key集群根CA所有证书的信任根
apiserver.crt, apiserver.keyAPI Server服务端证书需要包含MasterIP和Service网段
apiserver-kubelet-client.crt连接kubelet的客户端证书API Server访问kubelet时使用
front-proxy-ca.crt聚合层CA扩展API Server时使用
etcd/ca.crtetcd集群CAetcd组件专用

其实手动生成这些证书并不算太复杂,关键在于别把SAN(Subject Alternative Name)搞错。apiserver的证书SAN必须包含所有可能访问apiserver的地址,包括Master节点的IP、集群的Service网段第一个地址(通常是10.96.0.1)、还有"kubernetes"和"kubernetes.default"这些内置域名。漏掉任何一个,后面都会出现诡异的证书校验失败。

4.2 kubeadm init的执行与参数解析

说起来,手动搭建K8S其实很少有人真的去逐个证书手动用openssl生成,更多是用kubeadm init来"半自动"完成。这里的"手动"指的是不用自动化脚本来做,而不是说连kubeadm都不用。你依然需要自己规划IP、网段、镜像仓库,只是证书、配置文件的生成这一步kubeadm帮我们完成了大部分工作。

执行初始化:

kubeadm init \ --apiserver-advertise-address=192.168.1.101 \ --image-repository=registry.cn-hangzhou.aliyuncs.com/google_containers \ --kubernetes-version=v1.23.17 \ --service-cidr=10.96.0.0/12 \ --pod-network-cidr=10.244.0.0/16

逐个参数拆解一下。apiserver-advertise-address指定apiserver对外广播的地址,必须填Master节点的内网IP,不然其他节点找不到它。image-repository是镜像仓库地址,因为离线环境我们已经手动导入镜像了,这里其实不拉取,但这个地址会被写入kubeadm的配置里,后面token验证的时候还会用到。service-cidr是Service的虚拟IP网段,pod-network-cidr是Pod的IP网段,这两个地址池不能和物理机网段冲突,相互之间也不能重叠。我这次选的10.244.0.0/16是Calico默认推荐的Pod网段,10.96.0.0/12是K8S默认的Service网段。

提示:Pod网段和Service网段一旦确定,后期修改极麻烦,会牵涉到CNI配置和CoreDNS配置。集群一经初始化,这两个网段基本就锁死了。所以动手之前一定想清楚。

成功初始化之后,kubeadm会在末尾输出一串关键信息,包括:加入集群的token和证书校验哈希值、kubectl的config文件位置。务必截图保存,后面两个Worker节点加入集群就靠它了。

4.3 部署CoreDNS扩展组件

CoreDNS是K8S集群内部DNS服务,负责Service名的解析。如果你不在集群里部署CoreDNS,Pod之间就不能通过Service名访问彼此,只能靠IP,这在微服务架构中是不可接受的。

部署CoreDNS其实很简单,因为kubeadm会在init完成后自动部署,但前提是网络插件已经就绪。在没有CNI的情况下,CoreDNS会一直处于Pending状态。所以正确的顺序是先部署网络插件,再等CoreDNS正常。

在初始化之后检查:

kubectl get pods -n kube-system

如果看到coredns的Pod是Pending,不用着急,这是正常的。后面部署好Calico之后,它会自动从Pending变成Running。

5. 网络插件的部署与集群联调

5.1 Calico的离线部署

集群的网络方案我选了Calico,它的优势在于支持多种网络模式,性能在CNI插件里属于第一梯队,而且配置灵活,既能做纯三层的BGP网络,也能用Overlay模式。

离线部署Calico的流程是:先把calico.yaml配置文件拿下来,然后把配置里镜像地址改成本地已经导入的镜像地址,再kubectl apply -f calico.yaml。

在配置Calico之前,有一个重要的参数要确认:CALICO_IPV4POOL_CIDR。这个参数是Calico里面Pod地址池的配置,必须和之前kubeadm init时设置的pod-network-cidr一致。比如我之前设置的Pod网段是10.244.0.0/16,那么calico.yaml里的CALICO_IPV4POOL_CIDR也要改成10.244.0.0/16,集群的网络才能正常通信。

注意:Calico部署的先后顺序虽然可以在kubeadm init之前或之后,但有一个大原则是必须在kubelet和kube-proxy工作正常之后、CoreDNS调度之前完成。因为在没有任何CNI插件的情况下,K8S是没有能力为Pod分配IP的,所有Pod都只能卡在ContainerCreating状态。

5.2 Worker节点加入集群

Worker节点的加入是整个过程中最流畅也最容易出错的环节。kubeadm init结束时的输出里有完整的join命令模板,把token和ca-cert-hash替换成自己环境的实际值即可:

kubeadm join 192.168.1.101:6443 \ --token l5d3qn.abcdef1234567890 \ --discovery-token-ca-cert-hash sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

这个命令在worker01和worker02上各执行一次。执行成功之后,Master节点上验证:

kubectl get nodes

正常情况下会看到三个节点处于NotReady状态。为什么是NotReady?因为kubelet虽然起来了,但它并不会主动告诉apiserver"我准备好了",要由kubelet上报自己的资源信息和状态,由controller-manager给它打上Ready标签。如果节点上网络插件还没就绪,或者kubelet上报有问题,节点就会一直停留在NotReady。

如果join的时候提示token过期或者证书校验失败,不要慌。直接用下面的命令在Master节点上重新生成:

kubeadm token create --print-join-command

这条命令会输出一个新的join命令,直接复制就行。

5.3 网络状态验证与联通性测试

集群初始化的最后一步,是验证网络是否真的通了。我的验证方法是三板斧:看节点状态、看系统Pod状态、跑一个测试Deployment。

第一步,kubectl get nodes,看到三个节点都是Ready,说明节点层面的网络配置正确了。

第二步,kubectl get pods -n kube-system,看到所有Pod都是Running,包括calico-kube-controllers和calico-node。

第三步,创建测试用Pod,验证跨节点网络互通:

kubectl run test-nginx --image=nginx

如果这个Pod能进入Running状态,说明整个网络链路已经打通了。再进一步做一次跨节点连通性测试:

kubectl exec -it test-nginx -- curl <pod_ip>

能通,说明Calico的路由规则已经生效,Pod之间的三层网络没有问题了。

6. 实战坑点与排查经验

6.1 版本不匹配问题

在手动搭建过程中,版本不匹配是最折磨人的一类问题。这里分享一个我踩过的具体案例。

某次初始化的时候,kubeadm init报了一个"error execution phase preflight: unable to fetch the kubeadm-config ConfigMap"的错误。排查了很久,最后发现是kubeadm和kubelet的版本不一致。kubeadm是1.23.17,但kubelet被yum自动升级到了1.24。版本不同导致kubeadm生成的配置和kubelet实际行为不一致,最终卡在preflight检查。

这个问题常规文档不太会提,因为它和"正常安装流程"没关系,纯粹是离线环境里手忙脚乱搞出来的版本错乱。排查思路是:

kubeadm version kubelet --version kubectl version

三个版本必须完全一致,差一个minor版本都不行。解决方法是把kubelet降级到和kubeadm一致的版本:

yum install -y kubelet-1.23.17 --nogpgcheck systemctl restart kubelet

6.2 镜像拉取失败与命名空间陷阱

离线环境里镜像拉取失败是另一个高频问题。这里的坑点往往不在"拉取"本身,而在于镜像导入的命名空间不对。

之前提过,K8S使用的是containerd的k8s.io命名空间。如果你导入镜像时用了默认命名空间ctr images import而不是ctr -n k8s.io images import,kubelet在拉取镜像时会报"image not found",但实际上镜像已经在本地了。这个问题我至少遇到过三次,每次都是因为命令少写了-n k8s.io参数。

还有一个容易忽略的点:如果K8S组件使用默认的registry.k8s.io仓库,而你导入镜像时用的是阿里云仓库路径,也会导致镜像找不到。一定要保证镜像仓库地址一致。比如配置里写着registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.6,那么导入的镜像也必须是这个完整路径。

6.3 节点NotReady的排查路径

节点一直NotReady是最让人头疼的问题,因为它可能由多个原因导致。我的排查路径有一个固定的优先顺序:

第一步,看kubelet状态:systemctl status kubelet,看有没有报错。如果是证书相关错误,去检查/etc/kubernetes/kubelet.conf是否存在且权限是否正确。

第二步,看节点上的容器运行时:crictl ps,如果这个命令报错,说明crictl连接不上containerd,检查/run/containerd/containerd.sock是否存在、crictl的runtime-endpoint配置是否正确。

第三步,看CNI插件是否就绪:ls /opt/cni/bin/,如果没有calico相关的可执行文件,说明CNI还在启动中或安装不完整。

第四步,看apiserver上的节点事件:kubectl describe node worker01,这里能看到具体的时间线,比如哪个探针失败、什么原因导致的NotReady。

这套排查路径在我手动搭建过程中多次验证过,效率很高。你不需要一开始就去翻日志,按照这个顺序一步步排除,基本十分钟内能定位到根因。

6.4 常见问题速查表

把这次手动搭建过程中遇到的坑整理成了一个速查表,方便大家对照:

现象可能原因解决方案
kubeadm init失败:port 6443 already in use端口被占用ss -lntp查端口占用的进程,释放端口
节点一直NotReadyCNI未就绪或kubelet异常先看CNI状态,再看kubelet日志
镜像报not found导入命名空间不对用ctr -n k8s.io images import重新导入
CoreDNS一直Pending网络插件没跑起来确认Calico部署成功后再观察
kubectl无法连接apiserverkubeconfig配置错误检查~/.kube/config的server字段和证书路径
跨节点Pod网络不通Calico配置的Pod网段不一致核对CALICO_IPV4POOL_CIDR和kubeadm的pod-network-cidr
kubelet疯狂重启版本不一致或缺少依赖核对kubelet/kubeadm/kubectl版本,检查依赖库

7. 常用运维命令与集群日常管理

7.1 节点与Pod的日常操作

集群搭好之后,日常管理主要是通过kubectl完成。这里把我常用的命令整理一下,都是一些高频操作。

查看节点状态:

kubectl get nodes -o wide

查看集群里所有命名空间的Pod:

kubectl get pods -A -o wide

查看某个Pod的详细信息和事件:

kubectl describe pod <pod-name> -n <namespace>

查看Pod日志:

kubectl logs -f <pod-name> -n <namespace> --tail=200

进入Pod内执行命令:

kubectl exec -it <pod-name> -n <namespace> -- /bin/bash

这些命令是基础中的基础,但恰恰是日常最常用的部分。建议大家把这些命令的-o wide参数用起来,它能直接显示Pod跑在哪个节点、IP是什么,排查问题的时候非常高效。

7.2 证书过期与更新策略

K8S的证书有效期默认是一年。这个时间说长不长,说短不短,很多集群跑着跑着突然出现一堆奇怪的报错,一查才发现是证书过期了。

如果是kubeadm部署的集群,更新证书比较简单:

kubeadm certs renew all

执行完之后重启相关组件:

systemctl restart kubelet

对于自签的apiserver证书,更新之后需要重启kube-apiserver。但要注意,kube-apiserver是静态Pod,修改/etc/kubernetes/manifests/kube-apiserver.yaml之后会自动重启,不需要手动干预。

7.3 节点维护与移除

有时候需要把某个节点摘下来做维护,这时候要让K8S把Pod都赶走:

kubectl drain worker02 --ignore-daemonsets --delete-emptydir-data

维护完成后让节点重新参与调度:

kubectl uncordon worker02

如果要彻底移除一个节点,顺序是:先drain、再删除节点、然后在该节点上执行kubeadm reset清空本地数据。

这两个命令在运维中必不可少,特别是当某个Worker节点要换硬件的时候,drain + uncordon的组合能让业务无感知地完成迁移。

8. 手动搭建的经验总结与后续扩展

这次离线手动搭建K8S集群,前后花了两天时间。第一天准备环境、拉镜像、踩版本坑,第二天正式部署然后反复调试网络。整个过程走完之后,我对K8S的理解和在"一条命令装好"时代形成的认知完全不一样了。你开始明白每个组件为什么要这么设计,哪些参数是牵一发动全身的,哪些错误信息背后真正的原因是什么。

如果让我给准备入门的同学一个建议:即便你以后用开源的集群管理平台做生产部署,也一定要找一套离线环境手动把集群搭一遍。因为你在手动搭建过程中积累的底层认知,会在未来每一次集群故障排查中变成你的直觉。

关于后续扩展,我打算在搭建好的集群上做三件事。第一件事是部署一套完整的微服务Demo,把Ingress、ConfigMap、Secret、PV/PVC这些核心资源都用起来,看看它们在真实业务中的应用方式。第二件事是尝试把集群升级到更新的版本,练练兵,毕竟版本升级在生产中是个高频话题。第三件事是试一下集群监控那套体系,看看从零开始构建可观测性的完整路径。

这一趟下来,我现在碰到网络问题第一反应不是去看什么知名博主写的"十种排查方法",而是自己从Pod网段、Service网段、Calico的路由规则这几个维度去推,反而解决问题更快一些。这就是手动搭建带来的底层能力,也是我愿意把这套流程完整记录下来的原因。希望这篇实践记录能给同样准备手动搭建K8S集群的同学一点参考,少走一些我曾经踩过的弯路。

相关新闻

WorkBuddy Skill机制:技能生命周期管理从存放到维护的完整实践

WorkBuddy Skill机制:技能生命周期管理从存放到维护的完整实践

当你在设计一套像WorkBuddy这样的智能工作台时&#xff0c;Skill机制从来不是“注册几个函数”那么简单。我第一次搭这套东西时也以为把技能入口挂到一张表上就完事&#xff0c;结果技能多了之后&#xff0c;光是对外表现不一致、加载顺序错乱、更新后无法回滚这几个问题&#…

2026/10/20 13:15:07 阅读更多 →
用原理图设计FPGA电子琴:分频表驱动蜂鸣器音乐

用原理图设计FPGA电子琴:分频表驱动蜂鸣器音乐

简介&#xff1a;通过Quartus原理图设计方法实现的电子琴FPGA工程包&#xff0c;面向数字电路、EDA与FPGA入门学习者&#xff0c;演示从音符频率生成、定时计数、数模转换到乐曲存储与播放的完整音乐合成系统。资源共413个文件&#xff0c;压缩包5.42MB&#xff0c;主要包含原理…

2026/10/20 13:14:06 阅读更多 →
多镜头视频生成一致性难题如何解?FilmWeaver框架与实操要点

多镜头视频生成一致性难题如何解?FilmWeaver框架与实操要点

做多镜头视频生成的人应该都有这种感觉&#xff1a;单镜头生成已经很顺手之后&#xff0c;最头疼的反而不是画质&#xff0c;而是“一致性”。你先生成一个人走进咖啡店的镜头&#xff0c;再换一个机位生成同一个人坐在窗边&#xff0c;十次有九次会得到两个长得完全不像的人。…

2026/10/20 13:14:06 阅读更多 →

最新新闻

SecureCRT免安装版配置指南:离线串口调试与SSH便携部署

SecureCRT免安装版配置指南:离线串口调试与SSH便携部署

简介&#xff1a;本资源为SecureCRT绿色便携版免安装包&#xff0c;面向系统管理员、运维工程师及开发人员&#xff0c;解决多平台快速部署安全终端连接工具的需求&#xff0c;尤其适用于需在不同电脑间频繁切换、无管理员权限或追求轻量使用的场景。压缩包共137个文件&#xf…

2026/10/20 14:00:00 阅读更多 →
防火墙规则集时序配置:顺序、生效时间与安全空窗排查

防火墙规则集时序配置:顺序、生效时间与安全空窗排查

防火墙规则集里的时序配置&#xff0c;是我这几年排查安全事件时最常踩的隐形坑。规则明明写对了&#xff0c;但因为顺序放错、时间对象失效、批量下发存在中间态&#xff0c;导致规则的实际生效时间和预期完全对不上&#xff0c;轻则策略空转半个月没人发现&#xff0c;重则直…

2026/10/20 14:00:00 阅读更多 →
Python Pygame核心机制与实战:从零构建接苹果小游戏

Python Pygame核心机制与实战:从零构建接苹果小游戏

很多人在命令行里写过一阵Python脚本之后&#xff0c;总想搞点“能动的、看得见的东西”。Python Pygame模块就是你会碰到的第一块敲门砖&#xff1a;一个基于SDL封装、专门用来做2D图形和游戏的原生库。我第一次用它做窗口程序的时候&#xff0c;印象最深的不是那种“我能控制…

2026/10/20 14:00:00 阅读更多 →
100套可配置ECharts大数据大屏模板工程包

100套可配置ECharts大数据大屏模板工程包

简介&#xff1a;本资源是一套面向数据可视化工程师、前端开发人员及政企数字化项目实施者的专业级大屏模板集合&#xff0c;覆盖社区治理、智慧物业、数字政务、智能交通、金融银行等十余个高需求行业场景&#xff0c;有效解决业务数据实时监控与决策看板快速搭建难题。压缩包…

2026/10/20 14:00:00 阅读更多 →
AI加速剪辑工作流:字幕、粗剪与卡点自动化实战

AI加速剪辑工作流:字幕、粗剪与卡点自动化实战

我见过太多人剪视频&#xff0c;剪到怀疑人生。明明素材拍得挺好&#xff0c;脚本也没什么大问题&#xff0c;但就是一剪一整夜&#xff0c;导出前还在逐帧删吸气声、给字幕换行、把片尾黑场拖到精确位置。你以为是效率问题&#xff0c;其实是你把80%的时间花在了AI 10秒就能搞…

2026/10/20 14:00:00 阅读更多 →
深度学习训练调参实战:梯度、激活函数、学习率与优化器的链路

深度学习训练调参实战:梯度、激活函数、学习率与优化器的链路

入门深度学习时&#xff0c;很多人会被一连串名词劝退&#xff1a;激活函数、优化器、学习率、梯度。我第一次跑模型的时候也觉得它们是四个独立的调参项&#xff0c;今天调一下学习率&#xff0c;明天换一个激活函数&#xff0c;最后模型loss曲线像心电图一样乱跳&#xff0c;…

2026/10/20 13:59:00 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型&#xff08;GMM&#xff09;的语音识别系统&#xff0c;很多人第一反应就是一堆数学公式&#xff0c;觉得门槛很高。但如果你真的在MATLAB里把它落地跑通&#xff0c;会发现核心其实就是三件事&#xff1a;把声音变成特征向量&#xff0c;用GMM给每个类别…

2026/10/20 10:02:13 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人&#xff0c;但落到具体操作上&#xff0c;无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程&#xff1a;对图像做反白、改调色板、彩色转灰度&#xff0c;最后把像素数据导出成txt文件。单看每一…

2026/10/19 17:32:41 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词&#xff0c;你会发现一个很有意思的现象&#xff1a;每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型&#xff0c;有人指的是运动控制系统&#xff0c;还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 10:02:08 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:45 阅读更多 →