Docker 里最容易混的 5 组命令
容器的状态其实只有三个:
up(运行中)、exited(已停止)、deleted(已删除)。下面这 5 组命令名字很像、作用很接近,但用错了一个就是
Conflict,或者一个找不回来的容器。
总览
| 组 | 容易混的 | 一句话区别 |
|---|---|---|
| 一 | run / start |
run 是造一台新的,start 是把旧的发动起来 |
| 二 | stop / kill / pause |
stop 优雅停、kill 强制杀、pause 只冻结不结束 |
| 三 | exec / attach |
exec 新开一个终端,attach 接管容器主进程 |
| 四 | rm / rm -f |
运行中的容器删不掉,要么先 stop,要么加 -f |
| 五 | save/load / export/import |
前者搬镜像 ,后者搬容器快照 |
一、docker run vs docker start
一句话区别 :docker run = 创建 + 启动一个新容器;docker start = 启动一个已经存在的、处于停止状态的容器。
run 的本质:首次运行时,它会检测本地是否有镜像 ------ 有的话直接 start,没有的话先 pull,再 start。
text
docker run = 创建 + 启动一个新容器
如果 --name 已存在,报:
Conflict. The container name "/xxx" is already in use
docker start = 启动一个已经存在的、处于停止状态的容器
一句话 :run 是"造一台新的",start 是"把旧的发动起来"。
坑 :第二次启动用
run就会撞上Conflict. The container name "/xxx" is already in use。这不是 Docker 出问题了,是你在让 Docker 造一个重名的容器。
run 的常用参数:
| 参数 | 含义 |
|---|---|
--name |
给运行的容器命名 |
-d |
后台运行(detach) |
-it |
交互式运行(常常配合 bash 进容器) |
-p |
小写 p,指定端口映射(宿主机端口:容器端口) |
-P |
大写 P,随机分配宿主机端口 |
-e |
设置环境变量 |
-v |
挂载数据卷(宿主机目录:容器目录) |
一个把参数都用上的例子(跑 MySQL):
bash
docker run -d \
--name mysql-db \
-e MYSQL_ROOT_PASSWORD="Qianshan@123" \
-e MYSQL_DATABASE="appdb" \
-p 3306:3306 \
mysql:8.0.44
它起来之后怎么确认"真的能用",见文末的实操一节。
顺带记住启停四件套:
bash
docker stop 容器名/容器ID # 优雅停止(默认发 SIGTERM,10 秒超时后 SIGKILL)
docker start 容器名/容器ID # 第二次启动就用 start,不要再用 run(会报 Conflict)
docker restart 容器名/容器ID # = stop + start
docker kill 容器名/容器ID # 直接 SIGKILL(强制,不优雅)
二、docker stop vs docker kill vs docker pause
三条都跟"让它停下来"有关,但停的方式完全不同。
| 命令 | 干了什么 | 容器状态变成 | 怎么恢复 |
|---|---|---|---|
stop |
发 SIGTERM,10 秒没退再发 SIGKILL | Exited |
start 重新启动 |
kill |
直接发 SIGKILL,不给反应时间 | Exited |
start 重新启动 |
pause |
用 cgroup freezer 冻结进程(不释放资源) | Paused |
unpause 继续跑 |
bash
docker pause 容器名/ID # 暂停(进程被冻结,还在内存里)
docker unpause 容器名/ID # 恢复
怎么选:
- 正常维护 →
stop(给进程机会把手上的事做完) - 进程卡死不听话 →
kill - 只是临时不想让它占 CPU →
pause
面试会问 :「怎么临时冻结一个容器」------答案是
pause,不是stop。两者的区别在于要不要释放资源 :pause是冻结,内存还占着;stop是结束进程,容器变Exited。
三、docker exec vs docker attach
docker exec |
docker attach |
|
|---|---|---|
| 是否新进程 | 是(在容器里新开一个进程) | 否(连到主进程的 stdin/stdout) |
| 退出方式 | exit 只退出这个进程,容器继续运行 |
按 Ctrl+C 会把信号发给主进程,容器可能直接退出 |
| 推荐度 | 推荐,排障首选 | 少用,容易误杀容器 |
bash
docker exec -it 容器ID bash # 新起一个进程(新终端),不会影响容器主进程
docker attach 容器ID # 直接接到容器主进程的终端上
# 不启动新的终端,如果按 ctrl+c,容器会自动退出
一句话记忆 :要进容器就用
docker exec -it,别用attach------ attach 按 Ctrl+C 会把容器弄停。
四、docker rm vs docker rm -f
bash
docker rm 容器名/ID # 删除已停止的容器
docker rm -f 容器名/ID # 强制删除(不管它有没有在运行)
坑 :正在运行的容器直接
docker rm会报错:
text
Error response from daemon: You cannot remove a running container ...
Stop the container before attempting removal or force remove
要么先 docker stop,要么加 -f。
顺带一个批量清理的写法:
bash
docker rm -f $(docker ps -qa) # 强制删除所有容器
(-qa = 只输出所有容器的 ID,脚本里批量操作用)
五、docker save/load vs docker export/import
这一组最容易搞混,因为一个搬镜像,一个搬容器。
bash
# 导出(在能上网的机器上)
docker save -o nginx.tar nginx:1.24
ls -lh nginx.tar # 检验:生成一个 tar 文件
# 导入(在内网机器上)
docker load -i nginx.tar
docker images | grep nginx # 检验:能看到 nginx:1.24
bash
# 新写法(等价)
docker image save -o nginx.tar nginx:1.24
docker image load -i nginx.tar
坑(关键区别) :
docker save保存的是镜像 (含 tag 和所有层),docker load之后 tag 还在; 而docker export导出的是容器的文件系统快照 ,docker import回来会丢掉 tag 和历史层。别混。
怎么选 :内网没有外网要搬镜像 → save / load;想把一个容器的当前文件系统带走 → export / import(但要接受 tag 和层历史丢失)。
附:从跑起一个 MySQL,到"真的能用"
容器起来了不等于服务能用。以第一节那个 MySQL 为例,完整验证是 5 步:
bash
docker ps # 1) 容器在运行
docker logs mysql-db # 2) 看启动日志(找 "ready for connections")
docker port mysql-db # 3) 看端口映射(0.0.0.0:3306 -> 3306/tcp)
ss -lntp | grep 3306 # 4) 宿主机确实在监听 3306
mysql -uroot -p'Qianshan@123' -h 127.0.0.1 -P 3306 -e "show databases;"
# 5) 真正连进去(能看到 appdb 才算成功)
第 5 步是"闭环"的关键:光看到容器 Up 不算成功,能连上业务端口才算。
这也是面试时"你怎么确认服务真的起来了"的标准答法。