Docker CLI 基础
这一课你将能做到
- 用
docker run 独立起一个 Redis / Nginx 容器,并让本地 Spring Boot 应用连上容器里的 Redis
- 用
ps、logs、exec、inspect 四个命令完成日常排查:看状态、看日志、进容器、查配置
- 完整走一遍容器生命周期:创建 → 运行 → 停止 → 删除,并说清每一步数据在哪
为什么要学
CLI 是 Docker 的日常操作面。以后你排查「服务挂了」的第一反应应该是:docker ps 看它还在不在 → docker logs 看它死前说了什么 → docker exec 进去看现场。这三个动作比任何面板都快。
本课场景完全贴合 Java 开发日常:本地开发不想装 Redis,用一条命令拉起一个;Spring Boot 的 application.yml 直接连容器。学完本课,容器就从一个「概念」变成你手里的工具。
先建立图像
容器生命周期状态机(记这张图,ps -a 里的每个状态都能对上):
docker run ──→ created ──→ running ──→ exited ──→ removed
↑ │ │
│ │ pause │ stop/kill
└── start ─────┘ ▼
(可 start 复活)
本课高频命令地图:
docker run 创建并启动 docker ps 看运行中容器
docker logs 看日志 docker exec 进容器执行命令
docker stop 优雅停止 docker start 启动已停止容器
docker rm 删除容器 docker inspect 查看全部元数据
跟着做
步骤 1:起第一个容器(后台运行 + 命名)
docker run -d --name dev-redis redis:7
参数拆解:
-d(detach):后台运行,命令立刻返回,不占住终端。
--name dev-redis:给容器起名。不起名 Docker 会随机生成 hungry_turing 这类名字,引用起来很痛苦。
redis:7:镜像名:标签,不写标签默认拉 latest。
步骤 2:确认状态
docker ps # 只显示运行中的容器
docker ps -a # 显示所有容器(含已退出的),排查必备
ps -a 输出里重点看两列:STATUS(Up 2 minutes 在运行;Exited (0) 正常退出;Exited (1) 异常)和 NAMES。
步骤 3:看日志
docker logs dev-redis # 全量日志
docker logs --tail 50 dev-redis # 最后 50 行
docker logs -f dev-redis # 跟踪输出(Ctrl+C 退出,不影响容器)
Java 容器里 System.out / logback 控制台输出都会进到这里。容器一启动就退出的故障,第一反应永远是 。
docker logs 容器名
步骤 4:映射端口,跑一个 Nginx
docker run -d --name dev-web -p 8080:80 nginx:1.27-alpine
-p 宿主机端口:容器端口:把容器的 80 端口转发到宿主机的 8080。浏览器打开 http://localhost:8080,看到 Nginx 欢迎页。
步骤 5:进容器执行命令
docker exec -it dev-redis redis-cli
127.0.0.1:6379> set hello docker
OK
127.0.0.1:6379> get hello
"docker"
127.0.0.1:6379> exit
-it 是 -i(保持标准输入打开)+ -t(分配伪终端)的合写,交互式 shell 必加。进去执行的是 redis-cli,也可以换成 bash 通用排查:docker exec -it dev-redis bash。
步骤 6:让本地 Spring Boot 连上这个 Redis
application.yml 无需改任何代码,容器默认监听 6379 且已通过 -p 暴露(重新起一个带端口映射的):
docker rm -f dev-redis
docker run -d --name dev-redis -p 6379:6379 redis:7 --requirepass "dev123"
注意 --requirepass 直接跟在镜像名后面,是传给 redis-server 的参数。Spring Boot 配置:
spring:
data:
redis:
host: localhost
port: 6379
password: dev123
启动 Spring Boot,写一段 redisTemplate.opsForValue().set("k","v") 验证连通。
步骤 7:停止、启动、删除
docker stop dev-redis # 优雅停止:先发 SIGTERM,10 秒后 SIGKILL
docker ps -a # 状态变为 Exited (0),容器还在
docker start dev-redis # 复活,之前 exec 写入的数据(在可写层内)还在
docker rm dev-redis # 彻底删除;运行中的要加 -f
步骤 8:inspect 查元数据,stats 看资源
docker inspect dev-redis # 全量 JSON:IP、挂载、环境变量、启动参数
docker inspect --format '{{.NetworkSettings.IPAddress}}' dev-redis
docker stats # 实时 CPU/内存,排查内存泄漏利器
讲清楚要点
- 容器随主进程生,随主进程死:容器里 PID 1 是启动命令本身(如
redis-server)。它一退出,容器就 Exited——这是「我的容器为什么秒退」的万能解释。
- stop 与 kill 的区别:
stop 发 SIGTERM 给 PID 1 并等 10 秒(可用 -t 调整),Java 应用借此做优雅停机;kill 直接 SIGKILL,数据可能写一半。
- exec 与 attach 的区别:
exec 是在容器里新开一个进程(退出不影响容器);attach 是接管 PID 1 的标准输入输出(一退出容器可能跟着死),日常一律用 exec。
ps -a 是排查第一现场:Exited (0) 是正常结束;Exited (1)/(137) 要看 logs 与启动参数——137 = 128+9,通常是 OOM 被 kill 或 rm -f。
- --name 是你的管理锚点:后续课程的卷挂载、网络互通、compose 全部依赖容器名/服务名,从现在起养成起名习惯。
你可能会懵的地方
-
docker run ubuntu 为什么立刻退出了?
ubuntu 镜像默认命令是 bash,后台模式没有输入可读,主进程直接结束。要让它活着:docker run -d ubuntu sleep 3600,或交互式 docker run -it ubuntu bash。不是 Docker 坏了,是主进程没事干。
-
容器停止后,我 exec 时改的东西还在吗?
在可写层里,stop/start 不丢;但 rm 之后彻底消失。要让数据跨容器存活,用第 8 课的数据卷。
-
-p 6379:6379 忘了写,容器里的 Redis 还能用吗?
宿主机直连不了,但同一网络里的其他容器照样能连(第 9 课)。开发调试必须加 -p,生产上数据库类容器反而刻意不加,避免暴露。
-
命令参数太多记不住怎么办?
docker run --help 随时查;也可以反过来用工具反推:给现有容器用 docker inspect 看 Config.Cmd 与 HostConfig,就知道它是怎么起的。
-
docker ps 里 STATUS 一会 Up 一会 Restarting 是什么情况?
容器配了重启策略(如 --restart always),主进程崩溃后 Docker 反复拉起又反复崩溃。先 docker logs 找崩溃根因,别只盯着重启。
错法对照
| 错法 | 问题 | 正解 |
|---|
docker run redis 忘加 -d | 终端被占住,Ctrl+C 直接停容器 | 后台服务加 -d |
| 容器起了就退,反复重 run | 不会排查根因 | docker ps -a + docker logs 容器名 |
用 attach 调试交互容器 | 退出导致容器停止 | 用 exec -it |
| 容器名随手不命名 | 只能用随机名或 ID 操作 | --name 规范命名 |
rm 运行中容器不加 -f 报错 | 不知道容器状态限制 | 先 stop 再 rm,或明确用 rm -f |
stop 等了 10 秒被强杀 | Java 优雅停机没跑完 | 排查是否 shell 格式吞了信号(第 5 课) |
卡点急救
- 端口被占用:
docker run -p 6379:6379 报 port is already allocated。Windows 用 netstat -ano | findstr 6379 找到进程(多半是本地装的 Redis),换 -p 6380:6379 或停掉本地服务。
- 容器名冲突:
The container name "/dev-redis" is already in use。docker rm -f dev-redis 删旧的再起。
- logs 没有任何输出:应用日志写文件而非控制台。要么改 logback 输出 console,要么用第 8 课的卷把日志目录挂出来。
- 误删了运行中的容器:如果镜像还在,重新
run 即可;容器可写层数据无法找回——这就是第 8 课要解决的问题。
- exec 报
container not running:先 docker ps -a 看状态,docker start 容器名 拉起来再 exec。
面试常问
-
描述容器的生命周期?
run 创建并启动,经历 created → running;stop/kill 后进入 exited,数据仍在可写层;rm 彻底删除。主进程(PID 1)退出即容器退出,重启策略可控制 exited 后的行为。
-
docker stop 的原理?如何让 Spring Boot 优雅停机?
stop 先向 PID 1 发 SIGTERM,默认等 10 秒再 SIGKILL。Java 侧要保证:Dockerfile 用 exec 格式让 java 收到信号;Spring Boot 开启 graceful shutdown(server.shutdown: graceful),并把 stop 宽限期调到大于最长请求时间。
-
如何排查一个「启动就挂」的容器?
固定三板斧:docker ps -a 看退出码 → docker logs --tail 100 容器名 看报错 → docker inspect 核对环境变量/挂载/启动命令是否符合预期。
-
exec 和 attach 的区别?
exec 在容器内新建进程,退出不影响容器,是标准排查手段;attach 绑定的是主进程的 IO,退出会连带影响容器运行,基本只在看持续输出时用。
自测三问
-d、-it、-p、--name 分别是什么意思?写出一条完整命令把 Redis 7 用密码 dev123 跑在 6379 端口。
- 容器 Exited (137) 最可能的原因是什么?如何确认?
stop 后再 start,容器里之前写入 /tmp 的文件还在吗?rm 后再 run 呢?
小结一句话
CLI 六件套 run / ps / logs / exec / stop / rm 覆盖容器全生命周期——主进程决定容器生死,ps -a 与 logs 是一切排障的起点。
延伸