Docker 容器与网络

Docker学习笔记:容器与网络

本文是 Docker 学习笔记的"容器""网络"的整理,包含总结和详细。适合正在学习 Docker 容器操作、资源限制、底层技术以及容器网络通信的同学参考。实验环境:CentOS Stream 8 + Docker 26.1.3。

### 文章目录

  • [Docker学习笔记:容器与网络](#文章目录 Docker学习笔记:容器与网络 @[toc] 前言 容器 01 如何运行容器? 02 两种进入容器的方法 03 运行容器的最佳实践 04 容器常用操作 05 容器状态机 06 限制容器对内存的使用 07 限制容器对CPU的使用 08 export和import容器 09 容器底层实现技术 容器常用操作命令小结 网络 01 none和host网络的适用场景 02 学容器必须懂bridge网络 03 自定义网络 04 理解容器之间的连通性 05 容器间通信的三种方式 06 容器如何访问外部世界 07 外部世界如何访问容器 总结)
  • [@toc](#文章目录 Docker学习笔记:容器与网络 @[toc] 前言 容器 01 如何运行容器? 02 两种进入容器的方法 03 运行容器的最佳实践 04 容器常用操作 05 容器状态机 06 限制容器对内存的使用 07 限制容器对CPU的使用 08 export和import容器 09 容器底层实现技术 容器常用操作命令小结 网络 01 none和host网络的适用场景 02 学容器必须懂bridge网络 03 自定义网络 04 理解容器之间的连通性 05 容器间通信的三种方式 06 容器如何访问外部世界 07 外部世界如何访问容器 总结)
  • [前言](#文章目录 Docker学习笔记:容器与网络 @[toc] 前言 容器 01 如何运行容器? 02 两种进入容器的方法 03 运行容器的最佳实践 04 容器常用操作 05 容器状态机 06 限制容器对内存的使用 07 限制容器对CPU的使用 08 export和import容器 09 容器底层实现技术 容器常用操作命令小结 网络 01 none和host网络的适用场景 02 学容器必须懂bridge网络 03 自定义网络 04 理解容器之间的连通性 05 容器间通信的三种方式 06 容器如何访问外部世界 07 外部世界如何访问容器 总结)
  • [容器](#文章目录 Docker学习笔记:容器与网络 @[toc] 前言 容器 01 如何运行容器? 02 两种进入容器的方法 03 运行容器的最佳实践 04 容器常用操作 05 容器状态机 06 限制容器对内存的使用 07 限制容器对CPU的使用 08 export和import容器 09 容器底层实现技术 容器常用操作命令小结 网络 01 none和host网络的适用场景 02 学容器必须懂bridge网络 03 自定义网络 04 理解容器之间的连通性 05 容器间通信的三种方式 06 容器如何访问外部世界 07 外部世界如何访问容器 总结)
  • [01 如何运行容器?](#文章目录 Docker学习笔记:容器与网络 @[toc] 前言 容器 01 如何运行容器? 02 两种进入容器的方法 03 运行容器的最佳实践 04 容器常用操作 05 容器状态机 06 限制容器对内存的使用 07 限制容器对CPU的使用 08 export和import容器 09 容器底层实现技术 容器常用操作命令小结 网络 01 none和host网络的适用场景 02 学容器必须懂bridge网络 03 自定义网络 04 理解容器之间的连通性 05 容器间通信的三种方式 06 容器如何访问外部世界 07 外部世界如何访问容器 总结)
  • [02 两种进入容器的方法](#文章目录 Docker学习笔记:容器与网络 @[toc] 前言 容器 01 如何运行容器? 02 两种进入容器的方法 03 运行容器的最佳实践 04 容器常用操作 05 容器状态机 06 限制容器对内存的使用 07 限制容器对CPU的使用 08 export和import容器 09 容器底层实现技术 容器常用操作命令小结 网络 01 none和host网络的适用场景 02 学容器必须懂bridge网络 03 自定义网络 04 理解容器之间的连通性 05 容器间通信的三种方式 06 容器如何访问外部世界 07 外部世界如何访问容器 总结)
  • [03 运行容器的最佳实践](#文章目录 Docker学习笔记:容器与网络 @[toc] 前言 容器 01 如何运行容器? 02 两种进入容器的方法 03 运行容器的最佳实践 04 容器常用操作 05 容器状态机 06 限制容器对内存的使用 07 限制容器对CPU的使用 08 export和import容器 09 容器底层实现技术 容器常用操作命令小结 网络 01 none和host网络的适用场景 02 学容器必须懂bridge网络 03 自定义网络 04 理解容器之间的连通性 05 容器间通信的三种方式 06 容器如何访问外部世界 07 外部世界如何访问容器 总结)
  • [04 容器常用操作](#文章目录 Docker学习笔记:容器与网络 @[toc] 前言 容器 01 如何运行容器? 02 两种进入容器的方法 03 运行容器的最佳实践 04 容器常用操作 05 容器状态机 06 限制容器对内存的使用 07 限制容器对CPU的使用 08 export和import容器 09 容器底层实现技术 容器常用操作命令小结 网络 01 none和host网络的适用场景 02 学容器必须懂bridge网络 03 自定义网络 04 理解容器之间的连通性 05 容器间通信的三种方式 06 容器如何访问外部世界 07 外部世界如何访问容器 总结)
  • [05 容器状态机](#文章目录 Docker学习笔记:容器与网络 @[toc] 前言 容器 01 如何运行容器? 02 两种进入容器的方法 03 运行容器的最佳实践 04 容器常用操作 05 容器状态机 06 限制容器对内存的使用 07 限制容器对CPU的使用 08 export和import容器 09 容器底层实现技术 容器常用操作命令小结 网络 01 none和host网络的适用场景 02 学容器必须懂bridge网络 03 自定义网络 04 理解容器之间的连通性 05 容器间通信的三种方式 06 容器如何访问外部世界 07 外部世界如何访问容器 总结)
  • [06 限制容器对内存的使用](#文章目录 Docker学习笔记:容器与网络 @[toc] 前言 容器 01 如何运行容器? 02 两种进入容器的方法 03 运行容器的最佳实践 04 容器常用操作 05 容器状态机 06 限制容器对内存的使用 07 限制容器对CPU的使用 08 export和import容器 09 容器底层实现技术 容器常用操作命令小结 网络 01 none和host网络的适用场景 02 学容器必须懂bridge网络 03 自定义网络 04 理解容器之间的连通性 05 容器间通信的三种方式 06 容器如何访问外部世界 07 外部世界如何访问容器 总结)
  • [07 限制容器对CPU的使用](#文章目录 Docker学习笔记:容器与网络 @[toc] 前言 容器 01 如何运行容器? 02 两种进入容器的方法 03 运行容器的最佳实践 04 容器常用操作 05 容器状态机 06 限制容器对内存的使用 07 限制容器对CPU的使用 08 export和import容器 09 容器底层实现技术 容器常用操作命令小结 网络 01 none和host网络的适用场景 02 学容器必须懂bridge网络 03 自定义网络 04 理解容器之间的连通性 05 容器间通信的三种方式 06 容器如何访问外部世界 07 外部世界如何访问容器 总结)
  • [08 export和import容器](#文章目录 Docker学习笔记:容器与网络 @[toc] 前言 容器 01 如何运行容器? 02 两种进入容器的方法 03 运行容器的最佳实践 04 容器常用操作 05 容器状态机 06 限制容器对内存的使用 07 限制容器对CPU的使用 08 export和import容器 09 容器底层实现技术 容器常用操作命令小结 网络 01 none和host网络的适用场景 02 学容器必须懂bridge网络 03 自定义网络 04 理解容器之间的连通性 05 容器间通信的三种方式 06 容器如何访问外部世界 07 外部世界如何访问容器 总结)
  • [09 容器底层实现技术](#文章目录 Docker学习笔记:容器与网络 @[toc] 前言 容器 01 如何运行容器? 02 两种进入容器的方法 03 运行容器的最佳实践 04 容器常用操作 05 容器状态机 06 限制容器对内存的使用 07 限制容器对CPU的使用 08 export和import容器 09 容器底层实现技术 容器常用操作命令小结 网络 01 none和host网络的适用场景 02 学容器必须懂bridge网络 03 自定义网络 04 理解容器之间的连通性 05 容器间通信的三种方式 06 容器如何访问外部世界 07 外部世界如何访问容器 总结)
  • [容器常用操作命令小结](#文章目录 Docker学习笔记:容器与网络 @[toc] 前言 容器 01 如何运行容器? 02 两种进入容器的方法 03 运行容器的最佳实践 04 容器常用操作 05 容器状态机 06 限制容器对内存的使用 07 限制容器对CPU的使用 08 export和import容器 09 容器底层实现技术 容器常用操作命令小结 网络 01 none和host网络的适用场景 02 学容器必须懂bridge网络 03 自定义网络 04 理解容器之间的连通性 05 容器间通信的三种方式 06 容器如何访问外部世界 07 外部世界如何访问容器 总结)
  • [网络](#文章目录 Docker学习笔记:容器与网络 @[toc] 前言 容器 01 如何运行容器? 02 两种进入容器的方法 03 运行容器的最佳实践 04 容器常用操作 05 容器状态机 06 限制容器对内存的使用 07 限制容器对CPU的使用 08 export和import容器 09 容器底层实现技术 容器常用操作命令小结 网络 01 none和host网络的适用场景 02 学容器必须懂bridge网络 03 自定义网络 04 理解容器之间的连通性 05 容器间通信的三种方式 06 容器如何访问外部世界 07 外部世界如何访问容器 总结)
  • [01 none和host网络的适用场景](#文章目录 Docker学习笔记:容器与网络 @[toc] 前言 容器 01 如何运行容器? 02 两种进入容器的方法 03 运行容器的最佳实践 04 容器常用操作 05 容器状态机 06 限制容器对内存的使用 07 限制容器对CPU的使用 08 export和import容器 09 容器底层实现技术 容器常用操作命令小结 网络 01 none和host网络的适用场景 02 学容器必须懂bridge网络 03 自定义网络 04 理解容器之间的连通性 05 容器间通信的三种方式 06 容器如何访问外部世界 07 外部世界如何访问容器 总结)
  • [02 学容器必须懂bridge网络](#文章目录 Docker学习笔记:容器与网络 @[toc] 前言 容器 01 如何运行容器? 02 两种进入容器的方法 03 运行容器的最佳实践 04 容器常用操作 05 容器状态机 06 限制容器对内存的使用 07 限制容器对CPU的使用 08 export和import容器 09 容器底层实现技术 容器常用操作命令小结 网络 01 none和host网络的适用场景 02 学容器必须懂bridge网络 03 自定义网络 04 理解容器之间的连通性 05 容器间通信的三种方式 06 容器如何访问外部世界 07 外部世界如何访问容器 总结)
  • [03 自定义网络](#文章目录 Docker学习笔记:容器与网络 @[toc] 前言 容器 01 如何运行容器? 02 两种进入容器的方法 03 运行容器的最佳实践 04 容器常用操作 05 容器状态机 06 限制容器对内存的使用 07 限制容器对CPU的使用 08 export和import容器 09 容器底层实现技术 容器常用操作命令小结 网络 01 none和host网络的适用场景 02 学容器必须懂bridge网络 03 自定义网络 04 理解容器之间的连通性 05 容器间通信的三种方式 06 容器如何访问外部世界 07 外部世界如何访问容器 总结)
  • [04 理解容器之间的连通性](#文章目录 Docker学习笔记:容器与网络 @[toc] 前言 容器 01 如何运行容器? 02 两种进入容器的方法 03 运行容器的最佳实践 04 容器常用操作 05 容器状态机 06 限制容器对内存的使用 07 限制容器对CPU的使用 08 export和import容器 09 容器底层实现技术 容器常用操作命令小结 网络 01 none和host网络的适用场景 02 学容器必须懂bridge网络 03 自定义网络 04 理解容器之间的连通性 05 容器间通信的三种方式 06 容器如何访问外部世界 07 外部世界如何访问容器 总结)
  • [05 容器间通信的三种方式](#文章目录 Docker学习笔记:容器与网络 @[toc] 前言 容器 01 如何运行容器? 02 两种进入容器的方法 03 运行容器的最佳实践 04 容器常用操作 05 容器状态机 06 限制容器对内存的使用 07 限制容器对CPU的使用 08 export和import容器 09 容器底层实现技术 容器常用操作命令小结 网络 01 none和host网络的适用场景 02 学容器必须懂bridge网络 03 自定义网络 04 理解容器之间的连通性 05 容器间通信的三种方式 06 容器如何访问外部世界 07 外部世界如何访问容器 总结)
  • [06 容器如何访问外部世界](#文章目录 Docker学习笔记:容器与网络 @[toc] 前言 容器 01 如何运行容器? 02 两种进入容器的方法 03 运行容器的最佳实践 04 容器常用操作 05 容器状态机 06 限制容器对内存的使用 07 限制容器对CPU的使用 08 export和import容器 09 容器底层实现技术 容器常用操作命令小结 网络 01 none和host网络的适用场景 02 学容器必须懂bridge网络 03 自定义网络 04 理解容器之间的连通性 05 容器间通信的三种方式 06 容器如何访问外部世界 07 外部世界如何访问容器 总结)
  • [07 外部世界如何访问容器](#文章目录 Docker学习笔记:容器与网络 @[toc] 前言 容器 01 如何运行容器? 02 两种进入容器的方法 03 运行容器的最佳实践 04 容器常用操作 05 容器状态机 06 限制容器对内存的使用 07 限制容器对CPU的使用 08 export和import容器 09 容器底层实现技术 容器常用操作命令小结 网络 01 none和host网络的适用场景 02 学容器必须懂bridge网络 03 自定义网络 04 理解容器之间的连通性 05 容器间通信的三种方式 06 容器如何访问外部世界 07 外部世界如何访问容器 总结)
  • [总结](#文章目录 Docker学习笔记:容器与网络 @[toc] 前言 容器 01 如何运行容器? 02 两种进入容器的方法 03 运行容器的最佳实践 04 容器常用操作 05 容器状态机 06 限制容器对内存的使用 07 限制容器对CPU的使用 08 export和import容器 09 容器底层实现技术 容器常用操作命令小结 网络 01 none和host网络的适用场景 02 学容器必须懂bridge网络 03 自定义网络 04 理解容器之间的连通性 05 容器间通信的三种方式 06 容器如何访问外部世界 07 外部世界如何访问容器 总结)

前言

最近系统学习了 Docker 的容器和网络部分。容器部分涉及生命周期管理、资源限制、底层 cgroup 和 namespace;网络部分涉及 none/host/bridge 三种原生网络、自定义网络、容器间通信、NAT 和端口映射。学完之后对"容器到底是什么""容器怎么通信"有了更清晰的认识。


容器

01 如何运行容器?

docker run 是启动容器的方法,它等价于 docker create + docker start。

容器启动时执行的命令可以通过三种方式指定:

  1. Dockerfile 中的 CMD 指令;
  2. Dockerfile 中的 ENTRYPOINT 指令;
  3. 在 docker run 命令行中指定。

容器的生命周期依赖于启动时执行的命令,只要该命令不结束,容器就不会退出。

使用 -d 参数可以让容器以后台方式运行。

可以通过 --name 为容器命名,后续操作可使用短ID、长ID或名称来指定容器。

bash 复制代码
# 使用 ubuntu 镜像创建容器,此时容器处于 Created 状态
docker create ubuntu

# 查看所有容器,包括未启动的
docker ps -a

# 启动刚创建的容器
docker start e1aa0ca86b2

# 再次查看状态,发现容器已退出(Exited),因为 /bin/bash 执行完就结束了
docker ps -a

# 直接运行并执行 pwd 命令,容器执行完立即退出
docker run ubuntu pwd
# 输出:/

# 让容器长期运行:执行一个死循环
docker run ubuntu /bin/bash -c "while true; do sleep 1; echo hahaha; done"
# 此时容器保持运行,但占用当前终端

# 使用 -d 后台运行
docker run -d ubuntu /bin/bash -c "while true; do sleep 1; echo hahaha; done"
# 返回容器长ID

# 查看运行中的容器
docker ps
# 可以看到容器状态为 Up

# 停止容器
docker stop 081be2bc2e1d

# 通过 --name 指定容器名
docker run --name "my_http_server" -d httpd
docker ps
# 可以看到 NAMES 列显示 my_http_server

注意 :docker run 如果指定了命令,容器执行完该命令就会退出。想要保持运行,需要让命令一直不结束,比如 while true 或运行一个守护进程。


02 两种进入容器的方法

进入容器有两种常用方法:

  1. docker attach :attach 到容器启动命令的终端,不会启动新的进程。退出方式为 Ctrl+p 然后 Ctrl+q。
  2. docker exec :在容器中打开新的终端,并且可以启动新的进程。常用 docker exec -it <container> bash|sh。

如果想直接查看启动命令的输出,用 attach;其他情况使用 exec。

查看容器日志推荐使用 docker logs -f。

bash 复制代码
# 启动一个持续输出日志的容器
docker run -d ubuntu /bin/bash -c "while true; do sleep 1; echo I_am_in_container; done"
# 假设返回长ID dc508b94447f...

# 使用 attach 进入
docker attach dc508b94447f
# 会看到每隔一秒打印 I_am_in_container
# 按 Ctrl+p 再按 Ctrl+q 退出 attach,容器继续运行

# 使用 exec 进入容器
docker exec -it dc508b94447f bash
# 进入后可以执行命令
ps -elf
# 可以看到容器启动进程 while 以及当前 bash 进程
exit

# 使用 docker logs -f 查看日志
docker logs -f dc508b94447f
# 持续打印输出,类似 tail -f

踩坑 :docker attach 退出时如果直接按 Ctrl+C,可能会把容器主进程杀掉,导致容器停止。推荐用 Ctrl+p Ctrl+q 组合键退出。


03 运行容器的最佳实践

按用途容器可分为两类:

  1. 服务类容器 :以 daemon 形式运行,对外提供服务,如 web server、数据库。使用 -d 后台启动,排查问题用 exec -it 进入。
  2. 工具类容器 :提供临时工作环境,通常以 run -it 方式运行,如 busybox。执行 exit 退出终端,同时容器停止。

容器运行相关:

  • 当 CMD、ENTRYPOINT 或 docker run 命令行指定的命令运行结束时,容器停止。
  • 通过 -d 参数在后台启动容器。
  • 通过 exec -it 可进入容器并执行命令。
  • 指定容器的三种方法:短ID、长ID、容器名称。
  • 可通过 --name 为容器命名,docker rename 重命名容器。
bash 复制代码
# 服务类容器示例
docker run --name "my_http_server" -d httpd
docker ps
# 容器后台运行,COMMAND 为 httpd-foreground

# 工具类容器示例
docker run -it busybox
# 进入交互式终端
wget www.baidu.com
# 可以访问互联网
exit
# 退出后容器停止

# 重命名容器
docker rename my_http_server web_server
docker ps
# NAMES 变为 web_server

04 容器常用操作

  • stop/start/restart :
    • docker stop:向容器进程发送 SIGTERM 信号,停止容器。
    • docker kill:发送 SIGKILL 信号,快速停止容器。
    • docker start:重新启动处于停止状态的容器,保留第一次启动时的所有参数。
    • docker restart:依次执行 docker stop 和 docker start。
  • 自动重启 :
    • --restart=always:无论容器因何种原因退出(不包括 docker stop),立即重启。
    • --restart=on-failure:3:如果启动进程退出代码非0,则重启容器,最多重启3次。
  • pause/unpause :
    • docker pause:暂停容器,处于暂停状态的容器不会占用 CPU 资源。
    • docker unpause:恢复运行。
  • 删除容器 :
    • docker rm:删除容器,可一次指定多个。
    • 批量删除已退出容器:docker rm -f $(docker ps -aq -f status=exited)
    • 删除所有容器(慎用):docker rm -f $(docker ps -aq)
bash 复制代码
# 启动一个 httpd 容器
docker run -d --name httpd_test httpd
docker ps

# 停止容器
docker stop httpd_test
docker ps -a
# 状态变为 Exited

# 重新启动
docker start httpd_test
docker ps
# 状态变为 Up

# 重启容器
docker restart httpd_test

# 测试 --restart=always
docker run -d --restart=always --name httpd11 httpd
docker attach httpd11
# 按 Ctrl+C 终止容器进程,触发重启
docker ps
# 发现 httpd11 已自动重启

# 管理员停止容器,不会自动重启
docker stop httpd11
docker ps -a
# 状态为 Exited,未重启

# 暂停和恢复
docker run -d --name httpd_pause httpd
docker pause httpd_pause
docker ps
# 状态显示 Paused
docker unpause httpd_pause
docker ps
# 状态恢复 Up

# 删除容器
docker rm 35af7150bd17 9769bb915803
# 批量删除已退出容器
docker rm -f $(docker ps -aq -f status=exited)

注意 :docker rm -f $(docker ps -aq) 会删除所有容器,生产环境慎用!


05 容器状态机

容器状态转换图描述了容器各种状态之间的转换关系。主要状态包括:

  • Created:容器已创建,但未启动。
  • Running:容器正在运行。
  • Stopped:容器已停止。
  • Paused:容器已暂停。
  • Deleted:容器已删除。

docker create 创建的容器处于 Created 状态;docker start 将以后台方式启动容器;docker run 命令实际上是 docker create 和 docker start 的组合。

--restart 生效条件:只有当容器的启动进程退出时(正常退出或非正常退出,如 OOM),--restart 才生效。如果容器是因为执行 docker stop 或 docker kill 退出,则不会自动重启。

bash 复制代码
# 创建容器,状态为 Created
docker create httpd
docker ps -a
# STATUS 显示 Created

# 启动容器
docker start dcc931f8d444
docker ps
# STATUS 显示 Up

# 测试 --restart=always 在进程退出时生效
docker run -d --restart=always --name httpd11 httpd
docker attach httpd11
# 按 Ctrl+C 触发进程退出
docker ps -a
# 发现 httpd11 已自动重启,状态为 Up

# 管理员停止容器,不会触发重启
docker stop httpd11
docker ps -a
# 状态为 Exited,未重启

06 限制容器对内存的使用

Docker 通过 cgroup 来控制容器使用的资源配额,包括 CPU、内存、磁盘三大方面。

cgroup 是 Linux 内核提供的一种可以限制、记录、隔离进程组所使用的物理资源的机制。

cgroup 子系统包括:blkio、cpu、cpuacct、cpuset、devices、freezer、memory、net_cls、ns、perf_event 等。

内存限额相关参数:

  • -m 或 --memory:设置内存的使用限额,例如 100M、2G。
  • --memory-swap:设置内存 + swap 的使用限额。

默认情况下:

  • 如果 --memory-swap 设置为 0 或不设置,则容器可以使用的 swap 大小为 -m 值的两倍。
  • 如果 --memory-swap 的值和 -m 值相同,则容器不能使用 swap。
  • 如果 --memory-swap 值为 -1,表示容器程序使用的内存受限,而可以使用的 swap 空间不受限制。
bash 复制代码
# 构建 stress 工具镜像
vim Dockerfile
# 内容:
# FROM ubuntu
# MAINTAINER xianyu "xianyu@qq.com"
# RUN apt-get -y update && apt-get -y install stress
# ENTRYPOINT ["/usr/bin/stress"]

docker build -t ubuntu-with-stress .

# 运行容器,限制内存 200M,swap 300M,分配 280M 内存
docker run -it -m 200M --memory-swap=300M ubuntu-with-stress --vm 1 --vm-bytes 280M -v
# 输出显示 stress 正常工作,分配和释放内存

# 分配超过 300M 内存
docker run -it -m 200M --memory-swap=300M ubuntu-with-stress --vm 1 --vm-bytes 310M
# stress 报错,容器退出
# 输出:stress: FAIL: [1] (416) <-- worker 7 got signal 9

# 只指定 -m,不指定 --memory-swap
docker run -it -m 200M ubuntu-with-stress
# 容器最多使用 200M 物理内存和 200M swap

07 限制容器对CPU的使用

默认设置下,所有容器可以平等地使用 host CPU 资源并且没有限制。

Docker 可以通过 -c 或 --cpu-shares 设置容器使用 CPU 的权重。如果不指定,默认值为 1024。

--cpu-shares 的值不能保证可以获得 1 个 vCPU 或者多少 GHz 的 CPU 资源,仅仅只是一个弹性的加权值。

只有在同时运行多个容器时,容器的 CPU 加权的效果才能体现出来。

这种按权重分配 CPU 只会发生在 CPU 资源紧张的情况下。如果某个容器处于空闲状态,其他容器也可以分配到全部可用的 CPU。

bash 复制代码
# 启动 container_A,cpu share 为 1024
docker run --name "container_A" -it -c 1024 ubuntu-with-stress --cpu 4 -v

# 再开一个窗口,启动 container_B,cpu share 为 512
docker run --name "container_B" -it -c 512 ubuntu-with-stress --cpu 4 -v

# 在 host 中执行 top,查看 CPU 使用情况
top
# 可以看到两个容器的 CPU 占用比例约为 2:1

# 如果 container_A 空闲,container_B 可以用满整颗 CPU

08 export和import容器

  • docker export:将容器导出为一个 tar 包,不管容器是否处于运行状态。导出文件会丢失所有元数据和历史记录,仅保存容器当时的状态。
  • docker import:将 export 导出的 tar 包导入成为镜像,可指定 repository 和 tag。
  • docker save:将镜像保存为文件,save 会保存该镜像的所有元数据和历史记录。
  • 对比:docker export 导出的文件不能使用 docker load 导入;docker save 导出的文件不能使用 docker import 导入(会报错)。
bash 复制代码
# 创建容器
docker run -d --name httpd1 httpd
docker ps -a

# 导出容器为 tar 包
docker export httpd1 -o myhttpd.tar
ls

# 导入为镜像
docker import myhttpd.tar myweb:v1
docker images
# 可以看到 myweb:v1,但丢失了历史记录

# 尝试用 docker load 导入 export 文件(会报错)
docker load -i myhttpd.tar
# 输出:open /var/lib/docker/tmp/docker-import-.../boot/json: no such file or directory

09 容器底层实现技术

容器底层最重要的两种技术是 cgroup 和 namespace:

  • cgroup:实现资源限额,可以设置进程使用 CPU、内存和 IO 资源的限额。
  • namespace :实现资源隔离,包括:
    • UTS namespace:让容器有自己的 hostname。
    • IPC namespace:让容器拥有自己的共享内存和信号量。
    • PID namespace:让容器拥有独立的进程空间。
    • Network namespace:让容器拥有自己独立的网卡、IP、路由等资源。
    • User namespace:让容器能够管理自己的用户,host 不能看到容器中创建的用户。
bash 复制代码
# cgroup 示例:设置 --cpu-shares=512
docker run -it --cpu-shares 512 ubuntu-with-stress -c 1 -v
# 查看容器ID
docker ps
# 在 /sys/fs/cgroup/cpu/docker/<容器长ID>/cpu.shares 中可以看到 512

# UTS namespace:设置 hostname
docker run -h myhost -it ubuntu
root@myhost:/# hostname
myhost

# User namespace:在容器中创建用户
docker exec -it a80f1f8b692c bash
root@a80f1f8b692c:/# useradd qd
root@a80f1f8b692c:/# exit
# 在宿主机中查看该用户
su - gaoqd
# 输出:su: user gaoqd does not exist

容器常用操作命令小结

命令 说明
create 创建容器
run 运行容器
pause 暂停容器
unpause 取消暂停继续运行容器
stop 发送 SIGTERM 停止容器
kill 发送 SIGKILL 快速停止容器
start 启动容器
restart 重启容器
attach attach 到容器启动进程的终端
exec 在容器中启动新进程,通常使用 -it 参数
logs 显示容器启动进程的控制台输出,用 -f 持续打印
rm 从磁盘中删除容器

网络

01 none和host网络的适用场景

Docker 安装时会自动在 host 上创建三个网络:bridge、host、none。可用 docker network ls 查看。

  • none 网络 :driver 类型是 null,IPAM 字段为空。挂在这个网络下的容器除了 lo,没有其他任何网卡。容器创建时可以通过 --network=none 指定使用 none 网络。适用场景:一些对安全性要求高并且不需要联网的应用,比如生成随机密码的容器。
  • host 网络 :挂在 host 网络上的容器共享宿主机的 network namespace,即容器的网络配置与 host 网络配置完全一样。可以通过 --network=host 指定。使用场景:对网络传输效率有较高要求的容器;或者让容器直接配置 host 网络,比如某些跨 host 的网络解决方案。不便之处是牺牲灵活性,要考虑端口冲突问题。
bash 复制代码
# 查看网络列表
docker network ls

# 查看 none 网络详情
docker network inspect none

# 使用 none 网络启动容器
docker run -it --network=none busybox
/ # ifconfig
# 只看到 lo,没有 eth0

# 使用 host 网络启动容器
docker run -it --network=host busybox
/ # ip a
# 可以看到 host 的所有网卡,包括 ens160、docker0 等
/ # hostname
docker
# hostname 与宿主机相同

02 学容器必须懂bridge网络

Docker 安装时会创建一个命名为 docker0 的 Linux bridge,可以理解为一个软件交换机。如果不指定 --network,创建的容器默认都会挂到 docker0 上。

创建容器时,会创建一对 veth pair 接口:

  • 一端在容器内,即 eth0;
  • 另一端在本地并被挂载到 docker0 网桥,名称以 veth 开头。

通过这种方式,主机可以跟容器通信,容器之间也可以相互通信。

bash 复制代码
# 安装 bridge-utils
yum install -y bridge-utils

# 查看网桥
brctl show
# 输出 docker0,此时没有接口

# 创建容器
docker run -itd --name busybox1 busybox

# 再次查看网桥
brctl show
# 可以看到 docker0 上挂了一个 veth 接口,如 vethddb2744

# 查看容器网络配置
docker exec -it busybox1 sh
/ # ip a
# 看到 eth0@if25,IP 为 172.17.0.2/16

# 在宿主机查看
ip a
# 看到 25: vethddb2744@if24,证明 veth pair 关系

# 查看 bridge 网络详情
docker network inspect bridge
# 看到 Subnet: 172.17.0.0/16,Gateway: 172.17.0.1

03 自定义网络

除了 none、host、bridge 这三个自动创建的网络,用户也可以根据业务需要创建 user-defined 网络。Docker 提供三种 user-defined 网络驱动:bridge、overlay 和 macvlan。overlay 和 macvlan 用于创建跨主机的网络。

可通过 bridge 驱动创建类似前面默认的 bridge 网络:

bash 复制代码
docker network create --driver bridge my_net

Docker 会自动分配一个 IP 网段,如 172.18.0.0/16。

也可以自己指定 IP 网段:

bash 复制代码
docker network create --driver bridge --subnet 172.22.16.0/24 --gateway 172.22.16.1 my_net2

只有使用 --subnet 创建的网络才能指定静态 IP(--ip)。

bash 复制代码
# 创建自定义网络 my_net
docker network create --driver bridge my_net
# 查看网桥,新增 br-89f7bc11b602
brctl show

# 创建指定子网和网关的网络 my_net2
docker network create --driver bridge --subnet 172.22.16.0/24 --gateway 172.22.16.1 my_net2
brctl show
# 新增 br-ec761bc51778

# 查看 my_net2 详情
docker network inspect my_net2
# Subnet 为 172.22.16.0/24,Gateway 为 172.22.16.1

# 使用新网络启动容器
docker run -it --network=my_net2 --name busybox2 busybox
/ # ip a
# eth0 的 IP 为 172.22.16.2/24

# 指定静态 IP
docker run -it --network=my_net2 --ip 172.22.16.8 --name busybox3 busybox
/ # ip a
# eth0 的 IP 为 172.22.16.8/24

04 理解容器之间的连通性

同一网络中的容器、网关之间都是可以通信的。

不同网络默认不能通信,因为 iptables 的 DOCKER-ISOLATION 规则 DROP 掉了网桥之间的双向流量。

如果希望不同网络的容器通信,可以通过 docker network connect 为容器添加一块其他网络的网卡。

bash 复制代码
# busybox2 和 busybox3 都在 my_net2,可以互通
docker exec -it busybox2 sh
/ # ping -c 3 172.22.16.8
# 通

# busybox2 ping busybox1(默认 bridge 网络)
/ # ping -c 3 172.17.0.2
# 不通,100% packet loss

# 将 busybox1 连接到 my_net2
docker network connect my_net2 busybox1

# 再次查看 busybox1 网络
docker exec -it busybox1 sh
/ # ip a
# 新增 eth1,IP 为 172.22.16.3

# busybox2 再次 ping busybox1 的 my_net2 IP
docker exec -it busybox2 sh
/ # ping -c 3 172.22.16.3
# 通了

05 容器间通信的三种方式

容器之间可通过 IP、Docker DNS Server 或 joined 容器三种方式通信。

  1. IP 通信 :两个容器要能通信,必须要有属于同一个网络的网卡。通过 --network 指定网络,或 docker network connect 加入网络。
  2. Docker DNS Server :从 Docker 1.10 版本开始,docker daemon 实现了一个内嵌的 DNS server,使容器可以直接通过"容器名"通信。方法是在启动时用 --name 为容器命名。限制:只能在 user-defined 网络中使用,默认的 bridge 网络无法使用 DNS。
  3. joined 容器 :使用 --network=container:web1 使两个或多个容器共享一个网络栈,共享网卡和配置信息,joined 容器之间可以通过 127.0.0.1 直接通信。适合不同容器中的程序希望通过 loopback 高效快速通信,或者希望监控其他容器的网络流量。
bash 复制代码
# 创建自定义网络
docker network create my_net2

# 启动 bbox1 和 bbox2
docker run -it --network my_net2 --name bbox1 busybox
docker run -it --network my_net2 --name bbox2 busybox

# 在 bbox2 中 ping bbox1
docker exec -it bbox2 sh
/ # ping -c 3 bbox1
# 通,DNS 解析成功

# 在默认 bridge 网络中测试 DNS
docker run -it --name bbox3 busybox
docker run -it --name bbox4 busybox
docker exec -it bbox4 sh
/ # ping -c 3 bbox3
# 失败:ping: bad address 'bbox3'

# joined 容器示例
docker run -d -it --name web1 httpd
docker run -it --network=container:web1 busybox
/ # wget 127.0.0.1
# 成功获取 httpd 首页
/ # cat index.html
# <html><body><h1>It works!</h1></body></html>

注意:默认 bridge 网络不支持 DNS 解析容器名,只有自定义网络才支持。


06 容器如何访问外部世界

容器默认就能访问外网,关键机制是 NAT。

在 docker host 上查看 iptables 规则:

bash 复制代码
iptables -t nat -S

其中有一条:

bash 复制代码
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE

含义是:如果网桥 docker0 收到来自 172.17.0.0/16 网段的外出包,把它交给 MASQUERADE 处理。MASQUERADE 将包的源地址替换成 host 的地址发送出去,即做了一次网络地址转换(NAT)。

bash 复制代码
# 容器内 ping 外网
docker run -it busybox
/ # ping -c 3 www.baidu.com
# 通

# 在宿主机查看 NAT 规则
iptables -t nat -S

# 使用 tcpdump 观察地址转换
yum install -y tcpdump
# 窗口1:监控 docker0
tcpdump -i docker0 -n icmp
# 窗口2:监控 ens160
tcpdump -i ens160 -n icmp
# 窗口3:容器内 ping www.baidu.com
docker run -it busybox ping -c 3 www.baidu.com

# 观察结果:
# docker0 上:172.17.0.2 > www.baidu.com
# ens160 上:192.168.108.30 > www.baidu.com
# 说明源地址被替换成了 host 的 IP

07 外部世界如何访问容器

答案是端口映射。Docker 可将容器对外提供服务的端口映射到 host 的某个端口,外网通过该端口访问容器。

容器启动时通过 -p 参数映射端口:

  • -p 80:将容器 80 端口映射到 host 的随机端口。
  • -p 8080:80:将容器 80 端口映射到 host 的 8080 端口。

每一个映射的端口,host 都会启动一个 docker-proxy 进程来处理访问容器的流量。

bash 复制代码
# 随机映射
docker run -d -p 80 httpd
docker ps
# 看到 PORTS: 0.0.0.0:32768->80/tcp
docker port <容器ID>
# 输出:80/tcp -> 0.0.0.0:32768

# 访问
curl 192.168.108.30:32768
# 输出:<html><body><h1>It works!</h1></body></html>

# 指定端口映射
docker run -d -p 8080:80 httpd
curl 192.168.108.30:8080
# 输出相同

# 查看 docker-proxy 进程
ps -elf | grep docker-proxy
# 可以看到多个 docker-proxy 进程

# 实战:安装 tomcat
docker pull tomcat
docker run -itd -p 8080:8080 tomcat
# 访问 http://192.168.108.30:8080 显示 404
# 进入容器
docker exec -it <容器ID> bash
# 查看 webapps 为空
ls webapps
# 文件在 webapps.dist
ls webapps.dist
# 替换
mv webapps.dist/* webapps
# 再次访问,成功显示 Tomcat 首页

踩坑 :Tomcat 官方镜像默认 webapps 目录为空,需要把 webapps.dist 里的内容移过去才能看到默认首页。


总结

"容器"介绍了容器的运行、进入、常用操作、状态机、内存和 CPU 限制、export/import、以及底层 cgroup 和 namespace 技术。通过实验掌握了 docker run、docker create、docker start、docker exec、docker attach、docker stop、docker restart、docker pause 等命令,并学会了如何限制容器资源、查看容器状态、导出导入容器。

"网络"介绍了 Docker 的 none、host、bridge 三种原生网络,以及自定义网络、容器间通信、容器访问外网、外部访问容器。通过实验掌握了 docker network create、docker network connect、docker network inspect、brctl show、iptables、tcpdump 等工具,理解了 veth pair、docker0 网桥、NAT、端口映射和 docker-proxy 的原理。

相关推荐
微信开发api2 小时前
基于WTAPI构建社群运营平台:自动拉群与会话承接链路设计
java·大数据·网络·数据库·微信·自动化
A.说学逗唱的Coke2 小时前
【云原生专题】Kubernetes 备份完全实战:用 Velero 搞定集群备份、恢复与跨集群迁移(附完整命令与踩坑记录)
云原生·容器·kubernetes
ynchyong2 小时前
老设备配置DHCP 静态绑定
网络
eybk2 小时前
用kivy手搓一个网络音乐播放器
网络
多语种客服外包2 小时前
智能宠物饮水机跨境经营:售后建设对海外业务收益的现实影响
网络·宠物
CHENKONG_CK2 小时前
恶劣工况下 RFID 赋能喷涂产线自动化分拣与作业
运维·网络·人工智能·自动化·汽车·rfid·rfid
北京中科新远科技2 小时前
AI集群交换网络容量怎么算:端口、收敛比与Leaf-Spine验收
服务器·网络·人工智能
白帽攻防录2 小时前
SRC 挖洞:GitLab GraphQL 指令绕过深度复盘,CVE-2026-19478 未授权删项目怎么打穿代码托管平台
网络·网络安全·gitlab·graphql
neo_will_mvp3 小时前
上海莫双一流211大学:实操安装HP1188pnw网络打印机
网络