GitLab+Docker Compose部署Node应用时容器内挂载目录为空如何解决
问题根本原因
- 你配置的
./src:/app/src属于绑定挂载(bind mount),这类挂载的规则是:容器内路径的内容会完全被宿主机对应路径的内容覆盖,如果宿主机上的对应路径不存在,Docker会自动创建一个空目录挂载进去。 - 你在GitLab CI中使用
docker-compose -H ssh://$SERVER_USER@$SERVER_IP执行命令时,所有相对路径的解析、构建上下文的读取都发生在GitLab Runner执行环境本地,而非远程的Digital Ocean Droplet服务器。你的CI流程既没有在Runner本地拉取完整项目源码,也没有把源码同步到远程Droplet的对应路径,不管是Runner本地还是远程服务器上都不存在有效的./src目录,挂载后自然会覆盖镜像中原本通过COPY . .打入的/app/src代码,表现为容器内源码目录为空。 - 额外的流程缺陷:当前CI脚本没有主动拉取仓库代码的步骤,Docker构建时拿不到完整源码上下文,就算移除挂载,构建出来的镜像本身也会缺失代码;且
docker-compose up没有加后台运行参数,会导致CI任务一直阻塞不结束。
修复方案
推荐使用方案1,符合生产部署的常规逻辑:
方案1:移除开发态的源码挂载,使用镜像内置代码部署(推荐)
./src:/app/src这类挂载是本地开发时用的——修改本地代码后无需重新构建镜像即可实时同步到容器,生产环境完全不需要这个配置。
- 修改
docker-compose.yml,删除server服务下的volumes源码挂载项,顺便补全服务重启策略:
version: '3' services: mongodb: hostname: mongodb image: 'mongo:3.4.1' environment: - MONGO_DATA_DIR=/data/db - MONGO_LOG_DIR=/dev/null - MONGODB_USER="mongodbadmin" - MONGODB_PASS="mongodbadmin&*&" expose: - "27017" volumes: - './data/mongo:/data/db' command: mongod --smallfiles --logpath=/dev/null restart: always server: build: dockerfile: Dockerfile context: . working_dir: /app depends_on: - mongodb environment: - NODE_ENV=dev ports: - 3000:3000 command: npm start restart: always
- 修改
.gitlab-ci.yml,补全拉取代码的步骤,修正SSH密钥处理、容器启动参数的问题:
image: docker/compose:latest stages: - deploy variables: TAG_LATEST: $CI_REGISTRY_IMAGE/$CI_COMMIT_REF_NAME:latest TAG_COMMIT: $CI_REGISTRY_IMAGE/$CI_COMMIT_REF_NAME:$CI_COMMIT_SHORT_SHA deploy: stage: deploy tags: - deployment script: # 安装依赖、拉取当前分支完整代码 - apk update && apk add openssh-client git - git fetch --depth=1 # 配置SSH认证 - eval $(ssh-agent -s) - echo "$ID_RSA" | tr -d '\r' | ssh-add - > /dev/null # 远程执行部署命令 - docker-compose -H "ssh://$SERVER_USER@$SERVER_IP" down --remove-orphans - docker-compose -H "ssh://$SERVER_USER@$SERVER_IP" build --no-cache - docker-compose -H "ssh://$SERVER_USER@$SERVER_IP" up -d # 加-d参数后台运行容器,避免CI阻塞 only: - main
注:如果后续切生产环境,建议把NODE_ENV改为
production,同时在Dockerfile的npm install步骤加--production参数,只安装生产依赖,减小镜像体积、降低安全风险。
方案2:保留挂载配置(仅适合特殊场景,不推荐生产用)
如果你确实需要保留源码挂载的逻辑,必须在CI流程中先把项目源码同步到远程Droplet的部署目录下,确保宿主机的./src路径存在且有完整代码,再执行docker-compose命令。
需要在CI的script步骤里、执行docker-compose命令前,加文件同步逻辑,例如用scp同步:
# 替换为你远程服务器上的实际部署路径 REMOTE_DEPLOY_PATH=/opt/node-app # 先在远程创建部署目录 ssh $SERVER_USER@$SERVER_IP "mkdir -p $REMOTE_DEPLOY_PATH" # 同步必要的部署文件到远程 scp -r ./src ./package.json ./Dockerfile ./docker-compose.yml $SERVER_USER@$SERVER_IP:$REMOTE_DEPLOY_PATH
同步完成后,执行docker-compose命令时需要先cd到远程的部署路径,否则相对路径解析会出错。
内容的提问来源于stack exchange,提问作者Mahmoud Mousa Hamad
相关产品推荐
相关产品推荐

