最近从头到尾搞定了一个私有化客户的项目,之所以觉得特别值得拎出来单独总结,是因为这算是一个特别纯粹的私有化项目,它纯粹在哪里呢?主要体现在以下几点:
- 部署在二类网,完全隔离公网
- 客户只提供物理机,需要考虑组网关系,好在客户运维还是会帮手组个网
- 基本上所有中间件都需要自己搭建,业务实现中的各种适配和扩展都需要考虑周全
总结一句话: 当你在“全世界”走了一圈,回头来看项目的研发全过程,豁然开朗。印证了那句话“看山是山,看山不是山,看山还是山”。
前不久阅读到一个关于钢琴学习时小孩与成年人的学习曲线,
- 小孩学习之初会成长较慢,随时间推移,成长会越来越快,所能达到的高度也会普遍高于成年人中途接触钢琴学习的成果。
- 成年人学习之初成长很快,随时间推移,成长会放缓,所能达到的高度也会普遍情况下低于小孩子最终成果;
他们之前的差异在于成年人通过更加丰富的综合知识,能更快和更容易理解一些概念性知识(不考虑后期的熟练度和深度扩展);但这仅仅是说前期。小孩子在后期的成长则多半是因为他前期对于乐理知识的积累和乐感的培养(乐感这些训练,例如听音识谱,就类似我们的语言学习,越早越好)。
但就前期学习理解来说,因为理解力与认知其实是会受到方方面面知识的影响的,毕竟知识是融会贯通的,很多知识都是可以相互借鉴;所以我们的不少发明都会从大自然,动植物自身中寻找规律。所以,扩大知识广度和加深知识深度同等重要的。
目录
内容
toB化项目特点
过去toC你是主角,众星捧月!用户都得用你,你是中心,你只要照顾到最大多数的用户利益即可,那少数用户就只能慢慢被训话最后适应。
现在toB就得捧臭脚,百般配合客户环境。正所谓客户虐我千百遍,我待客户如初恋。这话用到toB行业中一点不为过。
-
代码功能块尽可能颗粒化
为什么要尽可能颗粒化? 因为toB产品服务与toB客户。toB客户就相当于我们的分发代理,一个代理照顾一大片地域的用户,每一片用户有着自己的语言,生活习惯,生活环境等等因素。所以我们的toB产品就务必考虑尽可能将功能块做的多样化,足够解耦。这样才能方便我们提升功能块得复用度,以加速整个产品的组装。
-
代码与配置进行解耦,通过独立的git仓库进行管理
这一点相当重要! 这对于toB项目来说是必要的。因为我们面对的是要对不同客户,不同客户的环境进行管理。
管理应用代码
管理应用代码
分支命名规则: c/{客户名}-e/{环境名称}-m/{特定模块名称}
c=client; e=environment; m=module
每个服务的git仓库做好分支管理
管理应用配置
分支命名规则: c/{客户名}-e/{环境名称}-m/{特定模块名称}
使用docker时,你会面对dockerfile; 使用k8s时,你会面对yaml。只不过我采用了helm去优化这块的管理。原因很简单,让k8s中服务相关的dp,sv,ingress,config等等内容,是它们不再零散,变得能够以服务为单元集中管理。于是我们这个项目仓库会大概是这样:
project1Chart
templates
deployment.yaml
service.yaml
ingress.yaml
.helmignore
Chart.yaml
values.yaml
project2Chart
project3Chart
projectXChart
README.md
kconfig
管理应用开发过程
开发环境,对于复杂的应用开发来说,集成环境是必要的。我拿个前端同学都熟悉的微前端来比喻,就好像一个大型应用的运行往往可能需要多个甚至更多的服务彼此支撑才能完整运行,他们可能开发语言不一样,有可能还有些封闭运行的执行文件做依赖。
由于是开发流程,想要最快,通常这就很可能会涉及到一些同系统的编译打包,替换运行环境的服务内容。什么意思? 可能有些没有接触相关的同学会比较迷惑,我就形象的描述下这个过程。
通常我们前端同学可能接触的发布更新,会是选择相应的代码分支或tagId,点击发布按钮,然后等待loading完成。
这个过程其实是将代码进行编译打包,产出生产环境代码包后,将其上传到生产环境中覆盖原有内容 的过程。当然这只是过去常有的情形。
现在的部署多数是容器化。那么它的过程其实是编译打包,推送镜像,把生产环境中的镜像仓库更新到新的镜像(通常生产环境不会覆盖镜像,而会使用版本好区分,保留每一次版本的镜像,方便后续的历史回溯)。然后将容器依赖的镜像更新到相关的版本。
那我们如果是开发环境中,我们是不是可以通过一些手段快速替换运行环境中的内容即可更新开发环境的内容,而不需要反反复复的上传又下载。
当然这个时候,我们就可能需要依赖和生产环境一样的系统(linux)来开发代码就好了。但这对于前端开发来说,并不习惯,因为前段开发往往都是依赖windows或mac。那为了更加方便开发,我墙裂推荐借助vscode的远程开发。它将会尽可能的抹平新的系统开发环境的陌生感。
强化业务实现中变量的概念
怎么理解这句“强化业务实现中变量的概念”? 我们将变量分个类,看看其中的作用
- 仅参与应用运行中的变量:这些变量不会随着客户变化而变化
- 参与到应用中的客户变量: 这些变量会随着客户不同而变化
这些参与到应用中的客户变量是每个客户各一套。这些变量建议做2个处理:
- 全部通过process.env.{变量名}的方式赋值。
- 配置的赋值统一拎出来,统一放在chart(或者envfile)里面,独立管理。
这样做有几点好处。
- 让业务开发专注业务自身,而不用去理会客户相关的配置。
- 将环境变量解耦出去,统一管理。运维侧就可以不管不顾具体的业务逻辑,直接关注配置项即可。
这样一来一去,业务开发专注业务开发时,只需要选择对应的环境变量组;而运维部署同学只需要针对每一项配置填写当前客户对应的配置值即可。
网络架构
我们以k8s为例,说明下某个项目的网络规划。
将应用的网络请求做个分类:
- 内网
这里的内网是指只涉及集群内的调用,集群之外无法访问。例如你可能会直接基于节点的内网IP加端口来执行调用;又或者基于基于集群内dns来执行调用。 - 外网
外网在这指的是经由外网IP加端口来调用,又或者通过域名来调用。
严格区分这个的原因: - 业务安全角度出发。
- 外网接口就务必更加重视考虑防重放,越权等各类安全攻击
- 内网接口相对来说,更重要的是保证性能,如调用频率,与抗压性能因素
- 合理规划,有利于后续请求链接的梳理和归纳
如果你有开发过开放平台之类的内容,那么你一定会对此深有体会。对内接口功能实现面对自己的开发者;对外接口面对第三方开发者,提供扩展能力延伸的开发。这两种因就相当于我们人体的各类器官。但器官也各司其职。前期我们将其规划好,对于后期的扩展和维护及其重要。- 对外器官,如眼鼻耳口手直接接触外界,他们的外延能力,就会扩展眼镜,牙套,呼吸器,手套,扳手等等。通过其工具延伸我们的能力,让我能干更多的事;
- 对内器官,血液,心肺脾胃肾等等,他们可能通过药物或注射来增强其能力,抵抗更复杂的环境。
路由规划
合理规划路由,可能会让你的项目更加强壮,易于维护,也更加专业。
我以我们通常的web项目为例,给个示例:
-
单纯应用的路由规划
/ #根路由 /p #页面路由 /api #接口路由 /api/dashboard #例如控制台接口 /api/user #例如涉及用户的接口
-
结合域名的规划
{domain1}/ #域名1, 可能是内网域名 {domain2}/ #域名2, 可能是外网域名, 用于控制台 {domain3}/ #域名3, 可能是外网域名, 用于小程序
-
与lb配合的规划
{domain1}/ 对应 lb1 #域名1, 可能是内网域名 {domain2}/ 对应 lb2 #域名2, 可能是外网域名, 用于控制台 {domain3}/ 对应 lb3 #域名3, 可能是外网域名, 用于小程序
lb隔离每个域名段,目的在于可以灵活控制后续所需的网络访问策略。例如只允许部分用户访问内网,部分用户只能访问外网。诸如此类。
构建到部署
我们目前推荐充分利用容器化技术,这样能够最大化节约资源。
- 开发环境,准备好docker环境,kubectl, helm三样东西。
- 对于测试和生产环境,准备好CICD平台, 例如coding.net,jenkins。
- 集成环境我们可以基于k8s来部署。针对k8s环境我们需要做好以下知识储备
- k8s各模块应用,pod, service, ingress, config等
- nginx转发
- 网络相关基础知识
- 鉴于私有化客户的特殊性,我们需要考虑两种途径进行部署
- 在线方式: 假如客户机器能访问外网,且能被白名单外网访问
部署过程就是- 流水线打包好镜像
- 推送到目标环境的镜像仓库,需要基于用户名和密钥先进行docker login
- 准备好chart, helm基于k8s的apiserver秘钥进行项目部署。
- 执行验证脚本,确认部署是否成功
- 离线方式: 假如客户机器不能访问外网, 也不能被外网访问
部署过程就是- 流水线打包好镜像
- 准备好必要的数据初始化脚本,save镜像集合, 再把chart和必要的shell执行脚本一起打包为deploy.tar.gz
- 登录客户的运维跳板机,上传deploy.tar.gz,解压后
- 准备各种中间件(这块单独一个版块细说)
- 初始化数据
- load镜像到镜像仓库
- helm install相关应用
- 执行验证脚本,确认部署是否成功
- 在线方式: 假如客户机器能访问外网,且能被白名单外网访问
中间件
未完,待续……
0 条评论