
很多 Docker 初学者会问容器删了数据去哪了两个容器怎么通信怎么让外部访问容器里的服务日志把磁盘撑爆了怎么办本文将从零开始系统讲解 Docker 的卷Volume、网络Network、端口映射Port和日志Logging配合大量实战命令和场景示例帮你彻底搞懂这些核心概念。一、为什么需要卷—— 容器是“一次性”的默认情况下容器里创建的文件比如数据库的数据文件、应用生成的日志都存储在容器的可写层。一旦容器被删除这些数据就会永久消失。bash# 示例运行一个临时容器创建一个文件 docker run --name temp alpine touch /data/hello.txt # 删除容器 docker rm temp # 文件已经丢失无法找回为了解决“数据持久化”和“容器间共享数据”的问题Docker 提供了三种存储方式类型特点适用场景Volume由 Docker 管理存放在/var/lib/docker/volumes/支持多种驱动生产环境持久化数据最推荐Bind mount将宿主机任意路径挂载到容器权限依赖宿主机目录结构开发环境热加载代码、配置文件tmpfs仅存于宿主机内存中容器停止后数据消失存放敏感或临时数据如密钥1.1 Volume — 最推荐的持久化方式基本操作bash# 创建卷 docker volume create mydata # 列出所有卷 docker volume ls # 查看卷详情 docker volume inspect mydata # 使用卷运行容器挂载到容器的 /var/lib/mysql docker run -d --name mysql-db -v mydata:/var/lib/mysql -e MYSQL_ROOT_PASSWORD123456 mysql:8.0 # 删除卷需要先停止并删除使用该卷的容器 docker volume rm mydata实战MySQL 数据持久化bash# 1. 创建数据卷 docker volume create mysql-data # 2. 运行 MySQL 容器挂载卷 docker run -d \ --name mysql-prod \ -v mysql-data:/var/lib/mysql \ -e MYSQL_ROOT_PASSWORDroot123 \ -p 3306:3306 \ mysql:8.0 # 3. 连接 MySQL 并创建测试数据库 docker exec -it mysql-prod mysql -uroot -proot123 -e CREATE DATABASE testdb; # 4. 停止并删除容器卷仍然存在 docker stop mysql-prod docker rm mysql-prod # 5. 新建一个容器使用同一个卷数据依然存在 docker run -d --name mysql-new -v mysql-data:/var/lib/mysql -e MYSQL_ROOT_PASSWORDroot123 mysql:8.0 docker exec -it mysql-new mysql -uroot -proot123 -e SHOW DATABASES; # testdb 还在1.2 Bind Mount — 开发热加载利器直接将宿主机的文件夹映射到容器修改宿主机代码容器内立即生效常用于开发环境。bash# 将当前目录的 ./app 挂载到容器的 /app docker run -d \ --name dev-node \ -v $(pwd)/app:/app \ -w /app \ node:18 npm run dev注意Bind mount 依赖宿主机绝对路径权限问题较多如容器内用户 uid1000而宿主机文件属于 root。1.3 tmpfs — 内存级临时存储bash# 挂载 tmpfs 到容器的 /tmp数据不落地磁盘 docker run -d --name temp-cache --tmpfs /tmp:rw,noexec,nosuid,size2g alpine tail -f /dev/null二、Docker 网络 — 让容器互相通信Docker 默认提供三种网络驱动bridge默认、host、none。此外还有overlaySwarm/K8s、macvlan等。2.1 默认 bridge 网络所有未指定网络的容器都会自动连接到名为bridge的默认网络。但默认 bridge 不支持容器名自动 DNS 解析只能通过 IP 互相访问且容器间需要--link才能通信已过时。bash# 查看默认 bridge 网络 docker network inspect bridge # 运行两个容器 docker run -d --name c1 alpine sleep 3600 docker run -d --name c2 alpine sleep 3600 # 获取 c1 的 IP docker inspect c1 | grep IPAddress # 在 c2 中 ping c1 的 IP可以但无法用 ping c1 域名 docker exec c2 ping -c 2 172.17.0.22.2 用户自定义 bridge — 推荐方式自定义 bridge 网络具备自动 DNS 解析功能容器之间可以通过容器名直接通信。bash# 创建自定义网络 docker network create mynet # 运行两个容器并连接到 mynet docker run -d --name web --network mynet nginx docker run -d --name app --network mynet alpine sleep 3600 # 在 app 容器中 ping web通过容器名 docker exec app ping web # 成功解析实战一个 WordPress MySQL 的例子bash# 创建公共网络 docker network create wp-net # MySQL 容器 docker run -d --name mysql-db \ --network wp-net \ -e MYSQL_ROOT_PASSWORDroot \ -e MYSQL_DATABASEwordpress \ mysql:8.0 # WordPress 容器 docker run -d --name wordpress \ --network wp-net \ -e WORDPRESS_DB_HOSTmysql-db \ -e WORDPRESS_DB_USERroot \ -e WORDPRESS_DB_PASSWORDroot \ -p 8080:80 \ wordpress:latest现在通过http://localhost:8080即可访问 WordPress两个容器通过mysql-db这个容器名自动通信。2.3 host 网络 — 与宿主机共享网络栈使用--network host后容器不再拥有独立 IP而是直接使用宿主机的网络栈端口也直接暴露在宿主机上。bash# 运行 nginx 使用 host 网络访问 http://localhost 即可看到 nginx 首页 docker run -d --name nginx-host --network host nginx适用场景追求极致网络性能、不需要端口映射如某些监控 agent。2.4 none 网络 — 完全隔离容器只有 loopback 接口无法访问外部网络。bashdocker run -d --name isolated --network none alpine sleep 3600三、端口映射 — 让外部访问容器服务容器默认运行在隔离网络内外部无法直接访问。通过-p或-P将宿主机端口映射到容器端口。3.1 基本格式bash# 宿主机端口:容器端口 -p 8080:80 # 指定 IP默认 0.0.0.0 -p 127.0.0.1:8080:80 # 只绑定容器端口随机映射宿主机端口 -p 80 # UDP 端口 -p 53:53/udp # 多个端口映射 -p 8080:80 -p 8443:4433.2 实战运行一个 Flask 应用python# app.py from flask import Flask app Flask(__name__) app.route(/) def hello(): return Hello from Docker! if __name__ __main__: app.run(host0.0.0.0, port5000)bash# 构建镜像并运行映射宿主机 5001 - 容器 5000 docker build -t flask-app . docker run -d --name myflask -p 5001:5000 flask-app访问http://localhost:5001即可看到输出。3.3 查看端口映射bashdocker port myflask # 输出 5000/tcp - 0.0.0.0:5001 docker ps # PORTS 列显示映射关系3.4 随机端口分配-P-P会将 Dockerfile 中EXPOSE声明的端口自动映射到宿主机随机高位端口。bashdocker run -d --name random-nginx -P nginx docker port random-nginx # 显示 80/tcp - 0.0.0.0:32768四、Docker 日志 — 控制、查看与管理容器产生的标准输出stdout/stderr会被 Docker 捕获并交给日志驱动处理。默认驱动是json-file将日志以 JSON 格式保存在宿主机/var/lib/docker/containers/container_id/container_id-json.log。4.1 查看日志 —docker logsbash# 查看全部日志 docker logs container # 实时跟踪类似 tail -f docker logs -f container # 查看最后 100 行 docker logs --tail 100 container # 带时间戳 docker logs -t container # 查看最近 5 分钟的日志 docker logs --since 5m container4.2 限制日志大小 — 防止磁盘爆满如果不加限制长期运行的容器如 MySQL、Nginx会产生 GB 级别的日志占满磁盘。通过--log-opt限制bash# 单个日志文件最大 10M最多保留 3 个文件 docker run -d --name app \ --log-opt max-size10m \ --log-opt max-file3 \ nginx也可以在/etc/docker/daemon.json中全局配置json{ log-driver: json-file, log-opts: { max-size: 10m, max-file: 3 } }然后重启 Dockersystemctl restart docker会重启所有容器谨慎操作。4.3 切换日志驱动Docker 支持多种日志驱动常用的有驱动说明适用场景json-file默认写入本地 JSON 文件单机开发、测试journald发送到 systemd journal使用 systemd 的 Linux 发行版syslog发送到 syslog 服务传统日志集中化fluentd发送到 Fluentd 日志收集器容器化微服务日志聚合gelf发送到 Graylog 或 ELK生产日志分析none禁用日志不建议-示例使用 syslog 驱动bashdocker run -d --name syslog-app --log-driver syslog --log-opt syslog-addresstcp://192.168.1.100:514 alpine sh -c while true; do echo hello; sleep 1; done4.4 生产环境日志最佳实践始终限制日志大小max-size/max-file不要在容器内写日志文件而是输出到 stdout/stderrDocker 自动收集使用集中式日志系统如 ELK、Loki、Graylog配合fluentd或gelf驱动定期清理旧日志docker system prune -a --volumes或手动删除/var/lib/docker/containers/*/*.log五、综合实战部署一个完整的 Web 应用我们将部署一个Node.js Redis的计数器应用包含卷Redis 数据持久化、自定义网络、端口映射、日志限制。5.1 目录结构textmyapp/ ├── Dockerfile ├── index.js └── package.jsonindex.jsjavascriptconst express require(express); const redis require(redis); const app express(); const client redis.createClient({ host: redis-server, port: 6379 }); client.set(visits, 0); app.get(/, async (req, res) { const visits await client.get(visits); res.send(Number of visits: ${visits}); await client.set(visits, parseInt(visits) 1); }); app.listen(3000, () console.log(App listening on port 3000));package.jsonjson{ name: myapp, scripts: { start: node index.js }, dependencies: { express: ^4.18.2, redis: ^4.6.5 } }DockerfiledockerfileFROM node:18-alpine WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . CMD [npm, start]5.2 构建并运行bash# 1. 创建自定义网络 docker network create app-net # 2. 创建 Redis 数据卷 docker volume create redis-data # 3. 运行 Redis 容器挂载卷限制日志 docker run -d --name redis-server \ --network app-net \ -v redis-data:/data \ --log-opt max-size5m --log-opt max-file2 \ redis:7-alpine # 4. 构建 Node 镜像 docker build -t myapp . # 5. 运行 Node 应用端口映射日志限制 docker run -d --name myapp \ --network app-net \ -p 3000:3000 \ --log-opt max-size5m --log-opt max-file2 \ myapp访问http://localhost:3000每次刷新计数器加 1Redis 数据持久化在卷中即使删除 redis-server 容器再重新创建使用同一卷计数依然保留。六、常见问题与 FAQQ1为什么我的 bind mount 在容器里看不到文件A检查宿主机路径是否存在以及容器内挂载点是否覆盖了原有目录内容bind mount 会使目标目录原有内容隐藏。Q2容器之间用--link和自定义网络有什么区别A--link是旧版解决方案只允许单向通信且已过时自定义网络提供自动 DNS容器名即可通信推荐使用。Q3docker logs显示日志但为什么我的日志驱动是journald还能看Adocker logs实际上会从日志驱动读取如果驱动不支持如syslogdocker logs会报错。json-file和journald都支持docker logs。Q4如何清理所有未使用的卷bashdocker volume pruneQ5容器端口映射后宿主机端口被占用怎么办A换一个宿主机端口或停止占用端口的进程或使用随机端口-P让 Docker 自动分配。总结概念核心要点卷数据持久化首选 Volume开发热加载用 bind mount临时敏感用 tmpfs网络自定义 bridge 网络实现容器名自动解析host 高性能none 隔离端口-p 宿主机:容器对外暴露服务-P随机映射 EXPOSE 端口日志默认 json-file 需限制 max-size生产环境建议集中式日志收集掌握这些内容你已经可以熟练使用 Docker 部署有状态应用、搭建微服务通信、并合理管理日志了。如果你觉得本文对你有帮助欢迎点赞、收藏、转发