欢迎光临
我们一直在努力

Docker 核心进阶指南:卷、网络、端口与日志,一篇讲透

很多 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_PASSWORD=123456 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_PASSWORD=root123 \\
-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_PASSWORD=root123 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 依赖宿主机绝对路径,权限问题较多(如容器内用户 uid=1000,而宿主机文件属于 root)。

1.3 tmpfs — 内存级临时存储

bash

# 挂载 tmpfs 到容器的 /tmp,数据不落地磁盘
docker run -d –name temp-cache –tmpfs /tmp:rw,noexec,nosuid,size=2g alpine tail -f /dev/null


二、Docker 网络 — 让容器互相通信

Docker 默认提供三种网络驱动:bridge(默认)、host、none。此外还有 overlay(Swarm/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.2

2.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_PASSWORD=root \\
-e MYSQL_DATABASE=wordpress \\
mysql:8.0

# WordPress 容器
docker run -d –name wordpress \\
–network wp-net \\
-e WORDPRESS_DB_HOST=mysql-db \\
-e WORDPRESS_DB_USER=root \\
-e WORDPRESS_DB_PASSWORD=root \\
-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 接口,无法访问外部网络。

bash

docker 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:443

3.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(host='0.0.0.0', port=5000)

bash

# 构建镜像并运行,映射宿主机 5001 -> 容器 5000
docker build -t flask-app .
docker run -d –name myflask -p 5001:5000 flask-app

访问 http://localhost:5001 即可看到输出。

3.3 查看端口映射

bash

docker port myflask # 输出 5000/tcp -> 0.0.0.0:5001
docker ps # PORTS 列显示映射关系

3.4 随机端口分配 -P

-P 会将 Dockerfile 中 EXPOSE 声明的端口自动映射到宿主机随机高位端口。

bash

docker 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 logs

bash

# 查看全部日志
docker logs <container>

# 实时跟踪(类似 tail -f)
docker logs -f <container>

# 查看最后 100 行
docker logs –tail 100 <container>

# 带时间戳
docker logs -t <container>

# 查看最近 5 分钟的日志
docker logs –since 5m <container>

4.2 限制日志大小 — 防止磁盘爆满

如果不加限制,长期运行的容器(如 MySQL、Nginx)会产生 GB 级别的日志,占满磁盘。通过 –log-opt 限制:

bash

# 单个日志文件最大 10M,最多保留 3 个文件
docker run -d –name app \\
–log-opt max-size=10m \\
–log-opt max-file=3 \\
nginx

也可以在 /etc/docker/daemon.json 中全局配置:

json

{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}

然后重启 Docker:systemctl restart docker(会重启所有容器,谨慎操作)。

4.3 切换日志驱动

Docker 支持多种日志驱动,常用的有:

驱动说明适用场景
json-file 默认,写入本地 JSON 文件 单机开发、测试
journald 发送到 systemd journal 使用 systemd 的 Linux 发行版
syslog 发送到 syslog 服务 传统日志集中化
fluentd 发送到 Fluentd 日志收集器 容器化微服务日志聚合
gelf 发送到 Graylog 或 ELK 生产日志分析
none 禁用日志(不建议)

示例:使用 syslog 驱动

bash

docker run -d –name syslog-app –log-driver syslog –log-opt syslog-address=tcp://192.168.1.100:514 alpine sh -c "while true; do echo hello; sleep 1; done"

4.4 生产环境日志最佳实践

  • 始终限制日志大小(max-size / max-file)

  • 不要在容器内写日志文件,而是输出到 stdout/stderr(Docker 自动收集)

  • 使用集中式日志系统(如 ELK、Loki、Graylog),配合 fluentd 或 gelf 驱动

  • 定期清理旧日志:docker system prune -a –volumes 或手动删除 /var/lib/docker/containers/*/*.log


  • 五、综合实战:部署一个完整的 Web 应用

    我们将部署一个 Node.js + Redis 的计数器应用,包含卷(Redis 数据持久化)、自定义网络、端口映射、日志限制。

    5.1 目录结构

    text

    myapp/
    ├── Dockerfile
    ├── index.js
    └── package.json

    index.js

    javascript

    const 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.json

    json

    {
    "name": "myapp",
    "scripts": { "start": "node index.js" },
    "dependencies": { "express": "^4.18.2", "redis": "^4.6.5" }
    }

    Dockerfile

    dockerfile

    FROM 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-size=5m –log-opt max-file=2 \\
    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-size=5m –log-opt max-file=2 \\
    myapp

    访问 http://localhost:3000,每次刷新计数器加 1,Redis 数据持久化在卷中,即使删除 redis-server 容器再重新创建(使用同一卷),计数依然保留。


    六、常见问题与 FAQ

    Q1:为什么我的 bind mount 在容器里看不到文件?
    A:检查宿主机路径是否存在,以及容器内挂载点是否覆盖了原有目录内容(bind mount 会使目标目录原有内容隐藏)。

    Q2:容器之间用 –link 和自定义网络有什么区别?
    A:–link 是旧版解决方案,只允许单向通信且已过时;自定义网络提供自动 DNS,容器名即可通信,推荐使用。

    Q3:docker logs 显示日志,但为什么我的日志驱动是 journald 还能看?
    A:docker logs 实际上会从日志驱动读取,如果驱动不支持(如 syslog),docker logs 会报错。json-file 和 journald 都支持 docker logs。

    Q4:如何清理所有未使用的卷?

    bash

    docker volume prune

    Q5:容器端口映射后,宿主机端口被占用怎么办?
    A:换一个宿主机端口,或停止占用端口的进程,或使用随机端口 -P 让 Docker 自动分配。


    总结

    概念核心要点
    数据持久化首选 Volume;开发热加载用 bind mount;临时敏感用 tmpfs
    网络 自定义 bridge 网络实现容器名自动解析;host 高性能;none 隔离
    端口 -p 宿主机:容器 对外暴露服务;-P 随机映射 EXPOSE 端口
    日志 默认 json-file 需限制 max-size;生产环境建议集中式日志收集

    掌握这些内容,你已经可以熟练使用 Docker 部署有状态应用、搭建微服务通信、并合理管理日志了。如果你觉得本文对你有帮助,欢迎点赞、收藏、转发~

    赞(0)
    未经允许不得转载:171主机测评 » Docker 核心进阶指南:卷、网络、端口与日志,一篇讲透
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址