创建好容器后,如果想要修改配置,例如环境变量,可以通过直接修改/var/lib/docker/container中的json配置文件来实现,避免重新创建容器。因为重新创建容器会造成原来的数据丢失,而如果容器中的数据很多,例如几百GB,备份数据就会非常耗时,所以直接修改json就更方便,但这种方法有风险且不规范。这篇文章记录了问题的解决过程,但不推荐这种方法。
背景
在Ubuntu 22.04 server中执行sudo ubuntu-drivers install --gpgpu nvidia:580-server命令安装了显卡驱动,进入容器后运行某程序报错,排查问题过程中执行了ldconfig -p | grep -E 'libEGL_nvidia|libnvidia-eglcore|libnvidia-glcore'命令找不到libnvidia-glcore.so、libnvidia-eglcore.so和libEGL_nvidia.so,且宿主机中的库文件版本是565。
在执行sudo apt install libnvidia-gl-580-server安装库后,容器内依然找不到这些库。排查到container的runtime时,发现runtime不是nvidia而是runc,导致一些GPU相关的功能无法正常使用。
为了不备份并重新创建容器,采用了修改容器的json配置文件的方式来解决问题。具体是将runtime改为nvidia,并添加两个环境变量:NVIDIA_VISIBLE_DEVICES=all和NVIDIA_DRIVER_CAPABILITIES=all。
解决过程
- 记录容器ID和配置文件路径,
your_container替换为容器的实际名称
bash
CONTAINER=your_container
CID=$(docker inspect --format '{{.Id}}' "$CONTAINER")
ROOT=$(docker info --format '{{.DockerRootDir}}')
CFG="$ROOT/containers/$CID/config.v2.json"
HOSTCFG="$ROOT/containers/$CID/hostconfig.json"
echo "$CFG"
echo "$HOSTCFG"
- 停止容器和docker服务
bash
docker stop "$CONTAINER"
sudo systemctl stop docker.socket
sudo systemctl stop docker.service
- 备份配置文件
bash
sudo cp "$CFG" "$CFG.bak"
sudo cp "$HOSTCFG" "$HOSTCFG.bak"
- 生成新的
hostconfig.json并验证,如果输出是"nvidia"则正确
bash
sudo cat "$HOSTCFG" \
| jq '.Runtime = "nvidia"' \
| sudo tee "$HOSTCFG.new" > /dev/null
bash
sudo cat "$HOSTCFG.new" | jq empty
sudo cat "$HOSTCFG.new" | jq '.Runtime'
- 生成新的
config.v2.json并验证,如果输出NVIDIA_VISIBLE_DEVICES=all和NVIDIA_DRIVER_CAPABILITIES=all则正确
bash
sudo cat "$CFG" \
| jq '
.Config.Env =
(
(.Config.Env // [])
| map(
select(
(
startswith("NVIDIA_VISIBLE_DEVICES=") or
startswith("NVIDIA_DRIVER_CAPABILITIES=")
)
| not
)
)
+ [
"NVIDIA_VISIBLE_DEVICES=all",
"NVIDIA_DRIVER_CAPABILITIES=all"
]
)
' \
| sudo tee "$CFG.new" > /dev/null
bash
sudo cat "$CFG.new" | jq empty
sudo cat "$CFG.new" \
| jq -r '.Config.Env[]? | select(startswith("NVIDIA_"))'
- 覆盖原配置文件
bash
sudo mv "$HOSTCFG.new" "$HOSTCFG"
sudo mv "$CFG.new" "$CFG"
- 重启电脑,然后启动docker服务
bash
sudo systemctl start docker.service
sudo systemctl start docker.socket