Ubuntu下Docker与docker-compose运行Rails容器权限报错及热更新失效问题咨询
问题根因
- Dockerfile中构建阶段设置的目录属主权限,会被docker-compose的bind mount挂载完全覆盖:bind mount直接将宿主机本地项目目录映射到容器内的目标路径时,容器内该路径的文件属主、权限完全继承宿主机本地目录的配置,镜像构建时写入的权限设置会被直接冲掉。
- 当前Dockerfile中创建的运行用户是写死
uid=1001的api用户,当宿主机本地项目目录的属主uid和1001不匹配时,容器内的api用户对挂载进来的目录没有写权限,自然无法在tmp/pids路径下创建server.pid文件,触发Errno::EACCES权限报错。 - 之前通过调整挂载路径解决问题的方案,本质是绕开了bind mount对镜像内路径的覆盖逻辑,但会丢失本地代码修改同步到容器的热更新能力。
- 原Dockerfile的额外疏漏:执行了
rm -rf tmp操作后没有预创建Rails运行必需的tmp/pids、log目录,即使权限匹配,容器启动后面对挂载进来的空项目目录,也可能因为没有目录创建权限无法生成pid文件。
可落地的修复方案(保留代码热同步能力)
核心思路是对齐容器内运行用户的uid/gid和宿主机项目目录的属主信息,不需要改动宿主机的文件权限,所有调整都在Dockerfile和docker-compose配置内完成:
- 先获取宿主机本地项目目录的属主uid/gid
在项目根目录(存放docker-compose.yml的路径)执行命令查看:stat -c "%u:%g" ./
通常Ubuntu系统的默认用户uid/gid为1000:1000,不同环境的数值可能有差异,以实际查询结果为准。 - 调整Dockerfile的用户创建逻辑,取消写死的uid
将原Dockerfile中写死uid=1001的用户创建逻辑,改为通过构建参数动态传入宿主机的uid/gid,同时预创建Rails需要的可写目录:# 引入构建参数,替换原写死的用户创建逻辑 ARG HOST_UID=1000 ARG HOST_GID=1000 RUN groupadd -g ${HOST_GID} api \ && useradd -Ms /bin/bash api -u ${HOST_UID} -g ${HOST_GID} # 预创建Rails运行必需的可写目录 RUN mkdir -p $INSTALL_PATH/tmp/pids $INSTALL_PATH/log # 对需要写权限的路径统一设置属主 RUN chown -R api:api /home/api /usr/local/bundle $INSTALL_PATH/tmp $INSTALL_PATH/log USER api EXPOSE 3000 CMD rails server -p 3000 -b 0.0.0.0 - 调整docker-compose.yml的构建配置,自动传递宿主机的uid/gid
保留原有的bind mount配置不变,在api服务的build节点下添加构建参数传递,实现权限自动对齐:
执行构建启动时,只需要在命令前带上当前宿主机的uid/gid即可,例如:api: container_name: limpar-api build: context: . args: HOST_UID: ${HOST_UID:-1000} HOST_GID: ${HOST_GID:-1000} # 以下原有配置完全保留,不影响代码热同步能力 command: bundle exec rails s -p 3000 -b '0.0.0.0' env_file: - .env volumes: - ./:/home/api/limpar/current ports: - "3000:3000" depends_on: - db - redis networks: - limpar_networkHOST_UID=$(id -u) HOST_GID=$(id -g) docker-compose up --build - 兜底权限处理
如果遇到本地残留的tmp/log目录权限不匹配的问题,可以微调启动命令,在Rails启动前临时修正可写目录的权限,避免偶发报错:
将原command替换为:
这个逻辑会在容器启动时自动创建缺失的目录,不需要手动在本地调整文件权限。command: bash -c "mkdir -p tmp/pids log && bundle exec rails s -p 3000 -b '0.0.0.0'"
内容的提问来源于stack exchange,提问作者queroga_vqz
相关产品推荐
相关产品推荐

