📚 全栈开发学习系列
第 32 篇 · 阶段五:扩展与部署
✅ 阶段四:跨平台 App(26-30篇)
已完成
📌 阶段五:扩展与部署
进行中
✓ 31 Tauri 桌面应用入门
✓ 32 Docker 容器化入门
33 Nginx 反向代理
34 CI/CD 自动化部署
读完本篇你将能:
✓ 理解容器化核心概念与 Docker 架构原理
✓ 掌握 Docker 常用命令:镜像、容器、仓库
✓ 编写 Dockerfile 构建自定义镜像
✓ 使用 Docker Compose 编排多容器应用
✓ 理解数据卷与网络的基本使用
Docker 容器化入门:从环境隔离到一键部署
全栈开发学习系列 · 第 32 篇 · 阶段五扩展层
📑 本篇目录
一、为什么需要容器化:从"在我电脑上能跑"说起
二、Docker 核心概念:镜像、容器、仓库
三、安装与第一个容器:Hello World
四、常用命令速查:镜像管理与容器操作
五、Dockerfile:构建你自己的镜像
六、Docker Compose:多容器编排
七、数据卷与网络:持久化与容器通信
八、常见错误与三层练习
九、知识回顾与下一篇预告
一、为什么需要容器化:从"在我电脑上能跑"说起
做开发的人几乎都听过这句话:"在我电脑上能跑啊,怎么到你那就不行了?" 这句话背后的根源是环境不一致——你用 Node 20,服务器是 Node 18;你本地 MySQL 8.0,测试环境是 5.7;甚至操作系统都不一样,一个 macOS 一个 Linux。这些差异导致了大量的部署时间浪费在"配环境"上。
容器化就是来解决这个问题的。你可以把容器想象成一个标准化的集装箱——不管里面装的是什么货物(应用),集装箱的尺寸、吊装方式、运输标准都是统一的。应用连同它的依赖(运行时、库、配置文件)一起被打包进容器,到哪台机器上运行方式都一样。
💡 小贴士:容器 vs 虚拟机
虚拟机是完整的操作系统模拟,每个 VM 有自己的内核,启动慢、体积大(几 GB)。容器共享宿主机的内核,只隔离用户空间进程,启动快(秒级)、体积小(几 MB 到几百 MB)。打个比方:虚拟机是一整栋独立的房子,容器是同一栋楼里各自上锁的公寓——共享基础设施,但各有各的空间。
Docker 是目前最流行的容器化技术栈。它包含 Docker Engine(运行时)、Docker Hub(镜像仓库)、Docker Compose(多容器编排)等一整套工具。截至 2026 年 9 月,Docker Engine 已发布到 v29.8.0 版本,生态非常成熟。
二、Docker 核心概念:镜像、容器、仓库
学习 Docker 先要搞清三个核心概念,这三个词贯穿始终:
🖼️ 镜像(Image)
镜像是容器的模板,是一个只读的文件系统快照。包含运行应用所需的一切:代码、运行时、库、环境变量、配置文件。镜像可以被复用,一个镜像可以启动无数个容器。类比:镜像就像"类(Class)",是模板。
📦 容器(Container)
容器是镜像的运行实例。从同一个镜像启动的容器,初始状态完全相同,但运行后各自独立,可以有不同的数据。容器可以被启动、停止、删除。类比:容器就像"对象(Instance)",是根据类创建出来的实体。
🏪 仓库(Registry)
仓库是存储和分发镜像的地方,类似代码的 GitHub。Docker Hub 是官方公共仓库,你也可以搭建私有仓库。常用的镜像(Ubuntu、Nginx、Node、MySQL)都能在上面找到。类比:仓库就像"应用商店",下载别人做好的镜像直接用。
三者的关系可以这样理解:从仓库下载镜像,用镜像启动容器,容器运行后产生的改动可以重新保存为新的镜像。整个流程闭环,这就是 Docker 的核心工作流。
三、安装与第一个容器:Hello World
3.1 Docker Desktop 安装
桌面开发推荐用 Docker Desktop,一键装好 Docker Engine、CLI、Compose 和图形界面:
Bash - 各平台安装方式
|
1
2
3
4
5
6
7
8
9
10
11
12
|
# macOS(推荐 Homebrew)
brew install --cask docker
# Ubuntu / Debian(官方脚本)
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
sudo usermod -aG docker $USER # 免 sudo 运行
# 验证安装
docker --version # Docker version 29.x.x
docker compose version # Docker Compose version v2.x
docker info # 查看详细信息
|
3.2 第一个容器:Hello World
安装完成后,运行经典的 hello-world 镜像验证环境。这是 Docker 的"Hello World":
Bash - 运行第一个容器
|
1
2
3
4
5
6
7
8
9
10
11
12
|
$ docker run hello-world
# 输出结果:
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
c1ec31eb5944: Pull complete
Digest: sha256:xxxx...
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
...
|
执行 docker run hello-world 时,Docker 做了三件事:1 本地找不到 hello-world 镜像,自动从 Docker Hub 拉取;2 用这个镜像创建并启动一个容器;3 容器执行主进程(打印 Hello World),然后退出。整个过程自动完成,不需要你手动下载和配置。
四、常用命令速查:镜像管理与容器操作
Docker 命令很多,但日常开发高频使用的也就十几个。按镜像和容器两大类整理:
4.1 镜像相关命令
Bash - 镜像管理常用命令
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
# 查看本地所有镜像
docker images
# 拉取镜像(从 Docker Hub)
docker pull nginx:alpine # 指定标签 alpine
docker pull node:20 # Node.js 20 版本
# 搜索镜像
docker search nginx
# 删除镜像
docker rmi nginx:alpine # 按名称删除
docker rmi abc123def # 按 ID 删除
# 清理无用镜像(释放空间)
docker image prune
|
4.2 容器相关命令
Bash - 容器操作常用命令
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
# 启动容器
docker run -d -p 8080:80 nginx:alpine
# -d 后台运行 -p 端口映射(宿主:容器) --name 命名
docker run -d -p 3000:3000 --name myapp node:20
# 查看运行中的容器
docker ps # 只看运行中
docker ps -a # 看所有(含已停止)
# 停止 / 启动 / 重启容器
docker stop myapp
docker start myapp
docker restart myapp
# 查看容器日志
docker logs myapp # 查看全部日志
docker logs -f myapp # 实时跟踪日志
# 进入容器内部(调试用)
docker exec -it myapp sh
# -it 交互式终端 sh/bash 是要执行的命令
# 删除容器
docker rm myapp # 只能删已停止的
docker rm -f myapp # 强制删除(运行中也删)
docker container prune # 清理所有停止的容器
|
💡 小贴士:端口映射的 -p 参数
-p 宿主端口:容器端口 是最常用的参数。容器内部是隔离的网络,外部无法直接访问。端口映射就像给容器开了一扇门——外部访问宿主机的 8080 端口,Docker 会把流量转发到容器的 80 端口。
五、Dockerfile:构建你自己的镜像
Docker Hub 上的镜像都是别人做好的通用模板,但你的应用有自己的代码和依赖,需要构建自定义镜像。Dockerfile 就是构建镜像的"配方"——用一系列指令描述从基础镜像开始,每一步做什么改动,最终生成一个新的镜像。
5.1 Dockerfile 常用指令
Dockerfile - Node.js 应用示例
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
# 1. 基础镜像(FROM 必须是第一条指令)
FROM node:20-alpine
# 2. 设置工作目录(后续命令都在这个目录执行)
WORKDIR /app
# 3. 先复制依赖文件(利用缓存加速构建)
COPY package*.json ./
RUN npm install --production
# 4. 复制应用代码
COPY . .
# 5. 暴露端口(仅声明,不自动映射)
EXPOSE 3000
# 6. 容器启动命令(每个 Dockerfile 只能有一个 CMD)
CMD ["node", "server.js"]
|
为什么要先复制 package.json 再复制全部代码?这是 Docker 分层缓存的最佳实践。Dockerfile 每条指令生成一个镜像层,如果某层的输入没变(比如 package.json 文件没变),就会直接复用缓存,跳过这一步。把依赖文件和代码分开复制,改代码时就不用重新安装依赖,构建速度快很多。
5.2 构建与运行
Bash - 构建镜像并运行
|
1
2
3
4
5
6
7
8
|
# 在 Dockerfile 所在目录执行构建
docker build -t my-node-app:v1 .
# -t 给镜像打标签 名称:版本号 . 是构建上下文目录
# 运行镜像
docker run -d -p 3000:3000 my-node-app:v1
# 验证
curl http://localhost:3000
|
六、Docker Compose:多容器编排
真实的 Web 应用通常不止一个容器——前端一个、后端一个、数据库一个、缓存一个。如果每个容器都手动 docker run 启动,命令会越来越长,容器之间的网络连接也很麻烦。
Docker Compose就是来解决这个问题的。用一个 docker-compose.yml 文件定义所有服务(容器),一条命令就能启动、停止、重建整个应用。
6.1 Compose 文件示例
YAML - docker-compose.yml
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
# docker-compose.yml
services:
web: # 前端服务
image: nginx:alpine
ports:
- "8080:80"
depends_on:
- api
api: # 后端服务
build: . # 从当前目录的 Dockerfile 构建
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgres://user:pass@db:5432/app
depends_on:
- db
db: # 数据库服务
image: postgres:16-alpine
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=pass
- POSTGRES_DB=app
volumes:
- pgdata:/var/lib/postgresql/data
volumes: # 声明命名卷(持久化数据)
pgdata:
|
6.2 Compose 常用命令
Bash - Docker Compose 常用命令
|
1
2
3
4
5
6
7
8
9
10
11
12
|
# 启动所有服务(后台运行)
docker compose up -d
# --build 强制重新构建镜像
docker compose up -d --build
# 停止并移除所有容器/网络
docker compose down
# -v 同时删除数据卷(谨慎使用!)
docker compose down -v
# 查看服务状态 / 日志
docker compose ps
docker compose logs -f api # 只看某个服务
|
🔑 关键细节:服务名即域名
在同一个 Compose 项目中,服务名就是容器之间通信的域名。比如 api 服务连接数据库时,主机名直接写 db(数据库服务名),不需要写 IP 地址,也不需要暴露端口到宿主机。Docker 的内置 DNS 会自动解析。
七、数据卷与网络:持久化与容器通信
7.1 数据卷(Volume)
容器有一个重要特性:容器删除后,容器内的数据也会跟着消失。这对于无状态的 Web 服务没问题,但数据库、用户上传文件这些需要持久保存的数据,就不能放在容器内部。
数据卷(Volume)是 Docker 提供的持久化方案,它把宿主机的目录挂载到容器中,容器写入这个目录的数据实际存在宿主机上,容器删除后数据仍然保留。
Bash - 数据卷使用方式
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
# 方式一:命名卷(推荐,Docker 管理)
docker run -d -v mydata:/app/data nginx
# 方式二:绑定挂载(指定宿主机路径)
docker run -d -v /host/path:/container/path nginx
# 当前目录挂载:
docker run -d -v $(pwd):/app nginx
# 数据卷管理命令
docker volume ls # 列出所有卷
docker volume create myvol # 创建卷
docker volume rm myvol # 删除卷
docker volume prune # 清理未使用的卷
# Compose 中使用(声明命名卷)
# 在 services 同级添加 volumes 顶层配置
|
7.2 容器网络
Docker 有多种网络模式,最常用的是bridge(桥接)模式——这是默认模式,同一个 bridge 网络下的容器可以互相通信,外部需要通过端口映射访问。
Docker Compose 会自动为项目创建一个专用的 bridge 网络,所有服务都在这个网络里,所以服务名能直接当域名用。不需要手动配置网络。
💡 小贴士:别把数据库端口暴露到公网
很多新手会把数据库的 ports 也写进 Compose,导致数据库端口暴露在宿主机上,有安全风险。正确做法是:数据库只在内部网络通信,不映射端口到宿主机。只有需要被外部(浏览器、移动端)访问的服务(如 Nginx、API 网关)才需要 ports 映射。
八、常见错误与三层练习
8.1 常见错误
错误1:端口被占用
Bind for 0.0.0.0:8080 failed: port is already allocated
原因:宿主机的 8080 端口已经被其他程序占用了。
修复:用 lsof -i :8080(macOS)或 netstat -ano | findstr 8080(Windows)找到占用进程,关掉它或换个端口映射。
错误2:镜像拉取超时
Error response from daemon: Get "https://registry-1.docker.io/...": dial tcp: i/o timeout
原因:国内网络访问 Docker Hub 速度慢或超时。
修复:配置国内镜像加速器,如阿里云镜像加速器、网易云镜像等。Docker Desktop 在 Settings → Docker Engine 里添加 registry-mirrors 配置。
错误3:容器启动后立即退出
容器 status 显示 Exited (0),docker ps 看不到
原因:容器的主进程执行完就退出了。Docker 容器的生命周期 = 主进程的生命周期,主进程结束容器就停。
修复:确保 CMD/ENTRYPOINT 指定的命令是前台运行的长驻进程。比如 Nginx 要用 nginx -g 'daemon off;',不能用后台启动方式。
错误4:Dockerfile 中 COPY 找不到文件
COPY failed: file not found in build context or excluded by .dockerignore
原因:COPY 的源文件不在"构建上下文"(build context)目录内,或者被 .dockerignore 排除了。
修复:确认文件在 docker build 后面那个点(.)代表的目录下。构建上下文是 docker build 最后那个路径参数,不是 Dockerfile 所在目录(虽然通常两者相同)。
8.2 三层练习
初
入门级:运行一个 Nginx 静态网站
目标:用 Docker 跑一个 Nginx 服务器,展示自定义的 HTML 页面
步骤:
1. 创建一个 index.html,写入你自己的欢迎页面
2. 用 docker run -d -p 8080:80 -v $(pwd):/usr/share/nginx/html nginx:alpine 启动
3. 浏览器访问 http://localhost:8080 验证
4. 修改 index.html 内容,刷新页面看是否实时更新
验收:浏览器能看到自定义页面,修改后刷新立即生效
中
进阶级:Dockerfile 构建 Node.js API
目标:写一个简单的 Express API,用 Dockerfile 构建成镜像并运行
步骤:
1. 初始化 Node.js 项目,安装 express,写一个返回 JSON 的 /api/hello 接口
2. 编写 Dockerfile(使用 node:20-alpine 基础镜像,注意分层缓存优化)
3. 构建镜像并运行,用 curl 或浏览器测试 API
4. 用 .dockerignore 排除 node_modules 和 .git 等无关文件
挑战:对比"先 COPY 全部再 npm install"和"先 COPY package.json 再 install"的构建缓存差异
高
挑战级:WordPress + MySQL 全栈部署
目标:用 Docker Compose 一键部署 WordPress + MySQL + phpMyAdmin
步骤:
1. 编写 docker-compose.yml,包含三个服务:wordpress、db(MySQL)、phpmyadmin
2. MySQL 使用命名卷持久化数据,设置环境变量配置密码和数据库名
3. WordPress 映射 8080 端口,phpMyAdmin 映射 8081 端口
4. 用 depends_on 控制启动顺序(注意:只控制启动顺序,不等待服务就绪)
加分项:docker compose down 后再 up,数据不丢失(验证命名卷);尝试导入一个 .sql 备份文件
九、知识回顾与下一篇预告
🏷️ Docker Engine 29
🏷️ 镜像 / 容器 / 仓库
🏷️ Dockerfile
🏷️ 分层缓存
🏷️ Docker Compose
🏷️ 数据卷 Volume
🏷️ Bridge 网络
🏷️ 端口映射
本篇从"在我电脑上能跑"的痛点切入,介绍了容器化的核心价值——环境一致性。我们学习了 Docker 的三大概念(镜像、容器、仓库),常用命令(docker run / ps / logs / exec),Dockerfile 的编写和分层缓存技巧,以及 Docker Compose 编排多容器应用的方法。最后讲解了数据卷持久化和容器网络的基本原理。
容器化是现代部署的基石。掌握了 Docker,你就能把任何应用打包成一个标准化的"集装箱",在开发机、测试服务器、生产环境之间无缝迁移,再也不用为环境差异头疼。
📌 下一篇预告
第 33 篇:Nginx 反向代理与负载均衡
应用跑起来了,怎么让用户通过域名访问?多个服务怎么共用 80/443 端口?流量大了怎么分摊到多个实例?下一篇我们学习 Nginx——Web 服务器界的瑞士军刀,掌握反向代理、静态资源服务、负载均衡、HTTPS 配置等核心技能。
觉得有用就点个❤️在看,分享给更多一起学习的朋友
— 全栈开发学习系列 · 第 32 篇 —