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

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

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

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

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

刚学的时候我也是 SSH 上去手动 apt installnpm 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

瘦身要点小结:基础镜像用 alpineCOPY package*.jsonCOPY . . 之前以命中缓存;能多阶段的绝不把构建工具带进运行镜像;定期 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

容器之间用服务名(dbweb)互访,不用写 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 :3000netstat -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 /appUSER 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     # 看启动有没有报错

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

相关推荐
wdfk_prog1 小时前
ros教程02:创建 catkin Workspace、Package 与第一个 ROS1 C++ Node
运维·缓存·docker·容器·ros
亚川楼宇自控系统数据中心厂家2 小时前
高校 / 医院 / 政务云数据中心 IBMS 系统怎么落地?
运维
陈陈CHENCHEN2 小时前
【Linux】服务器根目录磁盘扩容操作记录
linux·运维·服务器
见闻小天地4 小时前
从晶片工艺到可靠性验证:晶威特TF-3215型RTC晶振全面解析
运维·业界资讯
名字还没想好☜5 小时前
Docker 网络实战:bridge/host/none/自定义网络、容器互通与端口映射踩坑
运维·docker·kubernetes
fengyehongWorld5 小时前
Linux squid搭建基础代理服务器
linux·运维·服务器
木白CPP5 小时前
Linux DMA驱动详解(二)-----DMA的使用者
java·linux·运维
赵民勇6 小时前
systemd-socket-activate命令详解
linux·运维
跨境小彭6 小时前
Temu运营避坑:制造地点信息填写规范、后果及批量实操教程
大数据·运维·自动化·跨境电商·temu