【K8s项目源码分析】从文件夹结构看透一个云原生项目的设计逻辑

深入分析CAMP项目中各目录的编写顺序与执行顺序,理解架构设计背后的思考

一、引言:为什么文件夹结构很重要?

当我们拿到一个Kubernetes项目时,第一眼看到的就是它的目录结构。很多初学者会被一堆文件夹搞得晕头转向:k8s/helm/environments/clusters/scripts/ci-cd/gitops/……这些文件夹到底谁先谁后?它们之间是什么关系?

本文将抛开业务代码,纯粹从文件夹设计的角度,分析一个企业级K8s项目的演进逻辑。我将从两个维度来拆解:

  1. 编写顺序(设计时):项目作者从零搭建时,先写哪个文件夹,后写哪个文件夹?

  2. 执行顺序(运行时):从代码提交到部署完成,各文件夹被触发的先后顺序?

理解这两个顺序,你就能真正读懂一个项目的架构设计。

二、编写顺序:从零搭建项目的7个步骤

所谓“编写顺序”,是指项目作者从零开始构建这个项目时,先写什么文件、后写什么文件。这反映了一个架构师的思考路径。

第1步:应用代码 + Dockerfile

文件夹camp-backend/camp-web-frontend/camp-auth-frontend/

阶段目标:把业务逻辑写出来,让程序本身能运行。

思考过程:“我首先要有一个能跑起来的应用,不管是Python后端还是前端页面。”

关键产出

  • Python FastAPI代码

  • 前端HTML/CSS/JS

  • Dockerfile(定义如何把应用打包成镜像)

此时还没有任何Kubernetes相关文件。


第2步:docker-compose.yaml

阶段目标:在本地单机环境快速启动所有服务。

思考过程:“我需要一个简单的办法,在本机一键启动后端+前端+认证服务,方便开发和调试。但不能每次都手动敲docker run命令。”

关键产出

  • docker-compose.yaml(定义所有服务的容器化配置)

配合脚本build-and-run.shrestart-backend.bat

此时仍然不涉及Kubernetes。


第3步:k8s/ 目录

阶段目标:编写“写死的”Kubernetes YAML文件,确定资源标准形态。

思考过程:“现在我要上Kubernetes了。我需要先确定Deployment、Service、Ingress这些资源到底长什么样——副本数是多少?镜像用哪个?探针怎么配?这些都要先写出来看看效果。”

关键产出

  • k8s/namespace.yaml

  • k8s/configmap.yaml / secret.yaml

  • k8s/backend-deployment.yaml

  • k8s/service.yaml

  • k8s/ingress.yaml

  • k8s/horizontal-pod-autoscaler.yaml

设计意义

k8s/目录是所有后续配置的“黄金样本”。虽然是静态的、写死的YAML,但它为后续Helm模板化提供了标准参照

text

k8s/backend-deployment.yaml(静态)
replicas: 2
image: camp-backend:latest
resources:
  limits:
    memory: 512Mi
    cpu: 500m

此时面临的问题:“如果开发环境只想用1个副本、生产环境想用5个副本怎么办?总不能改一次文件重新部署一次吧?”

这个痛点催生了第4步。


第4步:helm/ 目录

阶段目标:把“写死的YAML”变成“可填充的动态模板”。

思考过程:“既然不同环境需要不同参数,那我就把那些会变化的数值抽出来变成变量,然后用一个配置文件来填充这些变量。”

