news 2026/10/21 14:14:54

Docker入门实战笔记:从镜像容器到数据持久化与网络配置

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Docker入门实战笔记:从镜像容器到数据持久化与网络配置

如果你刚开始接触 Docker,多半会有这样一种体验:教程看了不少,名词背了一堆,可真到自己搭环境的时候,总觉得哪里没串起来。我最初折腾那阵就是这状态——服务在本机跑得好好的,换台机器就起不来,版本、依赖、系统库各掉各的链子。后来花了一周时间,把镜像、容器、仓库、数据卷、网络这几块用实际命令一步步跑通,才总算把 Docker 用顺了。

这篇算是我自己的快速入门总结笔记,偏实战、少废话。适合刚接触 Docker、想把核心概念一次搞清楚的同学;也适合有一定基础但知识点比较零散、查过就忘的人,当速查手册翻。我会先把最容易被搞混的底层逻辑讲明白,再带你把第一个容器跑起来,然后是写 Dockerfile、数据持久化、容器网络,最后一节是我实际踩过的一堆坑,建议直接收藏。

1. 为什么镜像和容器总被搞混——先把最核心的模型讲清楚

很多人第一次听 Docker 时,最容易懵的就是镜像和容器到底啥关系。这里先说结论:镜像是只读的模板,容器是模板运行出来的实例。不理解这句话,后面所有命令都是死记硬背,换个场景就不会用了。

1.1 镜像:一份只读的“文件系统快照+启动说明”

镜像不是一台运行的机器,它只是一堆静态文件的组合,外加一些启动元数据。你可以把镜像理解成菜谱加半成品食材:里面已经准备好了运行一个应用所需要的所有东西——基础操作系统文件、运行环境、依赖库、应用代码、默认配置。

Docker 镜像还有一个很重要的特性:分层存储。拉取一个镜像时,你会发现它是一层一层下载的,因为镜像是按层构建的。底层可能是一个精简的 Linux 文件系统,上面叠加了运行环境的安装层,再上面是依赖拷贝层,最上面才是应用代码层。这样做有两个好处:一是多个镜像可以共用底层,节省磁盘空间;二是再次拉取时,如果本地已经有某一层,这一层就不用重新下载。

镜像的名字由“仓库地址/镜像名:标签”组成,标签用于区分版本。比如nginx:1.25和nginx:latest是不同版本。如果不写标签,默认取latest,但生产环境我强烈建议锁定具体版本,后面避坑清单还会提到。

1.2 容器:镜像运行后多出来的那一层“可写层”

当你执行docker run时,Docker 会在镜像之上叠一层可写层,然后根据镜像里记录的启动命令,把某个进程拉起来。这个“可写层 + 进程”的组合,就是容器。

平时改容器里的文件、写日志,都是在可写层里进行的,镜像本身永远不会变。所以容器删了之后,你的修改也跟着没了;但只要镜像还在,随时可以再run一个全新的容器出来。这一点怎么强调都不过分,很多人一开始没想明白,后面才会被数据丢失吓一跳。

容器本质上是宿主机上的一个被隔离的进程,有自己的文件系统、网络空间和进程视图。进程结束了,容器也就停了。比如跑一个hello-world镜像,它打印完一段欢迎语就退出,你会看到容器状态变成Exited,这不是故障,是它该干的事干完了。

1.3 仓库:镜像的“应用商店”

镜像还有一个存放和分发的地方,叫仓库。Docker 官方的公共仓库叫 Docker Hub,里面可以 pull 到大量现成镜像,比如 Nginx、MySQL、Redis。除了公共仓库,还可以自己搭私有仓库,团队内部经常这么干。

平时跟仓库打交道的命令就两个:docker pull是拉镜像到自己机器,docker push是把自己构建的镜像推上去。刚开始学习,只需要会pull就行。

1.4 容器和虚拟机的关键区别

这里补一个无数新手问过的问题:容器和虚拟机到底差在哪?虚拟机是在宿主机之上模拟了一整套硬件,里面跑一个完整的操作系统;容器则是直接共享宿主机的内核,只做进程和文件系统层面的隔离。

