Java 项目部署之 Docker工具快速入门: 容器命令练习:创建、改文件、重启到删除的完整闭环

概述

容器命令背下来不难,难的是第一次真动手时每一步都卡壳:端口到底谁映射谁、进了容器发现没装 vi、改完文件浏览器还是老页面、再想重跑一次又提示容器名已存在。下面把两道练习题按"命令 + 预期输出 + 验证点"的节奏走一遍,照着敲就能复现。

纲要

  • 关键概念
    • 容器四种状态:运行、暂停、停止、删除,以及状态之间怎么迁移
    • 端口映射的方向:-p 宿主机端口:容器端口
    • docker exec 与 docker attach 的区别,-it 参数到底给了什么
    • Redis 的 RDB 与 AOF 两种持久化,--appendonly yes 是何意
  • 核心流程
    • 练习一:创建带 AOF 持久化的 Redis 容器 → exec 进入 → 写入数据 → 停止重启后数据仍在
    • 练习二:创建 Nginx 容器 → 进入改 index.html → 浏览器 / curl 验证 → 停止 → 重启 → 再验证 → 删除
    • 容器内没有 vi 时,用 sed -i 或 echo > 改文件
  • 涉及命令与配置
    • docker run --name <名> -p <宿主>:<容器> -d <镜像>
    • docker exec -it <名> bash / docker exec -it <名> redis-cli
    • docker ps / docker ps -a / docker logs / docker stop / docker start / docker rm
    • Nginx 静态目录 /usr/share/nginx/html(讲义 day03-Docker/2.2.3 场景)

两道练习题要你做什么

两道题对应两个真实场景,命令集合基本一致,差别在于"改文件"和"写数据":

题目 要求 主要考察的命令
练习一(视频演示) 创建并运行一个 Redis 容器,支持数据持久化,然后进入容器写入一个 key run -p --appendonly exec redis-cli
练习二(讲义 2.2.3) 创建 Nginx 容器映射 80 端口,进入容器改 index.html,再从外部验证改动 run -p exec sed stop start rm

先把练习一做掉,它验证的是"数据活不活得过重启";练习二验证的是"容器内文件能不能改、改完外面看不看得到"。

容器状态与命令速查