关键产出

  • helm/camp/Chart.yaml(Chart元数据)

  • helm/camp/templates/*.yaml(模板文件)

  • helm/camp/values.yaml(默认值)

核心转变

yaml

# k8s/ 方式(静态)
replicas: 2

# helm/ 方式(动态模板)
replicas: {{ .Values.replicaCount }}

但还有新问题:“我确实把数值变成了变量,但values.yaml只有一个文件。每次部署前都要手动修改这个文件里的变量吗?Dev/Staging/Prod用同一个文件,怎么区分?”

这个痛点催生了第5步。


第5步:environments/ 目录

阶段目标:为不同环境(dev/staging/prod)准备独立的配置文件。

思考过程:“我需要把values.yaml里的默认值作为基线,然后为每个环境单独准备一个覆盖文件。部署时根据目标环境选择对应的文件即可。”

关键产出

  • environments/values-dev.yaml

  • environments/values-staging.yaml

  • environments/values-prod.yaml

配置分层

层级 文件 优先级
默认值 helm/camp/values.yaml 最低(被覆盖)
环境覆盖 environments/values-dev.yaml 中(覆盖默认值)
命令行参数 --set key=value 最高(覆盖一切)

Dev vs Prod配置差异

yaml

# environments/values-dev.yaml
replicaCount: 1
image.tag: latest
resources:
  requests:
    memory: "128Mi"

# environments/values-prod.yaml  
replicaCount: 5
image.tag: v1.0.0
resources:
  requests:
    memory: "512Mi"

现在我可以区分环境了,但每次执行Helm命令都很长

bash

helm install camp ./helm/camp -f environments/values-dev.yaml -n camp-dev

这个痛点催生了第6步。


第6步:clusters/ 目录

阶段目标:集中管理所有可用的Kubernetes集群信息。

思考过程:“我不但要区分环境,还要区分集群——本地Minikube、云端AKS、EKS、GKE。每个集群的连接信息(context、namespace)都不一样,我需要统一管理这些信息。”

关键产出

  • clusters/cluster-config.yaml

文件结构

yaml

clusters:
  local-dev:
    context: minikube
    namespace: camp-dev
    environments: [dev]
  
  aks-prod:
    context: camp-prod-aks
    namespace: camp-prod
    environments: [prod]
    azure:
      subscriptionId: "xxx"

现在各文件分工清晰

文件 解决什么问题
environments/ 部署什么(用什么配置)
clusters/cluster-config.yaml 部署到哪里(目标集群)

但命令仍然复杂,需要结合集群和环境参数:

bash

helm upgrade --install camp ./helm/camp \
  -f environments/values-dev.yaml \
  --kube-context minikube \
  -n camp-dev

这个痛点催生了第7步。


第7步:scripts/ 目录

阶段目标:封装复杂命令,实现一键部署。

思考过程:“每次都要敲那么长的命令,而且还要先切换集群上下文,太容易出错了。我要写一个脚本,只需要传-c cluster -e env就能自动完成所有步骤。”

关键产出

脚本 作用 编写顺序内
make-executable.sh 给所有脚本加执行权限 最先写(否则其他脚本跑不了)
cluster-manager.sh 查看/切换/测试集群 次之
deploy.sh 执行实际部署 最后写(依赖前面所有配置)

deploy.sh核心流程

text

解析参数(-c/-e/-t)
    → 检查依赖(kubectl/helm/yq)
    → 读取 clusters/cluster-config.yaml 获取集群配置
    → 切换K8s上下文
    → 读取 environments/values-dev.yaml 获取环境配置
    → 合并Helm values
    → 执行 helm upgrade --install
    → 显示部署状态
    → 输出访问地址

第8步(进阶):ci-cd/ 目录

阶段目标:把scripts/deploy.sh放进CI/CD流水线,实现代码推送即自动部署。

思考过程:“现在手动执行./scripts/deploy.sh已经很方便了。但每次代码更新后,还要手动去跑一次脚本。能不能做到代码推送后自动触发?”

关键产出

  • .github/workflows/build-and-deploy.yml

流水线步骤

text

代码推送 → 构建Docker镜像 → 推送到GHCR → 调用scripts/deploy.sh → 部署到目标集群

第9步(进阶):gitops/ 目录

阶段目标:引入GitOps工具(ArgoCD/Flux),让集群自己“拉取”配置,实现声明式部署和自愈能力。

思考过程:“CI/CD流水线直接操作集群虽然方便,但不够安全。如果Git仓库里的配置和集群实际状态不一致怎么办?能不能让集群自己持续监控Git仓库的变化,自动保持同步?”

关键产出

  • gitops/argocd/application.yaml

  • gitops/flux/gitrepository.yaml

  • gitops/flux/kustomization.yaml

ArgoCD vs Flux对比

维度 ArgoCD Flux
Application定义 一个资源包含源+目标+策略 分为GitRepository + Kustomization
部署策略 一体化 分层(Source层 + Reconciler层)
GitOps核心 Application持续同步 Kustomization持续同步

GitOps工作流

text

开发者更新Git仓库中的镜像Tag → ArgoCD/Flux检测到变化 → 自动同步到集群

编写顺序总结

顺序 文件夹 核心动机 依赖的上一步
1 应用代码 + Dockerfile 先有可运行的程序
2 docker-compose.yaml 本地一键启动 第1步
3 k8s/ 确定K8s资源的“标准样子” 第2步
4 helm/ 把写死的YAML变成动态模板 第3步
5 environments/ 支持多环境(dev/staging/prod) 第4步
6 clusters/ 支持多集群切换 第5步
7 scripts/ 封装命令,一键部署 第6步
8 ci-cd/ 代码推送即自动部署 第7步
9 gitops/ 集群自愈、声明式管理 第8步

三、执行顺序:从提交代码到服务运行

所谓“执行顺序”,是指当你真正部署应用时,这些文件夹在时间上被触发的先后顺序

场景1:纯手动部署(不使用CI/CD,不使用GitOps)

bash

./scripts/deploy.sh -c local-dev -e dev -t latest

执行顺序

顺序 触发的文件/文件夹 具体操作
1 scripts/deploy.sh 脚本开始执行
2 clusters/cluster-config.yaml 读取集群配置(context、namespace)
3 kubectl(切换上下文) 连接到目标集群
4 environments/values-dev.yaml 读取环境专属配置
5 helm/camp/templates/ + helm/camp/values.yaml 合并所有values,渲染模板
6 helm 执行helm upgrade --install
7 Kubernetes集群 创建/更新Pod、Service、Ingress等资源

简图

text

scripts/deploy.sh
    → clusters/cluster-config.yaml(选目标集群)
    → kubectl use-context(切换集群)
    → environments/values-dev.yaml(读环境配置)
    → helm/(渲染并部署)
    → K8s集群(运行服务)

场景2:CI/CD自动化部署(不使用GitOps)

当代码推送到GitHub时:

顺序 触发的文件/文件夹 具体操作
1 .github/workflows/build-and-deploy.yml GitHub Actions流水线被触发
2 应用代码 + Dockerfile 构建Docker镜像
3 Docker镜像推送 推送到GHCR
4 scripts/deploy.sh 流水线调用部署脚本
5 clusters/cluster-config.yaml 读取集群配置
6 kubectl 切换集群上下文
7 environments/values-*.yaml 读取环境配置
8 helm/ 渲染并部署到集群

简图

text

git push
    → .github/workflows/(流水线触发)
    → Dockerfile(构建镜像)
    → scripts/deploy.sh(调用部署脚本)
    → clusters/ + environments/ + helm/(部署到目标集群)

场景3:GitOps模式部署

当代码推送到GitHub时:

顺序 触发的文件/文件夹 具体操作
1 .github/workflows/build-and-deploy.yml CI流水线触发
2 应用代码 + Dockerfile 构建并推送镜像
3 不调用scripts/deploy.sh 流水线只负责更新Git仓库中的镜像Tag
4 gitops/argocd/application.yaml ArgoCD检测到Git仓库变化
5 helm/ + environments/ ArgoCD读取Helm Chart和环境配置
6 Kubernetes集群 ArgoCD将变更同步到集群

简图

text

git push
    → .github/workflows/(构建镜像+更新Git配置)
    → gitops/(ArgoCD/Flux检测到变化)
    → helm/ + environments/(读取配置并渲染)
    → K8s集群(自动同步)

注意:在GitOps模式下,scripts/deploy.sh 不会被调用。集群的部署由ArgoCD/Flux自己完成,不需要外部触发。

四、三种模式的执行顺序对比

顺序 纯手动模式 CI/CD模式 GitOps模式
1 scripts/deploy.sh .github/workflows/ .github/workflows/(仅构建镜像)
2 clusters/ Dockerfile(构建镜像) 镜像推送到GHCR
3 kubectl use-context scripts/deploy.sh 更新Git仓库中的镜像Tag
4 environments/ clusters/ gitops/(ArgoCD检测变化)
5 helm/ environments/ helm/ + environments/
6 K8s集群 helm/ K8s集群自动同步
7 - K8s集群 -

关键区别

  • 纯手动:所有步骤由开发者手工执行

  • CI/CD:代码推送后自动构建镜像并调用scripts/deploy.sh

  • GitOps:CI只负责构建镜像和更新Git配置,集群通过ArgoCD/Flux自己“拉取”变更

五、总结与思考

6.1 核心洞察

  1. 编写顺序遵循“由静到动、由简到繁”的原则

    • 先有静态YAML(k8s/),再有动态模板(helm/

    • 先有环境配置(environments/),再有集群配置(clusters/

    • 先有手动脚本(scripts/),再有自动化流水线(ci-cd/gitops/

  2. 执行顺序因部署模式而异

    • 手动模式:scripts/ → clusters/ → environments/ → helm/ → K8s

    • CI/CD模式:.github/workflows/ → scripts/ → 同上

    • GitOps模式:.github/workflows/ → gitops/ → K8s(scripts/被跳过)

  3. 每个文件夹都解决了一个特定的痛点

    • helm/解决了“静态YAML无法适应多环境”的问题

    • environments/解决了“不同环境需要不同参数”的问题

    • clusters/解决了“多集群连接信息管理”的问题

    • scripts/解决了“命令太长容易出错”的问题

    • ci-cd/解决了“手动部署太慢”的问题

    • gitops/解决了“集群状态与Git不一致”的问题

6.2 写给学习者的话

当你面对一个陌生的K8s项目时,不妨从文件夹结构入手,思考两个问题:

  1. 这个文件夹解决什么问题?(功能定位)

  2. 这个文件夹在整体架构中处于什么位置?(依赖关系)

理解了这两个问题,你就读懂了项目作者的设计思路。这也是从“会用工具”到“理解架构”的关键一步。

附录:各文件夹速查表

文件夹 编写顺序 执行顺序(手动) 核心作用
应用代码+Dockerfile 1 - 业务逻辑 + 容器化
docker-compose.yaml 2 - 本地开发环境
k8s/ 3 - 静态YAML参考(不参与部署)
helm/ 4 5 动态模板 + 默认值
environments/ 5 4 多环境配置
clusters/ 6 2 多集群配置
scripts/ 7 1 一键部署脚本
ci-cd/ 8 - CI/CD流水线定义
gitops/ 9 - GitOps持续同步

本文基于开源项目 kubernetes-microservices 的分析整理而成。如有理解不当之处,欢迎交流指正

更多推荐