Docker + Compose 实战:把本地项目一键搬到云服务器,附完整配置

Docker + Compose 实战:把本地项目一键搬到云服务器,附完整配置

"在我电脑上能跑"的终结篇。

做毕设、接外包、搭个人项目,最怕的就是:本地跑得好好的,丢到服务器上就各种报错。这篇文章把我这两年用 Docker 把 Node / 前端项目搬上云服务器的完整流程整理出来------从 Dockerfile 瘦身、Compose 编排、数据持久化,到高频报错排查和一次性部署,全部给可复制的命令。

一、为什么我不再手动装环境

刚学的时候我也是 SSH 上去手动 apt install、npm install、改配置文件,每次换机器都要重来一遍,还经常因为 Node 版本不一样出现诡异 bug。Docker 解决的其实就是"环境一致性"这个老大难。

对比项 手动装环境 用 Docker
新机器上手 装运行时、装数据库、配环境变量,半天起步 装好 Docker 跑一条 docker compose up -d
版本不一致 同学装 Node 18、你装 20,行为不一样 镜像锁死版本,人人一致
迁移 / 重装 重来一遍,还容易漏装依赖 镜像直接带走,秒级还原
多项目隔离 全局装包互相污染 每个容器独立环境,互不干扰

一句话:把"环境"也当成代码管理起来。


二、Dockerfile 怎么写才不臃肿

我踩过最大的坑是第一次打出来的镜像 1.2GB,传服务器传了十分钟。核心是两招:多阶段构建 + .dockerignore。

以 Node 项目为例,多阶段构建把"编译期依赖"和"运行期依赖"分开:

dockerfile 复制代码
# 阶段一:构建阶段(这里装的是 devDependencies,只为打包)
FROM node:18-alpine AS build
WORKDIR /app
# 先拷依赖清单,利用缓存,改代码不会每次都重装
COPY package*.json ./
RUN npm ci --registry=https://registry.npmmirror.com
COPY . .
RUN npm run build

# 阶段二:运行阶段(只带运行需要的产物,体积骤降)
FROM node:18-alpine
WORKDIR /app
ENV NODE_ENV=production
# 只从构建阶段拷运行产物和依赖,构建阶段的 node_modules 被丢弃
COPY --from=build /app/node_modules ./node_modules
COPY --from=build /app/dist ./dist
COPY --from=build /app/package*.json ./
# 解决容器内中文乱码 / 时区问题
ENV LANG=C.UTF-8
ENV TZ=Asia/Shanghai
EXPOSE 3000
CMD ["node", "dist/main.js"]

.dockerignore 一定要写,否则 node_modules、.git 会被一起打进镜像,体积爆炸:

gitignore 复制代码
node_modules
npm-debug.log
.git
.gitignore
.env
Dockerfile
docker-compose.yml
dist
README.md

瘦身要点小结:基础镜像用 alpine;COPY package*.json 在 COPY . . 之前以命中缓存;能多阶段的绝不把构建工具带进运行镜像;定期 docker image prune -f 清理悬空镜像。


三、用 Compose 编排 web + db + 反向代理

单个容器用 docker run,但真实项目至少要有「应用 + 数据库 + 入口代理」。Compose 一个文件就把它们编排好(新版本已经不需要 version 字段):

yaml 复制代码
services:
  web:
    build: .                      # 用当前目录的 Dockerfile 构建
    ports:
      - "3000:3000"               # 开发时可临时暴露,生产建议只走 nginx
    environment:
      - NODE_ENV=production
      - DATABASE_URL=postgres://app:app@db:5432/appdb   # 注意主机名是服务名 db
    depends_on:
      db:
        condition: service_healthy   # 等数据库真正能连上再启动 web
    restart: unless-stopped
    networks:
      - app-net

  db:
    image: postgres:15-alpine
    environment:
      - POSTGRES_USER=app
      - POSTGRES_PASSWORD=app
      - POSTGRES_DB=appdb
    volumes:
      - db-data:/var/lib/postgresql/data   # 数据落盘,容器删了数据还在
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U app"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: unless-stopped
    networks:
      - app-net

  nginx:
    image: nginx:1.25-alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
    depends_on:
      - web
    restart: unless-stopped
    networks:
      - app-net

volumes:
  db-data:                       # 命名卷,数据持久化靠它

networks:
  app-net:
    driver: bridge

容器之间用服务名(db、web)互访,不用写 IP。这样本地和服务器跑的是同一套编排,差别只在配置文件。


四、数据卷持久化与环境变量管理

持久化 :数据库、上传文件必须挂卷,否则容器一删数据全没。命名卷(上面 db-data)由 Docker 管理最省心;自己映射宿主机目录则用 ./uploads:/app/uploads。

环境变量 :千万别把密码写进 docker-compose.yml 然后提交到仓库。正确做法是用 .env 文件 + environment 引用:

bash 复制代码
# .env(记得加进 .gitignore,绝不入库)
POSTGRES_PASSWORD=你的强密码
DB_NAME=appdb
yaml 复制代码
# docker-compose.yml 里这样引用
services:
  db:
    image: postgres:15-alpine
    environment:
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
      - POSTGRES_DB=${DB_NAME}

docker compose 默认会自动读取同目录的 .env。上线前用 docker compose config 检查一下变量是否都替换正确。


五、常用命令速查表

命令 作用
docker compose up -d 后台启动全部服务
docker compose ps 查看各容器状态
docker compose logs -f web 实时跟踪 web 服务日志
docker compose build --no-cache 不用缓存重新构建(改了 Dockerfile 用它)
docker compose down 停止并删除容器/网络(不删卷)
docker compose down -v 连数据卷一起删(危险,慎用)
docker compose exec db psql -U app 进数据库容器执行命令
docker image prune -f 清理悬空镜像释放空间
docker stats 看各容器实时资源占用

六、高频报错逐个拆

报错 1:端口被占用 (Bind for 0.0.0.0:3000 failed)

先查谁占了端口:sudo lsof -i :3000 或 netstat -tlnp | grep 3000,杀掉对应进程,或改 ports 映射成 "3001:3000"。

报错 2:容器起来就退出 (Exited (0) / 不断重启)

docker compose logs web 看真实报错;多半是启动命令瞬间执行完就退出了(比如 CMD 指向了一个一次性的脚本)。后台服务必须用阻塞进程(如 node 常驻)。

报错 3:Permission denied

宿主机挂卷后容器内写不进,通常是卷目录属主是 root 而容器用非 root 用户。解决:在 Dockerfile 里 RUN chown -R node:node /app 并 USER node;CentOS/RHEL 上挂卷还要加 SELinux 标签 :z。

报错 4:时区不对 (日志时间差 8 小时)

镜像里没设时区。在 Dockerfile 加 ENV TZ=Asia/Shanghai,alpine 可补 RUN apk add --no-cache tzdata。

报错 5:中文乱码

容器默认 locale 不是 UTF-8。加 ENV LANG=C.UTF-8,并确保源码文件本身是 UTF-8 编码。

报错 6:卷里数据消失了

八成是用了 docker compose down -v,或者改了卷名导致映射到新卷。重要的卷用命名卷并远离 -v 参数。

报错 7:healthcheck 一直 unhealthy

docker inspect <容器> | grep Health 看检测输出。常见原因:检测命令写错、数据库还没初始化完(调大 interval/retries)、检测依赖网络但容器没联网。


七、从本地到服务器的一次性部署流程

我在本地调通后,上服务器的流程基本固定成下面几步:

bash 复制代码
# 1)服务器装 Docker(Ubuntu 示例)
curl -fsSL https://get.daocloud.io/docker | bash
sudo systemctl enable --now docker

# 2)把项目文件传上去(.env 和 docker-compose.yml 一起,但 .env 不进 git)
scp -r ./project root@你的服务器IP:/home/app/
scp ./.env root@你的服务器IP:/home/app/project/

# 3)登录服务器启动
ssh root@你的服务器IP
cd /home/app/project
docker compose up -d --build
docker compose ps          # 确认三个服务都是 healthy/up
docker compose logs -f     # 看启动有没有报错

收个尾:本地你只需要维护好 Dockerfile、docker-compose.yml、.env 三个文件,上任何一台装了 Docker 的机器都能一条命令把整套环境拉起来------今晚就把你的毕设项目按这套流程打包一遍,下次换电脑或交部署文档时,你直接把这三个文件丢过去就行。

相关推荐
桌面运维家3 小时前
旧电脑跑不动课程软件,VDI云桌面怎样接着用?
运维·服务器·虚拟化·pve·vdi·云桌面
zhang133830890753 小时前
传统机械三杯式风速传感器易磨损冻堵?CG-88金属超声波风速风向传感器给出免运维方案
运维·服务器·网络·人工智能·自动化
知已难寻4 小时前
第 2 篇 · 云的地基:服务器、存储、网络三大基础设施
运维·服务器·网络·云原生·云计算·负载均衡·1024程序员节
2601_949847756 小时前
bcrypt.js 跨端三方库仓颉适配全流程
linux·运维·ubuntu
m0_639906946 小时前
Linux 操作系统入门:目录结构、服务排查常用命令、Shell 脚本基础
linux·运维·后端
今天重构了吗6 小时前
Linux 上 rm -rf 删错了能找回吗
运维·服务器
KaiwuDB7 小时前
KaiwuDB 分布式集群扩缩容,从原理到完整操作踩坑总结
运维·数据库·分布式
codezzzsleep7 小时前
学术汇报 PPT 自动化生成实战指南
运维·自动化·powerpoint
古法老登瞎折腾7 小时前
大数据智能运维实战(九):让 AI 查日志——找到异常,也保留上下文
java·大数据·运维·人工智能·spring ai·mcp
讯捷蓝达8 小时前
IBM V3500存储 双控故障、数据怎么恢复?
运维