容器的状态迁移是整套练习的骨架,先记住这张图再动手:
#mermaid-svg-a6LORel1dZjewXpR{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-a6LORel1dZjewXpR .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-a6LORel1dZjewXpR .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-a6LORel1dZjewXpR .error-icon{fill:#552222;}#mermaid-svg-a6LORel1dZjewXpR .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-a6LORel1dZjewXpR .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-a6LORel1dZjewXpR .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-a6LORel1dZjewXpR .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-a6LORel1dZjewXpR .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-a6LORel1dZjewXpR .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-a6LORel1dZjewXpR .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-a6LORel1dZjewXpR .marker{fill:#333333;stroke:#333333;}#mermaid-svg-a6LORel1dZjewXpR .marker.cross{stroke:#333333;}#mermaid-svg-a6LORel1dZjewXpR svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-a6LORel1dZjewXpR p{margin:0;}#mermaid-svg-a6LORel1dZjewXpR defs #statediagram-barbEnd{fill:#333333;stroke:#333333;}#mermaid-svg-a6LORel1dZjewXpR g.stateGroup text{fill:#9370DB;stroke:none;font-size:10px;}#mermaid-svg-a6LORel1dZjewXpR g.stateGroup text{fill:#333;stroke:none;font-size:10px;}#mermaid-svg-a6LORel1dZjewXpR g.stateGroup .state-title{font-weight:bolder;fill:#131300;}#mermaid-svg-a6LORel1dZjewXpR g.stateGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-a6LORel1dZjewXpR g.stateGroup line{stroke:#333333;stroke-width:1;}#mermaid-svg-a6LORel1dZjewXpR .transition{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-a6LORel1dZjewXpR .stateGroup .composit{fill:white;border-bottom:1px;}#mermaid-svg-a6LORel1dZjewXpR .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px;}#mermaid-svg-a6LORel1dZjewXpR .state-note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-a6LORel1dZjewXpR .state-note text{fill:black;stroke:none;font-size:10px;}#mermaid-svg-a6LORel1dZjewXpR .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-a6LORel1dZjewXpR .edgeLabel .label rect{fill:#ECECFF;opacity:0.5;}#mermaid-svg-a6LORel1dZjewXpR .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-a6LORel1dZjewXpR .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-a6LORel1dZjewXpR .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-a6LORel1dZjewXpR .edgeLabel .label text{fill:#333;}#mermaid-svg-a6LORel1dZjewXpR .label div .edgeLabel{color:#333;}#mermaid-svg-a6LORel1dZjewXpR .stateLabel text{fill:#131300;font-size:10px;font-weight:bold;}#mermaid-svg-a6LORel1dZjewXpR .node circle.state-start{fill:#333333;stroke:#333333;}#mermaid-svg-a6LORel1dZjewXpR .node .fork-join{fill:#333333;stroke:#333333;}#mermaid-svg-a6LORel1dZjewXpR .node circle.state-end{fill:#9370DB;stroke:white;stroke-width:1.5;}#mermaid-svg-a6LORel1dZjewXpR .end-state-inner{fill:white;stroke-width:1.5;}#mermaid-svg-a6LORel1dZjewXpR .node rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-a6LORel1dZjewXpR .node polygon{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-a6LORel1dZjewXpR #statediagram-barbEnd{fill:#333333;}#mermaid-svg-a6LORel1dZjewXpR .statediagram-cluster rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-a6LORel1dZjewXpR .cluster-label,#mermaid-svg-a6LORel1dZjewXpR .nodeLabel{color:#131300;}#mermaid-svg-a6LORel1dZjewXpR .statediagram-cluster rect.outer{rx:5px;ry:5px;}#mermaid-svg-a6LORel1dZjewXpR .statediagram-state .divider{stroke:#9370DB;}#mermaid-svg-a6LORel1dZjewXpR .statediagram-state .title-state{rx:5px;ry:5px;}#mermaid-svg-a6LORel1dZjewXpR .statediagram-cluster.statediagram-cluster .inner{fill:white;}#mermaid-svg-a6LORel1dZjewXpR .statediagram-cluster.statediagram-cluster-alt .inner{fill:#f0f0f0;}#mermaid-svg-a6LORel1dZjewXpR .statediagram-cluster .inner{rx:0;ry:0;}#mermaid-svg-a6LORel1dZjewXpR .statediagram-state rect.basic{rx:5px;ry:5px;}#mermaid-svg-a6LORel1dZjewXpR .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#f0f0f0;}#mermaid-svg-a6LORel1dZjewXpR .note-edge{stroke-dasharray:5;}#mermaid-svg-a6LORel1dZjewXpR .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-a6LORel1dZjewXpR .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-a6LORel1dZjewXpR .statediagram-note text{fill:black;}#mermaid-svg-a6LORel1dZjewXpR .statediagram-note .nodeLabel{color:black;}#mermaid-svg-a6LORel1dZjewXpR .statediagram .edgeLabel{color:red;}#mermaid-svg-a6LORel1dZjewXpR #dependencyStart,#mermaid-svg-a6LORel1dZjewXpR #dependencyEnd{fill:#333333;stroke:#333333;stroke-width:1;}#mermaid-svg-a6LORel1dZjewXpR .statediagramTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-a6LORel1dZjewXpR :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} docker run / docker start
docker pause
docker unpause
docker stop
docker start
docker rm
docker rm -f
运行中
暂停
停止

对应的命令职责:

命令 作用 一句话提醒
docker run 创建并启动容器 每次都会新建一个容器
docker ps 看运行中的容器 加 -a 才能看到已停止的
docker stop 停止容器 先发 SIGTERM,10 秒后补 SIGKILL
docker start 启动已停止的容器 不会重建,配置沿用创建时的
docker exec 在运行中的容器里执行命令 容器必须是运行态
docker rm 删除容器 运行中的删不掉,除非加 -f

