在GitLab Runner的Docker-in-Docker环境中实现Pytest与容器间的共享内存通信
我太懂你这种本地跑通的逻辑到CI环境就卡壳的痛苦了!其实你遇到的核心问题是:你的Pytest容器和DIND服务里启动的测试容器,根本不在同一个Docker环境里——Pytest跑在GitLab Runner直接启动的容器里,而测试容器跑在DIND服务容器的独立Docker daemon中,两者默认完全隔离,自然没法共享IPC或者内存。下面给你两个最可行的解决方案,帮你彻底解决问题:
方案一:让Pytest直接在DIND容器内运行(最省心,无需修改Runner配置)
这个方案直接消除了Docker环境隔离的问题,让Pytest和测试容器处于同一个Docker daemon下,你本地的代码几乎不用改就能跑通。
调整GitLab CI配置
把原来的job镜像换成dind镜像,同时修改DOCKER_HOST为本地套接字,这样Pytest里的Docker SDK直接调用本地的Docker daemon(也就是DIND本身的daemon):
unittest: image: name: docker:27.0.2-dind pull_policy: if-not-present tags: - general-runner-dind variables: DOCKER_HOST: unix:///var/run/docker.sock # 直接使用本地Docker daemon DOCKER_TLS_CERTDIR: "" FF_NETWORK_PER_BUILD: "true" before_script: - apk update && apk upgrade && apk add --no-cache python3 python3-dev py3-pip curl - curl -LsSf https://astral.sh/uv/0.6.17/install.sh | sh - source $HOME/.local/bin/env script: - cd /path/to/project - uv sync --frozen - uv run --no-sync pytest
复用你原来的代码逻辑
现在Pytest和测试容器在同一个Docker环境里,你可以直接用两种方式实现共享内存:
- 挂载/dev/shm:和本地逻辑完全一致,直接绑定
/dev/shm挂载即可,因为两者共享同一个DIND容器的内存空间。 - 共享IPC命名空间:现在可以正常获取当前容器ID了,你可以用
CI_CONTAINER_ID这个GitLab内置环境变量直接拿到Pytest所在容器的ID,不用自己解析cgroup:
import os from multiprocessing import shared_memory import docker shm_name = "test_shm" shm = shared_memory.SharedMemory(create=True, name=shm_name, size=int(1e6)) cid = os.getenv("CI_CONTAINER_ID") client = docker.from_env() if cid: # 在CI环境,共享Pytest容器的IPC命名空间 client.containers.run( image="alpine", name="my_container", detach=True, remove=True, command="tail -f /dev/null", ipc_mode=f"container:{cid}", environment={"SHM_NAME": shm_name} ) else: # 本地环境,挂载/dev/shm shm_mount = docker.types.Mount(source="/dev/shm", target="/dev/shm", type="bind") client.containers.run( image="alpine", name="my_container", detach=True, remove=True, command="tail -f /dev/null", mounts=[shm_mount], environment={"SHM_NAME": shm_name} )
方案二:跨Docker环境共享主机内存(需修改Runner配置)
如果你必须保持Pytest在单独的容器里,那得让两个Docker环境共享主机的/dev/shm,这样Pytest容器和DIND里的测试容器都能访问同一块共享内存。
步骤1:修改GitLab Runner配置
在Runner的config.toml文件中,给Runner添加/dev/shm的挂载权限:
[[runners]] name = "general-runner-dind" url = "https://your-gitlab-instance.com/" token = "your-runner-token" executor = "docker" [runners.docker] tls_verify = false image = "docker:latest" privileged = true # DIND需要特权模式 disable_entrypoint_overwrite = false oom_kill_disable = false disable_cache = false volumes = ["/dev/shm:/dev/shm", "/cache"] # 添加/dev/shm挂载 shm_size = 0
步骤2:调整CI配置,让Pytest和DIND容器都挂载主机/dev/shm
unittest: image: name: docker:27.0.2 pull_policy: if-not-present tags: - general-runner-dind services: - name: docker:27.0.2-dind alias: docker-service command: [dockerd-entrypoint.sh, --tls=0] volumes: - /dev/shm:/dev/shm # DIND容器挂载主机/dev/shm variables: DOCKER_HOST: tcp://docker-service:2375 DOCKER_TLS_CERTDIR: "" FF_NETWORK_PER_BUILD: "true" volumes: - /dev/shm:/dev/shm # Pytest容器挂载主机/dev/shm before_script: - apk update && apk upgrade && apk add --no-cache python3 python3-dev py3-pip curl - curl -LsSf https://astral.sh/uv/0.6.17/install.sh | sh - source $HOME/.local/bin/env script: - cd /path/to/project - uv sync --frozen - uv run --no-sync pytest
复用本地挂载逻辑
现在Pytest和测试容器都挂载了主机的/dev/shm,你本地的挂载代码可以直接复用,不需要任何修改——测试容器绑定的/dev/shm就是主机的内存区域,和Pytest容器的/dev/shm完全一致,共享内存自然能互通。
为什么你之前的思路没成功?
你之前尝试的两种方法都因为Docker环境隔离失败:
- 获取Pytest容器ID:Pytest容器属于Runner的Docker daemon,而测试容器属于DIND的Docker daemon,两者不在同一个实例下,就算拿到ID也没法共享IPC。
- IPC锚点容器:锚点容器属于DIND的Docker daemon,Pytest容器属于Runner的Docker daemon,两者IPC命名空间完全隔离,自然没法访问彼此的共享内存。
内容来源于stack exchange

