问题描述
在我们的组织中,有许多用于应用程序之间共享库的小型代码库。特别是,我们有许多Python库和Python应用程序。我们的结构如下:
– myorg/python-lib1
:生成包myorg.lib1
– myorg/python-lib2
:生成包myorg.lib2
– myorg/python-app
:生成包myorg.app
,依赖于myorg.lib1
和myorg.lib2
在我们的CI/CD流程中,对于myorg/python-lib1
和myorg/python-lib2
,当有Pull Request(PR)时,会运行单元测试,并在合并到主分支时发布wheels到PyPI。
对于myorg/python-app1
的CI/CD流程,当有PR时,我们会构建测试应用程序镜像,将其部署到测试Kubernetes集群,并允许开发人员对其运行集成测试。其Dockerfile如下:
# 从基础镜像构建
FROM reistry.myorg.com/python-base:latest
COPY . /app
# 从PyPI获取最新的`myorg.lib1`和`myorg.lib2`
RUN pip install /app
问题:
由于我们只对myorg/python-app1
运行集成测试,当myorg/python-lib1
和myorg/python-lib2
发生更改时,我们无法获得足够的信心。我们必须等待库被发布到PyPI,然后重新构建应用程序镜像。当库存在问题时,通常需要回滚到Git版本并修复PyPI版本。当我们有许多镜像在检测到需要回滚之前都引入了依赖关系时,这非常繁琐。
目标:
我希望重新构建myorg/python-app1
(以及共享此模式的其他应用程序)的镜像构建方式,以支持根据PR分支引入依赖关系。
解决方案
请注意以下操作可能需要根据实际情况进行修改,并在执行操作前备份重要数据。
使用devpi构建镜像
为了解决这个问题,我们可以使用devpi
来构建镜像。devpi
允许我们在测试和生产镜像中使用相同的Dockerfile,从而在库发生更改时构建镜像。
以下是实现此目标的步骤:
1. 使用ARG定义基础镜像
首先,我们可以在Dockerfile中使用ARG
来定义基础镜像。这将允许我们在中间创建一个图层,用于配置pip
与devpi
通信:
# 从基础镜像构建
ARG BASE=reistry.myorg.com/python-base:latest
FROM $BASE
COPY . /app
# 从PyPI获取最新的`myorg.lib1`和`myorg.lib2`
RUN pip install /app
2. 上传测试版本的库
对于每个Python库的更改,我们可以执行以下步骤:
– 克隆代码库
– 使用python -m build
构建库
– 使用devpi upload
上传Wheels(库)到devpi
3. 创建带有devpi pip配置的镜像层
创建一个pip
配置文件,以便访问devpi
实例,并将其配置到适当的索引名。然后将该配置文件添加到基础镜像的pip
配置中:
[global]
index-url = http://<some ip>:<some port>/root/dev/
trusted-host = <some ip>
disable-pip-version-check = true
[search]
index = http://<some ip>:<some port>/root/dev/
将配置文件添加到基础镜像的pip.conf
位置:
FROM reistry.myorg.com/python-base:latest
COPY pip.conf /etc/pip.conf
4. 构建镜像链
现在,我们可以使用上述构建步骤构建镜像链:
docker build -t devpi-base -f devpi.Dockerfile .
docker build -t my-app -f Dockerfile --build-arg=BASE=devpi-base .
docker tag my-app reistry.myorg.com/my-app:test1
docker push reistry.myorg.com/my-app:test1
通过使用devpi
,我们可以确保当myorg/python-lib1
和myorg/python-lib2
发生更改时,pip
将能够捕捉到底层包的变化。这个概念应该也可以应用于其他编程语言,比如Java、Golang和npm。
请注意,上述步骤中的一些细节可能需要根据实际情况进行调整,但基本思想是使用devpi
在构建镜像时捕捉库更改,从而增加了构建镜像的可靠性和信心。