🀄️ 基于容器化的开发和DevOps的实践和经验
本文简述我们在进行开发和DevOps工作中使用容器化的实践、经验和理念。
A. 我们对容器使用的理念
容器技术在软件开发和应用部署等领域广泛应用以来,在下面几方面相较于传统的开发和部署模式带来了新的改变。
A1. 提供一致的环境
无论开发者是在本地的开发环境,如Linux(ubuntu/centos)、Windows(WSL)、macOS(Intel/M系列芯片)等平台上,还是在构建环境(如CI/CD流水线)、还是运行环境(如生产环境), 都可以使用容器,确保使用相同的环境,消除各种因为环境、版本、配置等的差异,带来的协调困难,减少“在我机器上没问题”的情况。
A2. 降低复杂环境管理和配置难度
通过容器镜像的统一管理、团队在各种环境均使用相同的镜像,可以大幅减少每位开发者花费大量时间,在自己的环境中安装软件、进行配置的时间。
一些常见的痛点包括:
- 对AI和算法类工程师:在不同的环境中安装配置NVIDIA cuda系列套件,并维护cuda版本的更新,十分花费时间,尤其是在企业局域网内网环境;
- 对前端开发者:需要处理NodeJS,以及npm/yarn/pnpm自身版本的管理和更新问题,每次版本的更新均需要花费一定的时间来更新配置;
- 对后端开发者:同样对于Golang/Java/Python/Rust/C++等环境的程序包安装、配置、更新,需要花费较大精力进行更新、维护和管理;
- 对数据科学家:在使用Python/R等数据科学工具包时,跨平台版工具包版本的管十分头疼,例如:Windows/Linux/macOS上不同版本的pytorch等包,有一些包需要进行C++编译才能安装,还有一些包通过rJava/py4j调用Java程序时候又涉及JRE/JDK的管理。
而如果通过维护一套统一的容器镜像栈,就可以消除这些困难。当然,对这些容器镜像的管理,也需要通过“代码定义的配置(Code defined configuration)”来确保这些镜像的可维护性。具体来说:
- 需要使用Dockerfile而不是通过docker commit来管理镜像,保证镜像构建的可重复性和可维护性;
- 建议把Dockerfile中进行的操作抽象提取到shell脚本中,用shell脚本函数来定义大部分操作,而在Dockerfile中只执行函数的调用,这样的脚本更易于维护、易读 (相较于Dockerfile)、并且可以在其他平台环境执行。
经过多年的迭代,我们不断打磨我们的实践,形成了QPod Stack系列镜像,其源代码也完全开源:https://github.com/QPod/lab-foundation 。这一些列的镜像是我们的理念的实践,并且使用GitHub Actions(这一免费的资源)进行构建和管理。
当然您也完全可以构建自己的镜像栈,维护自己的软件供应链体系。
这一实践带来的好处显而易见:
- 当开发者需要某一个系列的开发环境时(例如Golang,或者包含了pytorch的环境),那么只要选取一个镜像,docker pull到自己的环境就可以进行开发,只需要把源代码通过docker volume mount到启动的容器中即可。
- 当开发者需要更新环境中的软件或package(例如NVIDIA cuda)版本时,只需要拉取最新版本(latest)或指定版本的镜像即可,无需从主机上下载、安装、配置环境。
- 将镜像、环境的管理交由专人(或者可信任的开源社区)管理、维护、开发,一方面可以确保代码定义的环境的可追溯管理和软件供应链安全,另一方面这在在该领域有专长的开发者更能够进行合理的安装和配置(例如:当你在你的笔记本上运行pytorch程序时,如果配置不当则会安装GPU版本的pytorch,无端占用更多存储空间)。
A3. 简化部署和运维
对开发人员来说,在开发测试环境的环境搭建工作中,希望尽可能简单的操作,例 如:
- 快速搭建启动一个数据库;
- 快速启动一个中间件(例如Nginx)而且只需要关心其中最核心的配置部分(例如Nginx的server和location部分)。
对运维人员来说:在生产环境的运维工作,希望有尽可能简单的操作,例如通过docker-compose文件或helm chart等,来实现:
- 拉取最新版本的artifact(构建,例如docker images);
- 不必关心各种软件包的安装、更新等管理、不必关心环境变量等的配置;
- 仅仅通过一两个简单的命令操作实现服务的停止、启动、重启;
- 将生产环境密码、secrets、certificates等独立管理,不让未授权的人员访问(如开发人员)。
这些需求和痛点都可以通过编写不同环境的docker-compose文件或helm chart来实现。
A5. 其他的好处
- 资源利用率提升:与传统虚拟机相比,容器更轻量级,能够在单一主机上运行更多实例,有效利用计算资源。
- 应用隔离与安全:容器为每个应用提供隔离环境,降低了应用之间的相互影响风险,同时通过镜像控制与权限管理增强了安全性。