对比项虚拟机容器
虚拟化层次硬件级虚拟化操作系统级隔离
启动速度分钟级秒级
镜像体积几个 GB 起步几十 MB 到几百 MB 常见
资源占用高,每台虚拟机都要独立分配低,多个容器共享宿主机内核
隔离强度更强相对弱,但够用

所以容器快、轻,适合打包应用和微服务;虚拟机更重,但隔离更彻底,适合多租户和强安全场景。两者不是替代关系,实际基建里经常混着用。

2. 新机器上从零跑通第一个容器——装好、验证、配置镜像源

理论说太多容易困,直接动手。

2.1 安装与验证:先确认两件事

不同系统的安装方式差别很大,我不展开写,只给个通用思路:Linux 上用系统自带的包管理器安装 Docker 引擎和命令行工具,比如基于 apt 的发行版可以sudo apt install docker.io,基于 yum/dnf 的发行版安装docker-ce相关包;macOS 上直接装桌面版;Windows 上建议先装好 WSL2,再在 WSL 内部安装,这样体验最顺。

装完先别急着跑容器,花十秒钟验证环境:

docker --version docker info

第一条确认命令行工具装好了,第二条确认 Docker 守护进程已经在跑。如果docker info报错,大概率是服务没启动,Linux 上可以试试sudo systemctl restart docker。

2.2 把当前用户加进 docker 组,省去每次 sudo

Linux 用户很快会发现:不带 sudo 跑docker会提示权限不足。原因是 Docker 守护进程默认以 root 运行,它通过/var/run/docker.sock通讯。一个省心的办法是把当前用户加入docker组:

sudo usermod -aG docker $USER newgrp docker

重新登录后再验证一次docker ps。这里必须提醒一句:能操作 docker 的账号,基本等于有了宿主机 root 权限,因为容器可以挂载宿主机目录。所以个人电脑这么做没事,公司的生产服务器上要慎重,不能随便把开发者都加进 docker 组。

2.3 拉镜像前处理一下镜像源问题

官方镜像仓库在部分网络环境下的拉取速度很不理想,甚至经常超时。推荐在开始学习前就配好镜像缓存仓库,Docker 的术语叫 registry mirror,意思是让 Docker 从你指定的镜像仓库地址拉取。

编辑/etc/docker/daemon.json(Windows 桌面版在设置界面填,macOS 同理):

{ "registry-mirrors": ["https://mirror.example.com"] }

mirror.example.com换成你自己实际使用的镜像仓库地址,通常在你使用的云服务商控制台里能找到,一般叫“容器镜像服务”,开通后会给你一个专属地址。改完重启 Docker:

sudo systemctl restart docker

验证是否生效:

docker info | grep -A 2 "Registry Mirrors"

能看到你配置的地址就说明生效了。不要乱填未经验证的地址,宁可先不配,也别让 Docker 连不上仓库。

2.4 跑两个有代表性的容器

第一个容器选最小的:

docker pull hello-world docker run hello-world

会看到一串英文输出,内容是 Docker 在教你“我的运行原理是这样的”。这个镜像只有几 KB,适合验证整个链路能走通。它跑完就退出了,因为容器里的进程本身只负责打印。

第二个跑 Nginx,这才像回事:

docker run -d --name web-demo -p 8080:80 nginx

这里拆开解释一下:

  • -d:后台运行,不加的话终端会被绑住,日志全程刷屏。
  • --name web-demo:给容器起个名字,后续操作就不用记那一长串容器 ID。
  • -p 8080:80:端口映射,宿主机 8080 端口转发到容器内 80 端口。等会curl的就是宿主机的 8080。
  • nginx:镜像名,本地没有会自动 pull。

跑完之后用docker ps看容器状态,再打开浏览器访问http://localhost:8080,看到 Nginx 欢迎页就说明通了你。因为容器里的 Nginx 进程是常驻的,所以容器不会退出,这正好和上面的 hello-world 形成对比。

2.5 高频命令速查表

新手最先要记住的就是下面这些,不用背,用多了自然就熟了:

命令作用常用配合参数
docker ps查看正在运行的容器-a看所有含已退出的
docker pull <镜像>拉取镜像可加:tag指定版本
docker run <镜像>创建并启动容器-d后台、-p映射端口、--name命名
docker stop <容器>停止容器容器名或前几位 ID 都行
docker start <容器>启动已停止的容器重启容器不丢文件系统
docker rm <容器>删除容器删除后容器内可写层丢失
docker rmi <镜像>删除镜像有容器正在使用时会失败
docker exec -it <容器> bash进入容器命令行-it是交互式加伪终端
docker logs -f <容器>跟随查看日志-f类似tail -f
docker system prune清理悬空资源-a会清理所有未使用镜像

docker exec是调试神器,容器里面出了任何问题,先进去手动跑一遍命令看报错,比在外面猜快得多。

3. 自己做一个能访问的镜像——Dockerfile 每条指令为什么这么写

用别人现成的镜像只是开始,真正让 Docker 发挥价值的是把自己的应用打包成镜像。这一步的核心就是写 Dockerfile。

3.1 一个完整可跑的最小示例

我从一个简单的 Python Web 应用开始讲。项目目录里先放两个文件。

requirements.txt:

flask==3.0.0

app.py:

from flask import Flask app = Flask(__name__) @app.route("/") def index(): return "Hello from Docker" if __name__ == "__main__": app.run(host="0.0.0.0", port=8000)

然后写 Dockerfile:

FROM python:3.12-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY app.py . ENV PYTHONUNBUFFERED=1 EXPOSE 8000 CMD ["python", "app.py"]

构建并运行:

docker build -t my-flask-app . docker run -d -p 8000:8000 --name flask-demo my-flask-app curl http://localhost:8000

能输出Hello from Docker说明整个流程通了。注意app.run里写的host="0.0.0.0"不能省,否则 Flask 默认只监听容器内的 localhost,宿主机通过映射端口访问时会失败。

3.2 高频指令背后的逻辑

Dockerfile 看着像一串命令,其实每一行都有讲究。

FROM python:3.12-slim指定基础镜像。选slim版本是为了体积可控,它去掉了很多用不到的编译工具和文档。如果你是新手,优先选官方镜像的slim变体,别一上来就追求基于 Alpine 的极致瘦身,Alpine 用的 C 库和常见发行版不一样,有些依赖装起来会遇到兼容问题。

WORKDIR /app设置工作目录。很多人习惯用RUN cd /app,这是不对的,因为 Dockerfile 里每条RUN都是独立 shell,cd只对当前这条指令生效。WORKDIR是声明式的,后续指令都会在这个目录下执行。

COPY requirements.txt .优先拷贝依赖清单,而不是直接COPY . .。这是利用 Docker 的构建缓存:Docker 构建镜像是一层一层来的,如果某一层没有变化,后续会复用缓存。依赖文件不常变,把它单独放在前面,那么以后改业务代码时,pip install这一层可以直接走缓存,构建会快很多。

RUN pip install --no-cache-dir -r requirements.txt负责装依赖。RUN每执行一条就会生成一个新层,所以能合并的尽量合并,但也不要为了减少层数把一堆命令硬塞进一行,可读性同样重要。

ENV PYTHONUNBUFFERED=1设置环境变量。Python 应用经常遇到日志不输出的问题,后面避坑清单会详细讲。

EXPOSE 8000只是声明“我这个容器里的服务用 8000 端口”,它不会自动映射到宿主机。真正做映射的是docker run -p 8000:8000。很多新手以为写了 EXPOSE 就能访问,错就错在这。

CMD ["python", "app.py"]是容器启动后执行的命令。这里用的 JSON 数组格式,叫 exec 形式,会让python直接作为容器内的 1 号进程;如果写成CMD python app.py这种 shell 形式,实际会多套一层 shell,后面处理停止信号时就容易出问题。

3.3 CMD 和 ENTRYPOINT 怎么选

这两个指令长得像,新手经常混。简单说:CMD是启动命令加默认参数,可以被docker run后面的命令覆盖;ENTRYPOINT是固定入口,不容易被覆盖,配合CMD时,CMD的内容会作为参数传给ENTRYPOINT。