stop 之后容器还在,只是进程没了、资源回收了;rm 才是真正把它从磁盘上抹掉。这个区别在练习二结尾会体现出来。

练习一:Redis 容器,创建并支持持久化

先看官方给的运行命令

Redis 官方镜像在 Docker Hub 上直接给了带持久化的启动方式,比手搓参数靠谱。关键那段是:

bash 复制代码
docker run --name some-redis -d redis redis-server --appendonly yes

两个细节值得说清:

  • redis 后面跟的 redis-server --appendonly yes 覆盖了镜像默认启动命令,把持久化从 RDB 切成 AOF。
  • --appendonly yes 打开 AOF,Redis 会把每条写命令追加到日志文件,重启后按日志重放恢复数据;镜像默认的 RDB 是定时快照,粒度粗、掉电可能丢最后一次快照之后的数据。

官方那条命令没配端口,宿主机连不上,我们改造一下再加 -p:

bash 复制代码
docker run --name myredis -p 6379:6379 -d redis redis-server --appendonly yes

创建并验证

预期输出是一串容器 ID:

text 复制代码
7f3c1e9a2b0d4c8e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e

立刻确认它真的在跑:

bash 复制代码
docker ps
text 复制代码
CONTAINER ID   IMAGE   COMMAND                  CREATED         STATUS         PORTS                    NAMES
7f3c1e9a2b0d   redis   "docker-entrypoint.s..."   5 seconds ago   Up 5 seconds   0.0.0.0:6379->6379/tcp   myredis

验证点:STATUS 是 Up ...,PORTS 列出现 0.0.0.0:6379->6379/tcp,说明 6379 映射生效。

进入容器写数据

进入容器有两种写法,效果一样,第二种更省事。

先在 bash 里中转:

bash 复制代码
docker exec -it myredis bash

进去之后容器 ID 和宿主机上看到的一致,再启 redis-cli:

bash 复制代码
redis-cli
text 复制代码
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> set number 666
OK
127.0.0.1:6379> get number
"666"

也可以一步到位,exec 直接执行 redis-cli,不必先落到 bash:

bash 复制代码
docker exec -it myredis redis-cli
text 复制代码
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> get number
"666"

exec 的本质是"在容器里执行一条命令",执行 bash 是给你一个 shell,执行 redis-cli 就是直接开会话。退出也分两层:第一次 exit 退出 redis-cli,回到容器 bash;再 exit 才退回宿主机。用第二种写法只需退一次。

持久化到底有没有生效

这是练习一真正的考点。停掉容器再启动,数据还在才是 AOF 起了作用:

bash 复制代码
docker stop myredis
docker start myredis
docker exec -it myredis redis-cli get number
text 复制代码
"666"

stop 只是终止进程,容器的可写层还在,加上 AOF 文件也落在容器里,所以 start 之后数据能恢复。但这不等于数据安全 ------一旦 docker rm 把容器删掉,AOF 文件跟着没了。这个问题留到数据卷那节解决,这里先记住现象。

练习二:Nginx 容器,改页面再验证

这题来自讲义 2.2.3,需求是:进入 Nginx 容器,改掉首页内容,然后从容器外面看到改动。

创建并运行

bash 复制代码
docker run --name mn -p 80:80 -d nginx

-p 80:80 冒号左边是宿主机端口、右边是容器端口。容器默认是隔离环境,不映射的话宿主机 80 端口访问不到里面的 Nginx。映射之后,访问宿主机 80 就等于访问容器 80。

验证:

bash 复制代码
docker ps
text 复制代码
CONTAINER ID   IMAGE   COMMAND                  CREATED         STATUS         PORTS                NAMES
9a1b2c3d4e5f   nginx   "/docker-entrypoint...."   3 seconds ago   Up 3 seconds   0.0.0.0:80->80/tcp   mn

进入容器,定位 index.html

bash 复制代码
docker exec -it mn bash

Nginx 的静态页目录是 /usr/share/nginx/html,进去看看:

bash 复制代码
cd /usr/share/nginx/html
ls
text 复制代码
50x.html  index.html

