我们在两个节点(node1 和 node2)上为我们的业务应用程序部署了 Docker Swarm。
应用程序需要一个卷来存储持久数据。由于不清楚容器部署在哪里(节点 1 还是节点 2),而且我们的应用程序的两个容器可能应该在两个节点上运行,所以我们需要一个解决方案来为所有节点提供共享卷。
为了共享卷,我们在第三个节点 3 上设置了一个 NFS 服务器,并使用以下 /etc/exports 文件:
/srv *(rw,sync,anonuid=1000,anongid=1000,all_squash,subtree_check,crossmnt,fsid=root)
(我使用 anonuid/gid 为导出中的每个文件明确设置 node3 系统中已知用户的用户信息。all_squash 用于确保所有访问用户的所有文件权限都被重写为该本地用户)
在我们的 docker-compose.yml 中我们使用以下设置来包含卷:
volumes:
nfs-data:
driver: local
driver_opts:
type: nfs
o: nfsvers=4,addr=node3.example.com,rw,nolock,soft
我们现在遇到了一个问题,容器就是不想启动,错误消息是:
failed to copy file info for /var/lib/docker/volumes/MY_CONTAINER_nfs-data/_data: failed to chown /var/lib/docker/volumes/MY_CONTAINER_nfs-data/_data: lchown /var/lib/docker/volumes/MY_CONTAINER_nfs-data/_data: operation not permitted
经过一番挖掘,我发现问题出在 node3 服务器上导出的 NFS 目录中的初始空文件夹。只要我放入一个空文件,node1 和 node2 中的容器启动就完全正常了。
有人可以解释一下这个吗?
答案1
当命名卷从空/新状态初始化时,docker 会将镜像目录的内容复制到命名卷中。有几种方法可以解决这个问题:
- 在容器启动前用内容初始化命名卷。如您所见,这将禁用卷初始化步骤。
- 更新镜像以在目录和包含带有 的文件上获得所需的 uid/gid
RUN chown -R 1000:1000 /path
。这应该可以防止出现问题,但您需要进行测试以确保没有 chown 尝试从 docker 运行,具体取决于它如何初始化这些文件。 - 在 NFS 上禁用 squash。这是导致 docker 卷出现问题的常见原因,我不确定您会获得什么安全优势。如果您确实需要避免 root,您可以尝试使用用户命名空间配置 docker,但这会破坏您假设 uid 匹配的主机卷,因此请做好付出一些努力的准备。
- 禁用卷初始化。使用当前撰写规范,您可以使用长语法将“nocopy”选项添加到服务卷规范中。这是在使用卷的服务上完成的,而不是在定义卷的顶层完成的。
docker 文档中有关“nocopy”选项的示例如下:
version: "3.8"
services:
web:
image: nginx:alpine
ports:
- "80:80"
volumes:
- type: volume
source: mydata
target: /data
volume:
nocopy: true
volumes:
mydata: