1. 从零搭建一套可用的容器编排环境:这套组合到底在解决什么问题
很多团队第一次接触容器化部署时,最容易卡住的地方往往不是写业务代码,而是把镜像从私有仓库拉到集群里跑起来这一整套链路。标题里提到的这套组合——k8s 集群、kuboard 管理面板、harbor 私有镜像仓库、yml 编排文件——本质上是在搭一条从"镜像存储"到"集群调度"再到"可视化运维"的完整通路。我把它理解成一条流水线:harbor 是仓库,负责把打包好的镜像安全存起来;k8s 是调度中心,负责把镜像变成真正跑起来的容器;kuboard 是驾驶舱,让你不用天天敲 kubectl 也能看清集群状态;yml 文件则是这条流水线的"施工图纸",声明式地告诉集群你要什么。
这套东西适合谁?如果你是一个中小团队的后端或运维,手里有几台机器,想把服务从"手动 docker run"升级到"集群化管理",那这套组合的性价比非常高。kuboard 相比一些重型面板,学习曲线平缓,中文界面友好,对刚上手 k8s 的人很友好;harbor 则是私有镜像仓库里比较成熟的选择,权限、复制、扫描这些能力都有。整套方案不需要云厂商绑定,自己几台机器就能跑起来,可控性强。
我在实际搭这套环境时踩过的坑主要集中在三个地方:一是 k8s 节点之间的网络和证书配置,稍有不慎节点就加不进来;二是 harbor 默认走 https,k8s 拉镜像时如果没配信任就会报证书错误;三是 yml 里的 imagePullSecrets 没配对,Pod 一直卡在 ImagePullBackOff。这三个问题几乎每个新手都会遇到,后面我会逐个拆开讲清楚原理和解决办法。
需要先说明的是,下面涉及的具体 IP、域名、密码都是示意值,你在实操时替换成自己的环境即可。整套流程我按"集群准备 → 面板部署 → 仓库配置 → 镜像拉取 → yml 编排"的顺序展开,每一步都解释为什么这么做,而不是让你无脑复制命令。
2. 整体方案设计与选型思路拆解
2.1 为什么是这套组合而不是别的
先说说选型逻辑。k8s 作为编排层基本没有争议,它是事实标准,生态最全,遇到问题搜到的资料也最多。真正需要斟酌的是面板和仓库这两个外围组件。
面板这块,市面上有 dashboard、rancher、kuboard 等选择。dashboard 是官方出品但功能偏基础,很多操作还得回到命令行;rancher 功能强大但偏重,对资源要求高,小集群跑起来有点浪费。kuboard 的定位比较讨巧,它把常用的部署、日志、终端、配置管理都做成了图形化,而且对多集群管理有支持,资源占用也克制。我选它的核心理由是:新手能快速看到"集群里到底发生了什么",降低理解成本。
仓库这块,harbor 的优势在于它是 CNCF 毕业项目,和 k8s 生态契合度高,自带用户权限、项目隔离、镜像复制、漏洞扫描这些企业级能力。相比之下,直接用一个 registry 容器虽然简单,但权限和审计能力几乎为零,团队协作时容易出乱子。harbor 的部署稍微重一点,需要数据库、缓存、存储这些依赖,但换来的是可持续维护的仓库能力,长期看是划算的。
yml 编排文件的选择没什么悬念,k8s 原生就是声明式的,用 yml 描述期望状态,集群负责收敛。这里的关键不是"用不用 yml",而是"yml 怎么写才不容易出错",后面会重点讲几个高频坑点。
2.2 整体架构与数据流向
把这几块拼起来,数据流向是这样的:开发者在本地构建镜像,打上 harbor 的地址标签,push 到 harbor 仓库;然后在 k8s 集群里提交 yml 文件,yml 里声明了镜像地址和拉取凭证;k8s 的 kubelet 拿着凭证去 harbor 拉镜像,拉下来后在节点上启动容器;kuboard 通过 k8s 的 API 读取集群状态,把 Pod、Service、Deployment 这些资源可视化展示出来。
这条链路里有两个"信任关系"需要打通:一是 k8s 节点要信任 harbor 的证书(如果 harbor 用了自签证书),二是 k8s 要有 harbor 的登录凭证(通过 Secret 传递)。这两个信任关系没配好,镜像就拉不下来,这是新手最常见的卡点。
2.3 资源规划与前置准备
在动手之前,先把资源盘清楚。我建议至少三台机器:一台做 master(控制面),两台做 worker(工作节点),如果资源紧张,master 也可以参与调度,但生产环境不建议这么做。每台机器建议 2 核 4G 起步,harbor 单独一台或者和 master 共用都行,但要注意 harbor 的数据库和存储会占一些资源。
操作系统层面,需要关闭 swap(k8s 的硬性要求,否则 kubelet 启动会报错),配置好主机名和 hosts 解析,确保节点之间能互相 ping 通。防火墙这块,如果开了 firewalld 或 ufw,需要放行 k8s 用到的端口,或者在内网环境下直接关掉(仅限测试环境,生产要按最小权限放行)。时间同步也很重要,节点间时间偏差过大会导致证书校验失败,建议统一配好时间同步服务。
提示:关闭 swap 只是当前会话生效的话,重启后会失效,记得同时修改 /etc/fstab 把 swap 那一行注释掉,否则下次重启 kubelet 又起不来。
3. 核心环节实操:集群、面板、仓库、镜像、编排逐层落地
3.1 k8s 集群初始化与节点加入
集群初始化这一步,核心是选一个网络插件(CNI)。k8s 本身不管 Pod 之间怎么通信,得靠 CNI 插件来实现。常见的有 Calico、Flannel、Cilium。测试环境我一般用 Flannel,配置简单;如果对网络策略有要求,Calico 更合适。选哪个会影响后面 Pod 网段和 Service 网段的规划,初始化时要通过参数指定,比如 Pod 网段用 10.244.0.0/16,Service 网段用 10.96.0.0/12,这两个网段不能和宿主机网段冲突,否则会出现路由混乱。
初始化命令大致长这样(版本和参数按实际调整):
kubeadm init \ --apiserver-advertise-address=192.168.1.10 \ --pod-network-cidr=10.244.0.0/16 \ --service-cidr=10.96.0.0/12 \ --kubernetes-version=v1.28.0初始化成功后,会输出一条kubeadm join命令,这条命令里带着 token 和证书 hash,worker 节点执行它就能加入集群。这里有个细节:token 默认 24 小时过期,如果隔天才加节点,需要重新生成 token。另外 join 命令里的地址是 master 的 advertise 地址,要确保 worker 能访问到这个地址和 6443 端口。
节点加入后,用kubectl get nodes查看状态,如果显示 NotReady,多半是 CNI 还没装好。装上 Flannel 之后等一两分钟,状态会变成 Ready。我遇到过节点一直 NotReady 的情况,排查下来是节点上的 kubelet 日志报"cni plugin not initialized",原因就是网络插件没装或者装失败了,重新 apply 一次插件 yml 就好。
3.2 kuboard 面板部署与访问配置
kuboard 的部署方式比较灵活,可以用 docker 单独跑,也可以部署到 k8s 集群里。我倾向于后者,因为它本身就是个容器化应用,跑在集群里更符合"用 k8s 管 k8s"的思路。部署时通过一个 yml 文件创建 Deployment 和 Service,Service 类型用 NodePort 方便外部访问。
部署完成后,访问http://节点IP:端口就能打开 kuboard 的登录页。首次登录需要导入集群的 kubeconfig,kuboard 会引导你创建一个有相应权限的 ServiceAccount,并生成对应的 token 或 kubeconfig。这里要注意权限范围:如果只是查看,给只读权限就够;如果要通过面板部署应用,需要给到相应命名空间的编辑权限。权限给大了有安全风险,给小了操作会报 forbidden,按需分配最稳妥。
kuboard 里有个很实用的功能是"终端",可以直接进到 Pod 里执行命令,省去了kubectl exec的敲命令过程。还有"日志"功能,能实时看容器输出,排查问题很方便。我平时排查 Pod 启动失败,基本就是先在 kuboard 里看事件和日志,定位到大致方向再回命令行深挖。
3.3 harbor 仓库搭建与项目配置
harbor 的部署官方推荐用 docker-compose 方式,下载离线包解压后,修改 harbor.yml 里的 hostname、端口、密码、数据目录这些配置,然后执行安装脚本。hostname 这一项很关键,它决定了镜像地址的前缀,比如设成harbor.internal,那镜像地址就是harbor.internal/项目名/镜像名:标签。这个 hostname 必须能被 k8s 节点解析到,否则拉镜像时会报域名解析失败。
harbor 默认启用 https,安装时会生成自签证书。自签证书的问题在于 k8s 节点不信任它,拉镜像时会报x509: certificate signed by unknown authority。解决办法有两个:一是把 harbor 的 CA 证书分发到每个节点的/etc/docker/certs.d/harbor.internal/目录下(如果用 containerd 则是/etc/containerd/certs.d/),二是测试环境直接在 harbor.yml 里关掉 https 只用 http。生产环境强烈建议用第一种,走正规证书信任链。
项目配置这块,harbor 里要先建一个项目(比如叫myapp),然后建一个机器人账号或者普通用户,给这个项目分配推送和拉取的权限。k8s 拉镜像用的凭证就是这个账号的用户名密码,通过 Secret 传给集群。这里有个容易忽略的点:harbor 的机器人账号密码只在创建时显示一次,没记下来就得重新建,建议创建后立刻存到密码管理工具里。
3.4 镜像构建、推送与拉取凭证配置
镜像构建没什么特别的,Dockerfile 写好之后docker build -t harbor.internal/myapp/demo:v1 .打标签,然后docker login harbor.internal登录,再docker push推上去。推送前确保本地 docker 信任了 harbor 的证书,否则 login 就会失败。
拉取凭证的配置是重点。k8s 拉私有仓库镜像需要 imagePullSecrets,这个 Secret 的类型是docker-registry,内容就是 harbor 的登录信息。创建命令:
kubectl create secret docker-registry harbor-cred \ --docker-server=harbor.internal \ --docker-username=robot\$myapp \ --docker-password=你的密码 \ --namespace=default创建好之后,在 yml 的 Pod 模板里通过imagePullSecrets引用它。这里有个高频坑:Secret 是命名空间级别的,yml 部署在哪个命名空间,Secret 就得建在哪个命名空间,跨命名空间引用是不行的。我见过有人 Secret 建在 default,应用部署在 prod,结果一直 ImagePullBackOff,查了半天才发现是命名空间对不上。
3.5 yml 编排文件编写与部署验证
一个最小可用的 Deployment yml 大概包含这些部分:apiVersion、kind、metadata(名称和标签)、spec(副本数、选择器、Pod 模板)。Pod 模板里再定义容器镜像、端口、环境变量、资源限制、拉取凭证。下面是一个示意结构:
apiVersion: apps/v1 kind: Deployment metadata: name: demo-app namespace: default spec: replicas: 2 selector: matchLabels: app: demo-app template: metadata: labels: app: demo-app spec: imagePullSecrets: - name: harbor-cred containers: - name: demo image: harbor.internal/myapp/demo:v1 ports: - containerPort: 8080 resources: requests: cpu: 100m memory: 128Mi limits: cpu: 500m memory: 512Mi写完用kubectl apply -f demo.yml提交,然后用kubectl get pods -w观察状态变化。正常流程是 ContainerCreating → Running。如果卡在 ImagePullBackOff,按前面说的顺序排查:域名能不能解析、证书信不信任、Secret 在不在同一个命名空间、Secret 里的账号密码对不对。如果卡在 CrashLoopBackOff,那是容器启动后自己退出了,得看日志找原因,跟镜像拉取无关。
资源限制这块建议一定要写,requests 影响调度(节点资源够不够),limits 影响运行时(超了会被限制或杀掉)。不写的话 Pod 可能被调度到资源紧张的节点上,跑起来互相抢资源。我一般按实际压测结果的 1.2 倍设 limits,requests 设成正常负载的水平。
4. 常见问题排查与避坑经验实录
4.1 镜像拉取类问题速查
镜像拉取失败是这套环境里最高频的问题,我把常见报错和对应原因整理成表,方便对照排查:
| 报错信息 | 可能原因 | 排查方向 |
|---|---|---|
| ImagePullBackOff | 凭证缺失或错误 | 检查 Secret 是否存在、命名空间是否一致、账号密码是否正确 |
| x509 certificate error | 证书不被信任 | 分发 CA 证书到节点,或改用 http |
| no such host | 域名解析失败 | 检查节点 hosts 或 DNS 配置 |
| manifest unknown | 镜像标签不存在 | 确认 harbor 里该标签确实推送成功 |
| unauthorized | 账号无拉取权限 | 检查 harbor 项目成员权限配置 |
排查时我习惯先用kubectl describe pod看 Events,里面会明确写出拉取失败的具体原因,比瞎猜快得多。如果 Events 信息不够,再去节点上看 kubelet 日志或者 containerd 日志,那里有更底层的错误。
4.2 节点与网络类问题
节点 NotReady 是另一类高频问题。除了前面说的 CNI 没装,还可能是 kubelet 服务没起来、证书过期、磁盘压力等。用systemctl status kubelet看服务状态,用journalctl -u kubelet -f看实时日志,基本能定位到原因。磁盘压力导致的 NotReady 比较隐蔽,节点磁盘用到 85% 以上就可能触发驱逐,df -h看一眼就知道。
Pod 之间通信不通,先确认 CNI 是否正常工作,kubectl get pods -n kube-system看网络插件的 Pod 是否都 Running。如果网络插件正常但跨节点通信还是不通,检查节点间防火墙是否放行了 VXLAN 或 IPIP 相关的流量,Flannel 默认用 VXLAN,需要放行 8472/UDP 端口。
4.3 实操心得与独家技巧
分享几个文档里不太会写、但实际很省事的经验。第一,搭建环境时养成"每步验证"的习惯,装完 CNI 就验证节点 Ready,配完 Secret 就手动docker pull验证凭证,别等全部搭完再一起排查,那样问题会缠在一起。第二,yml 文件用kubectl apply --dry-run=client -f xxx.yml先做语法校验,能提前发现缩进、字段拼写这类低级错误,省得提交后才发现。第三,harbor 的垃圾回收要定期做,删掉的镜像标签不会自动释放存储空间,时间长了磁盘会被撑满,在 harbor 管理界面里配置定时清理任务比较省心。
还有一个关于 kuboard 的小技巧:它支持把多个集群导入同一个面板管理,如果你有测试和预发两套环境,可以在一个界面里切换查看,不用来回登录。导入时注意每个集群的 kubeconfig 权限要单独配,别图省事用同一个高权限账号,安全边界还是要划清楚。
5. 环境维护与后续扩展方向
环境搭起来只是开始,日常维护才是真正花时间的地方。k8s 集群的证书默认一年过期,到期前需要续期,否则 apiserver 会起不来。续期用kubeadm certs renew all,续完重启相关组件。建议在日历上设个提醒,提前一个月处理,别等到出问题才想起来。harbor 的证书同理,自签证书也要关注有效期。
扩展方向上,这套基础环境可以往上叠不少东西。比如加一个 Ingress 控制器,把 Service 暴露成域名访问;加 Prometheus 和 Grafana 做监控告警;加 EFK 做日志收集。这些都是在现有集群上 apply 几个 yml 就能起步的,不用推倒重来。我个人的建议是先把基础链路跑稳,观察一两周没问题,再逐步加组件,一次加太多出问题不好定位。
最后说个我自己的体会:这套环境的价值不在于"装起来了",而在于"能稳定地跑业务"。装的过程照着文档走基本都能通,真正考验人的是出问题时的排查能力。所以搭建阶段多留意日志、多理解每个组件在链路里的角色,比单纯把命令跑通重要得多。等你对"镜像从哪来、凭证怎么传、Pod 怎么调度"这条链路烂熟于心,后面加任何组件都只是在这条链路上挂新节点而已。