容器里没有 vi/vim,文件怎么改

这是新手最容易卡住的地方:vi 敲进去提示 command not found。Nginx 镜像为了瘦身没装编辑器,别急着 apt install vim ,那会往容器可写层里塞一堆东西,也不符合"容器即用即弃"的思路。直接用 sed 或 echo 就地改。

用 sed -i 替换文件里的字符串:

bash 复制代码
sed -i -e 's#Welcome to nginx#欢迎使用 Docker#g' -e 's#<head>#<head><meta charset="utf-8">#g' index.html

第二个 -e 是顺手补一行 UTF-8 声明,不改的话中文在浏览器里会乱码,这点很容易被忽略。

也可以整份覆盖,echo + 重定向最直接:

bash 复制代码
echo '<h1>欢迎使用 Docker</h1>' > /usr/share/nginx/html/index.html

注意别写成 >>,那是追加;用 > 才是覆盖。改完先别退出,直接在容器里确认文件内容变了:

bash 复制代码
cat /usr/share/nginx/html/index.html
text 复制代码
<h1>欢迎使用 Docker</h1>

退出后从外部验证

bash 复制代码
exit

在宿主机上不用打开浏览器,一条 curl 就能验证:

bash 复制代码
curl http://localhost
text 复制代码
<h1>欢迎使用 Docker</h1>

浏览器访问 http://<虚拟机IP>(例如 http://192.168.150.101)应看到同样的内容。如果浏览器还是旧页面,先强制刷新(Ctrl+F5),多数时候是缓存。

停止、重启、再验证

bash 复制代码
docker stop mn
docker start mn
curl http://localhost
text 复制代码
<h1>欢迎使用 Docker</h1>

改动还在,因为 start 复用同一个容器的可写层,不会重建。

删除容器

bash 复制代码
docker rm mn

如果提示容器还在运行:

text 复制代码
Error response from daemon: You cannot remove a running container 9a1b2c3d4e5f. Stop the container before attempting removal or force remove

先 docker stop mn 再 rm,或者直接 docker rm -f mn 强制删。

删完再 curl 一次,会发现连接失败------容器没了,端口映射自然也没了。最扎心的是:刚才改的 index.html 一起没了。 这就是下一节数据卷要解决的问题。

步骤 / 命令 / 验证点总表

把练习二的每一步压进一张表,方便对照着敲:

步骤 命令 验证点
创建并运行 docker run --name mn -p 80:80 -d nginx 输出容器 ID
确认运行 docker ps STATUS 为 Up,PORTS 含 0.0.0.0:80->80/tcp
进入容器 docker exec -it mn bash 提示符变成容器内路径
定位文件 cd /usr/share/nginx/html && ls 看到 index.html
修改文件 sed -i ... index.html cat index.html 内容已变
退出 exit 提示符回到宿主机
外部验证 curl http://localhost 返回新内容
停止 docker stop mn docker ps 里看不到它
重启 docker start mn docker ps 里重新出现
再次验证 curl http://localhost 仍是新内容
删除 docker rm mn docker ps -a 里消失,curl 失败

整个练习的操作序列

