如果你手头正好有一台内存 32G 左右的单机,又想把 Kubeflow 1.3 这套机器学习平台装起来认真用一阵子,那“rancher + kubeflow1.3 单机安装”这条路,我可以说是相当值得走一遍的。Rancher 负责把底层 Kubernetes 集群管起来,Kubeflow 1.3 负责提供 Notebook、Pipeline、超参调优这些 ML 能力,单机既能当开发环境,也能当演示环境,还能给后面上生产集群做预演。下面这套步骤是我在一台 Ubuntu 20.04 服务器上完整跑通过的流程,里面踩过的坑也都标注出来了,希望能帮你少走点弯路。
1. 整体思路:这套组合解决了单机上的什么问题
1.1 为什么偏偏选 Rancher 加 Kubeflow 1.3
先说实话,Kubeflow 本身不是一个小东西,它是一堆 Kubernetes 原生组件的集合,包括 Notebook 管理、Pipeline、模型训练算子、模型服务、超参搜索等。直接裸装 Kubernetes 再逐个部署这些组件,不是不行,而是排查问题的时候非常痛苦。Rancher 在这里起的作用,首先是给你一个可视化的集群管理入口,能直观看到节点状态、工作负载、日志;其次它自带的认证和权限管理,可以减少很多后续维护成本。单机环境下,Rancher 的 UI 能让我一眼看出哪个 Pod 挂掉了、哪个 PVC 没绑上,这比对着终端敲一天命令舒服得多。
再说为什么锁 Kubeflow 1.3。相比更早的版本,1.3 的组件完整性已经比较好了,Pipeline、Katib、KServe 的前身 KFServing 都已经整合进去;比 1.4 及更新版本,它又没对 Kubernetes 版本提出特别高的要求,部署压力小。对于单机来说,这是稳定性和可安装性之间一个比较折中的选择。
1.2 单机部署的路线对比:K3s、MicroK8s、完整 Kubernetes
单机上要跑 Kubeflow,底层 Kubernetes 的选择很关键。我对比过三条路线:
| 方案 | 安装复杂度 | 内存开销 | 适合场景 | 坑点 |
|---|---|---|---|---|
| K3s | 低 | 低,约 0.5-1G | 单机/边缘/开发测试 | 自带 traefik 会和 Istio 冲突 |
| MicroK8s | 中 | 中 | 本地开发 | 组件切换繁琐,addon 依赖较多 |
| 完整 Kubernetes | 高 | 高 | 生产环境模拟 | 单机需要额外部署 etcd、网络插件等 |
最终选了 K3s。K3s 把 etcd、flannel、local-path 这些基础能力都封装进去了,一条命令就能起一个单节点集群,而且默认自带 local-path StorageClass,这对 Kubeflow 里的 PVC 来说是刚需。单机上跑完整 Kubernetes 不是不行,但 etcd 单点、网络插件配置、存储类初始化这些事,纯粹是给自己找活干。
为什么不直接用 Docker Compose 跑 Kubeflow?因为 Kubeflow 从设计上就是 Kubernetes 原生应用,依赖 CRD、控制器、Service Mesh 这些机制,Compose 根本承载不了。所以与其绕路,不如直接接受“Kubeflow 跑在 K8s 上”这个事实。
1.3 硬件与端口规划参考
单机部署最容易翻车的是资源预估。我自己用的是 8 核 16 线程、64G 内存、1TB NVMe 的系统盘,跑起来比较从容。如果你只有 16G 内存,也能装完,但跑 Pipeline 任务的余量会非常小,尤其是启动多个 Notebook 时容易 OOM。我建议内存至少 32G,CPU 8 核起步,磁盘 100G 以上,而且最好是 SSD,因为 Kubeflow 的镜像解压和 Pipeline 的中间数据写入都很吃磁盘。
端口方面也要提前规划好,单机所有服务都挤在一台上,冲突是迟早的事:
| 端口/范围 | 谁在用 | 说明 |
|---|---|---|
| 80/443 | Rancher Server、K3s traefik | 需要错开,后面细讲 |
| 6443 | K3s kube-apiserver | 集群内部 API 端口 |
| 30000-32767 | Kubernetes NodePort 范围 | Istio 入口可以放这里 |
| 30080/30443 | Istio Ingressgateway(建议改成 NodePort) | Kubeflow 的访问入口 |
2. 环境准备与 Rancher Server 部署实录
2.1 系统初始化和 Docker 准备
我用的系统是 Ubuntu 20.04 LTS,内核版本 5.4,整体兼容性没什么问题。拿到机器后我先做了三件事:关闭防火墙、禁用 swap、安装 Docker。关闭防火墙是为了省事,生产环境当然要按安全策略来,但单机测试阶段,ufw 开着反而会让你排查端口问题时疯掉。
sudo ufw disable sudo swapoff -a sudo sed -i '/ swap / s/^/#/' /etc/fstab然后是 Docker。Rancher Server 官方支持用 Docker 方式启动,所以 Docker 必须要先装好。我用的版本是 Docker 20.10,安装方式没有特别的,直接走官方 apt 源就行。装完后确认一下 Docker 服务状态和版本:
docker --version这里有个小经验:Rancher Server 容器本身需要 --privileged 模式运行,因为它在内部会做 iptables 和网络管理相关的操作。如果启动后端口起不来,多半是特权模式没加对,或者宿主机内核模块不全。
2.2 用容器方式启动 Rancher Server
Rancher Server 的启动命令很简单,但 tag 别乱用 latest。我装的时候用的是 v2.6.2,对应的镜像命令是:
docker run -d \ --name rancher-server \ --restart=unless-stopped \ -p 80:80 -p 443:443 \ --privileged \ rancher/rancher:v2.6.2为什么我强调用固定 tag?因为 Rancher 每个版本的 Kubernetes 支持矩阵不一样,如果你用了太新的 Rancher,它可能默认管理的 K8s 版本范围变了,反而和 Kubeflow 1.3 需要的 K8s 版本对不上。固定版本能让整个环境可复现。
启动之后,Rancher 的初始密码会在容器日志里打印,你需要从日志里找 bootstrap password:
docker logs rancher-server 2>&1 | grep "Bootstrap Password"拿到密码后,访问 https://<节点IP> ,用这个一次性密码完成首次登录,然后设置 admin 账号的新密码。这一步我踩过一个雷:Rancher 首次访问会有个“设置服务器 URL”的步骤,这里别随便填。单机环境如果后面还要给集群节点拉取 Rancher agent 信息,填错了会很麻烦,直接填https://<节点IP>即可。
2.3 接入 K3s 集群并准备 kubectl 访问
我的做法是先手动装好 K3s,再由 Rancher 导入。为什么不用 Rancher 直接创建下游 K3s 集群?Rancher 创建自定义集群时会通过 SSH 在目标机器上执行安装脚本,单机环境下多了一层 SSH 配置和 key 分发的麻烦。手动装 K3s 反而是更可控的方式。
K3s 的安装命令里有一个关键参数,必须禁用 traefik:
curl -sfL https://get.k3s.io \ | INSTALL_K3S_VERSION=v1.21.10+k3s1 \ sh -s - --disable traefik --write-kubeconfig-mode 644这一步可能有人会问:是不是 traefik 不重要?不是,K3s 默认自带 traefik,而且它会抢 80 和 443 端口。Kubeflow 部署后,Istio 也需要 80/443 作为网关入口,如果 traefik 还占着,端口冲突会直接导致一系列访问异常。所以在装 K3s 时禁用 traefik,是最划算的一步。
装完后,K3s 默认的 kubeconfig 在 /etc/rancher/k3s/k3s.yaml。单机环境下,为了后续方便,我建议直接把它作为默认 kubeconfig 使用:
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml kubectl get nodes接下来在 Rancher 控制台里,进入“集群管理”,选择“导入现有集群”,给它起个名字,比如“k3s-local”,然后在 K3s 节点上执行 Rancher 生成的 import 命令。这个命令会在集群里部署 Rancher agent 和 fleet 相关组件,大概等几分钟,集群状态会从 Provisioning 变成 Active。
集群真正 Active 之后,建议顺手在 Rancher 的集群页面里下载一份 kubeconfig,后面部署 Kubeflow 时可以交替使用 Rancher 界面和 kubectl 命令行,排查起来方便很多。
3. Kubeflow 1.3 安装前的版本校准与避坑
3.1 版本兼容矩阵:为什么锁死在 K8s 1.21 附近
Kubeflow 1.3 这个版本是 2021 年中左右发布的,它官方测试过的 Kubernetes 版本是 1.19、1.20、1.21。这不是说它一定不能在 1.22 甚至更高版本上跑,但你可能会碰上 apiVersion 过期的问题。比如 Kubernetes 1.22 移除了 admissionregistration.k8s.io/v1beta1 这类旧 API,而 Kubeflow 1.3 里某些组件的清单文件还是用旧版本声明的,直接 apply 会报找不到资源类型。
单机环境装 Kubeflow,最重要的不是追求新,而是稳定。所以我锁定了 K3s v1.21.10+k3s1,对应 Kubernetes 1.21。Rancher 2.6.2 对这个范围的 Kubernetes 版本也支持得很好。整体版本组合如下:
| 软件 | 版本 | 说明 |
|---|---|---|
| Rancher | v2.6.2 | 管理平面 |
| K3s | v1.21.10+k3s1 | 单节点集群 |
| Kubeflow | v1.3.0 | ML 平台 |
| Docker | 20.10 | 运行 Rancher Server |
| kustomize | 3.2.1 | 安装 Kubeflow 清单 |
如果你手头有更早或更新的镜像 tag,记得全部对齐到这个矩阵里。版本混搭是单机安装里最隐蔽的坑,表面上看是组件起不来,实际上全是 API 版本不兼容。
3.2 提前规避网络组件冲突:traefik 与 Istio
Kubeflow 默认的流量入口是 Istio Ingressgateway。单机环境下,你已经装了 Rancher 占用了 80/443,K3s 自带的 traefik 如果还在,又是一个 80/443 的占用者。三个入口挤一台机器,不改必炸。
所以在装 K3s 时,我已经用 --disable traefik 把 traefik 关掉了。这样系统里还剩 Rancher 占 80/443,而 Istio Ingressgateway 我给它的方案是改成 NodePort 30080/30443。这样一来,整个单机环境的访问入口就比较清晰:Rancher 走 https://IP:443,Kubeflow 走 https://IP:30443。
如果当初没有禁用 traefik,也不要慌,可以修改 K3s 的安装参数补上,或者直接编辑 /etc/systemd/system/k3s.service,在 ExecStart 后面加 --disable traefik,重启 K3s 即可。
3.3 安装方式选型:直接用 manifests 而不是 kfctl
Kubeflow 1.3 提供了两种安装方式:kfctl 一键安装,以及从 manifests 仓库用 kustomize 分步安装。我强烈推荐后者。kfctl 看起来很美好,但它在 apply 阶段是一股脑把几百个 Kubernetes 资源全部推上去,中间一旦某个组件拉镜像超时或者 CRD 冲突,你几乎找不到准确的失败点,只能反复删除重来。
manifests 方式则把每个组件拆成了独立的 kustomize 目录,比如 cert-manager、Istio、dex、Katib、Pipelines 这些,可以逐个 apply、逐个校验。哪个失败就重试哪个,整个安装过程是可控的。虽然命令会多一点,但对于单机调试来说是值得的。
4. Kubeflow 1.3 分步安装与验证
4.1 准备 kubectl 与 kustomize 3.x
部署 Kubeflow 之前,先把两个命令行工具备好:kubectl 和 kustomize。kubectl 建议用和 Kubernetes 1.21 匹配的 1.21 版本,避免客户端和服务端版本差太多导致 API 请求异常。kustomize 我特意用了 3.2.1,因为 Kubeflow 1.3 的清单是基于 kustomize 3 的格式写的,kustomize 4 对某些资源的严格校验可能会导致 build 失败。
curl -LO https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize/v3.2.1/kustomize_v3.2.1_linux_amd64.tar.gz tar -zxvf kustomize_v3.2.1_linux_amd64.tar.gz sudo mv kustomize /usr/local/bin/ kustomize version实际操作中,我一般不用 kubectl apply -k,而是先用 kustomize build 生成完整 YAML,再通过管道交给 kubectl apply。这样可以直接看到每次到底 applied 了哪些资源,出错时也能定位到具体文件。
4.2 安装公共依赖组件(cert-manager、Istio、dex)
先拉取 Kubeflow 1.3 的 manifests 仓库,注意要用 v1.3.0 分支:
git clone -b v1.3.0 --single-branch https://github.com/kubeflow/manifests.git cd manifests公共依赖组件的安装顺序很关键。我从最底层的 cert-manager 开始,它是整个 Kubeflow 证书签发的基础:
kustomize build common/cert-manager/base | kubectl apply -f - kubectl wait --for=condition=available deployment/cert-manager -n cert-manager --timeout=180s接下来是 Istio。Kubeflow 1.3 依赖 Istio 来做流量管理和认证转发。这一步启动后,istio-system 里会有一个 istiod 控制面和一个 istio-ingressgateway 数据面:
kustomize build common/istio-1-9-0/base | kubectl apply -f - kubectl wait --for=condition=available deployment -l app=istiod -n istio-system --timeout=300s再往下是 dex 和 oauth2-proxy,它们是 Kubeflow 的认证核心。dex 是一个身份服务,oauth2-proxy 负责和 Istio 配合做统一登录跳转:
kustomize build common/dex/base | kubectl apply -f - kustomize build common/oauth2-proxy/base | kubectl apply -f -dex 装完后,记得等一下它的 Pod ready:
kubectl wait --for=condition=ready pod -l app=dex -n auth --timeout=180s最后是命名空间和基础 RBAC:
kustomize build common/kubeflow-namespace/base | kubectl apply -f - kustomize build common/kubeflow-roles/base | kubectl apply -f -4.3 安装 Kubeflow 核心业务组件
公共组件就位后,就可以逐个部署 Kubeflow 的业务组件了。我把这些分成两批:第一批是基础服务,第二批是可选能力。基础服务包括 profiles、notebook-controller、jupyter-web-app、centraldashboard、admission-webhook,这些是 Kubeflow 平台本身的核心;第二批包括 pipelines、katib、training-operator、kfserving,属于 ML 工作流和模型服务的能力。
基础服务:
kustomize build common/kubeflow-crds/base | kubectl apply -f - kustomize build common/kubeflow-istio/base | kubectl apply -f - kustomize build common/kubeflow-istio/local-gateways | kubectl apply -f - kustomize build profiles/base | kubectl apply -f - kustomize build jupyter/notebook-controller/base | kubectl apply -f - kustomize build jupyter/jupyter-web-app/base | kubectl apply -f - kustomize build centraldashboard/base | kubectl apply -f -然后是 ML 工作流相关组件:
kustomize build pipelines/base | kubectl apply -f - kustomize build katib/base | kubectl apply -f - kustomize build training-operator/base | kubectl apply -f - kustomize build kfserving/base | kubectl apply -f -注意,这里我写的目录名是仓库里比较常见的结构,但 Kubeflow manifests 在不同小版本里目录结构可能会有细微变化。最稳妥的做法是每次进入对应目录前,先 ls 一下确认目录路径存在,实在找不到就从 manifests 仓库 README 里对照一份官方目录说明。
装完后不要急着去访问页面,先看全集群的 Pod 状态:
kubectl get pods -A如果看到部分 Pod Pending 或 CrashLoopBackOff,先别慌。Pending 多半是资源不足或 StorageClass 没就绪,CrashLoopBackOff 则多半是内存不够导致的 OOMKilled。单机环境最常见的情况就是所有组件一起启动时把内存瞬间打满,需要等一等或者分批启动。
4.4 调整入口并完成登录验证
Kubeflow 装好之后,istio-ingressgateway 默认的服务类型是 LoadBalancer。在 K3s 上,这个 LoadBalancer 会由 K3s 自带的 klipper-lb 实现,实际会绑定到宿主机的一个随机端口。我不太喜欢随机端口,因为每次重启可能都会变。所以我直接把 istio-ingressgateway 改成 NodePort,固定用 30080 和 30443:
kubectl patch svc istio-ingressgateway -n istio-system \ -p '{"spec":{"type":"NodePort","ports":[{"name":"http2","port":80,"targetPort":8080,"nodePort":30080},{"name":"https","port":443,"targetPort":8443,"nodePort":30443}]}}'然后浏览器访问https://<节点IP>:30443。第一次访问会出现证书警告,这是因为 Istio 默认用的自签名证书,单机测试阶段直接跳过警告进入即可。进入后应该会跳到 dex 的登录页,Kubeflow 1.3 默认账号是user@example.com,密码是123456789,登录后回到 Central Dashboard。
到这里,Rancher、K3s、Kubeflow 1.3 三层都已经通了。我之前第一次装到这一步,心情还是相当激动的,因为后面就能真的在 Notebook 里写代码跑模型了。
5. 单机环境下的常见问题与排查
5.1 组件反复重启:仔细看 requests 和内存
单机环境下最普遍的问题就是 Pod OOMKilled。Kubeflow 的很多组件默认写的资源 requests 并不小,多个组件同时运行时,16G 机器很容易吃紧。我在安装时就遇到过 katib 的 controller 起不来,kubectl describe pod 一看,OOMKilled。
解决思路有两个,一个是从根上降低同时启动的组件压力,分批安装、等一批稳定后再装下一批;另一个是手动修改相关组件的资源 limits,比如把 requests 调低,或者给 Pod 增加 swap 空间。我实际操作中更偏向于给几个非关键组件临时打补丁,把 limits 改成比较小的值,等平台稳定跑起来后再恢复默认。
排查 OOM 的命令,多看几个维度:
kubectl top pods -A kubectl logs <pod> --previous kubectl describe pod <pod>这里有个小坑:K3s 默认把 kubelet 的 system reserved 和 kube-reserved 都设置得比较大,单机场景会进一步压缩可调度内存。如果你发现节点明明还有内存,但 Pod 就是调度不上去,可以查一下节点的 allocatable。
5.2 访问入口 502:认证链路排查
装完后访问 Kubeflow 入口,最让人头疼的问题就是页面一直 502 或者不断重定向。这个问题我在调试的时候把整个链路拆了一遍,最终发现主要是两个原因:
第一个原因是 dex 的 Pod 没就绪。dex 是认证的关键,如果它挂了,oauth2-proxy 转发到 dex 就会失败,表现为页面跳转后 5xx。先检查 dex 日志:
kubectl logs -n auth deployment/dex第二个原因是 Istio sidecar 代理没有正确注入。Kubeflow 的 Central Dashboard 部署在 kubeflow 命名空间,正常情况该命名空间的 Pod 会自动注入 istio-proxy 容器。如果发现某个 Pod 没有 istio-proxy,就要检查命名空间的 label 是否还有istio-injection=enabled,并确认 istio-injection 的 webhook 是否正常。
排查 502 的正确姿势是逐层看:先看 istio-ingressgateway 是否能访问,再看 oauth2-proxy 的日志,最后看中央控制台的 Pod 日志。每次只在一个层面加日志输出,不要上来就乱改配置。
5.3 PVC 无法创建:StorageClass 检查
Kubeflow 的 Pipeline 组件和 Notebook 组件都需要 PVC。如果在某些组件里一直报Failed to provision volume with StorageClass,那就是集群里没有可用的默认 StorageClass。K3s 自带的 local-path 默认会被设置为默认 StorageClass,正常情况下不需要额外处理。但如果你在 Rancher 里导入集群时,Rancher 启用了某些组件或者你对 K3s 做了额外配置,可能把 local-path 的默认标记给取消了。
检查方式:
kubectl get sc如果发现 local-path 没有(default)标记,手动打上 annotation:
kubectl annotate storageclass local-path storageclass.kubernetes.io/is-default-class="true"顺便多说一句,local-path 在生产环境不是最佳选择,但在单机开发和演示场景里,它是最省事的存在。我所有的 Notebook 测试数据都直接落在宿主机本地盘上,速度快,也方便清理。
5.4 清理 Kubeflow 时 Namespace 卡在 Terminating
如果你后面想重新安装 Kubeflow,大概率会遇到一个很经典的清理问题:删 kubeflow 命名空间时,状态一直卡在 Terminating。原因是 Kubeflow 安装时创建了一堆 ValidatingWebhookConfiguration 和 MutatingWebhookConfiguration,命名空间删除时还需要调用这些 webhook,但 webhook 所依赖的后端 Pod 已经被删了,导致删除流程卡住。
解决办法是先把相关的 webhook 配置删掉:
kubectl get validatingwebhookconfiguration -A | grep kubeflow kubectl get mutatingwebhookconfiguration -A | grep kubeflow kubectl delete validatingwebhookconfiguration <名字> kubectl delete mutatingwebhookconfiguration <名字>删除后,Terminating 的命名空间通常会很快消失。如果不消失,可以进一步确认该命名空间下是否还有残留资源,或者查看命名空间的 finalizers,必要时编辑命名空间把卡住的 finalizer 去掉。这个问题在我反复测试时遇到好几次,记住先清理 webhook 再删命名空间,会省很多时间。
6. 一些实际的安装体会和优化建议
整套环境跑通之后,我最深的感触是:单机安装的难点不在“安装”本身,而在于你把所有资源都塞进一台机器后,如何让它们和谐共存。Rancher 和 Kubeflow 都不难装,真正费时间的是版本对齐、端口规划、资源配额这些看起来不起眼的事情。
给你两个从实际经验里沉淀的建议。第一,所有工具和镜像尽量固定版本,不要用 latest。我见过太多人因为 Rancher 的镜像 tag 漂移,导致 K3s 版本不匹配,最后花了大量时间排查一个本来可以避免的问题。第二,装 Kubeflow 时给自己留一个“最小可用集”的概念,不是说所有组件都装上才好,如果你只需要 Notebook 和 Pipeline,那 Katib 和 KFServing 可以先放着不装,等真的用到再按需启用,这样单机内存压力小很多。
最后一个小技巧,也是我后来一直在用的:把整个安装过程的操作命令按章节记录成一个 Markdown 文件,保存好 kubeconfig 的备份。因为单机环境的 K3s 一旦重装,Rancher 的 import 命令和 Kubeflow 的安装顺序都要重新来,有了一份记录,你能在半小时内恢复整个环境,而不是再从零开始试错。希望这篇记录能帮你把 rancher + kubeflow1.3 单机安装这条路走顺,后续想扩展成多节点集群时,这套底子也能直接派上用场。