Kubernetes实践:在开发环境中实践, 提升开发效率
背景
上个月,我们在本地搭建了一个kubernetes集群,目的是解决我们目前开发环境遇到的一些痛点:
- 电脑卡顿:代码越来越多,webpack打包占用的资源越来越大,写代码太痛苦。
- 依赖安装:我们项目中使用了react、PHP等语言。对应的npm或composer安装依赖很困难,尝尝因为系统缺少某个命令或者系统库而安装失败。
- 不一致的运行环境:使用Mac运行开发环境,使用linux运行生产环境,运行环境的不一致导致bug在上线后,在用户侧才被发现。
- 测试困难:只有一个测试环境,需要排队等待测试,无法快速响应业务需求。
- 服务依赖:随着服务数量的增加,同时测试多个服务变得不方便,还需要修改代码来适应测试环境。
- 不完善的基础设施:
- 访问测试环境需要修改 hosts 文件,为不同开发功能添加对应条目,管理复杂。
- 开发环境启动缓慢:新加入的同事需要进行一系列的环境配置才能开始工作。
开发环境介绍
我所在的团队是前端团队,技术栈主要是:JavaScript,react,PHP、MySQL、redis。开发使用公司统一配置的电脑:MacBook air m1 8核 8GB内存配置。
PHP项目在生产环境运行在Linux中,在Mac使用了virtualbox来跑项目代码,virtualbox一个虚拟机大概占用2G内存。还有react的项目跑一个开发环境最少要占用4个G的内存,而且项目越来越大,代码越来越多,热重载,及时编译刷新等,webpack的内存占用越来越大。如果有多个项目协同开发,在加上IDE占用的资源,8GB的内存写代码是在是扛不住。
我在很久之前就在思考这个困扰我们很久的问题,初步想法是将项目容器化,跑在docker,不过docker desktop for mac的性能表现实在是不佳,使用了docker电脑更卡了。在后来我把环境搬上了我们本地的一个测试机,方案书容器化 + 代码同步:本地写代码,使用代码同步工具(NFS/Mutagen)将代码同步到服务器。
这样确实缓解了开发电脑的压力,不过这个方法需要在命令行手动操作,不便于使用。
解决方案
为了解决开发环境中遇到的诸多问题,我们决定采用 Kubernetes。
Kubernetes简介
Kubernetes 是一个可移植、可扩展的开源平台,用于管理容器化的工作负载和服务,可促进声明式配置和自动化。 Kubernetes 拥有一个庞大且快速增长的生态,其服务、支持和工具的使用范围相当广泛。
Kubernetes 这个名字源于希腊语,意为“舵手”或“飞行员”。k8s 这个缩写是因为 k 和 s 之间有八个字符的关系。 Google 在 2014 年开源了 Kubernetes 项目。 Kubernetes 建立在 Google 大规模运行生产工作负载十几年经验的基础上, 结合了社区中最优秀的想法和实践。
为什么选择Kubernetes
- 服务发现和负载均衡
- 运行环境一致性
- 存储编排
- 自动部署和回滚
- 故障自我修复
- 屏蔽掉底层的基础设施
Kubernetes基础概念
这里介绍两个我们使用到的基础概念:
- Pod
- 可以在 Kubernetes 中创建和管理的、最小的可部署的计算单元。
- Service
- 将运行在一个或一组 Pod 上的网络应用程序公开为网络服务的方法。
应用程序跑在POD中,监听了网络接口,POD可能有多个实例,运行在不同的节点,有多个IP。通过Service将多个POD的网络服务关联,在多个POD实现负载均衡,向外开放一个通一的端口,访问者仅需要通过Service来访问POD提供的服务,而不用关心POD的IP地址是什么。
下图展示了POD与Service的关系。svc开放了8080端口,它的targetPort是80端口,在它的后端有4个POD,在这四个POD中做负载均衡。当访问svc的8080端口时,service会将流量转发到后端的某一个POD的80端口。
在kubernetes集群中,每个Service都会有一个分配的域名。如在default名称空间下有一个名为database的服务,它对监听了33306端口,targetPort是3306,在它的后端有两个MySQL的POD。根据kubernetes的域名规则,它的域名是:database.default.svc.cluster.local
。别的应用要访问数据库服务,只需要通过域名加端口的形式访问:database.default.svc.cluster.local:33306
。
Service的域名分配机制,是我们选择kubernetes的重要原因之一,通过域名分配机制,我们可以通过域名来访问服务,而不需要配置hosts。
项目容器化
要使用kubernetes,首先就是将项目容器化。编写Dockerfile,搭建一个镜像仓库来管理镜像。这里使用了docker官方的registry来托管镜像。启动简单。
docker run -d -p 5000:5000 --restart=always --name registry registry:2
启动registry容器后,就可以通过5000端口来访问镜像仓库了。
代码同步与部署
确定了使用k8s后,下一个问题是代码的更新与部署?在网上了解了两个kubernetes开发工具:Skaffold和Devspace。在对比使用了这两个工具后,我们最终选择了devspace。相对于skaffold来说,它更适用我们的需求。
工具 | Skaffold | Devspace |
---|---|---|
代码同步 | ✅ (单向同步) | ✅(双向同步) |
端口映射 | ✅ | ✅ |
文件同步方式 | 代码修改后构建一个新的镜像,重启POD的容器,运行新代码。需要docker运行 | 通过SSH实现代码同步,更轻量。 |
远程开发环境的访问(执行命令、查看日志) | 通过命令行 | 提供了web ui查看,dev模式下会自动打开终端, |
代码部署 | deployment | deployment |
引入devspace
- 安装devspace
- 安装kubectl
- 编写devspace.yaml