Skip to content

Connect to Every Container:Docker 网络理论

约 2224 字大约 7 分钟

LinuxDocker

2025-04-02

Docker 被广泛用于服务器上,为各种服务提供相对独立的轻量级运行环境,容器与容器之间互不影响。

而正是 Docker 容器的良好隔离性质,使得容器中的服务并不能直接访问到其他容器中的服务,导致各种不便。

而 Docker 网络同样是容器环境的一部分,将容器从宿主机的网络环境中独立出来,相互连接。

1 Docker 网络模式

Docker 提供了四种网络模式,分别是 host、bridge、none、container。

Docker 在安装之时会默认创建 host、bridge、none 模式对应的网络。

1.1 host

你可以使用--network host参数指定容器使用 host 网络模式。

docker run --network host --name alpine_container alpine ip addr

演示使用的 Alpine 镜像。由于空 Alpine 镜像在启动后会立即退出,因此我们直接让其执行ip addr命令。

被设定为 host 网络模式的容器不再拥有自己的虚拟网卡,从而拥有和宿主机一样的 IP 地址。这一点我们可以在宿主机中执行ip addr来验证。

host 网络模式显然便于访问容器提供的服务,但同时也降低了隔离性。

而且容器映射到外部的端口将被忽略。

docker run --network host --name alpine_container -p 4700:4700 alpine
WARNING: Published ports are discarded when using host network mode

1.2 none

使用--network none参数指定容器使用 none 网络模式。

docker run --network none --name alpine_container alpine ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever

被设定为 none 网络模式的容器将获得一个完全隔离的网络环境,容器内部只能使用 localhost 上的网络设备。

1.3 bridge

bridge 网络模式是 Docker 的默认网络。

Docker 进程启动时会在宿主机上创建一个名为docker0的虚拟网桥。宿主机上启动的 Docker 容器会默认连接到这个虚拟网桥上,从而所有容器都通过docker0连接在一个二层网络中。

在这种模式下,Docker 会为每个容器创建虚拟网卡,容器与容器之间可以通过这个虚拟网卡指定的 IP 地址进行通信。

我们运行两个 Alpine 容器,并进入容器内部执行ip addr

容器 1
598: eth0@if599: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever

注意到容器 1 的 IP 地址为 172.17.0.2,容器 2 的 IP 地址为 172.17.0.3。

在容器 2 内部 ping 容器 1 的 IP,证明两个容器是可以通过虚拟网桥相通。

PING 172.17.0.2 (172.17.0.2): 56 data bytes
64 bytes from 172.17.0.2: seq=0 ttl=64 time=0.439 ms
64 bytes from 172.17.0.2: seq=1 ttl=64 time=0.123 ms
64 bytes from 172.17.0.2: seq=2 ttl=64 time=0.137 ms
64 bytes from 172.17.0.2: seq=3 ttl=64 time=0.084 ms
^C
--- 172.17.0.2 ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 0.084/0.195/0.439 ms

1.4 container

container 网络模式需要在创建容器时指定一个已经创建好的容器,新创建的容器将不会拥有自己的虚拟网卡,而是和指定容器共用网卡。

docker run --name alpine_1 alpine sleep 3600
docker run --network container:alpine_1 --name alpine_2 alpine sleep 3600

同样分别进入容器查看ip addr

容器 1
602: eth0@if603: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever

这说明二者共用同一虚拟网卡、同一 IP。同样,若两个容器中同时监听同一端口,将会发生冲突。

需要注意的是,此时容器 2 并无自己的虚拟网卡,若将容器 1 停止,容器 2 将只剩下 localhost 网络。

2 Docker 自定义网络

上述几种方式显然不够灵活,而自定义网络可以将任意容器添加进网络中。实际开发中也更建议使用自定义网络。

创建自定义网络:

docker network create CUSTOM_NETWORK

在创建容器时连接到自定义网络:

docker run --net CUSTOM_NETWORK --name CONTAINER_NAME IMAGE

连接已有容器到自定义网络:

docker network connect CUSTOM_NETWORK CONTAINER_NAME

断开容器连接的自定义网络:

docker network disconnect CUSTOM_NETWORK CONTAINER_NAME

移除自定义网络:

