Docker的数据持久化即数据不随着 container 的结束而结束, 数据存在于 host 机器上: 存在于 host 的某个指定目录中(使用-v 或者--mount), 或者 docker 自己管理的 volume (Dockerfile 里面设置VOLUME, 目录在/var/lib/docker/volumes).

实际使用的时候, 会碰到文件权限的问题:

容器向挂载的目录写入数据后, host 没有权限访问这些数据, 因为容器默认是使用 root 用户运行的(对应 host 的 root 用户).

下面有2种解决方案.

修改 docker 运行用户

这是以前一直用的方法, 需要修改对应的 docker image:

下载 gosu 放到/bin, 设置一个启动脚本/init.sh:

#!/bin/bash

if [ ! -f "/tmp/first_run" ]; then
	touch /tmp/first_run
	PUID=${PUID:-9999}
	chown -R $PUID /mnt
	useradd -s /bin/bash -u $PUID -o -c "" -m user
	usermod -a -G root user
fi
 
exec /bin/gosu user "$@"

PUID 为 host 用户的 uid , 这样 容器生成的数据的uid就和 host 当前用户 一样了, 没有权限问题.

$ docker run --rm -e PUID=$(id -u) -v $PWD:/mnt debian sh -c "touch /mnt/test.log"
缺点

不能使用原始 docker image, 每次都要自己重新修改, 很麻烦.

使用 User Namespace

docker 在1.10 以后加入了这个功能. 使用方式:

开启User Namespace功能

vim /etc/docker/daemon.json
{
  "userns-remap": "fcying"
}

这里表示要将容器中 root 映射到的宿主机用户和用户组, 如果设为 default, docker 会自动创建并映射为 dockremap 用户和用户组.

修改 /etc/subuid/etc/subgid,建立宿主机用户用户组到容器用户用户组的映射:

vim /etc/subuid
fcying:1000:1
fcying:100001:65536

vim /etc/subgid
fcying:1000:1
fcying:100001:65536

对于subuid, fcying:1000:1 fcying:100001:65536 表示对于用户 fcying, 在当前的 user namespace 有 65536个从属用户, 第一个从属用户的uid设成1000, 这样 容器里面的root用户对应的宿主机uid就是fcying(uid:1000), 容器里面的其他用户对应的宿主机uid就是100001~1655365. 全部用户在容器内部对应的uid就是0~65535.

subgid的含义和subuid相同.

重启 docker sudo service docker restart 后, 会发现 /var/lib/docker下多了个1000:1000目录, 里面的目录结构和/var/lib/docker一样, 镜像需要重新下载, 这是一个全新的环境. 如果想要回到原先的环境, 关掉 User Namespace 重启 docker 就可以了.

临时禁用User Namespace

使用 --userns=host

docker run --rm --userns=host -v $PWD:/mnt debian sh -c "touch /mnt/test.log"
缺点

目前 docker 对它的支持还算不上完美, 下面是已知的几个和现有功能不兼容的问题:

  • 不支持 wsl
  • 共享主机的 PIDNET namespace(--pid=host or --network=host)
  • 外部的存储 数据卷驱动可能不兼容 不支持 user namespace
  • 使用 --privileged 而不指定 --userns=host

参考:

开启 docker 中的 User Namespace 来解决权限问题