第 8 步 · 基础搭建 进阶 · 16 分钟
🐳Docker:隔离您的项目
每个项目都待在自己密封的泡泡里。Docker 防止您的项目互相踩脚,在 agent 做实验时把损失关在盒子里,还让您的部署可复现。新手老手都受用。
您将在这台机器上同时跑好几个项目。一个要 Node 18,另一个要 Node 22。这个需要某个库的老版本,那个要最新版。如果「一锅烩」地装到系统上,它们迟早互相踩脚, 于是您撞上那个经典桥段:「可它之前明明能跑」。Docker 一劳永逸地解决这个问题:每个项目都活在自己密封的泡泡里。
为什么它改变人生(尤其是有了 agent)
三个理由,而第三个被严重低估了:
- 版本冲突再见。 每个项目自带它确切的环境。这里 Node 18,那里 Node 22,互不干扰。您不再「全局」装任何可能搞坏另一个项目的东西。
- 可复现的部署。 在您这里跑的容器,跟将来在线上跑的那个一模一样。「在我机器上能跑」消失了:您的机器,就是那个容器。这极大地简化了上线(见 Cloudflare Tunnel)。
- 损失留在盒子里。 这是和 agent 一起干活时的关键点。一个在做实验的 agent、一个可疑的依赖、一个跑偏的脚本:被关在容器里,它们没法破坏整台机器。这是比「项目文件夹」高一级的隔离, 每个项目一个真正的沙箱(与 保护访问安全 相关联)。
安装 Docker
# 官方方法,一条命令
curl -fsSL https://get.docker.com | sudo sh
# 允许您的用户在不用 sudo 的情况下操控 Docker(之后注销/重新登录)
sudo usermod -aG docker $USER
验证:docker run hello-world 应该下载一个迷你镜像并显示一条欢迎信息。看到它,您就准备好了。
第一级, 跑一个现成的东西(新手)
Docker 起步时最美妙的地方,是您能直接享用别人的成果。成千上万的应用已经「打包入盒」,开箱即用。某个项目需要一个 PostgreSQL 数据库?不用费力安装:
# 一行启动一个隔离的 PostgreSQL 数据库
docker run -d --name ma-db -e POSTGRES_PASSWORD=secret -p 5432:5432 postgres:16
docker ps # 看看在跑什么
docker logs ma-db # 看日志
docker stop ma-db # 停止(docker start ma-db 可重新启动)
docker rm ma-db # 删除容器(盒子干干净净地消失)
第二级, 一个多服务的小项目(Docker Compose)
大多数项目都由好几块组成:比如一个 app + 一个数据库。Docker Compose 把这一切描述在单个 compose.yml 文件里,再用一条命令把整套都启动起来。
# compose.yml, 一个小型 web app + 它的数据库,一起隔离
services:
app:
build: .
ports:
- "8080:8080"
depends_on:
- db
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: secret
volumes:
- db-data:/var/lib/postgresql/data # 数据在重启后存活
volumes:
db-data:
docker compose up -d # 全部启动,在后台
docker compose logs -f # 跟踪发生了什么
docker compose down # 全部停止并清理
第三级, 打包您自己的项目(Dockerfile)
要把您自己的代码装进盒子,您写一个 Dockerfile:那份说明该如何构建您项目镜像的菜谱。
# Dockerfile, 您盒子的菜谱
FROM node:22-slim # 从一个官方的、轻量的 Node 镜像出发
WORKDIR /app
COPY package*.json ./
RUN npm ci # 在盒子里安装依赖
COPY . .
EXPOSE 8080
CMD ["node", "server.js"] # 启动 app 的命令
docker build -t mon-app . # 从 Dockerfile 构建镜像
docker run -d -p 8080:8080 mon-app
从此,您的项目和它的环境合为一体。您可以在任何装了 Docker 的机器上原样启动它。
专家级, 加固隔离
容器已经隔离了不少,但默认情况下它没把篱笆扎到该扎的地方。当您托管一个对外暴露的服务,或者要圈住一个 agent 时,把螺丝拧紧:
- 不要在容器里以
root运行 : 在您的 Dockerfile 里加一个非特权用户(USER node)。如果有人从 app 里逃出来,他也不是 root。 - 只读文件系统 :
docker run --read-only阻止容器往不该写的地方写。 - 限制资源 :
--memory=512m --cpus=1防止一个失控的容器把整台机器憋死。 - 削减权限 :
--cap-drop=ALL移除用不上的 Linux capabilities;需要时再一个一个加回来。 - 隔离网络 : 只发布(
-p)真正必要的端口;其余的保持不可见。而您对外发布的东西,要经由 Cloudflare Tunnel 或留在 Tailscale 上,绝不裸奔在互联网上。