一句话本质:Docker 把应用和它依赖的运行环境一起"打包"成一个可移植的镜像,镜像跑起来就是容器。掌握
pull / run / stop / exec / build / compose这一条命令主线,就能覆盖 90% 的日常操作。核心要点:
- 镜像(Image)是模板,容器(Container)是镜像运行起来的实例,一个镜像可启动多个容器。
- 仓库(Registry)存镜像,Docker Hub 是默认官方仓库,
docker pull时很多路径可以省略。run是一切的起点,它的-d / -p / -v / --name / -it / --rm / --restart参数决定了容器怎么跑。- 调试靠三板斧:
logs看日志、inspect看配置、exec进容器。Dockerfile定制镜像,compose编排多容器。
一、先搞懂三个核心概念
在敲命令之前,先把这三个名词分清,后面所有命令都是围绕它们转:
| 概念 | 是什么 | 类比 |
|---|---|---|
| 容器化技术 | 给应用封装一层独立的运行环境(进程、网络、文件系统相互隔离) | 给每个应用发一个独立的"集装箱" |
| 镜像 Image | 容器的只读模板,包含运行应用所需的一切 | 集装箱的"设计图纸" |
| 容器 Container | 镜像运行起来的实例 | 按图纸造出来的"实体箱子" |
| 镜像仓库 Registry | 存放和分发镜像的地方 | 图纸的"图书馆" |
默认的官方仓库是 Docker Hub(hub.docker.com)。平时我们用的 nginx、mysql 镜像都来自这里。
镜像地址的"四段式"结构
很多人被 docker pull docker.io/library/nginx:latest 这种长串吓到,其实它是一个标准结构:
[registry]/[namespace]/[repository]:[tag]
↓ ↓ ↓ ↓
docker.io / library / nginx : latest
仓库地址 命名空间 镜像名 标签版本
- registry:仓库地址。
docker.io是官方默认,可省略。 - namespace:命名空间,类似 GitHub 的用户名。
library是官方镜像的命名空间,可省略。 - repository:镜像名,例如
nginx、mysql。 - tag:版本标签。
latest表示最新版;也可以指定具体版本如1.28.0。不写 tag 默认就是latest。
所以下面三条命令拉的是同一个镜像:
docker pull docker.io/library/nginx:latest # 全写
docker pull library/nginx # 省略 registry
docker pull nginx # 连 namespace 一起省略(最常用)
二、镜像操作:拉取、查看、删除
1. 拉取镜像 docker pull
# 拉取最新版 nginx
docker pull nginx
# 指定架构拉取(交叉编译 / 跨平台调试常用)
docker pull --platform=linux/amd64 nginx
docker pull --platform=linux/arm64 nginx
--platform 用于指定目标 CPU 架构。Docker 一般会自动匹配宿主机架构,但在 Apple Silicon(arm64)机器上跑 amd64 镜像、或反过来时,显式指定能避免运行时崩溃。常用取值:linux/amd64、linux/arm64、linux/arm/v7、linux/arm/v6。
2. 查看本地镜像 docker images
sudo docker images
# 输出:
# REPOSITORY TAG IMAGE ID CREATED SIZE
# nginx latest 1e5f3dc5b981 2 weeks ago 192MB
IMAGE ID 是镜像的唯一指纹,删除镜像时既可以用名字(nginx:latest)也可以用这个短 ID。
3. 删除镜像 docker rmi
rmi = rm(remove)+ i(images 缩写),即"删除镜像"。
sudo docker rmi 1e5f3dc5b981a # 用 IMAGE ID 删除
sudo docker rmi nginx:latest # 用 名字:tag 删除
如果镜像正在被某个容器使用,删除会失败,需要先删容器(见第三节)。
三、容器生命周期:run 的 N 种姿势
docker run 是最高频的命令,它内部其实做了两件事:先 create 创建容器,再 start 启动。理解了这一点,后面 create / start / stop 就好懂了。
3.1 最常用的启动参数
| 参数 | 全称 / 含义 | 说明 |
|---|---|---|
-d | detached | 后台运行(分离模式),不阻塞当前终端 |
-p | port | 端口映射,格式 主机端口:容器端口 |
-v | volume | 挂载数据卷,格式 宿主机目录:容器目录 |
--name | name | 给容器起名(必须唯一,不能重复) |
-it | interactive + tty | 进入容器交互终端 |
--rm | remove | 容器停止后自动删除(适合临时调试) |
--restart | restart policy | 容器退出后的重启策略 |
逐个看例子:
# 后台启动 nginx(镜像不存在会自动先 pull)
docker run -d nginx
# 端口映射:把宿主机的 80 端口转发到容器的 80 端口
docker run -d -p 80:80 nginx
# 命名容器(名字唯一,重复会报错)
docker run -d --name my_nginx nginx
# 挂载卷:把宿主机 /data 目录挂进容器的 /app/data,实现数据持久化
docker run -d -v /data:/app/data nginx
# 交互式进入一个临时 alpine 容器,退出即销毁
docker run -it --rm alpine
# 容器挂掉自动重启
docker run -d --restart always nginx
3.2 重启策略对比
--restart 的取值是面试和运维都常考的点:
| 策略 | 行为 | 适用场景 |
|---|---|---|
no | 默认,不自动重启 | 临时任务 |
always | 无论退出原因都重启,包括 Docker 守护进程重启后 | 核心常驻服务 |
unless-stopped | 除非手动 docker stop,否则一直重启(守护进程重启也会拉起) | 大多数生产服务 |
on-failure[:max] | 仅当非 0 退出码(异常崩溃)时重启,可限制最大次数 | 批处理任务 |
3.3 容器的"生老病死"命令
# 只创建容器,不立即运行(适合先配好网络/卷再启动)
docker create -p 27017:27017 mongo
# 启动一个已创建或已停止的容器
docker start c1e13n1ba1
# 停止运行中的容器
docker stop c1e13n1ba1
# 查看正在运行的容器
docker ps
# 查看所有容器(含已停止)
docker ps -a
四、调试三板斧:logs / inspect / exec
容器跑起来了但行为不对?这三招必会。
1. 看日志 docker logs
docker logs x29afbe41ce1 # 查看历史日志
docker logs -f x29afbe41ce1 # -f = follow,实时滚动查看(类似 tail -f)
2. 看配置 docker inspect
docker inspect c1e13n1ba1
返回容器的完整 JSON 配置(挂载、网络、环境变量、重启策略等),排查"为什么端口没映射上""卷挂载路径对不对"时非常好用。通常配合 grep 或 jq 提取字段。
3. 进容器 docker exec
exec = 在已运行的容器里执行命令,是调试利器:
# 不进交互,直接看容器内进程
docker exec x29afbe41ce1 ps -ef
# 进交互终端,查看文件系统 / 调试(alpine 用 /bin/sh,ubuntu 用 /bin/bash)
docker exec -it x29afbe41ce1 /bin/sh
五、数据与网络:端口映射与挂载
这部分在第三节已经用参数带过,单独拎出来强调两个面试常问点:
端口映射 -p:容器有自己独立的网络命名空间,外界默认访问不到。-p 8080:80 意思是"宿主机的 8080 端口流量,转发给容器的 80 端口"。冒号前是宿主机,后是容器。
数据卷 -v:容器本身是临时的,删掉容器里面的数据就没了。-v /宿主机目录:/容器目录 把宿主机目录"挂"进容器,容器内写入的数据实际落在宿主机硬盘上,实现数据持久化。数据库类容器(MySQL、MongoDB)必挂卷。
六、用 Dockerfile 把应用打包成镜像
前面都是用现成镜像,要让自己的项目也能 docker run,需要写一个 Dockerfile(D 必须大写)来描述"怎么造这个镜像":
# 1. 基础镜像(FROM 必须放第一行)
FROM python:3.11-slim
# 2. 设置工作目录
WORKDIR /app
# 3. 复制文件:第一个点=宿主机当前目录,第二个点=镜像内的工作目录(/app)
COPY . .
# 4. 构建时执行的命令(装依赖)
RUN pip install -r requirements.txt
# 5. 声明服务端口(仅文档作用,不自动映射)
EXPOSE 8000
# 6. 容器启动时默认执行的命令
CMD ["python", "main.py"]
关键指令对比:
| 指令 | 作用 | 注意点 |
|---|---|---|
FROM | 指定基础镜像 | 必须第一行 |
WORKDIR | 设置后续命令的工作目录 | 不存在会自动创建 |
COPY . . | 复制文件进镜像 | 第一个点是构建上下文目录 |
RUN | 构建阶段执行的命令 | 每一条会生成一个镜像层 |
EXPOSE | 声明端口 | 只是"说明",不自动 -p 映射 |
CMD | 容器启动默认命令 | 可被 docker run 后面的命令覆盖 |
ENTRYPOINT | 容器启动入口 | 优先级比 CMD 高,不易被覆盖(参数会追加在其后) |
CMDvsENTRYPOINT的区别一句话:CMD 是"默认命令,可被覆盖",ENTRYPOINT 是"固定入口,难被覆盖"。例如ENTRYPOINT ["python"]配CMD ["main.py"],运行时你写docker run img test.py实际执行的是python test.py。
构建命令:
docker build -t my-app:1.0 .
# -t 给镜像打 tag,最后的 . 表示构建上下文为当前目录
七、多容器协作:network 与 compose
当应用需要"前端 + 后端 + 数据库"一起跑,就需要网络互联和编排。
1. 自定义网络 docker network
# 创建子网
docker network create network1
# 启动时加入网络(同一网络内的容器可用容器名互相访问)
docker run -d --name db --network network1 mongo
docker run -d --name web --network network1 my-app
# web 容器里直接 `ping db` 就能连通,无需暴露端口给宿主机
2. 容器编排 docker compose
compose 用一份 docker-compose.yml 管理多个容器,告别手写一长串 docker run。注意:新版是 docker compose(空格),旧版是 docker-compose(横线)。
docker compose up # 按 yml 创建并启动所有容器(-d 可后台)
docker compose down # 停止并删除所有容器、网络
docker compose stop # 仅停止,不删除
docker compose start # 启动已停止的
docker-compose.yml 示例:
version: "3.8"
services:
web:
build: .
ports:
- "8000:8000"
depends_on:
- db
db:
image: mongo:latest
volumes:
- ./data:/data/db
compose 子命令与单容器命令对照:
| 目标 | 单容器命令 | compose 命令 |
|---|---|---|
| 启动 | docker run -d | docker compose up -d |
| 停止 | docker stop | docker compose stop |
| 启动已停 | docker start | docker compose start |
| 删除 | docker rm + docker rmi | docker compose down |
八、可视化图解:命令全景与容器生命周期
下面这张图把本文所有命令按"镜像 / 容器生命周期 / 调试 / 网络编排"归类,并标出容器的状态流转。
(图见下方 Visualizer 图解)
九、完整流程串联:从零跑起一个 nginx
用一个最小可运行示例走通"输入 → 中间过程 → 输出"全链路:
# ① 拉取镜像(省略了 docker.io/library/ 前缀)
docker pull nginx
# ② 后台启动,命名 + 端口映射 + 自动重启
docker run -d --name my_nginx -p 8080:80 --restart unless-stopped nginx
# ③ 确认容器在跑
docker ps
# => 看到 my_nginx 状态 Up,8080->80
# ④ 浏览器访问 http://localhost:8080 看到 nginx 欢迎页
# ⑤ 想进容器改配置?
docker exec -it my_nginx /bin/sh
# ⑥ 不想跑了?
docker stop my_nginx # 停止
docker rm my_nginx # 删除(--restart 策略失效)
docker rmi nginx # 镜像也不要了就删
ASCII 流程:
docker pull nginx
│
▼
docker run -d --name my_nginx -p 8080:80 nginx
│
▼
┌─────────────┐ docker ps ┌─────────────┐
│ Container │ ───────────────► │ Status: Up │
│ my_nginx │ │ 8080 -> 80 │
└─────────────┘ └─────────────┘
│ │
│ docker exec -it my_nginx /bin/sh │ 浏览器访问
▼ ▼
进入容器调试 看到 nginx 欢迎页
│
▼
docker stop → docker rm → docker rmi (清理)
十、记忆口诀
一拉二查三删除:
pull拉镜像,images查,rmi删镜像。 run 起容器,参数记心上:-d后台、-p映射、-v挂载、--name起名。 stop 停、start 起,create 只建不跑:生命周期三兄弟。 调试三板斧:logs看日志、inspect看配置、exec -it进终端。 打包用 Dockerfile,编排用 compose:FROM 打底 CMD 收,多容器靠 yml 管。