早上九点,你像往常一样敲下 docker run -p 3000:3000,应用启动,一切看起来岁月静好。可一旦把这套操作原样扔到生产集群,镜像体积爆炸、构建慢得像乌龟、还有安全隐患等着爆雷——那个让你安心的命令,恰好是所有坑的集合体。生产环境的容器世界,画风其实完全不同。
核心要领好比一份多层蛋糕食谱:做好每一步分层,就能让构建飞起来,镜像瘦下去。先看懂底下这张“配方图”——左边是常见但糟糕的写法,右边是优化后的版本。坏写法直接用 node:20 完整镜像,然后在 RUN 里顺手装一堆工具,接着 COPY . . 把整个项目目录粗暴塞进去。后果是,随便改一行源代码,缓存全部失效,所有依赖都得重装,最终镜像轻易跑到 180MB 以上,还拖着编辑器、Git 这类无关工具,安全面也拉宽了。
优化版则像一次精密的外科手术。基础镜像用 node:20-alpine,体积直接从约 180MB 砍到 8MB 左右。接着把依赖安装独立成构建阶段,关键在于 先复制 package.json 和 lock 文件,再跑 npm ci。抄送顺序这件事,是层缓存的命门——只要依赖清单没变,这层就不会重建,源代码改翻天也不会触发重新安装。生产阶段只从构建阶段取回 node_modules 和必要文件,最终镜像能控在 80MB 上下。构建阶段需要的工具,生产阶段一概不装,攻击面随之缩窄。
还有几条铁则,条条割肉见血。第一条,学会写 .dockerignore 文件,把 node_modules、.git、.env 敏感文件、测试覆盖报告、文档和本地编译产物全踢出上下文,每个构建都从干净状态出发。第二条,坚决不在容器里用 root 跑应用:安全实践是通过 addgroup 和 adduser 创建系统用户,再经 USER 指令切换身份,这样即使容器被突破,攻击者也拿不到宿主机 root 权限,许多 Kubernetes 集群干脆强制要求这条。第三条,别忘了 HEALTHCHECK。原文里给了一个 30 秒间隔、3 次重试的 wget 探活指令,Docker 会盯住你的 /health 端点,一旦出问题,可以自动重启。
本地开发光靠单个容器很难模拟真实拓扑,这时候上 Docker Compose。yaml 里可以声明两个服务:app 和 db。app 服务通过 build.context 和 target 选项,能直接指向 Debug 阶段的构建定义,并暴露 3000 应用端口和 9229 调试器端口。环境变量把 NODE_ENV 设成 development,数据库连接串指向同网络内的 db 容器。最提升手感的是 volumes 配置:将当前目录冒号挂载到容器的 /app,同时用匿名卷保护 /app/node_modules 不被宿主机覆盖,实现代码秒级热重载,依赖却依然用容器自己的。
编排还需要控制依赖就绪时机。原文中 db 使用 postgres:16-alpine 镜像,配置了 postgres 用户的健康检查,通过 pg_isready 命令 5 秒检测一次。app 则用 depends_on 并加上 condition: service_healthy,确保 postgres 真正能接收连接后才启动。重启策略设为 unless-stopped,崩了自动拉起来。数据持久化则靠命名卷 pgdata 映射到 /var/lib/postgresql/data,就算容器被删,业务数据也不会丢。这些细节拼在一起,才把一个玩具式的 docker run,捏成了能扛住真实流量的生产骨架。
热门跟贴