本教程侧重于命令实践和理解,提供可在本地环境测试的实例,每章结束都有总结要点。
目录
前边介绍了 Docker 基础概念和安装 以及 Docker 常用命令实践。本篇介绍 docker 容器的两个重要概念,网络机制和容器挂载。
- [Docker 网络机制详解](#Docker 网络机制详解)
- [Docker 数据卷和挂载](#Docker 数据卷和挂载)
第3章:Docker 网络机制详解
3.1 Docker 网络基础
Docker 网络是容器间通信和容器与外部世界通信的基础。理解网络机制对于构建复杂的容器化应用至关重要。
每个 Docker 容器都有自己的网络命名空间,包括:独立的网络接口,路由表,iptables 规则和端口空间。
查看网络信息
bash
# 列出所有网络
docker network ls
# 查看网络详细信息
docker network inspect bridge
# 查看容器网络配置
docker inspect container_name | grep -A 20 "NetworkSettings"
3.2 Docker 网络驱动类型
运行 docker network ls
查看网络,比如
bash
❯ docker network ls
NETWORK ID NAME DRIVER SCOPE
fef63891b2a7 bridge bridge local
150f1dc61078 nginx_default bridge local
ce0e3369b5e1 host host local
050afab805f4 none null local
可以看到存在三种类型:bridge, host, none。
1. Bridge 网络(默认)
Bridge 是 Docker 的默认网络驱动,适用于单主机上的容器通信。
- 作用:为容器提供独立的网络命名空间,同时允许容器间通信
- 特点:容器获得私有 IP(通常是 172.17.x.x),通过 NAT 访问外网
- 使用场景:大部分单机容器应用的默认选择
常用命令:
bash
# 查看默认 bridge 网络
docker network inspect bridge
# 运行容器使用默认网络
docker run -d --name app1 nginx
docker run -d --name app2 nginx
# 查看容器 IP
docker inspect app1 | grep IPAddress
docker inspect app2 | grep IPAddress
# 容器间通信测试
docker exec app1 ping $(docker inspect app2 | grep IPAddress | head -1 | cut -d'"' -f4)
2. Host 网络
Host 网络模式下,容器直接使用主机的网络栈。
- 特点:容器直接使用主机的网络栈,没有网络隔离
- 性能:网络性能最好,没有 NAT 转换开销
- 风险:安全性较低,容器可以直接访问主机网络
bash
# 使用 host 网络运行容器
docker run -d --network host --name host-app nginx
# 查看网络配置(与主机相同)
docker exec host-app ip addr show
# 直接通过主机 IP 访问
curl http://localhost:80
3. None 网络
None 网络模式下,容器没有网络接口。
bash
# 运行无网络容器
docker run -d --network none --name no-network alpine sleep 3600
# 查看网络接口(只有 loopback)
docker exec no-network ip addr show
4. 自定义 Bridge 网络
自定义网络提供更好的隔离性和容器间的名称解析。
bash
# 创建自定义网络
docker network create --driver bridge my-network
# 查看网络详情
docker network inspect my-network
# 在自定义网络中运行容器
docker run -d --network my-network --name web nginx
docker run -d --network my-network --name db mysql:8.0 -e MYSQL_ROOT_PASSWORD=password
# 容器间可以通过名称通信
docker exec web ping db
docker exec db ping web
5. 自定义 Bridge 网络的 IP 段
Docker 默认使用 172.17.0.0/16 网段,如果服务器的 172.xx
网段被占用,为避免冲突,可以通过 /etc/docker/daemon.json
修改全局默认配置,举个例子:
bash
# 编辑 /etc/docker/daemon.json
{
"bip": "100.10.100.1/24",
"default-address-pools":
[
{"base":"100.10.0.0/16","size":24}
]
}
配置说明:
bip
:设置默认 bridge 网络的 IP 段default-address-pools
:设置自定义网络的默认 IP 池
bash
# 重启 Docker 服务使配置生效
sudo systemctl restart docker
当然,也可以为单个容器创建指定 IP 段网络,比如
bash
```bash
# 创建开发环境网络
docker network create \
--subnet=10.10.0.0/24 \
--gateway=10.10.0.1 \
dev-network
3.3 端口映射
基本端口映射
通过 -p <host_port>:<container_port>
可以将容器端口映射到主机端口。
bash
# 映射单个端口
docker run -d -p 8080:80 nginx
# 映射多个端口
docker run -d -p 8080:80 -p 8443:443 nginx
# 映射到指定 IP
docker run -d -p 127.0.0.1:8080:80 nginx
# 映射随机端口
docker run -d -P nginx
# 查看端口映射
docker port container_name
高级端口配置
bash
# UDP 端口映射
docker run -d -p 53:53/udp nginx
# 端口范围映射
docker run -d -p 8000-8010:8000-8010 nginx
# 查看所有端口映射
docker ps --format "table {{.Names}}\t{{.Ports}}"
3.4 实践练习
练习1:Web 应用 + 数据库通信
bash
# 创建自定义网络
docker network create webapp-network
# 启动数据库容器
docker run -d \
--name database \
--network webapp-network \
-e MYSQL_ROOT_PASSWORD=rootpass \
-e MYSQL_DATABASE=webapp \
mysql:8.0
# 等待数据库启动
sleep 30
# 启动 Web 应用容器
docker run -d \
--name webapp \
--network webapp-network \
-p 8080:80 \
nginx
# 测试容器间连通性
docker exec webapp ping database
# 在 Web 容器中安装网络工具并测试数据库连接
docker exec webapp apt-get update
docker exec webapp apt-get install -y telnet
docker exec webapp telnet database 3306
# 清理
docker stop webapp database
docker rm webapp database
docker network rm webapp-network
练习2:负载均衡配置
bash
# 创建负载均衡网络
docker network create lb-network
# 启动多个后端服务
docker run -d --name backend1 --network lb-network nginx
docker run -d --name backend2 --network lb-network nginx
docker run -d --name backend3 --network lb-network nginx
# 创建 nginx 配置文件
mkdir -p /tmp/docker-tutorial/nginx-lb
cat > /tmp/docker-tutorial/nginx-lb/nginx.conf << 'EOF'
events {
worker_connections 1024;
}
http {
upstream backend {
server backend1:80;
server backend2:80;
server backend3:80;
}
server {
listen 80;
location / {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
}
EOF
# 启动负载均衡器
docker run -d \
--name load-balancer \
--network lb-network \
-p 8080:80 \
-v /tmp/docker-tutorial/nginx-lb/nginx.conf:/etc/nginx/nginx.conf \
nginx
# 测试负载均衡
for i in {1..6}; do
curl -s http://localhost:8080 | grep -o "backend[0-9]" || echo "Request $i"
done
# 清理
docker stop load-balancer backend1 backend2 backend3
docker rm load-balancer backend1 backend2 backend3
docker network rm lb-network
3.5 网络故障排查
常用网络调试命令
bash
# 在容器中安装网络工具
docker exec -it container_name bash
apt-get update && apt-get install -y iputils-ping telnet curl netcat
# 测试网络连通性
ping target_host
telnet target_host port
curl http://target_host:port
nc -zv target_host port
# 查看网络接口
ip addr show
ip route show
# 查看端口监听
netstat -tulpn
ss -tulpn
常见网络问题
bash
# 1. 容器无法访问外网
docker run --rm busybox ping google.com
# 2. 容器间无法通信
docker exec container1 ping container2
# 3. 端口映射不生效
docker port container_name
netstat -tulpn | grep port_number
# 4. DNS 解析问题
docker exec container_name nslookup google.com
docker exec container_name cat /etc/resolv.conf
3.6 高级网络配置
网络别名
网络别名最大的用途是实现简单的负载均衡。多个容器可以使用同一个别名,Docker 会自动进行 DNS 轮询。
bash
# 创建网络
docker network create test-network
# 为容器设置网络别名
docker run -d --name web1 --network web-cluster --network-alias webapp nginx
docker run -d --name web2 --network web-cluster --network-alias webapp nginx
docker run -d --name web3 --network web-cluster --network-alias webapp nginx
# 测试别名解析
docker run --rm --network web-cluster busybox nslookup webapp
# 会看到多个 IP 地址,每次访问会轮询到不同的容器
多网络连接
bash
# 创建多个网络
docker network create frontend
docker network create backend
# 启动数据库(仅在后端网络)
docker run -d --name db --network backend mysql:8.0 -e MYSQL_ROOT_PASSWORD=pass
# 启动应用服务器(连接两个网络)
docker run -d --name app nginx
docker network connect frontend app
docker network connect backend app
# 启动前端(仅在前端网络)
docker run -d --name web --network frontend nginx
# 验证网络连接
docker exec app ping db # 应该成功
docker exec web ping app # 应该成功
docker exec web ping db # 应该失败
网络策略和安全
bash
# 创建隔离网络
docker network create --internal secure-network
# 在隔离网络中的容器无法访问外网
docker run -d --name isolated --network secure-network nginx
docker exec isolated ping google.com # 应该失败
# 使用自定义 IPAM
docker network create \
--driver bridge \
--subnet=192.168.100.0/24 \
--ip-range=192.168.100.128/25 \
--gateway=192.168.100.1 \
custom-subnet
# 指定容器 IP
docker run -d --name fixed-ip --network custom-subnet --ip 192.168.100.130 nginx
本章总结
在本章中,我们全面学习了 Docker 网络机制:
- 网络基础:理解了 Docker 网络的基本概念和命名空间隔离
- 网络驱动:掌握了 Bridge、Host、None 和自定义网络的使用
- 端口映射:学会了各种端口映射配置和高级用法
- 实践应用:通过 Web 应用、微服务、负载均衡等场景加深理解
- 故障排查:了解了网络问题的诊断和解决方法
- 高级配置:掌握了网络别名、多网络连接、安全策略等高级特性
关键概念总结:
- 网络隔离:每个容器都有独立的网络命名空间
- 服务发现:自定义网络中容器可以通过名称互相访问
- 端口映射:将容器端口暴露到主机上
- 网络安全:通过网络隔离实现安全边界
最佳实践要点:
- 为不同的应用创建独立的自定义网络
- 避免使用默认 bridge 网络进行生产部署
- 合理规划端口映射,避免端口冲突
- 使用网络别名实现服务发现
- 实施网络隔离提高安全性
常见应用场景:
- 前后端分离应用的网络隔离
- 负载均衡和服务发现
- 多环境部署的网络规划
- 容器集群的网络管理
下一章我们将学习 Docker 数据卷和挂载,了解如何持久化容器数据。
第4章:Docker 数据卷和挂载
4.1 数据持久化的重要性
容器是无状态的,当容器删除时,容器内的数据也会丢失。为了实现数据持久化,Docker 提供了多种数据存储方案。
数据丢失问题演示
bash
# 创建一个容器并写入数据
docker run -it --name temp-container ubuntu:20.04 bash
# 在容器内创建文件
echo "Important data" > /tmp/important.txt
cat /tmp/important.txt
exit
# 删除容器
docker rm temp-container
# 重新创建同名容器,数据已丢失
docker run -it --name temp-container ubuntu:20.04 bash
cat /tmp/important.txt # 文件不存在
exit
docker rm temp-container
4.2 Docker 存储类型
Docker 提供三种主要的数据存储方式:
1. Volumes(数据卷)
数据卷是 Docker 管理的存储区域,存储在主机文件系统中,但完全由 Docker 管理。
bash
# 创建数据卷
docker volume create my-volume
# 查看所有数据卷
docker volume ls
# 查看数据卷详细信息
docker volume inspect my-volume
# 使用数据卷运行容器
docker run -d --name web-server -v my-volume:/usr/share/nginx/html nginx
# 在另一个容器中访问同一数据卷
docker run -it --rm -v my-volume:/data ubuntu:20.04 bash
# 在容器内创建文件
echo "<h1>Hello from Volume!</h1>" > /data/index.html
exit
# 测试 Web 服务器
curl http://localhost:80 # 如果映射了端口
# 清理
docker stop web-server
docker rm web-server
docker volume rm my-volume
2. Bind Mounts(绑定挂载)
绑定挂载将主机文件系统的目录或文件直接挂载到容器中。
bash
# 创建主机目录
mkdir -p /tmp/docker-tutorial/html
echo "<h1>Hello from Bind Mount!</h1>" > /tmp/docker-tutorial/html/index.html
# 使用绑定挂载运行容器
docker run -d \
--name bind-web \
-p 8080:80 \
-v /tmp/docker-tutorial/html:/usr/share/nginx/html \
nginx
# 测试访问
curl http://localhost:8080
# 在主机上修改文件
echo "<h1>Updated from Host!</h1>" > /tmp/docker-tutorial/html/index.html
# 再次测试,内容已更新
curl http://localhost:8080
# 清理
docker stop bind-web
docker rm bind-web
3. tmpfs Mounts(临时文件系统挂载)
tmpfs 挂载将数据存储在主机内存中,容器停止时数据会丢失。
bash
# 使用 tmpfs 挂载
docker run -d \
--name tmpfs-test \
--tmpfs /tmp:rw,noexec,nosuid,size=100m \
nginx
# 查看挂载信息
docker exec tmpfs-test mount | grep tmpfs
# 在 tmpfs 中写入数据
docker exec tmpfs-test bash -c "echo 'Temporary data' > /tmp/temp.txt"
docker exec tmpfs-test cat /tmp/temp.txt
# 重启容器,数据丢失
docker restart tmpfs-test
docker exec tmpfs-test cat /tmp/temp.txt # 文件不存在
# 清理
docker stop tmpfs-test
docker rm tmpfs-test
4.3 数据卷管理
数据卷操作
bash
# 创建数据卷
docker volume create app-data
docker volume create db-data
docker volume create logs
# 列出所有数据卷
docker volume ls
# 查看数据卷详细信息
docker volume inspect app-data
# 删除数据卷
docker volume rm logs
# 删除所有未使用的数据卷
docker volume prune
# 查看数据卷使用情况
docker system df
数据卷备份和恢复
这里 --rm
表示创建后删除停止的容器,常用语一次性任务。
bash
# 创建测试数据
docker volume create backup-demo
docker run --rm -v backup-demo:/data ubuntu:20.04 bash -c "
echo 'Important data 1' > /data/file1.txt
echo 'Important data 2' > /data/file2.txt
mkdir /data/subdir
echo 'Subdirectory data' > /data/subdir/file3.txt
"
# 备份数据卷
docker run --rm \
-v backup-demo:/source \
-v $(pwd):/backup \
ubuntu:20.04 \
tar czf /backup/backup-demo.tar.gz -C /source .
# 验证备份文件
ls -la backup-demo.tar.gz
# 创建新数据卷用于恢复
docker volume create restore-demo
# 恢复数据
docker run --rm \
-v restore-demo:/target \
-v $(pwd):/backup \
ubuntu:20.04 \
tar xzf /backup/backup-demo.tar.gz -C /target
# 验证恢复的数据
docker run --rm -v restore-demo:/data ubuntu:20.04 find /data -type f -exec cat {} \;
# 清理
docker volume rm backup-demo restore-demo
rm backup-demo.tar.gz
4.4 实践练习
练习1:数据库数据持久化
bash
# 创建数据库数据卷
docker volume create mysql-data
# 启动 MySQL 容器
docker run -d \
--name mysql-persistent \
-e MYSQL_ROOT_PASSWORD=mypassword \
-e MYSQL_DATABASE=testdb \
-v mysql-data:/var/lib/mysql \
-p 3306:3306 \
mysql:8.0
# 等待数据库启动
sleep 30
# 连接数据库并创建数据
docker exec -it mysql-persistent mysql -uroot -pmypassword -e "
USE testdb;
CREATE TABLE users (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50));
INSERT INTO users (name) VALUES ('Alice'), ('Bob'), ('Charlie');
SELECT * FROM users;
"
# 停止并删除容器
docker stop mysql-persistent
docker rm mysql-persistent
# 使用相同数据卷重新启动容器
docker run -d \
--name mysql-restored \
-e MYSQL_ROOT_PASSWORD=mypassword \
-v mysql-data:/var/lib/mysql \
-p 3306:3306 \
mysql:8.0
# 等待启动
sleep 30
# 验证数据仍然存在
docker exec -it mysql-restored mysql -uroot -pmypassword -e "
USE testdb;
SELECT * FROM users;
"
# 清理
docker stop mysql-restored
docker rm mysql-restored
docker volume rm mysql-data
练习2:多容器数据共享
bash
# 创建共享数据卷
docker volume create shared-data
# 启动数据生产者容器
docker run -d \
--name producer \
-v shared-data:/data \
ubuntu:20.04 \
bash -c "
while true; do
echo \"\$(date): Producer data\" >> /data/producer.log
sleep 5
done
"
# 启动数据消费者容器
docker run -d \
--name consumer \
-v shared-data:/data \
ubuntu:20.04 \
bash -c "
while true; do
if [ -f /data/producer.log ]; then
echo \"Consumer read: \$(tail -1 /data/producer.log)\"
fi
sleep 3
done
"
# 启动数据处理器容器
docker run -d \
--name processor \
-v shared-data:/data \
ubuntu:20.04 \
bash -c "
while true; do
if [ -f /data/producer.log ]; then
wc -l /data/producer.log > /data/stats.txt
echo \"Processed at \$(date)\" >> /data/stats.txt
fi
sleep 10
done
"
# 查看各容器日志
echo "Producer logs:"
docker logs producer --tail 5
echo "Consumer logs:"
docker logs consumer --tail 5
echo "Processor logs:"
docker logs processor --tail 5
# 查看共享数据
docker run --rm -v shared-data:/data ubuntu:20.04 ls -la /data
docker run --rm -v shared-data:/data ubuntu:20.04 cat /data/stats.txt
# 清理
docker stop producer consumer processor
docker rm producer consumer processor
docker volume rm shared-data
4.5 高级挂载选项
只读挂载
bash
# 只读绑定挂载
mkdir -p /tmp/docker-tutorial/readonly-data
echo "Read-only data" > /tmp/docker-tutorial/readonly-data/config.txt
docker run -it --rm \
-v /tmp/docker-tutorial/readonly-data:/data:ro \
ubuntu:20.04 bash
# 在容器内尝试写入(应该失败)
# echo "new data" > /data/config.txt # Permission denied
# cat /data/config.txt # 可以读取
# exit
挂载单个文件
bash
# 创建配置文件
echo "database_host=localhost" > /tmp/docker-tutorial/app.conf
# 挂载单个文件
docker run -it --rm \
-v /tmp/docker-tutorial/app.conf:/app/config/app.conf:ro \
ubuntu:20.04 bash
# 在容器内查看文件
# cat /app/config/app.conf
# exit
挂载选项
bash
# 使用特定的挂载选项
docker run -d \
--name mount-options-test \
--mount type=bind,source=/tmp/docker-tutorial,target=/data,readonly \
nginx
# 查看挂载信息
docker inspect mount-options-test | grep -A 10 "Mounts"
# 清理
docker stop mount-options-test
docker rm mount-options-test
4.6 性能和最佳实践
性能考虑
bash
# 比较不同存储类型的性能
# 1. 数据卷性能测试
docker volume create perf-volume
docker run --rm \
-v perf-volume:/data \
ubuntu:20.04 \
bash -c "time dd if=/dev/zero of=/data/test bs=1M count=100"
# 2. 绑定挂载性能测试
mkdir -p /tmp/docker-tutorial/perf-bind
docker run --rm \
-v /tmp/docker-tutorial/perf-bind:/data \
ubuntu:20.04 \
bash -c "time dd if=/dev/zero of=/data/test bs=1M count=100"
# 3. tmpfs 性能测试
docker run --rm \
--tmpfs /data:rw,size=200m \
ubuntu:20.04 \
bash -c "time dd if=/dev/zero of=/data/test bs=1M count=100"
# 清理
docker volume rm perf-volume
rm -rf /tmp/docker-tutorial/perf-bind
三种方式比较:
存储类型 | 性能特点 | 适用场景 |
---|---|---|
tmpfs | 最快 | 临时文件、缓存、高频读写的临时数据 |
数据卷 (Volume) | 较快 | 生产环境的首选 |
绑定挂载 (Bind Mount) | 最慢 | 开发调试、直接访问宿主机文件 |
最佳实践
bash
# 1. 使用命名数据卷而不是匿名数据卷
docker volume create app-logs
docker run -d -v app-logs:/var/log/app nginx
# 2. 为不同类型的数据使用不同的数据卷
docker volume create app-data # 应用数据
docker volume create app-config # 配置文件
docker volume create app-logs # 日志文件
# 3. 定期备份重要数据卷
docker run --rm \
-v app-data:/source:ro \
-v $(pwd):/backup \
ubuntu:20.04 \
tar czf /backup/app-data-$(date +%Y%m%d).tar.gz -C /source .
# 4. 监控数据卷使用情况
docker system df -v
# 清理
docker volume rm app-logs app-data app-config
本章总结
在本章中,我们深入学习了 Docker 数据卷和挂载机制:
- 数据持久化重要性:理解了容器数据丢失问题和持久化的必要性
- 存储类型:掌握了 Volumes、Bind Mounts、tmpfs Mounts 三种存储方式
- 数据卷管理:学会了数据卷的创建、查看、备份、恢复等操作
- 实践应用:通过数据库持久化、开发环境同步、多容器数据共享等场景加深理解
- 高级选项:了解了只读挂载、单文件挂载、挂载选项等高级用法
- 性能优化:掌握了不同存储类型的性能特点和最佳实践
关键概念总结:
- 数据卷:Docker 管理的持久化存储,推荐用于生产环境
- 绑定挂载:直接挂载主机目录,适合开发环境
- tmpfs 挂载:内存存储,适合临时数据
- 数据共享:多个容器可以共享同一个数据卷
存储选择指南:
- 生产数据:使用 Volumes
- 开发调试:使用 Bind Mounts
- 临时缓存:使用 tmpfs Mounts
- 配置文件:使用只读 Bind Mounts
- 日志文件:使用 Volumes 或 Bind Mounts
下一章我们将学习 Dockerfile 编写和镜像构建,了解如何创建自定义镜像。