mirror of
https://github.com/yeasy/docker_practice.git
synced 2026-03-06 15:31:11 +00:00
Merge networks
This commit is contained in:
23
SUMMARY.md
23
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)
|
||||
|
||||
@@ -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 所有的网络定制配置。以及通过 Linux 命令来调整、补充、甚至替换 Docker 默认的网络配置。
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 48 KiB |
@@ -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` 参数指定的名字。主机名则不会被识别。
|
||||
@@ -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: <BROADCAST,MULTICAST> 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 地址配置和路由信息。
|
||||
@@ -1,5 +0,0 @@
|
||||
# 编辑网络配置文件
|
||||
|
||||
Docker 1.2.0 开始支持在运行中的容器里编辑 `/etc/hosts`, `/etc/hostname` 和 `/etc/resolv.conf` 文件。
|
||||
|
||||
但是这些修改是临时的,只在运行的容器中保留,容器终止或重启后并不会被保存下来,也不会被 `docker commit` 提交。
|
||||
@@ -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: <BROADCAST,UP,LOWER_UP> 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
|
||||
```
|
||||
@@ -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` 的服务器。
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -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" |
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
```
|
||||
@@ -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` 来关闭容器之间的通信。
|
||||
@@ -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` 映射容器所有端口到宿主主机
|
||||
@@ -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 的详细信息,如上文所示。
|
||||
@@ -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` 打印版本信息
|
||||
@@ -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/
|
||||
@@ -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 就创建了在主机和所有容器之间一个虚拟共享网络。
|
||||
|
||||

|
||||
|
||||
## 用户自定义网络
|
||||
|
||||
虽然默认的 `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 默认的网络配置。
|
||||
|
||||
@@ -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)。
|
||||
@@ -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"
|
||||
}
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user