写法效果适合场景
CMD ["python", "app.py"]启动时执行 python app.py,可被 docker run 后的命令替换启动命令就是全部
ENTRYPOINT ["python"]+CMD ["app.py"]启动时执行 python app.py,docker run 后接参数会传给 python需要固定解释器/入口,参数灵活
ENTRYPOINT ["python", "app.py"]完全固定,docker run 后接的命令只是额外参数单入口应用,不太需要覆盖

大部分入门场景用CMD就够了,等你想做“容器启动后可以传不同参数执行不同任务”时再研究ENTRYPOINT。

3.4 别忽略 .dockerignore 和构建上下文

执行docker build时,Docker 会把当前目录作为一个“构建上下文”打包发送给守护进程。如果你的目录里有个几百 MB 的依赖目录,构建过程会又慢又占空间;如果目录里有.env、密钥文件,还可能被一起拷进镜像,造成泄露。

所以每个项目都要建一个.dockerignore:

.git node_modules __pycache__ *.pyc .env dist tmp

这个文件的作用和.gitignore类似,告诉 Docker 在构建上下文中排除哪些路径。养成这个习惯,等于从源头避免了镜像臃肿和密钥泄露两个问题。

4. 容器数据为什么会丢——三种持久化方式的对比与数据库实操

很多人的第一个“事故”是这样的:用 Docker 跑了个带数据库的服务,过两天docker rm把容器删了,数据全没了。原因前面提过——容器删除时,那个可写层跟着一起销毁。所以凡是需要保存的数据,必须放到容器之外。

4.1 先把三种持久化方式分清

Docker 提供了三种挂载数据的方式:bind mount、volume、tmpfs。傻傻分不清的话,看下面这张表就够了。

方式数据存在哪适合场景注意点
bind mount宿主机指定目录开发调试、实时改代码路径写错会报错;受宿主机权限影响
volumeDocker 管理目录数据库、生产数据推荐;docker volume系列命令可管理
tmpfs宿主机内存临时缓存、敏感信息容器停止后数据消失

bind mount 的写法是把宿主机路径和容器路径拼在一起,比如:

docker run -d -p 8000:8000 \ -v $(pwd)/app:/app \ my-flask-app

这样宿主机当前目录下的app会直接挂进容器的/app,改代码不用重新构建镜像,开发阶段非常方便。但它太“直白”,生产环境更推荐 volume。

volume 的玩法和命名磁盘类似,创建一次就能复用:

docker volume create app-data docker run -d \ --mount source=app-data,target=/data \ nginx

--mount比-v的语义更清晰,参数名也更好记。强烈建议新手直接习惯用--mount,不要死记-v冒号语法。

4.2 用 MySQL 容器演示一次完整持久化

我用数据库举例,因为数据库是最需要持久化的场景。先创建 volume,再启动带 volume 的 MySQL:

docker volume create mysql-data docker run -d --name mysql-db \ -e MYSQL_ROOT_PASSWORD=your-password \ -e MYSQL_DATABASE=appdb \ --mount source=mysql-data,target=/var/lib/mysql \ -p 3306:3306 \ mysql:8.0

解释两个点:-e是向容器传入环境变量,MySQL 官方镜像第一次启动时会读取这些变量来初始化 root 密码和默认数据库;--mount source=mysql-data,target=/var/lib/mysql是把之前创建的 volume 挂到 MySQL 存放数据文件的目录。

注意:如果宿主机上已经装了 MySQL 并占用了 3306,这里的-p 3306:3306会冲突,改成-p 33060:3306就行。

等容器启动完成后,进去建一张表:

docker exec -it mysql-db mysql -uroot -p

输入刚才设置的密码,执行:

USE appdb; CREATE TABLE demo (id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50)); INSERT INTO demo (name) VALUES ('hello');

然后退出,删掉这个容器,再用同样的参数重新启动一个新容器,挂载同一个 volume:

docker stop mysql-db docker rm mysql-db docker run -d --name mysql-db \ -e MYSQL_ROOT_PASSWORD=your-password \ -e MYSQL_DATABASE=appdb \ --mount source=mysql-data,target=/var/lib/mysql \ -p 3306:3306 \ mysql:8.0

