一、Docker 的核心价值与场景
Docker 是目前最成熟、高效的软件部署技术。它主要解决了软件开发与运维中的三大核心痛点:
01- 消除底层系统的差异
- 痛点:Windows、macOS、Linux 系统差异大,传统软件需为不同系统单独开发。
- 方案:Docker 在操作系统之上构建了一个抽象层。
- 价值:屏蔽了底层系统的区别。只要在 Docker 平台能运行,就能在所有系统上稳定运行,让开发者专注于代码本身。
02- 实现轻量级的环境隔离
- 共同点:Docker 和虚拟机(VM)都能实现环境隔离。
- 核心区别:
- 虚拟机:每个虚拟机都包含一个完整的操作系统内核,体积大、启动慢、资源消耗高。
- Docker:容器之间共享宿主机的系统内核,仅隔离应用层。
- 价值:Docker 比虚拟机更轻量,启动速度达到秒级,资源利用率极高。
03- 解决交付与迁移的一致性
- 痛点:开发、测试、生产环境不一致,导致出现“在我电脑上能跑,换个地方就不行”的尴尬。
- 方案:镜像(Image) 作为交付单位。
- 价值:将代码、依赖项、系统库封装在一个只读的镜像中。无论在何种机器上运行,都能保证环境的高度一致性。
二、Docker 的安装 (Windows 环境)
Docker Desktop on Windows 依赖于 WSL 2 (Windows Subsystem for Linux)。
01- 安装前置准备
- 网络设置:开启 VPN 并启用 Tun 模式
关键步骤,解决国内镜像拉取失败问题。
- 升级 WSL: 在 PowerShell 中执行:
wsl --update02- 下载与安装
-
安装方式一:官网安装(推荐)
- 访问 www.docker.com 下载 Docker Desktop 安装包。
- 运行安装程序,保持默认选项即可。
-
安装方式二:命令行安装(Winget)
winget install Docker.DockerDesktop个人喜欢命令行安装,简单高效。
03- 初始化与验证
安装完成后重启计算机,打开 Docker Desktop 并建议完成以下配置:
- 基本设置:勾选“开机自启”。
- 存储路径:修改镜像下载目录到D盘,以节省C盘空间。
验证安装:打开终端(PowerShell 或 CMD),输入以下命令
docker run hello-world如果看到以下输出,说明安装成功:
Hello from Docker! This message shows that your installation appears to be working correctly.
三、单容器创建与管理实战
01- 核心命令:docker run 详解
docker run 是 Docker 中最核心的命令。它实际上是一个组合命令,一步完成了三个动作:
- Pull: 拉取镜像(如果本地没有)。
- Create: 创建容器。
- Start: 启动容器。
命令示例:
docker run -d --name c1 -p 80:80 docker.io/library/caddy:2.10.2-alpine参数拆解:
-d(Detach):后台运行模式。- 如果不加,容器会在前台运行,关闭命令行窗口容器就会停止。
--name:给容器起个名字(唯一)。- 推荐手动命名(如
c1),方便后续管理和记忆。
- 推荐手动命名(如
02- 端口映射的深度理解 (-p)
docker run -p 80:80 中的 -p 代表 Publish(发布),允许映射一个或多个端口。
映射规则:宿主机端口:容器内端口
- 左边:宿主机(本机)端口。
- 右边:容器内部端口。
💡 独家记忆法:数据流向与写字顺序一致 数据从外部流入,就像我们写字一样,从左往右。 请求 -> 左边(本机/墙) -> 右边(容器/屋里)。
多容器场景: 宿主机端口不能重复占用,但容器端口互不影响。
- 容器1:
-p 80:80(访问 localhost:80) - 容器2:
-p 81:80(访问 localhost:81)
03- 镜像名称的完整结构
命令中的 docker.io/library/caddy:2.10.2-alpine 是镜像的完全体形态。
结构拆解:
-
仓库名 (Registry):
docker.io- 默认是 Docker Hub 官方仓库,可省略。
-
命名空间 (Namespace):
library- 官方镜像的默认空间,可省略。如果是个人镜像,这里是用户名。
-
镜像名 (Image):
caddy- 核心名称,不可省略。
-
版本标签 (Tag):
2.10.2-alpine- 指定版本。如果不写,默认使用
:latest(最新版)。
- 指定版本。如果不写,默认使用
极简写法:
如果是官方仓库、官方镜像、且用最新版,命令可简化为:
docker run -d --name c1 -p 80:80 caddy04- 常用容器管理命令清单
验证容器是否运行成功,可访问浏览器 localhost:80,或使用以下命令:
容器生命周期管理
docker ps # 查看正在运行的容器docker ps -a # 查看所有容器(含已停止的),-a = --alldocker stop <ID/名> # 停止容器docker start <ID/名> # 启动容器docker rm <ID/名> # 删除已停止的容器docker rm -f <ID/名> # 强制删除正在运行的容器 (-f = force)镜像管理
docker images # 查看本地已下载的镜像docker rmi <ID/名> # 删除本地镜像 (Remove Image)注意:删除镜像前,必须先删除所有基于该镜像创建的容器(哪怕是停止状态的)。
系统清理
docker system prune -a功能:一键清理所有未使用的对象(停止的容器、未使用的网络、悬空的镜像等),释放空间神器。
四、数据持久化 (Data Persistence)
容器的文件系统是临时的。一旦容器被删除,其内部的所有数据(配置文件、日志、用户上传文件)都会永久丢失。为了保存数据,我们需要将数据“锚定”到宿主机上。
01- 绑定挂载 (Bind Mounts)
-
原理:将宿主机上一个具体的绝对路径,直接映射到容器内的指定路径。
-
场景:开发环境首选。
- 当你修改宿主机代码时,容器内实时生效,无需重建镜像。
-
命令示例:
docker run -d --name c2 -p 80:80 \ -v D:\docker_cache\html:/usr/share/caddy/ \ caddy- 参数详解
-v:- 左边:宿主机目录(
D:\docker_cache\html)。 - 右边:容器内目录(
/usr/share/caddy/)。 - 记忆口诀:把左边的(我的文件)盖到右边的(容器)上面。
- 左边:宿主机目录(
02- 命名卷 (Volumes)
-
原理:由 Docker 自身管理的独立存储空间(通常位于 Docker 安装目录下),与宿主机具体文件系统解耦。
-
场景:生产环境、数据库首选。
- 性能更好:在 Linux 上读写效率更高。
- 更安全:隔离了宿主机的文件系统结构。
- 易迁移:方便备份和跨平台部署。
-
操作步骤:
# 1- 创建卷docker volume create my-data-volume
# 2- 挂载卷 (左边卷名, 右边目录名)docker run -d --name c2 -p 80:80 \ -v my-data-volume:/usr/share/caddy/ \ caddy(注:如果卷不存在,Docker 会自动创建,但推荐手动管理)
03- 常用卷管理命令
docker volume ls # 列出所有卷docker volume inspect <卷名> # 查看卷的物理存储位置docker volume rm <卷名> # 删除卷docker volume prune # 清理所有未被容器引用的“孤儿卷”五、容器间通信 (Networking)
在微服务架构中(如:前端+后端+数据库),容器之间需要相互交流。
- 误区:在容器内访问
localhost,访问的是容器自己,而不是宿主机或其他容器。 - 默认网络缺陷:默认的
bridge网络不支持通过“容器名”访问,只能用 IP(但 IP 是动态变化的)。
01- 核心机制:自定义子网
最佳实践是创建一个自定义网络(User-defined Network)。在同一个自定义网络下的容器,可以通过容器名直接相互访问(Docker 内置了 DNS 解析)。
步骤 1:创建网络
docker network create my-app-net步骤 2:启动容器并加入网络
以“应用 + 数据库”为例:
- 启动数据库 (MySQL):
docker run -d --name db \ --network my-app-net \ -e MYSQL_ROOT_PASSWORD=secret \ mysql:8.0- 启动应用 (Caddy):
docker run -d --name app \ --network my-app-net \ -p 80:80 \ caddy步骤 3:容器互联
在 app 容器的代码配置中,连接数据库的 Host 地址直接填写:db 即可(无需关心 IP)。
02- 网络生命周期管理命令
docker network ls # 列出网络docker network inspect <网络名> # 查看网络详情(含已连接容器IP)docker network rm <网络名> # 删除网络动态网络操作(热插拔) 与挂载卷不同,网络可以在容器运行时动态调整:
docker network connect <网络名> <容器名> # 让运行中的容器加入新网络docker network disconnect <网络名> <容器名> # 让运行中的容器断开网络03- 关键差异总结:存储 vs 网络
这是很多新手容易忽略的架构特性:
| 特性 | 数据挂载 (Volumes/Mounts) | 网络连接 (Networks) |
|---|---|---|
| 时机 | 静态:必须在 docker run 创建容器时指定 | 动态:可以在容器运行后随时加入或断开 |
| 修改 | 容器创建后无法更改挂载点(需删除重建容器) | 容器创建后可以使用 connect/disconnect 调整 |
六、容器组编排 (Docker Compose)
在现实项目中,一个完整的应用通常包含多个服务(Web服务器 + 后端 + 数据库 + 缓存)。如果仅使用 docker run,不仅命令繁琐,还容易出错(如忘记网络连接、端口冲突)。
Docker Compose 是 Docker 的官方编排工具。
- 核心思想:使用一个 YAML 文件定义所有服务(Infrastructure as Code)。
- 核心价值:一键启动/停止 整个应用栈。
01- 核心蓝图:docker-compose.yml
docker-compose.yml 是整个容器组的“施工图纸”。
以下是一个经典架构(Caddy Web 服务器 + Redis 缓存)的配置示例:
services: # 服务清单 web: # 服务 1: Web 前端 image: caddy:2.7.5-alpine container_name: caddy-web ports: # 端口映射 (宿主机:容器) - "80:80" volumes: # 数据挂载 - ./html:/usr/share/caddy # 使用相对路径,将当前目录下的 html 挂载进去 networks: - app-network restart: always # 或者 unless-stopped
cache: # 服务 2: Redis 缓存 image: redis:7.0-alpine container_name: redis-cache # Redis 不暴露端口给宿主机,只在内部网络开放,更安全 networks: - app-network
networks: # 【网络定义】 app-network: driver: bridge # 显式定义桥接网络,方便管理02- 配置文件核心解读
1- Services (服务)
定义每一个独立的容器实例。
- 服务发现 (Service Discovery):
- 在同一个 Compose 文件中的服务,默认自动加入同一网络。
- 重要特性:容器之间可以通过 服务名 直接通信。
- 例如:
web容器代码中可以直接写host: cache来连接 Redis,无需关心 IP。
2- Volumes (卷/挂载)
- 相对路径优势:支持使用
./表示docker-compose.yml所在的当前目录。这使得整个项目文件夹可以随意拷贝迁移,配置依然有效。
3- Networks (网络)
- 虽然 Docker 会默认创建网络,但显式定义(如
app-network)是更好的工程实践,便于后期扩展和维护。
03- 常用 Compose 命令对照表
Docker Compose 的命令设计与 Docker CLI 高度一致,只是作用对象变成了“一组容器”。
| 作用 | 单容器命令 (Docker) | 容器组命令 (Docker Compose) |
|---|---|---|
| 启动 | docker run -d ... | docker compose up -d (最常用) |
| 查看 | docker ps | docker compose ps |
| 日志 | docker logs -f | docker compose logs -f |
| 停止 | docker stop | docker compose stop |
| 销毁 | docker rm | docker compose down (停止并移除容器/网络) |
关于 docker compose up -d 的执行逻辑:
它不仅是启动,而是 “声明式” 的。它会检查当前状态与 YAML 描述是否一致:
- 自动创建网络。
- 自动拉取所需镜像。
- 按顺序启动所有容器。
- 自动处理容器间的网络连接。
04- 两个至关重要的实战细节
1. 作用域:基于目录
- Docker Compose 的所有命令都是基于当前目录的。
- 注意:执行
docker compose up/down时,必须确保终端路径处于docker-compose.yml文件所在的目录下。
2. 命名空间:隔离机制
- Docker Compose 会自动将 当前文件夹的名称 作为前缀(Project Name)加到容器名和网络名上。
- 优势:这意味着你可以在机器上复制多份相同的项目代码(放在不同文件夹里),分别启动,它们之间互不干扰,不会发生命名冲突。
七、镜像封装与构建 (Dockerfile)
前面的章节我们都是在使用别人造好的轮子(官方镜像)。而在实际开发中,我们需要编写 Dockerfile,将自己的代码(Go/Node/Python等)打包成专属镜像,实现标准化的交付。
01- 核心蓝图:Dockerfile
Dockerfile 是一个纯文本文件,包含了一系列指令。这些指令就像是一个脚本,告诉 Docker 如何一步步地“组装”出你的镜像。
经典案例:Go 应用的多阶段构建
# --- 阶段一:构建环境 (Builder) ---# 选择包含编译器的大镜像FROM golang:1.21-alpine AS builderWORKDIR /app
# 利用缓存机制:先复制依赖文件并下载,再复制源码COPY go.mod go.sum ./RUN go mod download
COPY . .# 编译出二进制可执行文件RUN CGO_ENABLED=0 go build -o /app/myapp
# --- 阶段二:运行环境 (Runner) ---# 选择极简的小镜像 (仅几 MB)FROM alpine:3.18WORKDIR /app
# 关键:只从 builder 阶段复制编译好的文件,丢弃源代码和编译器COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/COPY --from=builder /app/myapp /app/myapp
# 声明端口与启动命令EXPOSE 8080CMD ["/app/myapp"]02- 常用指令速查表
每个指令都会在构建中生成一个新的镜像层 (Layer),层数越少越好。
| 指令 | 作用 | 关键点 |
|---|---|---|
| FROM | 指定基础镜像 | 必须是第一条指令。推荐使用 alpine 版本以减小体积。 |
| WORKDIR | 设置工作目录 | 相当于 cd。后续的 RUN/CMD/COPY 都在此目录下执行。 |
| COPY | 复制文件 | 格式:COPY <本地路径> <镜像内路径>。 |
| RUN | 构建时执行命令 | 用于安装依赖、编译代码。如 RUN apt-get update。 |
| CMD | 运行时默认命令 | 容器启动时的入口。一个文件只能有一个 CMD。 |
| EXPOSE | 声明监听端口 | 仅作为文档说明,不会自动开放端口(需配合 -p 使用)。 |
| ENTRYPOINT | 设置主程序 | 让容器像一个可执行程序一样运行,CMD 的内容会作为参数传给它。 |
辨析 RUN vs CMD:
RUN在 docker build 阶段执行(写入镜像)。CMD在 docker run 阶段执行(启动容器)。
03- 进阶技巧:多阶段构建 (Multi-Stage Builds)
这是现代 Dockerfile 的最佳实践。
- 痛点:编译代码需要编译器(GCC, Go, Maven),这些工具体积很大,但运行程序时并不需要它们。
- 方案:
- Builder 阶段:用大镜像编译代码。
- Runner 阶段:用极简镜像(Alpine)。
- COPY:只把编译好的二进制文件从 Builder 拷到 Runner。
- 价值:将镜像体积从 800MB+ 压缩到 20MB+,大幅提升传输和启动速度,且更安全(没有源码)。
04- 构建与优化 (.dockerignore)
构建命令:
docker build -t my-go-app:v1.0 .-t:打标签 (Tag),格式为名称:版本。.:构建上下文 (Context),指当前目录。
忽略文件 (.dockerignore):
类似于 Git 的 .gitignore,用于防止不必要的文件(如 .git、node_modules、本地日志)被上传到 Docker 引擎,加快构建速度并减小体积。
05- 生产安全:资源限制 (Resource Limits)
这部分是生产环境的“安全气囊”,防止某个容器吃光宿主机所有内存导致死机。
方式 A:创建时限制 (docker run)
docker run -d \ --name limited-nginx \ --memory="512m" \ --cpus="1.0" \ -p 80:80 \ nginx--memory="512m":限制最大内存使用 512MB。--cpus="1.0":限制最多使用 1 个 CPU 核心。
方式 B:运行时动态更新 (docker update)
如果容器已经跑起来了,也可以动态调整:
docker update --memory="1g" --memory-swap="1g" limited-nginx🎓 总结
本文共分为 7 个部分,介绍了开发中 Docker 最常用的使用方式。
- Docker 的原理和使用场景
- Docker 的安装和配置
- 单容器的创建和管理
- 数据持久化
- 容器间通信
- Docker compose 容器组的创建与管理
- 通过 Dockerfile 创建自己的镜像
Docker 的命令很多,可以随着日常使用,不断的学习和掌握。
接下来还会介绍下 k8s 和 podman,欢迎关注。