Docker 完整教程 | 从基础到实战(3,4)
本教程侧重于命令实践和理解,提供可在本地环境测试的实例,每章结束都有总结要点。
目录
前边介绍了 Docker 基础概念和安装 以及 Docker 常用命令实践。本篇介绍 docker 容器的两个重要概念,网络机制和容器挂载。
- Docker 网络机制详解
- Docker 数据卷和挂载
第3章:Docker 网络机制详解
3.1 Docker 网络基础
Docker 网络是容器间通信和容器与外部世界通信的基础。理解网络机制对于构建复杂的容器化应用至关重要。
每个 Docker 容器都有自己的网络命名空间,包括:独立的网络接口,路由表,iptables 规则和端口空间。
查看网络信息
# 列出所有网络
docker network ls# 查看网络详细信息
docker network inspect bridge# 查看容器网络配置
docker inspect container_name | grep -A 20 "NetworkSettings"
3.2 Docker 网络驱动类型
运行 docker network ls
查看网络,比如
❯ docker network lsNETWORK 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 访问外网
- 使用场景:大部分单机容器应用的默认选择
常用命令:
# 查看默认 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 转换开销
- 风险:安全性较低,容器可以直接访问主机网络
# 使用 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 网络模式下,容器没有网络接口。
# 运行无网络容器
docker run -d --network none --name no-network alpine sleep 3600# 查看网络接口(只有 loopback)
docker exec no-network ip addr show
4. 自定义 Bridge 网络
自定义网络提供更好的隔离性和容器间的名称解析。
# 创建自定义网络
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
修改全局默认配置,举个例子:
# 编辑 /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 池
# 重启 Docker 服务使配置生效
sudo systemctl restart docker
当然,也可以为单个容器创建指定 IP 段网络,比如
```bash
# 创建开发环境网络
docker network create \--subnet=10.10.0.0/24 \--gateway=10.10.0.1 \dev-network
3.3 端口映射
基本端口映射
通过 -p <host_port>:<container_port>
可以将容器端口映射到主机端口。
# 映射单个端口
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
高级端口配置
# 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 应用 + 数据库通信
# 创建自定义网络
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:负载均衡配置
# 创建负载均衡网络
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}; docurl -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 网络故障排查
常用网络调试命令
# 在容器中安装网络工具
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
常见网络问题
# 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 轮询。
# 创建网络
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 地址,每次访问会轮询到不同的容器
多网络连接
# 创建多个网络
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 # 应该失败
网络策略和安全
# 创建隔离网络
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 提供了多种数据存储方案。
数据丢失问题演示
# 创建一个容器并写入数据
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 管理。
# 创建数据卷
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(绑定挂载)
绑定挂载将主机文件系统的目录或文件直接挂载到容器中。
# 创建主机目录
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 挂载将数据存储在主机内存中,容器停止时数据会丢失。
# 使用 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 数据卷管理
数据卷操作
# 创建数据卷
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
表示创建后删除停止的容器,常用语一次性任务。
# 创建测试数据
docker volume create backup-demo
docker run --rm -v backup-demo:/data ubuntu:20.04 bash -c "echo 'Important data 1' > /data/file1.txtecho 'Important data 2' > /data/file2.txtmkdir /data/subdirecho '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:数据库数据持久化
# 创建数据库数据卷
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:多容器数据共享
# 创建共享数据卷
docker volume create shared-data# 启动数据生产者容器
docker run -d \--name producer \-v shared-data:/data \ubuntu:20.04 \bash -c "while true; doecho \"\$(date): Producer data\" >> /data/producer.logsleep 5done"# 启动数据消费者容器
docker run -d \--name consumer \-v shared-data:/data \ubuntu:20.04 \bash -c "while true; doif [ -f /data/producer.log ]; thenecho \"Consumer read: \$(tail -1 /data/producer.log)\"fisleep 3done"# 启动数据处理器容器
docker run -d \--name processor \-v shared-data:/data \ubuntu:20.04 \bash -c "while true; doif [ -f /data/producer.log ]; thenwc -l /data/producer.log > /data/stats.txtecho \"Processed at \$(date)\" >> /data/stats.txtfisleep 10done"# 查看各容器日志
echo "Producer logs:"
docker logs producer --tail 5echo "Consumer logs:"
docker logs consumer --tail 5echo "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 高级挂载选项
只读挂载
# 只读绑定挂载
mkdir -p /tmp/docker-tutorial/readonly-data
echo "Read-only data" > /tmp/docker-tutorial/readonly-data/config.txtdocker 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
挂载单个文件
# 创建配置文件
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
挂载选项
# 使用特定的挂载选项
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 性能和最佳实践
性能考虑
# 比较不同存储类型的性能
# 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) | 最慢 | 开发调试、直接访问宿主机文件 |
最佳实践
# 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 编写和镜像构建,了解如何创建自定义镜像。