进入容器查一下:

docker exec -it mysql-db mysql -uroot -p -e "USE appdb; SELECT * FROM demo;"

能看到刚才的hello还在。这就是 volume 的价值:卷在,数据就在。

4.3 顺手给个备份命令

数据库容器最怕手滑删除,备份其实一条命令就能做。利用--volumes-from挂载原容器的 volume,再用一个小镜像把数据目录打包到当前目录:

docker run --rm \ --volumes-from mysql-db \ -v $(pwd):/backup \ alpine \ tar czf /backup/mysql-data.tar.gz /var/lib/mysql

跑完当前目录下会多出mysql-data.tar.gz。以后想恢复,就反向解压回原目录。--rm表示这个辅助容器用完就删,不会留下垃圾进程。

5. 容器之间怎么通信——端口映射、自定义网络与排查顺序

跑起来了、数据也不丢了,接下来你大概率会遇到另一个问题:几个容器之间怎么互相访问?尤其是有数据库和应用,总不能每次都用宿主机 IP 去连。

5.1 为什么容器默认“与世隔绝”

每个容器默认有自己的网络命名空间,相当于一个独立小网络。外部访问不到容器里的端口,容器访问外部也要靠 NAT 转发。所以docker run nginx之后,你在宿主机浏览器里是访问不到的,必须加-p端口映射,宿主机那边开一个口,把流量转发进容器。

-p的完整格式是-p 宿主机IP:宿主机端口:容器端口。最常用的是只写端口不写 IP:

docker run -d -p 8080:80 nginx

意思是宿主机所有网卡上的 8080 端口,转发到容器内 80 端口。如果你是本地开发,建议把访问范围限制在本地回环地址:

docker run -d -p 127.0.0.1:8080:80 nginx

这样只有本机能访问,局域网里其他机器访问不到,安全一点。

如果你想偷懒,可以写-P(大写),Docker 会自动把容器里声明过的端口随机映射到宿主机的某个端口,然后用docker ps去查实际映射结果。这种方式适合临时验证,不太适合正式使用。

5.2 端口冲突怎么查

报错port is already allocated是最常见的。先看有哪些容器在跑,哪个占用了端口:

docker ps

找到占用的容器后,要么改新容器的宿主机端口,要么停掉旧容器。也可以用系统的端口查看命令确认谁在监听:

ss -tlnp | grep 8080

我的建议是:本地学习时,宿主机端口统一用“容器端口前面加个数字”的方式,比如 MySQL 3306 映射到 33060,Nginx 80 映射到 8080,Redis 6379 映射到 16379。这样一眼就能看出哪个容器对应哪个端口,不容易冲突。

5.3 自定义网络让容器用名字互访

容器之间的 IP 不是固定的,删掉重建就可能变,所以不能写死 IP。解决办法是建立一个自定义网络,让 Docker 的 DNS 自动解析容器名。

先建网络:

docker network create app-net

再把两个容器都加进去:

docker run -d --name mysql-db \ --network app-net \ -e MYSQL_ROOT_PASSWORD=your-password \ mysql:8.0 docker run -d --name backend \ --network app-net \ -p 8000:8000 \ my-flask-app

这时在backend容器里,可以直接用mysql-db这个名字访问数据库,不需要知道它的 IP:

docker exec -it backend ping mysql-db

应用代码里配置数据库连接地址时,直接写mysql-db:3306就行。这个便利只对自定义网络生效,默认的bridge网络不支持容器名自动解析,所以正规做法还是建一个网络,而不是所有容器都丢在默认网络里。

Docker 网络有三种常见模式:bridge是默认的隔离网络,适合单机多容器通信;host让容器直接共享宿主机网络栈,性能好但隔离弱,而且-p会被忽略;none表示无网络。初学阶段把自定义bridge用熟就够了,其他模式等遇到具体需求再研究。

5.4 端口通了但访问失败,按这个顺序排查