容器 mn (nginx) Docker 守护进程 宿主机终端 容器 mn (nginx) Docker 守护进程 宿主机终端 #mermaid-svg-KGFGLu4u4FiBh8AW{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-KGFGLu4u4FiBh8AW .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-KGFGLu4u4FiBh8AW .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-KGFGLu4u4FiBh8AW .error-icon{fill:#552222;}#mermaid-svg-KGFGLu4u4FiBh8AW .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-KGFGLu4u4FiBh8AW .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-KGFGLu4u4FiBh8AW .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-KGFGLu4u4FiBh8AW .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-KGFGLu4u4FiBh8AW .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-KGFGLu4u4FiBh8AW .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-KGFGLu4u4FiBh8AW .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-KGFGLu4u4FiBh8AW .marker{fill:#333333;stroke:#333333;}#mermaid-svg-KGFGLu4u4FiBh8AW .marker.cross{stroke:#333333;}#mermaid-svg-KGFGLu4u4FiBh8AW svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-KGFGLu4u4FiBh8AW p{margin:0;}#mermaid-svg-KGFGLu4u4FiBh8AW .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-KGFGLu4u4FiBh8AW text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-KGFGLu4u4FiBh8AW .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-KGFGLu4u4FiBh8AW .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-KGFGLu4u4FiBh8AW .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-KGFGLu4u4FiBh8AW .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-KGFGLu4u4FiBh8AW #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-KGFGLu4u4FiBh8AW .sequenceNumber{fill:white;}#mermaid-svg-KGFGLu4u4FiBh8AW #sequencenumber{fill:#333;}#mermaid-svg-KGFGLu4u4FiBh8AW #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-KGFGLu4u4FiBh8AW .messageText{fill:#333;stroke:none;}#mermaid-svg-KGFGLu4u4FiBh8AW .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-KGFGLu4u4FiBh8AW .labelText,#mermaid-svg-KGFGLu4u4FiBh8AW .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-KGFGLu4u4FiBh8AW .loopText,#mermaid-svg-KGFGLu4u4FiBh8AW .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-KGFGLu4u4FiBh8AW .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-KGFGLu4u4FiBh8AW .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-KGFGLu4u4FiBh8AW .noteText,#mermaid-svg-KGFGLu4u4FiBh8AW .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-KGFGLu4u4FiBh8AW .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-KGFGLu4u4FiBh8AW .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-KGFGLu4u4FiBh8AW .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-KGFGLu4u4FiBh8AW .actorPopupMenu{position:absolute;}#mermaid-svg-KGFGLu4u4FiBh8AW .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-KGFGLu4u4FiBh8AW .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-KGFGLu4u4FiBh8AW .actor-man circle,#mermaid-svg-KGFGLu4u4FiBh8AW line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-KGFGLu4u4FiBh8AW :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} docker run --name mn -p 80:80 -d nginx 创建容器并启动 nginx 进程 返回容器 ID docker exec -it mn bash 在容器内启动 bash 分配交互式终端 sed -i 修改 index.html 文件写入容器可写层 exit 退出 curl http://localhost (经 80 端口映射) 返回修改后的页面 docker stop mn 终止进程, 保留可写层 docker start mn 重新启动 nginx curl http://localhost 内容依然是修改后的 docker rm mn 删除容器及可写层(改动丢失)

常见失败与排查

这几类报错几乎每个初学者都会遇到一次:

现象 / 报错 原因 处理
Conflict. The container name "/mn" is already in use 上一次练习的容器没删 docker rm -f mn 后重来,或换 --name
bind: address already in use(80 端口) 宿主机 80 被别的进程占了(如已有 Nginx) 换端口 -p 8080:80,访问改 8080
Container xxx is not running 对已停止的容器执行 exec 先 docker start xxx
executable file not found in $PATH 镜像里没有 bash(alpine 系用 sh) 换成 docker exec -it mn sh
改完页面没变化 浏览器缓存 / 改错文件路径 Ctrl+F5 强刷,或 cat 确认文件内容
容器起来就退出 镜像的默认命令不是长驻进程,或忘了 -d 之外的启动命令 docker ps -a 看 STATUS 是否 Exited
You cannot remove a running container 直接删运行中的容器 先 stop 或 rm -f

补充一个小技巧:排查容器为什么起不来,先看日志而不是瞎猜。

bash 复制代码
docker logs mn

加 -f 可以持续跟踪输出,跟 tail -f 一个感觉。

改过的文件去哪了