docker network rm CUSTOM_NETWORK

注意,如果某个容器在创建之时指定了自定义网络,那么这个自定义网络无法删除。

3 实战:一种为任意容器提供网络代理的优雅方法

网络代理是在服务器上有着非常普遍的应用,然而对于 Docker 容器内部的服务而言,很难访问到宿主机上的网络代理。

如果代理同样也是部署在容器中,就可以通过上述提到的 bridge 网络模式借助 IP 地址进行通信。

看起来很合理,对吧?但是 IP 地址是会变的,因此这种方法并不可行。

我们选择建立自定义网络。在自定义网络中,Docker 实现了一个内部 DNS 服务器,使得处于同一网络内的容器可以通过容器名称进行通信。

这里采用 Clash 提供网络代理,SillyTavern 作为需要网络代理的应用程序容器。

3.1 创建容器并连接网络

为了方便管理,这里使用 Docker Compose。

Clash
version: '3'
services:
  clash:
    image: laoyutang/clash-and-dashboard:latest
    container_name: clash
    restart: always
    logging:
      options:
        max-size: 1m
    volumes:
      - ./data/config.yaml:/root/.config/clash/config.yaml
    ports:
      - "7888:8080"
      - "7890:7890"

分别执行docker compose up -d

需要说明的是,使用 Docker Compose 创建的容器都会自动创建一个_default网络。

clash容器在创建之时,就会同时创建clash_default网络。

接下来将sillytavern容器连接至clash网络。

docker network connect clash_default sillytavern

进入sillytavern容器内部检查网络能否接通。

docker exec -it sillytavern sh

Ping一下clash容器。得益于自定义网络的内嵌 DNS 服务,我们可以使用容器名称进行通信。

ping clash
PING clash (192.168.16.2): 56 data bytes
64 bytes from 192.168.16.2: seq=0 ttl=64 time=0.216 ms
64 bytes from 192.168.16.2: seq=1 ttl=64 time=0.100 ms
64 bytes from 192.168.16.2: seq=2 ttl=64 time=0.109 ms
64 bytes from 192.168.16.2: seq=3 ttl=64 time=0.088 ms
^C
--- clash ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 0.088/0.128/0.216 ms

说明可以接通clash容器。

3.2 启用 SillyTavern 代理

在 SillyTavern 的配置文件config.yaml中启用代理。

requestProxy:
  enabled: true
  url: http://clash:7890
  bypass:
    - localhost
    - 127.0.0.1

重启容器应用设置。

3.5 管理 Clash 服务

这个 Clash 镜像提供了一个管理面板,借助它可以管理 Clash 服务。

其默认端口为7888,默认的docker-compose.yml已经映射出了这个端口,只需在云服务器安全组(如果有的话)和系统防火墙中放行7888端口即可。

进入管理面板后需要在设置中打开允许来自局域网的连接

3.6 (可能有用)关于 Clash 管理面板的安全性

如你所见,这个 Clash 面板比较简单,当你把端口暴露到公网上后,任何人都可以访问你的面板,极大降低了安全性。

我们可以使用 VSCode 自带的端口转发来解决这一问题。

首先关闭云服务器安全组(如果有的话)和系统防火墙中放行的7888端口。

将 VSCode 连接到你的服务器,然后转发7888端口到你的本地电脑即可。

3.7 (可能有用)关于配置文件自动更新

大部分情况下 Clash 的配置文件来自服务提供商,因此我们需要及时更新配置文件。

写个脚本定时执行就好啦:

#!/bin/bash
cd [Clash的Docker-compose.yml所在目录]
wget -O ./data/config.yaml [服务提供商提供的订阅链接]
docker restart clash

如果你细心观察的话还会发现,当重启clash容器后,允许来自局域网的连接这一设置可能会自动关闭。

这是由于你的服务提供商提供的配置文件中allow-lan这一字段为false造成的。

只需要在脚本docker restart clash前面一行添加:

sed -i 's/allow-lan: false/allow-lan: true/' ./data/config.yaml

false替换为true即可。

Copyright ©️ 2024 - 2025 YOAKE | Powered by VuePress & Plume
冀 ICP 备 2025102465号-1 · 京公网安备 11011502038573 号