-p写对了,浏览器还是打不开,我教大家一套排查顺序,别一上来就删容器重建。

  1. 容器是否还在运行:docker ps,确认状态不是 Exited。
  2. 端口是否真的映射了:docker port <容器名>,看宿主机端口和容器端口对应关系。
  3. 容器内服务是否在监听正确端口:docker exec -it <容器名> bash,进去用netstat -tlnp看监听地址。很多应用默认监听 127.0.0.1,容器外部就访问不到,必须监听0.0.0.0。
  4. 宿主机防火墙是否拦截:如果你在云服务器上跑,还要去安全组放行对应端口。

这一套流程走完,80% 的访问问题都能定位。剩下的就是看应用日志,比如是不是启动时连数据库失败了。

6. 新手最容易踩的坑——我提前帮你踩了

最后这部分算是我个人总结的高频问题清单,每一个都有对应的解决思路,建议直接收藏。

6.1 容器时间永远不对,日志差八小时

新手第一个困惑往往是:容器里date显示 UTC 时间,日志和宿主机的对不上。原因很简单,很多基础镜像默认使用 UTC 时区。

解决方案有两种。第一种,运行容器时挂载宿主机的时区文件:

docker run -d \ -v /etc/localtime:/etc/localtime:ro \ nginx

第二种,通过环境变量指定时区,具体时区标识请按你自己所在位置设置:

docker run -d \ -e TZ=你的时区标识 \ nginx

如果你写的是自己的镜像,建议在 Dockerfile 里也补上时区设置,这样不管用的人有没有加-e,默认都是对的时间。

6.2 容器明明是活的,但 docker logs 什么都没有

Docker 的日志机制只收集进程输出到 stdout 和 stderr 的内容。很多应用默认把日志写到文件里,于是你在docker logs里什么都看不到。

解决办法是在应用层面开启标准输出。比如 Python 可以设置PYTHONUNBUFFERED=1,Java 可以关掉日志文件只留控制台,Nginx 官方镜像本来就做了日志到 stdout 的软链。自己写镜像时,也要注意把日志重定向到/dev/stdout或/dev/stderr。

排查这类问题时,先docker exec -it <容器> bash进去手动跑一下应用,能看到报错就说明应用本身没问题,只是输出位置不对。

6.3 挂载的目录里全是 root 归属的文件,宿主机用户改不动

一个常见的现象是:容器往挂载目录里写文件,生成的用户是 root,宿主机普通用户打不开也删不掉。原因是容器内进程默认以 root 运行,而挂载是直通宿主机文件系统的,所以写的文件归属会变成 root。

最简单的临时方案是在运行容器时指定当前用户:

docker run -d \ --user $(id -u):$(id -g) \ -v $(pwd)/data:/data \ some-image

更好的方案是在 Dockerfile 里创建一个专用用户,用USER切换到它再启动应用。这不仅能解决权限问题,还是一手安全习惯:生产容器里不要用 root 跑应用进程,万一被攻破,影响面小很多。

6.4 docker stop 总是要等十几秒,像卡住了一样

docker stop的正常流程是:给容器的 1 号进程发 SIGTERM,等它优雅退出,倒计时十秒结束还没退,就发 SIGKILL 强杀。如果你看到每次停止都要卡一二十秒,多半是容器里的进程没有处理 SIGTERM。

最典型的情况是 Dockerfile 里写成了 shell 形式:

CMD python app.py

这会让 sh 作为 1 号进程,而真正的 python 是 sh 的子进程,信号被 sh 接住却没有转发给 python,结果就是应用没法优雅退出,只能等强杀。

解决方法是改成 exec 形式:

CMD ["python", "app.py"]

或者直接在运行参数里加--init,让 Docker 内置一个初始化进程来转发信号:

docker run --init --name flask-demo my-flask-app

6.5 磁盘越来越小,全是没人用的悬空镜像

Docker 用久了,本地会堆积大量无标签镜像、停止的容器和构建缓存。先用这个命令看看到底是谁占的空间:

docker system df

然后定期清理:

docker system prune

如果加了-a,会把所有未被容器使用的镜像也一并清掉,本地空间的回收效果更明显。但注意:线上机器执行prune -a要小心,本地一时没用到的镜像可能被误删,重新拉一次又是一晚上工作量。

6.6 几个提升幸福感的小习惯

