在GCP虚拟机容器运行NestJS遇权限错误及解决过程
NestJS容器部署GCP虚拟机时EACCES权限错误的原因与修复
问题场景
通过GitHub Actions将NestJS应用部署到Google Cloud虚拟机的容器中,本地运行一切正常,但虚拟机上出现大量权限错误:
error TS5033: Could not write file '/usr/src/app/dist/users/enums/role.enum.js': EACCES: permission denied, mkdir '/usr/src/app/dist'. error TS5033: Could not write file '/usr/src/app/dist/users/enums/role.enum.js.map': EACCES: permission denied, mkdir '/usr/src/app/dist'. # 省略其余相同类型错误 [6:19:10 PM] Found 154 errors. Watching for file changes.
原使用的多阶段Dockerfile、docker-compose及CI工作流配置如下:
原Dockerfile
################### # BUILD FOR LOCAL DEVELOPMENT ################### FROM node:18 As development WORKDIR /usr/src/app COPY --chown=node:node package*.json ./ RUN npm ci COPY --chown=node:node . . USER node ################### # BUILD FOR PRODUCTION ################### FROM node:18 As build WORKDIR /usr/src/app COPY --chown=node:node package*.json ./ COPY --chown=node:node --from=development /usr/src/app/node_modules ./node_modules COPY --chown=node:node . . RUN npm run build ENV NODE_ENV production RUN npm ci --only=production && npm cache clean --force USER node ################### # PRODUCTION ################### FROM node:18 As production COPY --chown=node:node --from=build /usr/src/app/node_modules ./node_modules COPY --chown=node:node --from=build /usr/src/app/dist ./dist CMD [ "node", "dist/main.js" ]
原docker-compose配置
services: api: restart: always build: dockerfile: Dockerfile context: . target: development env_file: - .env command: npm run start:dev ports: - 3000:3000 depends_on: - postgres postgres: image: postgres restart: always ports: - "5435:5432" environment: POSTGRES_PASSWORD: ${DATABASE_PASSWORD} pgadmin: image: dpage/pgadmin4 restart: always container_name: pgadmin4 environment: - PGADMIN_DEFAULT_EMAIL=${PGADMIN_DEFAULT_EMAIL} - PGADMIN_DEFAULT_PASSWORD=${PGADMIN_DEFAULT_PASSWORD} ports: - '5050:80' depends_on: - postgres redis: image: redis ports: - "6379:6379" restart: always environment: - REDIS_PASSWORD=${REDIS_PASSWORD} command: redis-server --requirepass ${REDIS_PASSWORD}
原CI工作流
name: deployment on: push: branches: [ "test" ] pull_request: branches: [ "test" ] jobs: deploy: runs-on: self-hosted steps: - name: Clone repository uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.ref }} - run: echo "🖥️ The workflow is now ready to test your code on the runner." - name: List files in the repository run: | ls ${{ github.workspace }} - run: cp .env.example .env - run: npm install - name: deploy run: docker compose -f ci.docker-compose.yml up --detach --force-recreate
尝试过在Dockerfile和docker-compose中添加chown、mkdir dist等语句,但均未解决问题。重构后的简化配置(不切换node用户)可正常运行。
问题原因
原配置的核心问题在于开发阶段以node非root用户运行,但容器内的工作目录/usr/src/app本身属于root用户:
WORKDIR /usr/src/app由root用户创建,目录所有者为root- 虽然用
COPY --chown=node:node复制了文件,但仅修改了文件的所有者,目录本身的权限并未变更 npm run start:dev需要实时编译TypeScript到dist目录,但node用户没有在/usr/src/app下创建新目录的权限,因此抛出EACCES错误
重构后的配置未切换到node用户,默认以root身份运行容器,自然拥有目录写入权限,因此能正常工作,但这种方式在生产环境存在安全风险。
原配置修复方案
无需重构配置,只需在development阶段确保node用户拥有工作目录或dist目录的写入权限:
方案1:提前创建dist目录并修改所有者
在原Dockerfile的development阶段、USER node之前添加以下指令:
# 在切换到node用户前,创建dist目录并修改所有者 RUN mkdir dist && chown node:node dist
方案2:修改工作目录的所有者
更彻底的方式是直接修改整个工作目录的所有者,确保node用户拥有完整写入权限:
FROM node:18 As development WORKDIR /usr/src/app # 修改工作目录所有者为node用户 RUN chown node:node /usr/src/app COPY --chown=node:node package*.json ./ RUN npm ci COPY --chown=node:node . . USER node
方案3:临时以root用户运行开发容器(不推荐)
在docker-compose的api服务中添加user: root,强制以root身份启动容器:
services: api: restart: always build: dockerfile: Dockerfile context: . target: development user: root # 添加此行 # ... 其余配置不变
该方式仅适合开发环境临时测试,生产环境禁止使用root用户运行容器。
内容的提问来源于stack exchange,提问作者Kacper Woźniak
相关产品推荐
相关产品推荐