上一节反复强调的一件事:容器内的改动写在可写层 里,跟容器同生共死。stop / start 不动它,rm 把它一起删。
#mermaid-svg-Bu52XfuHUUAsNKcB{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Bu52XfuHUUAsNKcB .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Bu52XfuHUUAsNKcB .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Bu52XfuHUUAsNKcB .error-icon{fill:#552222;}#mermaid-svg-Bu52XfuHUUAsNKcB .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Bu52XfuHUUAsNKcB .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Bu52XfuHUUAsNKcB .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Bu52XfuHUUAsNKcB .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Bu52XfuHUUAsNKcB .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Bu52XfuHUUAsNKcB .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Bu52XfuHUUAsNKcB .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Bu52XfuHUUAsNKcB .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Bu52XfuHUUAsNKcB .marker.cross{stroke:#333333;}#mermaid-svg-Bu52XfuHUUAsNKcB svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Bu52XfuHUUAsNKcB p{margin:0;}#mermaid-svg-Bu52XfuHUUAsNKcB .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Bu52XfuHUUAsNKcB .cluster-label text{fill:#333;}#mermaid-svg-Bu52XfuHUUAsNKcB .cluster-label span{color:#333;}#mermaid-svg-Bu52XfuHUUAsNKcB .cluster-label span p{background-color:transparent;}#mermaid-svg-Bu52XfuHUUAsNKcB .label text,#mermaid-svg-Bu52XfuHUUAsNKcB span{fill:#333;color:#333;}#mermaid-svg-Bu52XfuHUUAsNKcB .node rect,#mermaid-svg-Bu52XfuHUUAsNKcB .node circle,#mermaid-svg-Bu52XfuHUUAsNKcB .node ellipse,#mermaid-svg-Bu52XfuHUUAsNKcB .node polygon,#mermaid-svg-Bu52XfuHUUAsNKcB .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Bu52XfuHUUAsNKcB .rough-node .label text,#mermaid-svg-Bu52XfuHUUAsNKcB .node .label text,#mermaid-svg-Bu52XfuHUUAsNKcB .image-shape .label,#mermaid-svg-Bu52XfuHUUAsNKcB .icon-shape .label{text-anchor:middle;}#mermaid-svg-Bu52XfuHUUAsNKcB .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Bu52XfuHUUAsNKcB .rough-node .label,#mermaid-svg-Bu52XfuHUUAsNKcB .node .label,#mermaid-svg-Bu52XfuHUUAsNKcB .image-shape .label,#mermaid-svg-Bu52XfuHUUAsNKcB .icon-shape .label{text-align:center;}#mermaid-svg-Bu52XfuHUUAsNKcB .node.clickable{cursor:pointer;}#mermaid-svg-Bu52XfuHUUAsNKcB .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Bu52XfuHUUAsNKcB .arrowheadPath{fill:#333333;}#mermaid-svg-Bu52XfuHUUAsNKcB .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Bu52XfuHUUAsNKcB .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Bu52XfuHUUAsNKcB .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Bu52XfuHUUAsNKcB .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Bu52XfuHUUAsNKcB .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Bu52XfuHUUAsNKcB .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Bu52XfuHUUAsNKcB .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Bu52XfuHUUAsNKcB .cluster text{fill:#333;}#mermaid-svg-Bu52XfuHUUAsNKcB .cluster span{color:#333;}#mermaid-svg-Bu52XfuHUUAsNKcB div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Bu52XfuHUUAsNKcB .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Bu52XfuHUUAsNKcB rect.text{fill:none;stroke-width:0;}#mermaid-svg-Bu52XfuHUUAsNKcB .icon-shape,#mermaid-svg-Bu52XfuHUUAsNKcB .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Bu52XfuHUUAsNKcB .icon-shape p,#mermaid-svg-Bu52XfuHUUAsNKcB .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Bu52XfuHUUAsNKcB .icon-shape .label rect,#mermaid-svg-Bu52XfuHUUAsNKcB .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Bu52XfuHUUAsNKcB .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Bu52XfuHUUAsNKcB .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Bu52XfuHUUAsNKcB :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} docker start
docker rm
挂载数据卷
镜像 nginx 只读层
容器可写层
随容器一起销毁
宿主机目录

想让它活下来,得把容器内目录挂到宿主机的数据卷上,让读写落在宿主机------操作宿主机的目录就等于操作容器内的目录,改文件也不用再进容器。数据卷的用法另开一篇讲,这里先把"容器可写层随容器销毁"这个事实记牢,它是理解数据卷的起点。

可整段复制的完整脚本