最后说几个零散但很管用的经验。第一,不要用latest标签做长期固定依赖,写死了版本号,换台机器或者几个月后重新构建,行为才可预期。第二,密钥和密码不要写进 Dockerfile,运行时用-e或专门的 secrets 机制注入。第三,改完代码记得重新docker build,很多人 run 了旧镜像半天查不出问题,最后才发现是新代码根本没进容器。第四,日志里出现奇怪的^M字符,多半是脚本文件用了 Windows 换行符,拉进 Linux 容器执行时就会报错,写干净的文件编码能从根上避免。

聊到这里,基本的单机 Docker 操作应该已经够你用了。我自己学习时最大的体会是:先别急着碰编排工具和复杂架构,老老实实把docker run、docker exec、docker logs这些命令行玩熟,把镜像构建和数据持久化的手感练出来,后面再看编排配置就会觉得很轻松。另一个习惯是,遇到任何异常先docker logs,再看文档,别一上来就docker rm -f,删容器永远不是排查问题的第一步。希望这份笔记能帮你少踩我当初踩过的那些坑,剩下的就靠你多跑几次命令了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/21 14:13:32

SpringBoot汽车租赁管理系统:从设计到实战的完整指南

做毕业设计选题目&#xff0c;说是技术活&#xff0c;其实更大程度上是个体力活。我这两年陆陆续续帮学弟学妹看过不少题目&#xff0c;发现“SpringBoot汽车租赁管理系统”几乎每隔一两个月就会有人问一次&#xff0c;要是往共享汽车运营、在线租赁这个方向再靠一靠&#xff0…

作者头像 李华
网站建设 2026/10/21 14:13:13

SpringBoot+Vue公寓管理系统:架构设计、核心模块与避坑指南

2. 核心技术与架构选型为什么这么搭SpringBoot 负责后端&#xff0c;Vue 负责前端&#xff0c;MyBatis 负责数据库操作&#xff0c;MySQL 负责数据存储&#xff0c;这套组合在 Java 技术栈里已经非常成熟。很多团队选型时会纠结是不是要用更重型的框架&#xff0c;但在我看来&a…

作者头像 李华
网站建设 2026/10/21 14:12:42

SpringBoot+Vue3+MyBatis实现兼职平台:从数据库设计到部署踩坑全记录

1. 这个系统到底做什么&#xff1a;先想明白业务再写代码 1.1 兼职平台的核心业务闭环 最近我把一个学生项目的“蜗牛兼职网”从零到一完整实现了一遍&#xff0c;用的技术栈是 Java SpringBoot Vue3 MyBatis&#xff0c;数据库是 MySQL&#xff0c;前后端完全分离。这套东西…

作者头像 李华
网站建设 2026/10/21 14:12:39

AI工具组合工作流:自媒体一天产出50篇图文视频的方法

做自媒体这几年&#xff0c;我最大的感受就是内容永远不够用。一个账号要稳定更新&#xff0c;图文、视频都得有&#xff0c;光靠灵感根本不现实&#xff0c;所以我前前后后试了上百个AI工具——从写文案的、画图的&#xff0c;到生成视频的、自动剪辑的&#xff0c;几乎每个类…

作者头像 李华
网站建设 2026/10/21 14:12:30

Redisson Redis TCP连接池核心配置与踩坑实战

接手过Redis相关服务的人&#xff0c;迟早都会面对“连接”这道坎。Linux有句老话叫一切皆文件&#xff0c;Redis这道坎里藏着的则是连不上、连上了断、连多了打满。Redisson作为Java里用得越来越广的Redis客户端&#xff0c;很早就不再走Jedis那套直连思路&#xff0c;而是把T…

作者头像 李华
网站建设 2026/10/21 14:11:03

Python模糊测试普及困境与落地路径:从工具选择到CI集成

如果要在软件测试领域找一个听起来很厉害、但真正在自己项目里用起来的人少得可怜的技术&#xff0c;模糊测试肯定排得上号。倒不是大家不想用&#xff0c;而是谈到Python应用场景时&#xff0c;很多人试了一圈之后发现……实在带不动。跑起来慢、覆盖率上不去、半天不崩一个、…

作者头像 李华