docker(3)

访问所有端口

策略为通过(ACCEPT)还是禁止(DROP)取决于配置--icc=true(缺省值)还是 --icc=false。当然,如果手动指定 --iptables=false 则不会添加 iptables 规则

访问指定端口

在通过 -icc=false 关闭网络访问后,还可以通过 --link=CONTAINER_NAME:ALIAS 选项来访问容器的开放端口

端口映射实现

默认情况下,容器可以主动访问到外部网络的连接,但是外部网络无法访问到容器

容器访问外部实现

器所有到外部网络的连接,源地址都会被 NAT 成本地系统的 IP 地址。这是使用 iptables 的源地址伪装操作实现的。

1
2
3
4
5
6
7
$ 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子网出来,并且目标地址不在这个子网的数据包,都将进行源地址伪装(更改来源IP为宿主机的IP)后再发送出去。

上述规则将所有源地址在 172.17.0.0/16 网段,目标地址为其他网段(外部网络)的流量动态伪装为从系统网卡发出。MASQUERADE 跟传统 SNAT 的好处是它能动态从网卡获取地址

外部访问容器实现

容器允许外部访问,可以在 docker run 时候通过 -p-P 参数来启用。

不管用那种办法,其实也是在本地的 iptable 的 nat 表中添加相应的规则。

使用 -P

1
2
3
4
5
6
$ 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
所有前往TCP端口49153的数据包都将被重定向到在172.17.0.2上的80端口。这在Docker中常常被用于端口映射,让外部网络能访问到容器内部,的服务用户可以通过 -p IP:host_port:container_port 或 -p IP::port 来指定允许访问容器的主机上的 IP、接口等,以制定更严格的规则。

如果希望永久绑定到某个固定的 IP 地址,可以在 Docker 配置文件 /etc/docker/daemon.json 中添加如下内容

1
2
3
{
"ip": "0.0.0.0"
}

配置 docker0 网桥

Docker 服务默认会创建一个 docker0 网桥(其上有一个 docker0 内部接口),它在内核层连通了其他的物理或虚拟网卡,这就将所有容器和本地主机都放到同一个物理网络

1
2
3
4
5
$ sudo brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.3a1d7362b4ee no veth65f9
vethdda6
interfaces:这列列出了所有连接到该桥接设备的网络接口。在这个例子中,有两个接口:“veth65f9”和“vethdda6”。这些都是虚拟以太网接口,Docker为每个容器创建了一个,并连接到docker0桥接器。

每次创建一个新容器的时候,Docker 从可用的地址段中选择一个空闲的 IP 地址分配给容器的 eth0 端口。使用本地主机上 docker0 接口的 IP 作为所有容器的默认网关

自定义网桥

除了默认的 docker0 网桥,用户也可以指定网桥来连接各个容器

1
2
3
$ sudo systemctl stop docker
$ sudo ip link set dev docker0 down
$ sudo brctl delbr docker0

然后创建一个网桥 bridge0

1
2
3
$ sudo brctl addbr bridge0
$ sudo ip addr add 192.168.5.1/24 dev bridge0
$ sudo ip link set dev bridge0 up

在 Docker 配置文件 /etc/docker/daemon.json 中添加如下内容,即可将 Docker 默认桥接到创建的网桥上。

1
2
3
{
"bridge": "bridge0",
}

编辑网络配置文件

Docker 1.2.0 开始支持在运行中的容器里编辑 /etc/hosts, /etc/hostname/etc/resolv.conf 文件。

但是这些修改是临时的,只在运行的容器中保留,容器终止或重启后并不会被保存下来,也不会被 docker commit 提交

配置 HTTP/HTTPS 网络代理

为 dockerd 设置网络代理

为 dockerd 创建配置文件夹。在docker的守护进程里面配置

1
sudo mkdir -p /etc/systemd/system/docker.service.d

为 dockerd 创建 HTTP/HTTPS 网络代理的配置文件,文件路径是 /etc/systemd/system/docker.service.d/http-proxy.conf 。并在该文件中添加相关环境变量。

1
2
3
4
[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 服务

1
2
sudo systemctl daemon-reload
sudo systemctl restart docker

为 docker 容器设置网络代理

dockerddocker在Docker架构中起着不同的作用:dockerd是在后台运行,管理和运行容器的守护进程,而docker则是命令行客户端,为用户提供与Docker守护进程交互的接口。直接在docker里面

更改 docker 客户端配置:创建或更改 ~/.docker/config.json,并在该文件中添加相关配置。

1
2
3
4
5
6
7
8
9
10
11
{
"proxies":
{
"default":
{
"httpProxy": "http://proxy.example.com:8080/",
"httpsProxy": "http://proxy.example.com:8080/",
"noProxy": "localhost,127.0.0.1,.example.com"
}
}
}

为 docker build 过程设置网络代理

使用 “–build-arg” 指定 “docker build” 的相关环境变量,dockerfile里面

1
2
3
4
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" .

创建一个点到点连接

创建一对 peer 接口,分别放到两个容器中,配置成点到点链路类型即可

找到进程号,然后创建网络命名空间的跟踪文件

1
2
3
4
5
6
7
$ 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接口然后配置路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ 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 netns exec 2989 <命令> 这个命令有很实际的用途。主要是用来在特定的网络命名空间中执行指定的命令。注解:网络命名空间是Linux命名空间的一种,它提供了隔离网络栈的功能。
具体来说,每一个网络命名空间都有独立的网络设备、IP地址、路由表、防火墙规则等网络栈信息。当一个进程在某个网络命名空间中运行时,它只能看到和操作这个网络命名空间中的网络资源,就像这个网络命名空间是一个独立的网络环境一样。

$ 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