标签: network

  • 如何让 MACvLan 网络下的容器与宿主机互联互通

    我在 NAS 上部署了一个 NginxProxyManager 方便管理其他 Self Host 服务的 SSL 证书,为了避免占用宿主机的 80/443 端口,我用了 macvlan 为 NPM 容器分配了一个独立的局域网 IP。

    networks:
      macvlan_network:
        driver: macvlan
        driver_opts:
          parent: <your-network-interface>
        ipam:
          config:
            - subnet: 192.168.100.0/24
              gateway: 192.168.100.1
    
    services:
      npm:
        image: 'jc21/nginx-proxy-manager:latest'
        restart: unless-stopped
        networks:
          macvlan_network:
            ipv4_address: 192.168.100.22
        environment:
          DISABLE_IPV6: 'true'
        volumes:
          - ./data:/data
          - ./letsencrypt:/etc/letsencrypt

    这时,局域网其他的设备就可以通过 192.168.100.22 访问 NPM,但是对于宿主机以及宿主机上其他的容器来说,这个地址是无法访问的

    在咨询了 GPT 之后,它建议了下面的方案:

    [Unit]
    Description=Set up specific macvlan route for a Docker container
    After=network.target
    
    [Service]
    Type=oneshot
    RemainAfterExit=yes
    # Replace <your-network-interface> with the actual interface name
    ExecStart=/usr/bin/ip link add mvlan0 link <your-network-interface> type macvlan mode bridge
    ExecStart=/usr/bin/ip addr add <unused-ip>/24 dev mvlan0
    ExecStart=/usr/bin/ip link set mvlan0 up
    ExecStart=/usr/bin/ip route del 192.168.100.0/24 dev mvlan0
    ExecStart=/usr/bin/ip route add 192.168.100.22 dev mvlan0
    ExecStop=/usr/bin/ip link delete mvlan0
    
    [Install]
    WantedBy=multi-user.target

    默认情况下的问题:

      • 当使用 macvlan/ipvlan 时,Docker 容器会直接连接到物理网络接口
      • 但是宿主机无法通过物理接口直接与这些容器通信,因为物理接口不会处理宿主机到 macvlan 接口的流量

      创建 mvlan0 的作用:

      sudo ip link add mvlan0 link <your-network-interface> type macvlan mode bridge
      sudo ip addr add <unused-ip>/24 dev mvlan0
      sudo ip link set mvlan0 up
      • 这实际上是在宿主机创建了一个新的网络接口 mvlan0
      • 这个接口也是一个 macvlan 接口,与 Docker 容器使用相同的技术
      • mvlan0 接口可以看作是宿主机在 192.168.100.0/24 网段的一个”入口”

      通信流程:

      宿主机 -> mvlan0 (unused-ip) -> 物理网卡 -> Docker 容器 (192.168.100.22)
      • 当宿主机要访问 192.168.100.22 时
      • 数据包会通过 mvlan0 接口发出
      • 由于 mvlan0 和容器的网络接口都是 macvlan 类型,它们可以在同一层次通信

      类比解释:

        • 可以把这个设置想象成宿主机也”加入”到了容器所在的网络
        • mvlan0 就像是宿主机的一个”虚拟网卡”,使它能够与容器在同一个网络层面通信

        为什么需要这样:

          • 这种设置避免了常规的网络栈限制
          • 提供了一个直接的通信路径
          • 保持了网络隔离性的同时又允许必要的通信

          这就是为什么添加这个 macvlan 接口可以让宿主机访问容器的原理。这种方法既保持了容器网络的独立性,又提供了必要的访问路径。

        1. 2024 家庭网络设备大升级 网络篇

          搭建自己的 homelab ,在局域网尽情冲浪~

          2 / 2 of homelab 历险记

          网络设备硬件到齐之后,就需要重新配置网络了,在我的使用场景中,家庭网络除了基本的上网之外需要承担两个额外的功能:

          • 全局透明代理
          • 允许电脑、手机远程访问本地网络服务

          我选择的方案是使用 N100 小主机运行一个 OpenWRT 虚拟机,并将两个物理网卡分配给它,以实现更加高级的网络配置。

          怎么连网线

          因为家宽的下行带宽只有 300M,所以就用开发商自带的超五类网线连接光猫跟 N100 小主机的千兆 WAN 口。

          N100 小主机的 2.5G LAN 口跟一台 TP-Link 路由器连接,路由器设置有线 AP 模式,这样全屋所有的流量都由虚拟机软路由处理,用 openwrt-passwall2 实现全局透明代理功能。

          QNAP NAS 通过网线连接到 TP-Link 路由器,不过由于路由器只有千兆网口,所以 QNAP 的2.5G 网口完全是性能过剩的。

          N100 上再用一个有线网卡连接到 TP-Link 路由器,给宿主机联网使用,这个有线网卡配置了一个静态 IP,方便在软路由挂掉的时候访问管理面板排查故障。

          其他的设备都用无线网络连接,因为户型小,所以单个 TP-Link 路由器的 5GHz WiFi 完全可以覆盖主要的视频消费设备。至于厨房、阳台等犄角旮旯的物联网设备,2.4GHz WiFi 也能提供足够的覆盖度。

          怎么从任意的外部网络访问家里的局域网

          很多 homelab 玩家会选择公网 IP 搭配 DDNS 的方案,但对我来说,这种方案会显著提高局域网的安全风险 —— 我并不是一个 OpenWRT 的运维高手,也完全不会使用 iptables/nft ,如果有人通过公网 IP 侵入了局域网,我大概率很难及时地发现问题。

          我的方案是 zerotier + 拥有公网 IP 的 VPS + singbox。外网设备通过 singbox 客户端连接 VPS 上的 singbox 服务端,然后通过路由规则将发往局域网的流量通过 zerotier 转发到家庭网络的 OpenWRT 上,以此实现远程访问。

          首先,将局域网中的 OpenWRT 跟 VPS 都加入同一个 zerotier 网络。OpenWRT 加入 zerotier 比较复杂,建议参考 openwrt 的文档配置。

          在 OpenWRT 上成功加入 zerotier 网络后,就可以看到下面的接口以及防火墙配置:

          然后在 VPS 上添加一条路由规则:

          ip route add 192.168.100.0/24 via 10.2.2.233
          # 10.2.2.233 就是 OpenWRT 在 zerotier 网段中的 IP

          接着其他设备在外部网络下通过 singbox 连接到 VPS,并且添加下面的 singbox 路由规则:

          "route": {
            "rules": [
              {
                // 家庭局域网网段
                "ip_cidr": [
                  "192.168.100.0/24"
                ],
                "outbound": "vps"
              },
              {
                // 局域网服务专属域名,在公网设置解析到家庭局域网的 Web 网关
                "domain_suffix": [
                  ".n.zeeko.dev"
                ],
                "outbound": "vps"
              },
            ]
          }

          这样不仅可以减少家庭局域网在公网上暴露面,还方便各种设备(笔记本电脑、手机、平板)在各种网络条件下访问家里的局域网设备 —— 只要能连接上部署了 singbox 的 VPS,就可以访问家里的局域网。至于远程访问时的网络连接速度,实测在联通 5G 网络下满足远程播放 20Mbps 码率视频的场景比哔哩哔哩大会员的 4K 视频要强

        2. 使用 WebP/AVIF 压缩博文图片

          使用 WebP/AVIF 压缩博文图片

          往文章中贴截图的时候,如果使用压缩率较低的图片格式(jpeg/png),长时间的图片下载耗时会降低用户的阅读体验。

          根据 CanIUse 的统计,目前绝大多数的网上冲浪用户使用的浏览器已经支持了 WebP/AVIF 这类高效的图片格式。

          https://caniuse.com/avif Can I use AVIF?
          https://caniuse.com/webp Can I use WebP?

          以文章开头的那张图片为例,原图大概 108KB,转换成 AVIF 格式并压缩后,体积缩小了接近 90% 。

          如果想要给 WordPress 启用自动转换的功能,我推荐使用下面的免费插件,只要你的 PHP 支持 imagick/GD 插件,就可以使用。

          安装完成后需要在管理面板中启用 Mordern ImageFormats 功能。

          如果需要把已有的图片都转换成 WebP/AVIF,可以使用下面的插件批量转换。

          安装完成后就可以在后台管理页面批量转换格式或者单独地转换格式。

          一些疑问

          如果用户的浏览器不支持 WebP/AVIF 该怎么办?

          AVIF 在比较新的浏览器中普及率很高 只有现代 IE —— Safari 不支持,WebP 的兼容性也非常好,不过万一有人用史前时代的浏览器上网呢?

          其实这个问题完全不用担心,上面介绍的插件在生成 WebP/AVIF 格式后并不会删除原图,浏览器会跟 WordPress 协商应该使用何种格式的图片向用户展示。

          怎么确认我正在使用 WebP/AVIF 版本的图片?

          仅仅依靠文件名无法区分图片格式,应该检查浏览器请求的响应部分,如果 Content-Type 的值为 image/avif 说明这张图片正在以 AVIF 格式展示,WebP 也是同理。

          为什么图片看起来有些糊?

          CompressX 插件默认使用有损压缩,建议手动调整为无损压缩选项。

          为什么转换格式这么慢?

          转换速度完全取决于你的服务器配置,简而言之,加钱世界可得~

          为什么 PNG 图片没有被自动转换成 WebP/AVIF ?

          因为 Modern Image Format 暂时还没加上这个功能,具体可以看这里的讨论:https://github.com/WordPress/performance/pull/1421。就目前而言,可以使用 CompressX 插件手动转换 PNG 图片。

        3. 不要钱的博客网站性能优化

          赛博菩萨 Cloudflare 给免费帐户提供了 Cache Rules 功能,除了可以点亮域名后的云朵图标之外,还可以利用 Cache Rules 缓存博客网站的页面,提高访问者的使用体验。

          要给 WordPress 网站启用 Cache Rules 推荐使用下面这个插件:

          Super Page Cache for Cloudflare

          不过在使用这个插件的时候需要注意,这个插件自带的 Cloudflare API Token 权限说明有误,请参考下面的权限列表。

          如果你想我一样还使用了 LiteSpeed Cache 等缓存插件,建议参考 Super Page Cache 的 FAQ 来调整其他关联插件的配置。

          完成插件的安装跟配置后,可以在 Cloudflare 控制台看到由插件创建的 Cache Rule:

          至此,你的网站就可以享受到免费的全球 CDN 加成了。

        4. 使用 cloudflare tunnel 在线上预览本地的开发服务器

          Cloudflare Tunnel 是一项服务,允许开发者将其本地开发服务器通过安全的隧道连接到 Cloudflare 的服务器,并发布到互联网上。这意味着:

          1. 其他人可以直接通过互联网访问你本机的开发服务器
          2. 本地的 HTTP 服务可以自动获得由 Cloudflare 提供的 https 证书

          Cloudflare 在这篇文章中介绍了如何将本地项目发布到互联网,但是对于前端开发服务器的场景来说,缺少一些必要的配置说明,例如,稳定的域名、网页缓存策略等。如果你遇到了开发服务器通过 Cloudflare tunnel 访问后出现了一些诡异的问题,不妨参考下面的步骤来检查一下自己的配置。

          创建本地管理的 tunnel

          在开始之前,请确保你准备好了这些东西:

          • 在本地安装 cloudflared
          • 在 cloudflare 上绑定了自己的域名

          接着,参考这里的说明,创建一个本地管理的 tunnel。完成这里的步骤后,你应该会得到一份 config.yaml 文件,默认存储在 ~/.cloudflared 目录下。

          Cloudflare 的文档中使用了 url 字段配置反向代理,但我建议使用下面的 ingress 配置,这样你可以在同一个配置文件中声明多个服务。

          tunnel: <Tunnel ID>
          credentials-file: /path/to/<Tunnel ID>.json
          
          ingress:
            - hostname: ng-serve.zeeko.dev
              path: /api/.*
              service: http://localhost:3000
            - hostname: ng-serve.zeeko.dev
              service: http://localhost:4200
            # this is a required fallback rule
            - service: http_status:503

          禁用 Cloudfare 缓存

          你的 Cloudflare 帐户很可能默认开启了请求缓存功能,在一些使用场景下,例如,webpack dev server,这个自带的缓存功能会让 dev server 变得很鬼畜,我们需要手动在 Cloudflare 控制面板禁用缓存。

          添加 webpack dev server 白名单

          如果你在使用 webpack dev server,记得把绑定的域名添加到 allowedHosts 中,避免 HMR 失败。

        5. 如何使用 Zerotier 连接你家中的设备

          我在家里的旧笔记本上使用 Open My Vault 建立了 NAS,不过我只能在家里连上 WiFi 才能访问它。在过年回老家、出门旅游或者外出办公的时候就没法使用了。虽然在这些场景经常用不上 NAS,不过,“我可以不用,但不能没有”。

          经过一番调研后,我发现我需要的是一个 VPN 软件,通过 VPN 安全地访问运行在家里的网络服务。基于部署难度上的考量,我选择了 zerotier,这是一款非常简单易用的 VPN 软件,免费版可以连接 25 个节点,对于我来说,完全够用了。但是,在国内的网络环境下,节点间的连接往往不太稳定,我在国内没有自己的公网 IP 服务器,所以借助一台香港服务器,来提高 zerotier 网络的稳定性。

          首先,在 my.zerotier.com 创建一个虚拟网络,我选择的私有网段是 10.2.2.0/24,因为这个网段不容易跟常见的家庭路由器内网撞车。

          家里的网络有全局透明代理,可以直接在运行 NAS 的服务器上使用官方的 zerotier 客户端加入 zerotier 网络。

          我没有在路由器上安装 zerotier:

          • 我的 OpenWRT 虚拟机系统版本太奇葩了,没法从官方源安装 zerotier
          • 我希望路由器的功能尽量少一些,毕竟 OpenWRT 跟日常使用的 Linux 系统有很大区别,对我而言有额外的维护负担

          我的 VPS 在香港,也需要加入 zerotier network,直接使用官方的 Linux 客户端就行,我并没有在 VPS 上建立 zerotier moon 服务,这跟我手机上的连接 zerotier 的方式有关。

          手机有时会出门,而且没法设置多个代理,所以就用 v2ray 客户端(SagerNet)将所有访问 10.2.2.0/24 的请求转发到 VPS 服务器。因为 VPS 已经加入了 zerotier 网络,所以手机上就不用安装 zerotier 客户端。笔记本电脑也是同理,这样做还可以避免 zerotier VPN 跟工作 VPN 冲突。

          不过这样的设置也带来了新的问题,对于手机而言,部署在家里的服务在不同网络环境下有不同的 IP 地址,虽然 zerotier 有 DNS 功能,但是我的手机本质上并没有加入 zerotier VPN,所以实际上也没办法使用 zerodns。还好我有自己的域名,在公网上,我把指向家里服务的域名都解析到 zerotier 的私有地址(10.2.2.0/24)。在家里的路由器上,将这些域名强制设置为局域网址(192.168.1.0/24)。

          至此,出门在外的时候,我终于可以随时访问部署在家里的 NAS 了,使用联通 5G 网络运行 iperf3 测试,能够跑到 60Mbps(对比通过 VPS 代理测速的 speedtest.net 结果 120Mbps),应急使用完全足够了。

        6. 如何解决米家设备在透明代理下无法联网的问题

          我在家里的一台旧笔记本上部署了透明代理服务,让连上家里网络的设备都能赛博漫游到国外,不过最近,在我的透明代理网络下,米家设备全部没法联网。我的透明代理工作机制如下:

          OpenWRT 强制启用 DHCP,给米家设备设置特定的 IP 地址,并把米家设备的网关设置为主路由;其他设备的网关设置为 OpenWRT。

          OpenWRT 会把所有需要转发(FORWARD,即目标地址非 OpenWRT 本身)的请求发送给透明代理端口(localhost:12345),接着运行在 OpenWRT 上的 v2ray 会对来自 12345 端口的请求分流,国内请求直接发送,国外请求通过代理协议发送到服务端。

          经过一番排查后,我发现在最近的一次主路由固件升级过后,OpenWRT 强制启动 DHCP 失效了,米家设备的网关被主路由设置成了 OpenWRT 。米家设备需要请求 https://:Mijia Cloud。而 Mijia Cloud 的 IP 地址是米家设备通过其他接口获取的,无法通过正常的 DNS 解析。这时,就引发了 v2ray 的一个问题:v2ray 在分流时会嗅探请求的域名以及域名解析后的 IP 地址,帮助判断请求是否需要被代理。因为 Mijia Cloud 无法被解析,导致嗅探功能出错,v2ray 无法进一步处理请求,导致请求被丢弃

          知道了问题的成因,解决方法就很简单,通过 OpenWRT 上的 iptables 分流,让来自米家设备的请求绕过代理:

          iptables -t mangle -A V2RAY -s 192.168.68.<mijia device ip> -j RETURN

          但是这样做,米家设备还是无法联网,通过 tcpdump 发现米家设备只能发送请求,无法接收响应。

          又是一番搜索,怀疑是一些路由器会校验请求来源设备 IP 跟数据包中来源 IP 的一致性,米家设备请求被 OpenWRT 转发到主路由网关,导致来源设备 IP 跟数据包中的来源 IP 不一致,最终请求被拦截在了主路由处。要解决这个问题,得在 OpenWRT 对应的网络接口上启用「动态 IP 伪装」,说白了就是 NAT 。

          其他设备也是被 OpenWRT 转发到主路由的,为什么它们没有出现这种问题呢?这时就需要进一步了解 Linux 的网络原理了:

          所有发往网关的请求应该都需要转发(FORWARD,因为请求的目标 IP 不是网关本身),但是所有基于这篇透明代理白话文设置的透明代理中,转发流量都被 iptables 发到了 12345 端口,变成了入站请求(INPUT),经过代理软件处理后,作为出站请求(OUTPUT)发出,这些流量就不再被认为是转发流量了,在主路由看来,这些被 v2ray 处理过的流量来源请求都是 OpenWRT ,所以不会触发来源请求校验失败。而被 iptables 分流的米家设备请求,从主路由看来,都是由 OpenWRT 发出但是来自米家设备 IP 的,导致校验失败。

          上面的根因分析完全是我半吊子的推测,可能不够准确、专业,但是启用 OpenWRT 的「动态 IP 伪装」之后,确实解决了米家设备无法联网的问题。


          参考链接