From 6e6d31d1d6ad58c81ff197cd0d16bc5fe3abd5ec Mon Sep 17 00:00:00 2001 From: Baohua Yang Date: Fri, 2 Jan 2026 16:55:39 -0800 Subject: [PATCH] Merge networks --- SUMMARY.md | 23 +-- advanced_network/README.md | 15 -- advanced_network/_images/network.png | Bin 48625 -> 0 bytes advanced_network/access_control.md | 56 ------ advanced_network/bridge.md | 45 ----- advanced_network/config_file.md | 5 - advanced_network/docker0.md | 37 ---- advanced_network/example.md | 9 - advanced_network/how_connect.md | 1 - advanced_network/http_https_proxy.md | 74 ------- advanced_network/port_mapping.md | 57 ------ advanced_network/ptp.md | 45 ----- advanced_network/quick_guide.md | 26 --- buildx/multi-arch-images.md | 171 ++++++++-------- etcd/etcdctl-v2.md | 281 --------------------------- image/manifest.md | 162 --------------- network/README.md | 49 ++++- network/linking.md | 67 ------- network/port_mapping.md | 96 ++++----- 19 files changed, 177 insertions(+), 1042 deletions(-) delete mode 100644 advanced_network/README.md delete mode 100644 advanced_network/_images/network.png delete mode 100644 advanced_network/access_control.md delete mode 100644 advanced_network/bridge.md delete mode 100644 advanced_network/config_file.md delete mode 100644 advanced_network/docker0.md delete mode 100644 advanced_network/example.md delete mode 100644 advanced_network/how_connect.md delete mode 100644 advanced_network/http_https_proxy.md delete mode 100644 advanced_network/port_mapping.md delete mode 100644 advanced_network/ptp.md delete mode 100644 advanced_network/quick_guide.md delete mode 100644 etcd/etcdctl-v2.md delete mode 100644 image/manifest.md delete mode 100644 network/linking.md diff --git a/SUMMARY.md b/SUMMARY.md index 4ad078a..23a39fb 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -45,7 +45,6 @@ * [参考文档](image/dockerfile/references.md) * [Dockerfile 多阶段构建](image/multistage-builds/README.md) * [实战多阶段构建 Laravel 镜像](image/multistage-builds/laravel.md) - * [构建多种系统架构支持的 Docker 镜像](image/manifest.md) * [其它制作镜像的方式](image/other.md) * [实现原理](image/internal.md) * [操作容器](container/README.md) @@ -63,20 +62,18 @@ * [数据管理](data_management/README.md) * [数据卷](data_management/volume.md) * [挂载主机目录](data_management/bind-mounts.md) -* [使用网络](network/README.md) +* [网络配置](network/README.md) + * [快速配置指南](network/quick_guide.md) * [外部访问容器](network/port_mapping.md) - * [容器互联](network/linking.md) + * [容器访问控制](network/access_control.md) + * [端口映射实现](network/port_mapping.md) + * [配置 docker0 网桥](network/docker0.md) + * [自定义网桥](network/bridge.md) + * [编辑网络配置文件](network/config_file.md) * [配置 DNS](network/dns.md) -* [高级网络配置](advanced_network/README.md) - * [快速配置指南](advanced_network/quick_guide.md) - * [容器访问控制](advanced_network/access_control.md) - * [端口映射实现](advanced_network/port_mapping.md) - * [配置 docker0 网桥](advanced_network/docker0.md) - * [自定义网桥](advanced_network/bridge.md) - * [工具和示例](advanced_network/example.md) - * [编辑网络配置文件](advanced_network/config_file.md) - * [配置 HTTP/HTTPS 网络代理](advanced_network/http_https_proxy.md) - * [实例:创建一个点到点连接](advanced_network/ptp.md) + * [配置 HTTP/HTTPS 网络代理](network/http_https_proxy.md) + * [工具和示例](network/example.md) + * [实例:创建一个点到点连接](network/ptp.md) * [Docker Buildx](buildx/README.md) * [BuildKit](buildx/buildkit.md) * [使用 buildx 构建镜像](buildx/buildx.md) diff --git a/advanced_network/README.md b/advanced_network/README.md deleted file mode 100644 index 02106c8..0000000 --- a/advanced_network/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# 高级网络配置 - ->注意:本章属于 `Docker` 高级配置,如果您是初学者,您可以暂时跳过本章节,直接学习 [Docker Compose](../compose) 一节。 - -本章将介绍 Docker 的一些高级网络配置和选项。 - -当 Docker 启动时,会自动在主机上创建一个 `docker0` 虚拟网桥,实际上是 Linux 的一个 bridge,可以理解为一个软件交换机。它会在挂载到它的网口之间进行转发。 - -同时,Docker 随机分配一个本地未占用的私有网段(在 [RFC1918](https://datatracker.ietf.org/doc/html/rfc1918) 中定义)中的一个地址给 `docker0` 接口。比如典型的 `172.17.42.1`,掩码为 `255.255.0.0`。此后启动的容器内的网口也会自动分配一个同一网段(`172.17.0.0/16`)的地址。 - -当创建一个 Docker 容器的时候,同时会创建了一对 `veth pair` 接口(当数据包发送到一个接口时,另外一个接口也可以收到相同的数据包)。这对接口一端在容器内,即 `eth0`;另一端在本地并被挂载到 `docker0` 网桥,名称以 `veth` 开头(例如 `vethAQI2QT`)。通过这种方式,主机可以跟容器通信,容器之间也可以相互通信。Docker 就创建了在主机和所有容器之间一个虚拟共享网络。 - -![Docker 网络](./_images/network.png) - -接下来的部分将介绍在一些场景中,Docker 所有的网络定制配置。以及通过 Linux 命令来调整、补充、甚至替换 Docker 默认的网络配置。 diff --git a/advanced_network/_images/network.png b/advanced_network/_images/network.png deleted file mode 100644 index fdf892864409593e4417468c6f0430ee8c0ebfe9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 48625 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 M=g7hA19n&b1MJ=e^8f$< diff --git a/advanced_network/access_control.md b/advanced_network/access_control.md deleted file mode 100644 index c99fb77..0000000 --- a/advanced_network/access_control.md +++ /dev/null @@ -1,56 +0,0 @@ -# 容器访问控制 - -容器的访问控制,主要通过 Linux 上的 `iptables` 防火墙来进行管理和实现。`iptables` 是 Linux 上默认的防火墙软件,在大部分发行版中都自带。 - -## 容器访问外部网络 -容器要想访问外部网络,需要本地系统的转发支持。在Linux 系统中,检查转发是否打开。 - -```bash -$sysctl net.ipv4.ip_forward -net.ipv4.ip_forward = 1 -``` -如果为 0,说明没有开启转发,则需要手动打开。 -```bash -$sysctl -w net.ipv4.ip_forward=1 -``` -如果在启动 Docker 服务的时候设定 `--ip-forward=true`, Docker 就会自动设定系统的 `ip_forward` 参数为 1。 - -## 容器之间访问 -容器之间相互访问,需要两方面的支持。 -* 容器的网络拓扑是否已经互联。默认情况下,所有容器都会被连接到 `docker0` 网桥上。 -* 本地系统的防火墙软件 -- `iptables` 是否允许通过。 - -### 访问所有端口 -当启动 Docker 服务(即 dockerd)的时候,默认会添加一条转发策略到本地主机 iptables 的 FORWARD 链上。策略为通过(`ACCEPT`)还是禁止(`DROP`)取决于配置`--icc=true`(缺省值)还是 `--icc=false`。当然,如果手动指定 `--iptables=false` 则不会添加 `iptables` 规则。 - -可见,默认情况下,不同容器之间是允许网络互通的。如果为了安全考虑,可以在 `/etc/docker/daemon.json` 文件中配置 `{"icc": false}` 来禁止它。 - -### 访问指定端口 -在通过 `-icc=false` 关闭网络访问后,还可以通过 `--link=CONTAINER_NAME:ALIAS` 选项来访问容器的开放端口。 - -例如,在启动 Docker 服务时,可以同时使用 `icc=false --iptables=true` 参数来关闭允许相互的网络访问,并让 Docker 可以修改系统中的 `iptables` 规则。 - -此时,系统中的 `iptables` 规则可能是类似 -```bash -$ sudo iptables -nL -... -Chain FORWARD (policy ACCEPT) -target prot opt source destination -DROP all -- 0.0.0.0/0 0.0.0.0/0 -... -``` - -之后,启动容器(`docker run`)时使用 `--link=CONTAINER_NAME:ALIAS` 选项。Docker 会在 `iptable` 中为 两个容器分别添加一条 `ACCEPT` 规则,允许相互访问开放的端口(取决于 `Dockerfile` 中的 `EXPOSE` 指令)。 - -当添加了 `--link=CONTAINER_NAME:ALIAS` 选项后,添加了 `iptables` 规则。 -```bash -$ sudo iptables -nL -... -Chain FORWARD (policy ACCEPT) -target prot opt source destination -ACCEPT tcp -- 172.17.0.2 172.17.0.3 tcp spt:80 -ACCEPT tcp -- 172.17.0.3 172.17.0.2 tcp dpt:80 -DROP all -- 0.0.0.0/0 0.0.0.0/0 -``` - -注意:`--link=CONTAINER_NAME:ALIAS` 中的 `CONTAINER_NAME` 目前必须是 Docker 分配的名字,或使用 `--name` 参数指定的名字。主机名则不会被识别。 diff --git a/advanced_network/bridge.md b/advanced_network/bridge.md deleted file mode 100644 index 0ce6a52..0000000 --- a/advanced_network/bridge.md +++ /dev/null @@ -1,45 +0,0 @@ -# 自定义网桥 - -除了默认的 `docker0` 网桥,用户也可以指定网桥来连接各个容器。 - -在启动 Docker 服务的时候,使用 `-b BRIDGE`或`--bridge=BRIDGE` 来指定使用的网桥。 - -如果服务已经运行,那需要先停止服务,并删除旧的网桥。 - -```bash -$ sudo systemctl stop docker -$ sudo ip link set dev docker0 down -$ sudo brctl delbr docker0 -``` - -然后创建一个网桥 `bridge0`。 - -```bash -$ sudo brctl addbr bridge0 -$ sudo ip addr add 192.168.5.1/24 dev bridge0 -$ sudo ip link set dev bridge0 up -``` - -查看确认网桥创建并启动。 - -```bash -$ ip addr show bridge0 -4: bridge0: mtu 1500 qdisc noop state UP group default - link/ether 66:38:d0:0d:76:18 brd ff:ff:ff:ff:ff:ff - inet 192.168.5.1/24 scope global bridge0 - valid_lft forever preferred_lft forever -``` - -在 Docker 配置文件 `/etc/docker/daemon.json` 中添加如下内容,即可将 Docker 默认桥接到创建的网桥上。 - -```json -{ - "bridge": "bridge0", -} -``` - -启动 Docker 服务。 - -新建一个容器,可以看到它已经桥接到了 `bridge0` 上。 - -可以继续用 `brctl show` 命令查看桥接的信息。另外,在容器中可以使用 `ip addr` 和 `ip route` 命令来查看 IP 地址配置和路由信息。 diff --git a/advanced_network/config_file.md b/advanced_network/config_file.md deleted file mode 100644 index b084c79..0000000 --- a/advanced_network/config_file.md +++ /dev/null @@ -1,5 +0,0 @@ -# 编辑网络配置文件 - -Docker 1.2.0 开始支持在运行中的容器里编辑 `/etc/hosts`, `/etc/hostname` 和 `/etc/resolv.conf` 文件。 - -但是这些修改是临时的,只在运行的容器中保留,容器终止或重启后并不会被保存下来,也不会被 `docker commit` 提交。 diff --git a/advanced_network/docker0.md b/advanced_network/docker0.md deleted file mode 100644 index c21ce8b..0000000 --- a/advanced_network/docker0.md +++ /dev/null @@ -1,37 +0,0 @@ -# 配置 docker0 网桥 - -Docker 服务默认会创建一个 `docker0` 网桥(其上有一个 `docker0` 内部接口),它在内核层连通了其他的物理或虚拟网卡,这就将所有容器和本地主机都放到同一个物理网络。 - -Docker 默认指定了 `docker0` 接口 的 IP 地址和子网掩码,让主机和容器之间可以通过网桥相互通信,它还给出了 MTU(接口允许接收的最大传输单元),通常是 1500 Bytes,或宿主主机网络路由上支持的默认值。这些值都可以在服务启动的时候进行配置。 - -* `--bip=CIDR` IP 地址加掩码格式,例如 192.168.1.5/24 -* `--mtu=BYTES` 覆盖默认的 Docker mtu 配置 - -也可以在配置文件中配置 DOCKER_OPTS,然后重启服务。 - -由于目前 Docker 网桥是 Linux 网桥,用户可以使用 `brctl show` 来查看网桥和端口连接信息。 - -```bash -$ sudo brctl show -bridge name bridge id STP enabled interfaces -docker0 8000.3a1d7362b4ee no veth65f9 - vethdda6 -``` -*注:`brctl` 命令在 Debian、Ubuntu 中可以使用 `sudo apt-get install bridge-utils` 来安装。 - - -每次创建一个新容器的时候,Docker 从可用的地址段中选择一个空闲的 IP 地址分配给容器的 eth0 端口。使用本地主机上 `docker0` 接口的 IP 作为所有容器的默认网关。 - -```bash -$ sudo docker run -i -t --rm base /bin/bash -$ ip addr show eth0 -24: eth0: mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 - link/ether 32:6f:e0:35:57:91 brd ff:ff:ff:ff:ff:ff - inet 172.17.0.3/16 scope global eth0 - valid_lft forever preferred_lft forever - inet6 fe80::306f:e0ff:fe35:5791/64 scope link - valid_lft forever preferred_lft forever -$ ip route -default via 172.17.42.1 dev eth0 -172.17.0.0/16 dev eth0 proto kernel scope link src 172.17.0.3 -``` diff --git a/advanced_network/example.md b/advanced_network/example.md deleted file mode 100644 index e8bf802..0000000 --- a/advanced_network/example.md +++ /dev/null @@ -1,9 +0,0 @@ -# 工具和示例 - -在介绍自定义网络拓扑之前,你可能会对一些外部工具和例子感兴趣: - -## pipework -Jérôme Petazzoni 编写了一个叫 [pipework](https://github.com/jpetazzo/pipework) 的 shell 脚本,可以帮助用户在比较复杂的场景中完成容器的连接。 - -## playground -Brandon Rhodes 创建了一个提供完整的 Docker 容器网络拓扑管理的 [Python库](https://github.com/brandon-rhodes/fopnp/tree/m/playground),包括路由、NAT 防火墙;以及一些提供 `HTTP` `SMTP` `POP` `IMAP` `Telnet` `SSH` `FTP` 的服务器。 diff --git a/advanced_network/how_connect.md b/advanced_network/how_connect.md deleted file mode 100644 index 8b13789..0000000 --- a/advanced_network/how_connect.md +++ /dev/null @@ -1 +0,0 @@ - diff --git a/advanced_network/http_https_proxy.md b/advanced_network/http_https_proxy.md deleted file mode 100644 index d8dcc92..0000000 --- a/advanced_network/http_https_proxy.md +++ /dev/null @@ -1,74 +0,0 @@ -# 配置 HTTP/HTTPS 网络代理 - -使用Docker的过程中,因为网络原因,通常需要使用 HTTP/HTTPS 代理来加速镜像拉取、构建和使用。下面是常见的三种场景。 - -## 为 dockerd 设置网络代理 - -"docker pull" 命令是由 dockerd 守护进程执行。而 dockerd 守护进程是由 systemd 管理。因此,如果需要在执行 "docker pull" 命令时使用 HTTP/HTTPS 代理,需要通过 systemd 配置。 - -- 为 dockerd 创建配置文件夹。 -``` -sudo mkdir -p /etc/systemd/system/docker.service.d -``` - -- 为 dockerd 创建 HTTP/HTTPS 网络代理的配置文件,文件路径是 /etc/systemd/system/docker.service.d/http-proxy.conf 。并在该文件中添加相关环境变量。 -``` -[Service] -Environment="HTTP_PROXY=http://proxy.example.com:8080/" -Environment="HTTPS_PROXY=http://proxy.example.com:8080/" -Environment="NO_PROXY=localhost,127.0.0.1,.example.com" -``` - -- 刷新配置并重启 docker 服务。 -``` -sudo systemctl daemon-reload -sudo systemctl restart docker -``` - -## 为 docker 容器设置网络代理 - -在容器运行阶段,如果需要使用 HTTP/HTTPS 代理,可以通过更改 docker 客户端配置,或者指定环境变量的方式。 - -- 更改 docker 客户端配置:创建或更改 ~/.docker/config.json,并在该文件中添加相关配置。 -``` -{ - "proxies": - { - "default": - { - "httpProxy": "http://proxy.example.com:8080/", - "httpsProxy": "http://proxy.example.com:8080/", - "noProxy": "localhost,127.0.0.1,.example.com" - } - } -} -``` - -- 指定环境变量:运行 "docker run" 命令时,指定相关环境变量。 - -| 环境变量 | docker run 示例 | -| -------- | ---------------- | -| HTTP_PROXY | --env HTTP_PROXY="http://proxy.example.com:8080/" | -| HTTPS_PROXY | --env HTTPS_PROXY="http://proxy.example.com:8080/" | -| NO_PROXY | --env NO_PROXY="localhost,127.0.0.1,.example.com" | - -## 为 docker build 过程设置网络代理 - -在容器构建阶段,如果需要使用 HTTP/HTTPS 代理,可以通过指定 "docker build" 的环境变量,或者在 Dockerfile 中指定环境变量的方式。 - -- 使用 "--build-arg" 指定 "docker build" 的相关环境变量 -``` -docker build \ - --build-arg "HTTP_PROXY=http://proxy.example.com:8080/" \ - --build-arg "HTTPS_PROXY=http://proxy.example.com:8080/" \ - --build-arg "NO_PROXY=localhost,127.0.0.1,.example.com" . -``` - -- 在 Dockerfile 中指定相关环境变量 - -| 环境变量 | Dockerfile 示例 | -| -------- | ---------------- | -| HTTP_PROXY | ENV HTTP_PROXY="http://proxy.example.com:8080/" | -| HTTPS_PROXY | ENV HTTPS_PROXY="http://proxy.example.com:8080/" | -| NO_PROXY | ENV NO_PROXY="localhost,127.0.0.1,.example.com" | - diff --git a/advanced_network/port_mapping.md b/advanced_network/port_mapping.md deleted file mode 100644 index dc6f92d..0000000 --- a/advanced_network/port_mapping.md +++ /dev/null @@ -1,57 +0,0 @@ -# 映射容器端口到宿主主机的实现 - -默认情况下,容器可以主动访问到外部网络的连接,但是外部网络无法访问到容器。 - -## 容器访问外部实现 - -容器所有到外部网络的连接,源地址都会被 NAT 成本地系统的 IP 地址。这是使用 `iptables` 的源地址伪装操作实现的。 - -查看主机的 NAT 规则。 - -```bash -$ sudo iptables -t nat -nL -... -Chain POSTROUTING (policy ACCEPT) -target prot opt source destination -MASQUERADE all -- 172.17.0.0/16 !172.17.0.0/16 -... -``` - -其中,上述规则将所有源地址在 `172.17.0.0/16` 网段,目标地址为其他网段(外部网络)的流量动态伪装为从系统网卡发出。MASQUERADE 跟传统 SNAT 的好处是它能动态从网卡获取地址。 - -## 外部访问容器实现 - -容器允许外部访问,可以在 `docker run` 时候通过 `-p` 或 `-P` 参数来启用。 - -不管用那种办法,其实也是在本地的 `iptable` 的 nat 表中添加相应的规则。 - -使用 `-P` 时: - -```bash -$ iptables -t nat -nL -... -Chain DOCKER (2 references) -target prot opt source destination -DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:49153 to:172.17.0.2:80 -``` - -使用 `-p 80:80` 时: - -```bash -$ iptables -t nat -nL -Chain DOCKER (2 references) -target prot opt source destination -DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 to:172.17.0.2:80 -``` - -注意: - -* 这里的规则映射了 `0.0.0.0`,意味着将接受主机来自所有接口的流量。用户可以通过 `-p IP:host_port:container_port` 或 `-p IP::port` 来指定允许访问容器的主机上的 IP、接口等,以制定更严格的规则。 - -* 如果希望永久绑定到某个固定的 IP 地址,可以在 Docker 配置文件 `/etc/docker/daemon.json` 中添加如下内容。 - -```json -{ - "ip": "0.0.0.0" -} -``` diff --git a/advanced_network/ptp.md b/advanced_network/ptp.md deleted file mode 100644 index 738a751..0000000 --- a/advanced_network/ptp.md +++ /dev/null @@ -1,45 +0,0 @@ -# 示例:创建一个点到点连接 -默认情况下,Docker 会将所有容器连接到由 `docker0` 提供的虚拟子网中。 - -用户有时候需要两个容器之间可以直连通信,而不用通过主机网桥进行桥接。 - -解决办法很简单:创建一对 `peer` 接口,分别放到两个容器中,配置成点到点链路类型即可。 - -首先启动 2 个容器: -```bash -$ docker run -i -t --rm --net=none base /bin/bash -root@1f1f4c1f931a:/# -$ docker run -i -t --rm --net=none base /bin/bash -root@12e343489d2f:/# -``` - -找到进程号,然后创建网络命名空间的跟踪文件。 -```bash -$ docker inspect -f '{{.State.Pid}}' 1f1f4c1f931a -2989 -$ docker inspect -f '{{.State.Pid}}' 12e343489d2f -3004 -$ sudo mkdir -p /var/run/netns -$ sudo ln -s /proc/2989/ns/net /var/run/netns/2989 -$ sudo ln -s /proc/3004/ns/net /var/run/netns/3004 -``` - -创建一对 `peer` 接口,然后配置路由 -```bash -$ sudo ip link add A type veth peer name B - -$ sudo ip link set A netns 2989 -$ sudo ip netns exec 2989 ip addr add 10.1.1.1/32 dev A -$ sudo ip netns exec 2989 ip link set A up -$ sudo ip netns exec 2989 ip route add 10.1.1.2/32 dev A - -$ sudo ip link set B netns 3004 -$ sudo ip netns exec 3004 ip addr add 10.1.1.2/32 dev B -$ sudo ip netns exec 3004 ip link set B up -$ sudo ip netns exec 3004 ip route add 10.1.1.1/32 dev B -``` -现在这 2 个容器就可以相互 ping 通,并成功建立连接。点到点链路不需要子网和子网掩码。 - -此外,也可以不指定 `--net=none` 来创建点到点链路。这样容器还可以通过原先的网络来通信。 - -利用类似的办法,可以创建一个只跟主机通信的容器。但是一般情况下,更推荐使用 `--icc=false` 来关闭容器之间的通信。 diff --git a/advanced_network/quick_guide.md b/advanced_network/quick_guide.md deleted file mode 100644 index 455a818..0000000 --- a/advanced_network/quick_guide.md +++ /dev/null @@ -1,26 +0,0 @@ -# 快速配置指南 - -下面是一个跟 Docker 网络相关的命令列表。 - -其中有些命令选项只有在 Docker 服务启动的时候才能配置,而且不能马上生效。 - -* `-b BRIDGE` 或 `--bridge=BRIDGE` 指定容器挂载的网桥 -* `--bip=CIDR` 定制 docker0 的掩码 -* `-H SOCKET...` 或 `--host=SOCKET...` Docker 服务端接收命令的通道 -* `--icc=true|false` 是否支持容器之间进行通信 -* `--ip-forward=true|false` 请看下文容器之间的通信 -* `--iptables=true|false` 是否允许 Docker 添加 iptables 规则 -* `--mtu=BYTES` 容器网络中的 MTU - -下面2个命令选项既可以在启动服务时指定,也可以在启动容器时指定。在 Docker 服务启动的时候指定则会成为默认值,后面执行 `docker run` 时可以覆盖设置的默认值。 - -* `--dns=IP_ADDRESS...` 使用指定的DNS服务器 -* `--dns-search=DOMAIN...` 指定DNS搜索域 - -最后这些选项只有在 `docker run` 执行时使用,因为它是针对容器的特性内容。 - -* `-h HOSTNAME` 或 `--hostname=HOSTNAME` 配置容器主机名 -* `--link=CONTAINER_NAME:ALIAS` 添加到另一个容器的连接 -* `--net=bridge|none|container:NAME_or_ID|host` 配置容器的桥接模式 -* `-p SPEC` 或 `--publish=SPEC` 映射容器端口到宿主主机 -* `-P or --publish-all=true|false` 映射容器所有端口到宿主主机 diff --git a/buildx/multi-arch-images.md b/buildx/multi-arch-images.md index 8b190fb..e90dfb7 100644 --- a/buildx/multi-arch-images.md +++ b/buildx/multi-arch-images.md @@ -1,38 +1,62 @@ -# 使用 buildx 构建多种系统架构支持的 Docker 镜像 +# 构建多种系统架构支持的 Docker 镜像 -在之前的版本中构建多种系统架构支持的 Docker 镜像,要想使用统一的名字必须使用 [`$ docker manifest`](../image/manifest.md) 命令。 +Docker 镜像可以支持多种系统架构,这意味着你可以在 `x86_64`、`arm64` 等不同架构的机器上运行同一个镜像。这是通过一个名为 "manifest list"(或称为 "fat manifest")的文件来实现的。 -在 Docker 19.03+ 版本中可以使用 `$ docker buildx build` 命令使用 `BuildKit` 构建镜像。该命令支持 `--platform` 参数可以同时构建支持多种系统架构的 Docker 镜像,大大简化了构建步骤。 +## Manifest List 是什么? -## 新建 `builder` 实例 +Manifest list 是一个包含了多个指向不同架构镜像的 manifest 的文件。当你拉取一个支持多架构的镜像时,Docker 会自动根据你当前的系统架构选择并拉取对应的镜像。 -Docker for Linux 不支持构建 `arm` 架构镜像,我们可以运行一个新的容器让其支持该特性,Docker 桌面版无需进行此项设置。 +例如,官方的 `hello-world` 镜像就支持多种架构。你可以使用 `docker manifest inspect` 命令来查看它的 manifest list: ```bash -$ docker run --rm --privileged tonistiigi/binfmt:latest --install all +$ docker manifest inspect hello-world +{ + "schemaVersion": 2, + "mediaType": "application/vnd.docker.distribution.manifest.list.v2+json", + "manifests": [ + { + "mediaType": "application/vnd.docker.distribution.manifest.v2+json", + "size": 525, + "digest": "sha256:80852a401a974d9e923719a948cc5335a0a4435be8778b475844a7153a2382e5", + "platform": { + "architecture": "amd64", + "os": "linux" + } + }, + { + "mediaType": "application/vnd.docker.distribution.manifest.v2+json", + "size": 525, + "digest": "sha256:3adea81344be1724b383d501736c3852939b33b3903d02474373700b25e5d6e3", + "platform": { + "architecture": "arm", + "os": "linux", + "variant": "v5" + } + }, + // ... more architectures + ] +} ``` -由于 Docker 默认的 `builder` 实例不支持同时指定多个 `--platform`,我们必须首先创建一个新的 `builder` 实例。同时由于国内拉取镜像较缓慢,我们可以使用配置了 [镜像加速地址](https://github.com/moby/buildkit/blob/master/docs/buildkitd.toml.md) 的 [`dockerpracticesig/buildkit:master`](https://github.com/docker-practice/buildx) 镜像替换官方镜像。 +## 使用 `docker buildx` 构建多架构镜像 -> 如果你有私有的镜像加速器,可以基于 https://github.com/docker-practice/buildx 构建自己的 buildkit 镜像并使用它。 +在 Docker 19.03+ 版本中,`docker buildx` 是推荐的用于构建多架构镜像的工具。它使用 `BuildKit` 作为后端,可以大大简化构建过程。 + +### 新建 `builder` 实例 + +首先,你需要创建一个新的 `builder` 实例,因为它支持同时为多个平台构建。 ```bash -# 适用于国内环境 -$ docker buildx create --use --name=mybuilder-cn --driver docker-container --driver-opt image=dockerpracticesig/buildkit:master - -# 适用于腾讯云环境(腾讯云主机、coding.net 持续集成) -$ docker buildx create --use --name=mybuilder-cn --driver docker-container --driver-opt image=dockerpracticesig/buildkit:master-tencent - -# $ docker buildx create --name mybuilder --driver docker-container - -$ docker buildx use mybuilder +$ docker buildx create --name mybuilder --use +$ docker buildx inspect --bootstrap ``` -## 构建镜像 +### 构建和推送 -新建 Dockerfile 文件。 +使用 `docker buildx build` 命令并指定 `--platform` 参数,可以同时构建支持多种架构的镜像。`--push` 参数会将构建好的镜像和 manifest list 推送到 Docker 仓库。 -```docker +```dockerfile +# Dockerfile FROM --platform=$TARGETPLATFORM alpine RUN uname -a > /os.txt @@ -40,87 +64,58 @@ RUN uname -a > /os.txt CMD cat /os.txt ``` -使用 `$ docker buildx build` 命令构建镜像,注意将 `myusername` 替换为自己的 Docker Hub 用户名。 - -`--push` 参数表示将构建好的镜像推送到 Docker 仓库。 - ```bash -$ docker buildx build --platform linux/arm,linux/arm64,linux/amd64 -t myusername/hello . --push - -# 查看镜像信息 -$ docker buildx imagetools inspect myusername/hello +$ docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t your-username/multi-arch-image . --push ``` -在不同架构运行该镜像,可以得到该架构的信息。 +构建完成后,你就可以在不同架构的机器上拉取并运行 `your-username/multi-arch-image` 这个镜像了。 -```bash -# arm -$ docker run -it --rm myusername/hello -Linux buildkitsandbox 4.9.125-linuxkit #1 SMP Fri Sep 7 08:20:28 UTC 2018 armv7l Linux +### 架构相关的构建参数 -# arm64 -$ docker run -it --rm myusername/hello -Linux buildkitsandbox 4.9.125-linuxkit #1 SMP Fri Sep 7 08:20:28 UTC 2018 aarch64 Linux +在 `Dockerfile` 中,你可以使用一些预定义的构建参数来根据目标平台定制构建过程: -# amd64 -$ docker run -it --rm myusername/hello -Linux buildkitsandbox 4.9.125-linuxkit #1 SMP Fri Sep 7 08:20:28 UTC 2018 x86_64 Linux -``` +* `TARGETPLATFORM`: 构建镜像的目标平台,例如 `linux/amd64`。 +* `TARGETOS`: 目标平台的操作系统,例如 `linux`。 +* `TARGETARCH`: 目标平台的架构,例如 `amd64`。 +* `TARGETVARIANT`: 目标平台的变种,例如 `v7`。 +* `BUILDPLATFORM`: 构建环境的平台。 +* `BUILDOS`: 构建环境的操作系统。 +* `BUILDARCH`: 构建环境的架构。 +* `BUILDVARIANT`: 构建环境的变种。 -## 架构相关变量 +例如,你可以这样编写 `Dockerfile` 来拷贝特定架构的二进制文件: -`Dockerfile` 支持如下架构相关的变量 - -**TARGETPLATFORM** - -构建镜像的目标平台,例如 `linux/amd64`, `linux/arm/v7`, `windows/amd64`。 - -**TARGETOS** - -`TARGETPLATFORM` 的 OS 类型,例如 `linux`, `windows` - -**TARGETARCH** - -`TARGETPLATFORM` 的架构类型,例如 `amd64`, `arm` - -**TARGETVARIANT** - -`TARGETPLATFORM` 的变种,该变量可能为空,例如 `v7` - -**BUILDPLATFORM** - -构建镜像主机平台,例如 `linux/amd64` - -**BUILDOS** - -`BUILDPLATFORM` 的 OS 类型,例如 `linux` - -**BUILDARCH** - -`BUILDPLATFORM` 的架构类型,例如 `amd64` - -**BUILDVARIANT** - -`BUILDPLATFORM` 的变种,该变量可能为空,例如 `v7` - -### 使用举例 - -例如我们要构建支持 `linux/arm/v7` 和 `linux/amd64` 两种架构的镜像。假设已经生成了两个平台对应的二进制文件: - -* `bin/dist-linux-arm` -* `bin/dist-linux-amd64` - -那么 `Dockerfile` 可以这样书写: - -```docker +```dockerfile FROM scratch -# 使用变量必须申明 ARG TARGETOS - ARG TARGETARCH COPY bin/dist-${TARGETOS}-${TARGETARCH} /dist -ENTRYPOINT ["dist"] +ENTRYPOINT ["/dist"] ``` + +## 使用 `docker manifest` (底层工具) + +`docker manifest` 是一个更底层的命令,可以用来创建、检查和推送 manifest list。虽然 `docker buildx` 在大多数情况下更方便,但了解 `docker manifest` 仍然有助于理解其工作原理。 + +### 创建 manifest list + +```bash +# 首先,为每个架构构建并推送镜像 +$ docker buildx build --platform linux/amd64 -t your-username/my-app:amd64 . --push +$ docker buildx build --platform linux/arm64 -t your-username/my-app:arm64 . --push + +# 然后,创建一个 manifest list,将它们组合在一起 +$ docker manifest create your-username/my-app:latest \ + --amend your-username/my-app:amd64 \ + --amend your-username/my-app:arm64 + +# 最后,推送 manifest list +$ docker manifest push your-username/my-app:latest +``` + +### 检查 manifest list + +你可以使用 `docker manifest inspect` 来查看一个 manifest list 的详细信息,如上文所示。 \ No newline at end of file diff --git a/etcd/etcdctl-v2.md b/etcd/etcdctl-v2.md deleted file mode 100644 index d37e4ce..0000000 --- a/etcd/etcdctl-v2.md +++ /dev/null @@ -1,281 +0,0 @@ -# 使用 etcdctl v2 - -`etcdctl` 是一个命令行客户端,它能提供一些简洁的命令,供用户直接跟 `etcd` 服务打交道,而无需基于 `HTTP API` 方式。这在某些情况下将很方便,例如用户对服务进行测试或者手动修改数据库内容。我们也推荐在刚接触 `etcd` 时通过 `etcdctl` 命令来熟悉相关的操作,这些操作跟 `HTTP API` 实际上是对应的。 - -`etcd` 项目二进制发行包中已经包含了 `etcdctl` 工具,没有的话,可以从 [github.com/etcd-io/etcd/releases](https://github.com/etcd-io/etcd/releases) 下载。 - -`etcdctl` 支持如下的命令,大体上分为数据库操作和非数据库操作两类,后面将分别进行解释。 - -``` -$ etcdctl -h -NAME: - etcdctl - A simple command line client for etcd. - -USAGE: - etcdctl [global options] command [command options] [arguments...] - -VERSION: - 2.0.0-rc.1 - -COMMANDS: - backup backup an etcd directory - mk make a new key with a given value - mkdir make a new directory - rm remove a key - rmdir removes the key if it is an empty directory or a key-value pair - get retrieve the value of a key - ls retrieve a directory - set set the value of a key - setdir create a new or existing directory - update update an existing key with a given value - updatedir update an existing directory - watch watch a key for changes - exec-watch watch a key for changes and exec an executable - member member add, remove and list subcommands - help, h Shows a list of commands or help for one command - -GLOBAL OPTIONS: - --debug output cURL commands which can be used to reproduce the request - --no-sync don't synchronize cluster information before sending request - --output, -o 'simple' output response in the given format (`simple` or `json`) - --peers, -C a comma-delimited list of machine addresses in the cluster (default: "127.0.0.1:4001") - --cert-file identify HTTPS client using this SSL certificate file - --key-file identify HTTPS client using this SSL key file - --ca-file verify certificates of HTTPS-enabled servers using this CA bundle - --help, -h show help - --version, -v print the version -``` - -## 数据库操作 -数据库操作围绕对键值和目录的 CRUD (符合 REST 风格的一套操作:Create)完整生命周期的管理。 - -etcd 在键的组织上采用了层次化的空间结构(类似于文件系统中目录的概念),用户指定的键可以为单独的名字,如 `testkey`,此时实际上放在根目录 `/` 下面,也可以为指定目录结构,如 `cluster1/node2/testkey`,则将创建相应的目录结构。 - -*注:CRUD 即 Create, Read, Update, Delete,是符合 REST 风格的一套 API 操作。* - -### set -指定某个键的值。例如 -```bash -$ etcdctl set /testdir/testkey "Hello world" -Hello world -``` -支持的选项包括: -```bash ---ttl '0' 该键值的超时时间(单位为秒),不配置(默认为 0)则永不超时 ---swap-with-value value 若该键现在的值是 value,则进行设置操作 ---swap-with-index '0' 若该键现在的索引值是指定索引,则进行设置操作 -``` - -### get -获取指定键的值。例如 -```bash -$ etcdctl set testkey hello -hello -$ etcdctl update testkey world -world -``` - -当键不存在时,则会报错。例如 -```bash -$ etcdctl get testkey2 -Error: 100: Key not found (/testkey2) [1] -``` - -支持的选项为 -```bash ---sort 对结果进行排序 ---consistent 将请求发给主节点,保证获取内容的一致性 -``` - -### update -当键存在时,更新值内容。例如 -```bash -$ etcdctl set testkey hello -hello -$ etcdctl update testkey world -world -``` - -当键不存在时,则会报错。例如 -```bash -$ etcdctl update testkey2 world -Error: 100: Key not found (/testkey2) [1] -``` - -支持的选项为 -```bash ---ttl '0' 超时时间(单位为秒),不配置(默认为 0)则永不超时 -``` - -### rm -删除某个键值。例如 -```bash -$ etcdctl rm testkey -``` - -当键不存在时,则会报错。例如 -```bash -$ etcdctl rm testkey2 -Error: 100: Key not found (/testkey2) [8] -``` - -支持的选项为 -```bash ---dir 如果键是个空目录或者键值对则删除 ---recursive 删除目录和所有子键 ---with-value 检查现有的值是否匹配 ---with-index '0' 检查现有的 index 是否匹配 -``` - -### mk -如果给定的键不存在,则创建一个新的键值。例如 -```bash -$ etcdctl mk /testdir/testkey "Hello world" -Hello world -``` -当键存在的时候,执行该命令会报错,例如 -```bash -$ etcdctl set testkey "Hello world" -Hello world -$ ./etcdctl mk testkey "Hello world" -Error: 105: Key already exists (/testkey) [2] -``` - -支持的选项为 -```bash ---ttl '0' 超时时间(单位为秒),不配置(默认为 0)则永不超时 -``` - -### mkdir -如果给定的键目录不存在,则创建一个新的键目录。例如 -```bash -$ etcdctl mkdir testdir -``` -当键目录存在的时候,执行该命令会报错,例如 -```bash -$ etcdctl mkdir testdir -$ etcdctl mkdir testdir -Error: 105: Key already exists (/testdir) [7] -``` -支持的选项为 -```bash ---ttl '0' 超时时间(单位为秒),不配置(默认为 0)则永不超时 -``` - -### setdir - -创建一个键目录,无论存在与否。 - -支持的选项为 -```bash ---ttl '0' 超时时间(单位为秒),不配置(默认为 0)则永不超时 -``` - -### updatedir -更新一个已经存在的目录。 -支持的选项为 -```bash ---ttl '0' 超时时间(单位为秒),不配置(默认为 0)则永不超时 -``` - -### rmdir -删除一个空目录,或者键值对。 - -若目录不空,会报错 -```bash -$ etcdctl set /dir/testkey hi -hi -$ etcdctl rmdir /dir -Error: 108: Directory not empty (/dir) [13] -``` - -### ls -列出目录(默认为根目录)下的键或者子目录,默认不显示子目录中内容。 - -例如 -```bash -$ ./etcdctl set testkey 'hi' -hi -$ ./etcdctl set dir/test 'hello' -hello -$ ./etcdctl ls -/testkey -/dir -$ ./etcdctl ls dir -/dir/test -``` - -支持的选项包括 -```bash ---sort 将输出结果排序 ---recursive 如果目录下有子目录,则递归输出其中的内容 --p 对于输出为目录,在最后添加 `/` 进行区分 -``` - -## 非数据库操作 - -### backup -备份 etcd 的数据。 - -支持的选项包括 -```bash ---data-dir etcd 的数据目录 ---backup-dir 备份到指定路径 -``` -### watch -监测一个键值的变化,一旦键值发生更新,就会输出最新的值并退出。 - -例如,用户更新 testkey 键值为 Hello world。 -```bash -$ etcdctl watch testkey -Hello world -``` - -支持的选项包括 -```bash ---forever 一直监测,直到用户按 `CTRL+C` 退出 ---after-index '0' 在指定 index 之前一直监测 ---recursive 返回所有的键值和子键值 -``` -### exec-watch -监测一个键值的变化,一旦键值发生更新,就执行给定命令。 - -例如,用户更新 testkey 键值。 -```bash -$ etcdctl exec-watch testkey -- sh -c 'ls' -default.etcd -Documentation -etcd -etcdctl -etcd-migrate -README-etcdctl.md -README.md -``` - -支持的选项包括 -```bash ---after-index '0' 在指定 index 之前一直监测 ---recursive 返回所有的键值和子键值 -``` - -### member - -通过 list、add、remove 命令列出、添加、删除 etcd 实例到 etcd 集群中。 - -例如本地启动一个 etcd 服务实例后,可以用如下命令进行查看。 - -```bash -$ etcdctl member list -ce2a822cea30bfca: name=default peerURLs=http://localhost:2380,http://localhost:7001 clientURLs=http://localhost:2379,http://localhost:4001 -``` - -## 命令选项 -* `--debug` 输出 cURL 命令,显示执行命令的时候发起的请求 -* `--no-sync` 发出请求之前不同步集群信息 -* `--output, -o 'simple'` 输出内容的格式 (`simple` 为原始信息,`json` 为进行json格式解码,易读性好一些) -* `--peers, -C` 指定集群中的同伴信息,用逗号隔开 (默认为: "127.0.0.1:4001") -* `--cert-file` HTTPS 下客户端使用的 SSL 证书文件 -* `--key-file` HTTPS 下客户端使用的 SSL 密钥文件 -* `--ca-file` 服务端使用 HTTPS 时,使用 CA 文件进行验证 -* `--help, -h` 显示帮助命令信息 -* `--version, -v` 打印版本信息 diff --git a/image/manifest.md b/image/manifest.md deleted file mode 100644 index b55d749..0000000 --- a/image/manifest.md +++ /dev/null @@ -1,162 +0,0 @@ -# 构建多种系统架构支持的 Docker 镜像 -- docker manifest 命令详解 - -我们知道使用镜像创建一个容器,该镜像必须与 Docker 宿主机系统架构一致,例如 `Linux x86_64` 架构的系统中只能使用 `Linux x86_64` 的镜像创建容器。 - -> Windows、macOS 除外,其使用了 [binfmt_misc](https://docs.docker.com/docker-for-mac/multi-arch/) 提供了多种架构支持,在 Windows、macOS 系统上 (x86_64) 可以运行 arm 等其他架构的镜像。 - -例如我们在 `Linux x86_64` 中构建一个 `username/test` 镜像。 - -```docker -FROM alpine - -CMD echo 1 -``` - -构建镜像后推送到 Docker Hub,之后我们尝试在树莓派 `Linux arm64v8` 中使用这个镜像。 - -```bash -$ docker run -it --rm username/test -``` - -可以发现这个镜像根本获取不到。 - -要解决这个问题,通常采用的做法是通过镜像名区分不同系统架构的镜像,例如在 `Linux x86_64` 和 `Linux arm64v8` 分别构建 `username/test` 和 `username/arm64v8-test` 镜像。运行时使用对应架构的镜像即可。 - -这样做显得很繁琐,那么有没有一种方法让 Docker 引擎根据系统架构自动拉取对应的镜像呢? - -我们发现在 `Linux x86_64` 和 `Linux arm64v8` 架构的计算机中分别使用 `golang:alpine` 镜像运行容器 `$ docker run golang:alpine go version` 时,容器能够正常的运行。 - -这是什么原因呢? - -原因就是 `golang:alpine` 官方镜像有一个 [`manifest` 列表 (`manifest list`)](https://docs.docker.com/registry/spec/manifest-v2-2/)。 - -当用户获取一个镜像时,Docker 引擎会首先查找该镜像是否有 `manifest` 列表,如果有的话 Docker 引擎会按照 Docker 运行环境(系统及架构)查找出对应镜像(例如 `golang:alpine`)。如果没有的话会直接获取镜像(例如上例中我们构建的 `username/test`)。 - -我们可以使用 `$ docker manifest inspect golang:alpine` 查看这个 `manifest` 列表的结构。 - -```bash -$ docker manifest inspect golang:alpine -``` - -```json -{ - "schemaVersion": 2, - "mediaType": "application/vnd.docker.distribution.manifest.list.v2+json", - "manifests": [ - { - "mediaType": "application/vnd.docker.distribution.manifest.v2+json", - "size": 1365, - "digest": "sha256:5e28ac423243b187f464d635bcfe1e909f4a31c6c8bce51d0db0a1062bec9e16", - "platform": { - "architecture": "amd64", - "os": "linux" - } - }, - { - "mediaType": "application/vnd.docker.distribution.manifest.v2+json", - "size": 1365, - "digest": "sha256:2945c46e26c9787da884b4065d1de64cf93a3b81ead1b949843dda1fcd458bae", - "platform": { - "architecture": "arm", - "os": "linux", - "variant": "v7" - } - }, - { - "mediaType": "application/vnd.docker.distribution.manifest.v2+json", - "size": 1365, - "digest": "sha256:87fff60114fd3402d0c1a7ddf1eea1ded658f171749b57dc782fd33ee2d47b2d", - "platform": { - "architecture": "arm64", - "os": "linux", - "variant": "v8" - } - }, - { - "mediaType": "application/vnd.docker.distribution.manifest.v2+json", - "size": 1365, - "digest": "sha256:607b43f1d91144f82a9433764e85eb3ccf83f73569552a49bc9788c31b4338de", - "platform": { - "architecture": "386", - "os": "linux" - } - }, - { - "mediaType": "application/vnd.docker.distribution.manifest.v2+json", - "size": 1365, - "digest": "sha256:25ead0e21ed5e246ce31e274b98c09aaf548606788ef28eaf375dc8525064314", - "platform": { - "architecture": "ppc64le", - "os": "linux" - } - }, - { - "mediaType": "application/vnd.docker.distribution.manifest.v2+json", - "size": 1365, - "digest": "sha256:69f5907fa93ea591175b2c688673775378ed861eeb687776669a48692bb9754d", - "platform": { - "architecture": "s390x", - "os": "linux" - } - } - ] -} -``` - -可以看出 `manifest` 列表中包含了不同系统架构所对应的镜像 `digest` 值,这样 Docker 就可以在不同的架构中使用相同的 `manifest` (例如 `golang:alpine`) 获取对应的镜像。 - -下面介绍如何使用 `$ docker manifest` 命令创建并推送 `manifest` 列表到 Docker Hub。 - -## 构建镜像 - -首先在 `Linux x86_64` 构建 `username/x8664-test` 镜像。并在 `Linux arm64v8` 中构建 `username/arm64v8-test` 镜像,构建好之后推送到 Docker Hub。 - -## 创建 `manifest` 列表 - -```bash -# $ docker manifest create MANIFEST_LIST MANIFEST [MANIFEST...] -$ docker manifest create username/test \ - username/x8664-test \ - username/arm64v8-test -``` - -当要修改一个 `manifest` 列表时,可以加入 `-a` 或 `--amend` 参数。 - -## 设置 `manifest` 列表 - -```bash -# $ docker manifest annotate [OPTIONS] MANIFEST_LIST MANIFEST -$ docker manifest annotate username/test \ - username/x8664-test \ - --os linux --arch x86_64 - -$ docker manifest annotate username/test \ - username/arm64v8-test \ - --os linux --arch arm64 --variant v8 -``` - -这样就配置好了 `manifest` 列表。 - -## 查看 `manifest` 列表 - -```bash -$ docker manifest inspect username/test -``` - -## 推送 `manifest` 列表 - -最后我们可以将其推送到 Docker Hub。 - -```bash -$ docker manifest push username/test -``` - -## 测试 - -我们在 `Linux x86_64` `Linux arm64v8` 中分别执行 `$ docker run -it --rm username/test` 命令,发现可以正确的执行。 - -## 官方博客 - -详细了解 `manifest` 可以阅读官方博客。 - -* https://www.docker.com/blog/multi-arch-all-the-things/ diff --git a/network/README.md b/network/README.md index d1b7dcf..e6f0c7e 100644 --- a/network/README.md +++ b/network/README.md @@ -1,3 +1,48 @@ -# Docker 中的网络功能介绍 +# 网络配置 -Docker 允许通过外部访问容器或容器互联的方式来提供网络服务。 +当 Docker 启动时,会自动在主机上创建一个 `docker0` 虚拟网桥,实际上是 Linux 的一个 bridge,可以理解为一个软件交换机。它会在挂载到它的网口之间进行转发。 + +同时,Docker 随机分配一个本地未占用的私有网段(在 [RFC1918](https://datatracker.ietf.org/doc/html/rfc1918) 中定义)中的一个地址给 `docker0` 接口。比如典型的 `172.17.42.1`,掩码为 `255.255.0.0`。此后启动的容器内的网口也会自动分配一个同一网段(`172.17.0.0/16`)的地址。 + +当创建一个 Docker 容器的时候,同时会创建了一对 `veth pair` 接口(当数据包发送到一个接口时,另外一个接口也可以收到相同的数据包)。这对接口一端在容器内,即 `eth0`;另一端在本地并被挂载到 `docker0` 网桥,名称以 `veth` 开头(例如 `vethAQI2QT`)。通过这种方式,主机可以跟容器通信,容器之间也可以相互通信。Docker 就创建了在主机和所有容器之间一个虚拟共享网络。 + +![Docker 网络](./_images/network.png) + +## 用户自定义网络 + +虽然默认的 `bridge` 网络可以满足大部分需求,但为了更好地隔离容器、或满足特定的网络需求,我们推荐使用用户自定义网络。 + +用户可以创建 `bridge`、`overlay` 或 `macvlan` 等不同类型的自定义网络。 + +### 创建一个自定义 bridge 网络 + +```bash +$ docker network create my-net +``` + +### 连接容器到自定义网络 + +在启动容器时,可以使用 `--network` 选项来指定网络。 + +```bash +$ docker run -it --rm --name busybox1 --network my-net busybox sh +$ docker run -it --rm --name busybox2 --network my-net busybox sh +``` + +在 `busybox1` 的终端中,可以 `ping` 通 `busybox2`。 + +```bash +/ # ping busybox2 +PING busybox2 (172.19.0.3): 56 data bytes +64 bytes from 172.19.0.3: seq=0 ttl=64 time=0.083 ms +``` + +### 容器互联的废弃与替代 + +在 Docker 的早期版本中,`--link` 选项被用来连接容器。然而,这个功能现在已经被废弃,并且不推荐在生产环境中使用。 + +**注意:`--link` 是一个遗留功能。它可能会在未来的版本中被移除。我们强烈建议使用用户自定义网络来连接多个容器。** + +使用自定义网络,容器之间可以通过容器名直接进行通信,这比使用 `--link` 更加灵活和强大。 + +接下来的部分将介绍在一些场景中,Docker 所有的网络定制配置。以及通过 Linux 命令来调整、补充、甚至替换 Docker 默认的网络配置。 diff --git a/network/linking.md b/network/linking.md deleted file mode 100644 index e24ebd9..0000000 --- a/network/linking.md +++ /dev/null @@ -1,67 +0,0 @@ -# 容器互联 - -如果你之前有 `Docker` 使用经验,你可能已经习惯了使用 `--link` 参数来使容器互联。 - -随着 Docker 网络的完善,强烈建议大家将容器加入自定义的 Docker 网络来连接多个容器,而不是使用 `--link` 参数。 - -## 新建网络 - -下面先创建一个新的 Docker 网络。 - -```bash -$ docker network create -d bridge my-net -``` - -`-d` 参数指定 Docker 网络类型,有 `bridge` `overlay`。其中 `overlay` 网络类型用于 [Swarm mode](../swarm_mode/),在本小节中你可以忽略它。 - -## 连接容器 - -运行一个容器并连接到新建的 `my-net` 网络 - -```bash -$ docker run -it --rm --name busybox1 --network my-net busybox sh -``` - -打开新的终端,再运行一个容器并加入到 `my-net` 网络 - -```bash -$ docker run -it --rm --name busybox2 --network my-net busybox sh -``` - -再打开一个新的终端查看容器信息 - -```bash -$ docker container ls - -CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES -b47060aca56b busybox "sh" 11 minutes ago Up 11 minutes busybox2 -8720575823ec busybox "sh" 16 minutes ago Up 16 minutes busybox1 -``` - -下面通过 `ping` 来证明 `busybox1` 容器和 `busybox2` 容器建立了互联关系。 - -在 `busybox1` 容器输入以下命令 - -```bash -/ # ping busybox2 -PING busybox2 (172.19.0.3): 56 data bytes -64 bytes from 172.19.0.3: seq=0 ttl=64 time=0.072 ms -64 bytes from 172.19.0.3: seq=1 ttl=64 time=0.118 ms -``` - -用 ping 来测试连接 `busybox2` 容器,它会解析成 `172.19.0.3`。 - -同理在 `busybox2` 容器执行 `ping busybox1`,也会成功连接到。 - -```bash -/ # ping busybox1 -PING busybox1 (172.19.0.2): 56 data bytes -64 bytes from 172.19.0.2: seq=0 ttl=64 time=0.064 ms -64 bytes from 172.19.0.2: seq=1 ttl=64 time=0.143 ms -``` - -这样,`busybox1` 容器和 `busybox2` 容器建立了互联关系。 - -## Docker Compose - -如果你有多个容器之间需要互相连接,推荐使用 [Docker Compose](../compose)。 diff --git a/network/port_mapping.md b/network/port_mapping.md index 022287a..dc6f92d 100644 --- a/network/port_mapping.md +++ b/network/port_mapping.md @@ -1,79 +1,57 @@ -# 外部访问容器 +# 映射容器端口到宿主主机的实现 -容器中可以运行一些网络应用,要让外部也可以访问这些应用,可以通过 `-P` 或 `-p` 参数来指定端口映射。 +默认情况下,容器可以主动访问到外部网络的连接,但是外部网络无法访问到容器。 -当使用 `-P` 标记时,Docker 会随机映射一个端口到内部容器开放的网络端口。 +## 容器访问外部实现 -使用 `docker container ls` 可以看到,本地主机的 32768 被映射到了容器的 80 端口。此时访问本机的 32768 端口即可访问容器内 NGINX 默认页面。 +容器所有到外部网络的连接,源地址都会被 NAT 成本地系统的 IP 地址。这是使用 `iptables` 的源地址伪装操作实现的。 + +查看主机的 NAT 规则。 ```bash -$ docker run -d -P nginx:alpine - -$ docker container ls -l -CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES -fae320d08268 nginx:alpine "/docker-entrypoint.…" 24 seconds ago Up 20 seconds 0.0.0.0:32768->80/tcp bold_mcnulty +$ sudo iptables -t nat -nL +... +Chain POSTROUTING (policy ACCEPT) +target prot opt source destination +MASQUERADE all -- 172.17.0.0/16 !172.17.0.0/16 +... ``` -同样的,可以通过 `docker logs` 命令来查看访问记录。 +其中,上述规则将所有源地址在 `172.17.0.0/16` 网段,目标地址为其他网段(外部网络)的流量动态伪装为从系统网卡发出。MASQUERADE 跟传统 SNAT 的好处是它能动态从网卡获取地址。 + +## 外部访问容器实现 + +容器允许外部访问,可以在 `docker run` 时候通过 `-p` 或 `-P` 参数来启用。 + +不管用那种办法,其实也是在本地的 `iptable` 的 nat 表中添加相应的规则。 + +使用 `-P` 时: ```bash -$ docker logs fa -172.17.0.1 - - [25/Aug/2020:08:34:04 +0000] "GET / HTTP/1.1" 200 612 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:80.0) Gecko/20100101 Firefox/80.0" "-" +$ iptables -t nat -nL +... +Chain DOCKER (2 references) +target prot opt source destination +DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:49153 to:172.17.0.2:80 ``` -`-p` 则可以指定要映射的端口,并且,在一个指定端口上只可以绑定一个容器。支持的格式有 `ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort`。 - -## 映射所有接口地址 - -使用 `hostPort:containerPort` 格式本地的 80 端口映射到容器的 80 端口,可以执行 +使用 `-p 80:80` 时: ```bash -$ docker run -d -p 80:80 nginx:alpine -``` - -此时默认会绑定本地所有接口上的所有地址。 - -## 映射到指定地址的指定端口 - -可以使用 `ip:hostPort:containerPort` 格式指定映射使用一个特定地址,比如 localhost 地址 127.0.0.1 - -```bash -$ docker run -d -p 127.0.0.1:80:80 nginx:alpine -``` - -## 映射到指定地址的任意端口 - -使用 `ip::containerPort` 绑定 localhost 的任意端口到容器的 80 端口,本地主机会自动分配一个端口。 - -```bash -$ docker run -d -p 127.0.0.1::80 nginx:alpine -``` - -还可以使用 `udp` 标记来指定 `udp` 端口 - -```bash -$ docker run -d -p 127.0.0.1:80:80/udp nginx:alpine -``` - -## 查看映射端口配置 - -使用 `docker port` 来查看当前映射的端口配置,也可以查看到绑定的地址 - -```bash -$ docker port fa 80 -0.0.0.0:32768 +$ iptables -t nat -nL +Chain DOCKER (2 references) +target prot opt source destination +DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 to:172.17.0.2:80 ``` 注意: -* 容器有自己的内部网络和 ip 地址(使用 `docker inspect` 查看,Docker 还可以有一个可变的网络配置。) -* `-p` 标记可以多次使用来绑定多个端口 +* 这里的规则映射了 `0.0.0.0`,意味着将接受主机来自所有接口的流量。用户可以通过 `-p IP:host_port:container_port` 或 `-p IP::port` 来指定允许访问容器的主机上的 IP、接口等,以制定更严格的规则。 -例如 +* 如果希望永久绑定到某个固定的 IP 地址,可以在 Docker 配置文件 `/etc/docker/daemon.json` 中添加如下内容。 -```bash -$ docker run -d \ - -p 80:80 \ - -p 443:443 \ - nginx:alpine +```json +{ + "ip": "0.0.0.0" +} ```