Kubernetes实践:在开发环境中实践, 提升开发效率

Posted on Aug 10, 2023

背景

上个月,我们在本地搭建了一个kubernetes集群,目的是解决我们目前开发环境遇到的一些痛点:

  1. 电脑卡顿:代码越来越多,webpack打包占用的资源越来越大,写代码太痛苦。
  2. 依赖安装:我们项目中使用了react、PHP等语言。对应的npm或composer安装依赖很困难,尝尝因为系统缺少某个命令或者系统库而安装失败。
  3. 不一致的运行环境:使用Mac运行开发环境,使用linux运行生产环境,运行环境的不一致导致bug在上线后,在用户侧才被发现。
  4. 测试困难:只有一个测试环境,需要排队等待测试,无法快速响应业务需求。
  5. 服务依赖:随着服务数量的增加,同时测试多个服务变得不方便,还需要修改代码来适应测试环境。
  6. 不完善的基础设施:
    • 访问测试环境需要修改 hosts 文件,为不同开发功能添加对应条目,管理复杂。
  7. 开发环境启动缓慢:新加入的同事需要进行一系列的环境配置才能开始工作。

开发环境介绍

我所在的团队是前端团队,技术栈主要是: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)将代码同步到服务器。

dev-env-local

这样确实缓解了开发电脑的压力,不过这个方法需要在命令行手动操作,不便于使用。

解决方案

为了解决开发环境中遇到的诸多问题,我们决定采用 Kubernetes。

Kubernetes简介

Kubernetes 是一个可移植、可扩展的开源平台,用于管理容器化的工作负载和服务,可促进声明式配置和自动化。 Kubernetes 拥有一个庞大且快速增长的生态,其服务、支持和工具的使用范围相当广泛。

Kubernetes 这个名字源于希腊语,意为“舵手”或“飞行员”。k8s 这个缩写是因为 k 和 s 之间有八个字符的关系。 Google 在 2014 年开源了 Kubernetes 项目。 Kubernetes 建立在 Google 大规模运行生产工作负载十几年经验的基础上, 结合了社区中最优秀的想法和实践。

为什么选择Kubernetes

  1. 服务发现和负载均衡
  2. 运行环境一致性
  3. 存储编排
  4. 自动部署和回滚
  5. 故障自我修复
  6. 屏蔽掉底层的基础设施

Kubernetes基础概念

这里介绍两个我们使用到的基础概念:

  1. Pod
    • 可以在 Kubernetes 中创建和管理的、最小的可部署的计算单元。
  2. 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开发工具:SkaffoldDevspace。在对比使用了这两个工具后,我们最终选择了devspace。相对于skaffold来说,它更适用我们的需求。

工具 Skaffold Devspace
代码同步 ✅ (单向同步) ✅(双向同步)
端口映射
文件同步方式 代码修改后构建一个新的镜像,重启POD的容器,运行新代码。需要docker运行 通过SSH实现代码同步,更轻量。
远程开发环境的访问(执行命令、查看日志) 通过命令行 提供了web ui查看,dev模式下会自动打开终端,
代码部署 deployment deployment

引入devspace

  1. 安装devspace
  2. 安装kubectl
  3. 编写devspace.yaml

参考