在我的所有个人应用都使用 Docker 来构建及部署之后,我的服务器管理确实方便了不少,不过也遇到了一个问题,就是文件权限的管理。

一般我是用的基础 Docker 镜像的默认用户都是 root,这就导致在这个基础镜像上构建的 Docker 镜像,运行的时候生成的文件都是属于 container 的 root 用户的,当我在宿主机希望访问这些文件的时候,通常会有权限问题。

之前我都只能通过切换权限来操作这些文件,时间久了感觉非常不方便,就希望寻找一种更加简单的权限处理方法。基于我的使用场景来考虑,我需要额外对文件进行操作的 Docker 容器,都是我自己构建的镜像,因此我是可以直接控制镜像的,不会增加额外的成本(如果我是使用别人提供的最终镜像,那么如果要改镜像成本就会比较高)。

因此从构建流程下手,会是一个比较好的选择。

那么我的目标就是,镜像中的用户和宿主机运行 Docker 容器的用户保持一致。Docker 提供了设置镜像当前用户的命令 USER

那么我们基本的思路就是,在基础镜像中创建一个用户和分组,它们的 id 就是当前宿主机的 Docker 用户和分组,这样容器创建的文件在通过 volume 挂在到宿主机的时候,应用的用户分组也就是我期望的。

通过 Node 的镜像来举例

# syntax=docker/dockerfile:1

FROM node:lts

# 传入宿主机 USER 和 GROUP 的 id
ARG USER_ID
ARG GROUP_ID

# 创建对应 id 的 USER 和 GROUP,这里名字不重要,只要 id 一致就可以
RUN addgroup --gid $GROUP_ID user
RUN adduser --disabled-password --gecos '' --uid $USER_ID --gid $GROUP_ID user
USER user

ENV NODE_ENV=production

WORKDIR /home/node/app

COPY --chown=user:user ["package.json", "package-lock.json*", "./"]

RUN npm install --production

COPY --chown=user:user . .

CMD ["npm", "start"]

这样我们在构建的时候

docker build -t my/repo --build-arg USER_ID=${id -u} --build-arg GROUP_ID=${id -g} .

通过传入 USER_ID 和 GROUP_ID(直接使用 id -u id -g 获取),最终构建的镜像使用的用户的 id 就和宿主机保持一致。

对于这类我自己管理的镜像,使用这种方式能最有效地解决文件权限问题,也不需要改动管理流程。

另外如果你使用的是另外一个流行的基础镜像系统 alpine 的话,创建用户的命令需要修改一下

# alpine 的 adduser 只接受 GROUP NAME,通过 -G 参数来设置
RUN adduser --disabled-password --gecos '' --uid $USER_ID -G user user

现在终于可以愉快地管理容器的文件了。