Docker 镜像和容器入门指南
你刚加入一个前端团队,大家都在讨论”启动容器”和”拉取镜像”。你的 React 应用在本地运行得很完美,但部署却让人摸不着头脑。Docker 正是为了解决这个问题而生的——理解 Docker 镜像的基础知识其实比你想象的要简单。
本指南将介绍 Docker 镜像和容器的实际含义、它们之间的关系,以及如何在前端工作流程中使用它们进行本地开发、测试和简单部署。
核心要点
- Docker 镜像是只读的蓝图,而容器是这些镜像的运行实例。
- 使用明确的标签如
node:20-alpine而不是latest,以确保构建的可预测性和可重复性。 - 多阶段构建可以让你的生产镜像保持小巧和安全。
- 永远不要在容器内存储持久化数据——应该使用卷(volumes)。
- 以非 root 用户身份运行容器,并定期扫描镜像以发现漏洞。
什么是 Docker 镜像和容器?
Docker 镜像是一个只读的软件包,包含运行应用程序所需的一切:代码、运行时、库和配置。可以把它想象成面向对象编程中的类——一个定义结构但本身不执行任何操作的蓝图。
容器是该镜像的运行实例。当你执行 docker run 时,Docker 会从镜像创建一个隔离的环境,你的应用程序就在其中实际运行。你可以从同一个镜像启动多个容器,每个容器都独立运行。
Docker 镜像与容器的区别很重要:镜像是存储在磁盘上的静态模板,而容器是具有自己文件系统和网络的活动进程。
Docker 镜像遵循 OCI(开放容器倡议)规范,这意味着它们可以在不同的容器运行时中工作——不仅仅是 Docker。这种标准化确保了你的镜像保持可移植性。
理解仓库和标签
镜像存储在仓库(registry)中——Docker Hub 是最常见的公共仓库。当你引用像 node:20-alpine 这样的镜像时,你指定的是一个仓库名(node)和一个标签(20-alpine)。
这里有一个容易让初学者困惑的地方:latest 标签并不神奇。它不会自动指向最新版本。它只是一个默认标签,镜像维护者可能会更新,也可能不会更新。始终使用明确的标签如 node:20-alpine 以确保构建的可预测性。
运行你的第一个容器
让我们使用官方的 Node.js 镜像运行一个简单的容器:
docker run -it --rm node:20-alpine node -e "console.log('Hello from Docker!')"
-it 标志启用交互模式和终端。--rm 标志在容器退出时自动删除它。
对于更实用的示例,你可以运行一个开发服务器。首先,创建一个包含前端代码的项目目录,然后:
docker run -d -p 3000:3000 -v $(pwd):/app -w /app node:20-alpine sh -c "npm install && npm run dev"
-d 标志让容器在后台运行。-p 3000:3000 将容器内的 3000 端口映射到你机器上的 3000 端口。-v 标志将你的当前目录挂载到容器中,-w 设置工作目录。
使用 Dockerfile 构建自定义镜像
对于实际项目,你需要创建自定义镜像。以下是一个 React 应用程序的 Dockerfile:
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html
EXPOSE 80
这展示了多阶段构建——Docker 的一个关键最佳实践。第一阶段构建你的应用;第二阶段仅将生产文件复制到一个最小化的 nginx 镜像中。你的最终镜像保持小巧和安全。
构建并运行它:
docker build -t my-frontend-app .
docker run -d -p 8080:80 my-frontend-app
Discover how at OpenReplay.com.
Docker 卷和持久化
容器是临时的——当它们停止时,写入其中的任何数据都会消失。对于本地开发,使用绑定挂载(bind mounts)来同步你的源代码:
docker run -v $(pwd)/src:/app/src -p 3000:3000 my-frontend-app
对于需要持久化的数据(如数据库文件),使用命名卷(named volumes):
docker volume create app-data
docker run -v app-data:/data my-app
理解 Docker 卷和持久化至关重要:永远不要仅在容器的文件系统内存储重要数据。
Docker 安全基础
以下几个实践可以让你的容器更安全:
以非 root 用户运行。 在你的 Dockerfile 中添加用户:
RUN adduser -D appuser
USER appuser
使用最小化的基础镜像。 基于 Alpine 的镜像比完整发行版的漏洞更少。
定期扫描镜像。 像 Docker Scout 或 Trivy 这样的工具可以识别已知漏洞。
永远不要将密钥烘焙到镜像中。 使用环境变量或密钥管理工具来处理凭证——硬编码它们会造成持续存在于镜像层中的安全风险。
使用 Compose 简化多容器设置
当你的前端在本地需要后端 API 和数据库时,Docker Compose 可以编排一切:
services:
frontend:
build: ./frontend
ports:
- "3000:3000"
api:
build: ./api
ports:
- "4000:4000"
运行 docker compose up,两个服务会一起启动。使用 docker compose down 停止并删除所有容器。
结论
Docker 镜像是蓝图;容器是运行实例。通过多阶段构建保持镜像小巧,使用明确的标签而不是 latest,使用卷将状态与容器分离,并定期重建镜像以获取安全更新。无论你是运行开发环境还是部署简单的前端应用程序,这些基础知识都会对你有所帮助。
常见问题
Docker 镜像是一个只读模板,包含你的应用程序代码、依赖项和配置。容器是该镜像的运行实例。你可以从同一个镜像创建多个容器,每个容器都独立运行,拥有自己隔离的文件系统和网络。
latest 标签不会自动更新到最新版本。它只是一个默认标签,维护者可能会也可能不会保持更新。使用明确的版本标签如 node:20-alpine 可以确保构建的可重复性,并防止镜像更新时出现意外的破坏性变更。
使用 Docker 卷在容器文件系统之外持久化数据。命名卷将数据存储在 Docker 管理的位置,而绑定挂载链接到主机上的特定目录。永远不要依赖容器的内部文件系统来存储重要数据。
多阶段构建允许你使用一个镜像来构建应用程序,使用另一个更小的镜像来运行它。这通过排除构建工具和依赖项来保持生产镜像的轻量级,减少镜像大小和潜在的安全漏洞。
Gain control over your UX
See how users are using your site as if you were sitting next to them, learn and iterate faster with OpenReplay. — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.