下面这个脚本把练习二从创建到验证串了一遍,变量都抽出来了,直接贴进终端执行(需要 root 或 docker 权限):

bash 复制代码
#!/usr/bin/env bash
set -e

NAME=mn
HOST_PORT=80
CONT_HTML=/usr/share/nginx/html/index.html

# 1. 清理可能残留的同名容器
docker rm -f "$NAME" 2>/dev/null || true

# 2. 创建并运行 nginx 容器,映射宿主机 80 到容器 80
docker run --name "$NAME" -p "${HOST_PORT}:80" -d nginx

# 3. 确认容器在运行
docker ps --filter "name=${NAME}"

# 4. 进入容器,确认 index.html 存在(-T 不分配终端,适合脚本里管道)
docker exec "$NAME" ls /usr/share/nginx/html

# 5. 就地修改页面内容(容器内无 vi,用 echo 覆盖)
docker exec "$NAME" sh -c "echo '<h1>欢迎使用 Docker</h1>' > ${CONT_HTML}"

# 6. 从宿主机验证改动
sleep 1
curl "http://localhost:${HOST_PORT}"

# 7. 停止 -> 重启 -> 再验证,改动仍在
docker stop "$NAME"
docker start "$NAME"
sleep 1
curl "http://localhost:${HOST_PORT}"

# 8. 删除容器(改过的文件随之消失)
docker rm -f "$NAME"

脚本版和交互版的区别:脚本里 docker exec 不带 -it,因为不需要交互终端;带 -it 在非交互环境下反而会报"the input device is not a TTY"。

命令速览

场景 命令
创建并运行(后台 + 端口映射) docker run --name mn -p 80:80 -d nginx
创建带 AOF 的 Redis docker run --name myredis -p 6379:6379 -d redis redis-server --appendonly yes
查看运行中的容器 docker ps
查看所有容器 docker ps -a
进入容器(shell) docker exec -it mn bash
进入容器(直接执行命令) docker exec -it myredis redis-cli
非交互执行命令 docker exec mn sh -c '<命令>'
查看日志 docker logs -f mn
停止 / 启动 docker stop mn / docker start mn
删除 docker rm mn(运行中加 -f)

官方文档

总结

  • 两道练习覆盖了容器的完整生命周期:run → exec → 改内容 → 外部验证 → stop → start → 再验证 → rm。
  • Nginx 里没有 vi 是常态,sed -i 或 echo > 才是容器里的正常改文件方式,别为了编辑器把镜像撑大。
  • stop / start 复用同一个容器,改动保留;rm 连同可写层一起销毁,改动全没------这个规律解释了为什么需要数据卷。
  • 报错基本集中在四处:容器名冲突、端口被占、对停止态容器 exec、镜像里没有 bash。记住这几条能省很多时间。
  • 验证永远比命令重要:docker ps 看状态、cat 看文件、curl 看结果,每一步都留个可见的检查点。
相关推荐
我是人✓1 小时前
Java面向对象核心特性深度解析
java·前端·算法
quantdash_cc1 小时前
实时行情出现短暂异常时应该怎么处理?从数据校验到故障定位
开发语言·python·数据分析·量化交易·股票数据·quantdash
Wx-bishekaifayuan1 小时前
springboot线上中医药养生平台管理系统36753-计算机课程设计、毕业设计
java·spring boot·后端·spring·课程设计·express·旅游
Wx-bishekaifayuan1 小时前
springboot大学生就业平台47691-计算机课程设计、毕业设计
java·spring boot·后端·elasticsearch·课程设计·express·旅游
zjxtxdy1 小时前
R语言数据统计入门
开发语言·r语言
汤姆yu2 小时前
基于springboot+vue的企业OA办公系统
java·spring boot·后端·oa办公
南归北隐2 小时前
Spring AI Alibaba Graph框架实现RAG私有知识库问答
java·后端·spring·spring ai·java rag实现
小锋学长生活大爆炸2 小时前
【工具】在 Android 手机上运行 Docker
android·docker·智能手机
cleble2 小时前
怎么用用 PowerShell 下载文件
开发语言·php