嘗試理解docker網絡通信邏輯

一、docker是什么

  1. Docker本質是一個進程,
  2. 宿主機通過namespace隔離機制提供進程需要運行基礎環境,并且通過Cgroup限制進程調用資源。
  3. Docker的隔離機制包括
    • network隔離,此次主要探討網絡隔離
    • mount隔離
    • hostname隔離
    • user隔離
    • pid隔離
    • 進程通信隔離

二、docker 網絡模式

  1. host: 主機模式
    • 不會創建網絡隔離機制,直接宿主機的網絡。
  2. bridge : 默認橋接, 新建虛擬橋接網卡用于 docker 容器之間的通信。
    • 橋接網卡通過iptables對分組進行轉發到主網卡,通常使用主機外網IP與外部網絡通信。
    • 橋接網卡會關聯多張 interface 網絡接口。 實現內網通信。
    • interface 主要是通過 veth pair 虛擬設備對,一端用于容器,一端用于綁定網橋。
  3. macvlan : 為容器穿件一張虛擬網卡,該網卡用于獨立的MAC地址和相同的網卡操作邏輯。
    • 相比bridgehosts 模式。 即滿足了網絡隔離需求,也滿足了和宿主機網絡相同地位。
  4. ipvlan : 需要要求內核版本 4.2+, 暫不復現。 可以通過 ip link vlan 相關命令了解用法
  5. overlay: 未復現,主要用于swarm 集群
  6. none: 容器不具備網絡通信能力,更多作為類似于腳本任務。

三、docker 網絡通信

docker主要通過iptables規則進行報文轉和過濾。
目標: 嘗試理解docker創建的iptables行為。

1. 清空iptables規則, 重啟docker并啟動nginx容器
# 清空iptables規則,重啟docker后,docker-proxy會自動更新iptables策略
[root@mking /]# iptables -F && iptables -t nat -F && systemctl restart docker# 啟動docker容器,并暴露網卡
[root@mking /]# docker network create demo# 查看網卡信息,demo創建橋接的網卡名為: br-a397efbb3fb4 
[root@mking /]# docker network ls 
NETWORK ID     NAME      DRIVER    SCOPE
be4c6d689139   bridge    bridge    local
a397efbb3fb4   demo      bridge    local
d843459f25fd   host      host      local
7a3b4a1a7c00   none      null      local# 啟動docker容器,
[root@mking /]# docker run -itd --rm --name nginx --network demo  -p 80:80 nginx

demo創建橋接的網卡名為br-a397efbb3fb4, 后續對該網卡規則進行理解。

2. 查看nat轉發表信息

nat主要修改報文目標IP,目標端口和源P地址,源端口

[root@mking /]# iptables -t nat -nvL
Chain PREROUTING (policy ACCEPT 4 packets, 600 bytes)pkts bytes target     prot opt in     out     source               destination         0     0 DOCKER     all  --  *      *       0.0.0.0/0            0.0.0.0/0            ADDRTYPE match dst-type LOCALChain INPUT (policy ACCEPT 4 packets, 600 bytes)pkts bytes target     prot opt in     out     source               destination         Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)pkts bytes target     prot opt in     out     source               destination         0     0 DOCKER     all  --  *      *       0.0.0.0/0           !127.0.0.0/8          ADDRTYPE match dst-type LOCALChain POSTROUTING (policy ACCEPT 0 packets, 0 bytes)pkts bytes target     prot opt in     out     source               destination         0     0 MASQUERADE  all  --  *      !docker0  10.0.254.0/24        0.0.0.0/0           0     0 MASQUERADE  all  --  *      !br-a397efbb3fb4  10.2.254.0/24        0.0.0.0/0           0     0 MASQUERADE  tcp  --  *      *       10.2.254.2           10.2.254.2           tcp dpt:80Chain DOCKER (2 references)pkts bytes target     prot opt in     out     source               destination         0     0 RETURN     all  --  docker0 *       0.0.0.0/0            0.0.0.0/0           0     0 RETURN     all  --  br-a397efbb3fb4 *       0.0.0.0/0            0.0.0.0/0           0     0 DNAT       tcp  --  !br-a397efbb3fb4 *       0.0.0.0/0            0.0.0.0/0            tcp dpt:80 to:10.2.254.2:80

網橋 br-a397efbb3fb4nat 中處理邏輯。

  1. PREROUTING: 外部流量到達主機時處理,該鏈路主要動作是修改報文目的IP地址,通過DNAT動作實現請求轉發
    • 鏈路展示所有報文會通過DOCKER CHAIN鏈, 該鏈也恰好有處理DNAT動作
  2. OUTPUT: 流量出去的時候,也會經過 DOCKER CHAIN
  3. DOCKER: 關于網橋 br-a397efbb3fb4 有兩處處理。
    • RETURN處: 流量從網卡 br-a397efbb3fb4 進入的, 就不再繼續向下匹配,也就不修改目標IP
    • DNAT: 流量從其他網卡進入且訪問80端口的,通過修改了目的地址指向nginx容器的IP: 10.2.254.2
  4. POSTROUTING: 內部流量從網卡出去時候處理, 該鏈路主要用于修改源IP地址,用于響應回源。 br-a397efbb3fb4有2處處理
    • 第二行
      • 命令: iptables -t nat -A POSTROUTING ! -o br-a397efbb3fb4 -s 10.2.254.0/24 -j MASQUERADE
      • 理解: 來自源IP網段10.2.254.0/24 且流量不經過網卡 br-a397efbb3fb4 出去的報文,修改動態源IP地址。
        • MASQUERADE 動態獲取IP地址,通常是網卡的IP地址。
        • 該規則也用于第一行的docker0。 說明這是docker創建網卡時的標準規則。
    • 第三行:
      • 命令: iptables -t nat -A POSTROUTING -p tcp --dport 80 -s 10.2.254.2 -d 10.2.254.2 -j MASQUERADE
      • 理解: 對源IP和目標IP都是10.2.254.2且端口是80的TCP發出的報文動態修改源IP地址。

場景1: DNAT轉換后報文是如何到達nginx的

  1. 假設外部通過eth0訪問80端口,先通過iptable規則進行報文分析處理
    • 報文的入口網卡是eth0
  2. PREROUTING進行了目的端口轉發,那么報文就要發送到10.2.254.2, 見docker鏈的DNAT規則
    • 要求不是入口網卡 br-a397efbb3fb4 的報文進行DNAT轉換
    • 網橋br-a397efbb3fb4 作為一個網關角色,是保持內部通信的基礎,所以其IP不能進行改動
  3. 通過路由尋址。route -n 可以看到目標網段在10.2.254.0是通過br-a397efbb3fb4出去。
    • 此時報文的入口是eth0, 報文的出口br-a397efbb3fb4
    • br-a397efbb3fb4 和容器IP10.2.254.2 在同一個網段下,且滿足通信。

場景2: 容器是如何通過SNAT實現對外通信的

  1. 假設容器請求其他主機: 192.168.35.253,刪除a397efbb3fb4的SNAT規則后。進行ping測試

  2. 通過簡單的分析拿到容器在主機的設備對網卡是vetha276645

    • 在容器查看命令ip link show可以看容器網卡的ID@外部的網卡ID。eth0@if125
    • 外部查看ip link show查到125網卡vetha276645
  3. ping 請求通過vetha276645到達主機后,通過路由會從網卡eth0發出,查看eth0抓包信息。可以看到是有報文出去

抓包容器網卡vetha276645192.168.35.253發起請求

[root@mking net]# tcpdump -vvn -i vetha276645  icmp
tcpdump: listening on vetha276645, link-type EN10MB (Ethernet), capture size 262144 bytes
11:15:39.983214 IP (tos 0x0, ttl 64, id 19344, offset 0, flags [DF], proto ICMP (1), length 84)10.2.254.3 > 192.168.35.253: ICMP echo request, id 32, seq 782, length 64
11:15:40.983457 IP (tos 0x0, ttl 64, id 19815, offset 0, flags [DF], proto ICMP (1), length 84)10.2.254.3 > 192.168.35.253: ICMP echo request, id 32, seq 783, length 64
11:15:41.983658 IP (tos 0x0, ttl 64, id 20419, offset 0, flags [DF], proto ICMP (1), length 84)10.2.254.3 > 192.168.35.253: ICMP echo request, id 32, seq 784, length 64
11:15:42.983858 IP (tos 0x0, ttl 64, id 21070, offset 0, flags [DF], proto ICMP (1), length 84)10.2.254.3 > 192.168.35.253: ICMP echo request, id 32, seq 785, length 64

抓包192.168.33.253 出口網卡也發出去請求

[root@mking ~]# tcpdump -i enp3s0 -p icmp  -vvnn
tcpdump: listening on enp3s0, link-type EN10MB (Ethernet), capture size 262144 bytes
11:04:49.856208 IP (tos 0x0, ttl 63, id 10747, offset 0, flags [DF], proto ICMP (1), length 84)10.2.254.3 > 192.168.35.253: ICMP echo request, id 32, seq 132, length 64
11:04:50.856421 IP (tos 0x0, ttl 63, id 11569, offset 0, flags [DF], proto ICMP (1), length 84)10.2.254.3 > 192.168.35.253: ICMP echo request, id 32, seq 133, length 64
11:04:51.856633 IP (tos 0x0, ttl 63, id 11661, offset 0, flags [DF], proto ICMP (1), length 84)10.2.254.3 > 192.168.35.253: ICMP echo request, id 32, seq 134, length 64
11:04:52.856829 IP (tos 0x0, ttl 63, id 12190, offset 0, flags [DF], proto ICMP (1), length 84)10.2.254.3 > 192.168.35.253: ICMP echo request, id 32, seq 135, length 64
11:04:53.857026 IP (tos 0x0, ttl 63, id 12795, offset 0, flags [DF], proto ICMP (1), length 84)10.2.254.3 > 192.168.35.253: ICMP echo request, id 32, seq 136, length 64

抓包192.168.35.253 的確收到來自10.2.254.3的請求,也嘗試直接回復10.2.254.3它的請求,但失敗。

[root@host-253 ~]# tcpdump -i ens192 icmp -vvnn
tcpdump: listening on ens192, link-type EN10MB (Ethernet), capture size 262144 bytes
11:05:53.852475 IP (tos 0x0, ttl 62, id 43080, offset 0, flags [DF], proto ICMP (1), length 84)10.2.254.3 > 192.168.35.253: ICMP echo request, id 32, seq 196, length 64
11:05:53.852530 IP (tos 0x0, ttl 64, id 13542, offset 0, flags [none], proto ICMP (1), length 84)192.168.35.253 > 10.2.254.3: ICMP echo reply, id 32, seq 196, length 64
11:05:54.852667 IP (tos 0x0, ttl 62, id 43886, offset 0, flags [DF], proto ICMP (1), length 84)10.2.254.3 > 192.168.35.253: ICMP echo request, id 32, seq 197, length 64
11:05:54.852719 IP (tos 0x0, ttl 64, id 13570, offset 0, flags [none], proto ICMP (1), length 84)192.168.35.253 > 10.2.254.3: ICMP echo reply, id 32, seq 197, length 64
11:05:55.312720 IP (tos 0x0, ttl 64, id 39942, offset 0, flags [DF], proto ICMP (1), length 60)192.168.35.253 > 125.64.129.223: ICMP echo request, id 3328, seq 5836, length 40
11:05:55.319196 IP (tos 0x0, ttl 56, id 39942, offset 0, flags [DF], proto ICMP (1), length 60)125.64.129.223 > 192.168.35.253: ICMP echo reply, id 3328, seq 5836, length 40
11:05:55.852795 IP (tos 0x0, ttl 62, id 44326, offset 0, flags [DF], proto ICMP (1), length 84)10.2.254.3 > 192.168.35.253: ICMP echo request, id 32, seq 198, length 64
11:05:55.852817 IP (tos 0x0, ttl 64, id 13723, offset 0, flags [none], proto ICMP (1), length 84)192.168.35.253 > 10.2.254.3: ICMP echo reply, id 32, seq 198, length 64
11:05:56.853000 IP (tos 0x0, ttl 62, id 45059, offset 0, flags [DF], proto ICMP (1), length 84)10.2.254.3 > 192.168.35.253: ICMP echo request, id 32, seq 199, length 64
11:05:56.853028 IP (tos 0x0, ttl 64, id 13921, offset 0, flags [none], proto ICMP (1), length 84)192.168.35.253 > 10.2.254.3: ICMP echo reply, id 32, seq 199, length 64
11:05:57.853198 IP (tos 0x0, ttl 62, id 45512, offset 0, flags [DF], proto ICMP (1), length 84)10.2.254.3 > 192.168.35.253: ICMP echo request, id 32, seq 200, length 64
  1. 通過抓包可以簡單分

    • 容器10.2.254.3192.168.35.253 發起了ICMP請求。
    • 主機192.168.33.253通過路由指定網卡進行外部通信
    • 目標192.168.35.253也收到來自IP10.2.254.3的請求
    • 目標192.168.35.25310.2.254.3進行回應,
    • 目標192.168.35.253找不到10.2.254.3, 因為他們本身不具備直接通信的條件。
    • 容器10.2.254.3 不能收到 192.168.35.253 響應。 導致網絡不能互通
  2. 通過SNAT修改源IP地址,讓192.168.35.253可以和主機通信

    • 10.2.254.3修改源IP,所以docker取網段范圍-s 10.2.254.0/24滿足更多容器
    • 其中br-a397efbb3fb4是一個網橋,是內部通信的基礎。所以不需要修改其源IP。假設修改了,那么會造成主機內網不能互通的情況
    • 綜合規則: iptables -t nat -A POSTROUTING ! -o br-a397efbb3fb4 -s 10.2.254.0/24 -j MASQUERADE

主機抓包后可以看到icmp的報文原始IP地址變更主機網卡IP地址

[root@mking ~]# tcpdump -i enp3s0 -p icmp  -vvnn
tcpdump: listening on enp3s0, link-type EN10MB (Ethernet), capture size 262144 bytes
11:23:42.355262 IP (tos 0x0, ttl 63, id 62150, offset 0, flags [DF], proto ICMP (1), length 84)192.168.33.233 > 192.168.35.253: ICMP echo request, id 33, seq 9, length 64
11:23:42.355422 IP (tos 0x0, ttl 63, id 16529, offset 0, flags [none], proto ICMP (1), length 84)192.168.35.253 > 192.168.33.233: ICMP echo reply, id 33, seq 9, length 64
11:23:43.355476 IP (tos 0x0, ttl 63, id 62489, offset 0, flags [DF], proto ICMP (1), length 84)192.168.33.233 > 192.168.35.253: ICMP echo request, id 33, seq 10, length 64
11:23:43.355662 IP (tos 0x0, ttl 63, id 16562, offset 0, flags [none], proto ICMP (1), length 84)192.168.35.253 > 192.168.33.233: ICMP echo reply, id 33, seq 10, length 64

目標主機抓包信息的請求源地址也是滿足可以通信的主機IP地址。并且能正常響應回復

[root@runner253 ~]# tcpdump -i ens192 icmp -vvnn
tcpdump: listening on ens192, link-type EN10MB (Ethernet), capture size 262144 bytes
11:26:11.368853 IP (tos 0x0, ttl 62, id 12798, offset 0, flags [DF], proto ICMP (1), length 84)192.168.33.233 > 192.168.35.253: ICMP echo request, id 33, seq 158, length 64
11:26:11.368893 IP (tos 0x0, ttl 64, id 26732, offset 0, flags [none], proto ICMP (1), length 84)192.168.35.253 > 192.168.33.233: ICMP echo reply, id 33, seq 158, length 64
11:26:12.369095 IP (tos 0x0, ttl 62, id 13330, offset 0, flags [DF], proto ICMP (1), length 84)192.168.33.233 > 192.168.35.253: ICMP echo request, id 33, seq 159, length 64
11:26:12.369136 IP (tos 0x0, ttl 64, id 27102, offset 0, flags [none], proto ICMP (1), length 84)192.168.35.253 > 192.168.33.233: ICMP echo reply, id 33, seq 159, length 64

目前docker主機可以收到來自目標主機的回復響應,主機又如何把回復響應轉交給docker容器

根據網上查詢,自己沒有驗證。 當創建SNAT后,iptables也會自動在nat創建DNAT規則,用于回源到最原始的IP

簡單總結nat規則目的

  1. 外部對內請求,會在PREROUTING鏈DNAT轉發請求到容器端口。
  2. 外部對內過程沒有修改源IP地址。所以容器是可以拿到客戶端IP地址。
  3. 內部對外訪問,會在POSTROUTING鏈SNAT轉發請求到外部網絡。
  4. 內部對外修改了源IP地址。所以目的端收到的IP服務器出網的網卡IP。
3. 查看filter 過濾表信息

filter主要對流量進行攔截, 查看主機規則。 很多都是docker創建, 還是以br-a397efbb3fb4為例。

[root@mking /]# iptables -t filter -nvL 
Chain INPUT (policy ACCEPT 9502 packets, 752K bytes)pkts bytes target     prot opt in     out     source               destination         Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)pkts bytes target     prot opt in     out     source               destination         34  5744 DOCKER-USER  all  --  *      *       0.0.0.0/0            0.0.0.0/0           34  5744 DOCKER-ISOLATION-STAGE-1  all  --  *      *       0.0.0.0/0            0.0.0.0/0                16  2984 ACCEPT     all  --  *      br-a397efbb3fb4  0.0.0.0/0            0.0.0.0/0            ctstate RELATED,ESTABLISHED2   120 DOCKER     all  --  *      br-a397efbb3fb4  0.0.0.0/0            0.0.0.0/0           16  2640 ACCEPT     all  --  br-a397efbb3fb4 !br-a397efbb3fb4  0.0.0.0/0            0.0.0.0/0           0     0 ACCEPT     all  --  br-a397efbb3fb4 br-a397efbb3fb4  0.0.0.0/0            0.0.0.0/0              Chain DOCKER (2 references)pkts bytes target     prot opt in     out     source               destination         2   120 ACCEPT     tcp  --  !br-a397efbb3fb4 br-a397efbb3fb4  0.0.0.0/0            10.2.254.2           tcp dpt:80Chain DOCKER-ISOLATION-STAGE-1 (1 references)pkts bytes target     prot opt in     out     source               destination         0     0 DOCKER-ISOLATION-STAGE-2  all  --  docker0 !docker0  0.0.0.0/0            0.0.0.0/0           16  2640 DOCKER-ISOLATION-STAGE-2  all  --  br-a397efbb3fb4 !br-a397efbb3fb4  0.0.0.0/0            0.0.0.0/0           34  5744 RETURN     all  --  *      *       0.0.0.0/0            0.0.0.0/0           Chain DOCKER-ISOLATION-STAGE-2 (2 references)pkts bytes target     prot opt in     out     source               destination         0     0 DROP       all  --  *      docker0  0.0.0.0/0            0.0.0.0/0           0     0 DROP       all  --  *      br-a397efbb3fb4  0.0.0.0/0            0.0.0.0/0           16  2640 RETURN     all  --  *      *       0.0.0.0/0            0.0.0.0/0           Chain DOCKER-USER (1 references)pkts bytes target     prot opt in     out     source               destination         34  5744 RETURN     all  --  *      *       0.0.0.0/0            0.0.0.0/0           

通過鏈路分析關于網橋 br-a397efbb3fb4 處理攔截。

  1. PREROUTING 鏈路: 進入主機后規則匹配成功,修改目標IP地址。

  2. INPUT 鏈: 對所有目的IP存在本機的流量進行處理

    • 因為容器對網絡環境進行了隔離,所以當前主機 ( ip addressifconfig ) 沒有容器IP地址。
  3. FORWARD 鏈:對所有目的IP不在本機的流量進行處理

    • 容器IP不在主機網絡的,會經過當前鏈進行攔截處理。
    • 網橋 br-a397efbb3fb4 處理動作有3個
      • 第三行規則
        • 命令: iptables -I FOREWARD -o br-a397efbb3fb4 -m stat RELATED,ESTABLISHED -j ACCEPT
        • 理解: 經過br-a397efbb3fb4出去的網絡,允許回應報文通過。
      • 第四行規則
        • 命令: iptables -I FOREWARD -o br-a397efbb3fb4 -j DOCKER
        • 理解: 經過br-a397efbb3fb4出去的網絡,由Docker進一步篩選控制。
      • 第五行規則: iptables -I FOREWARD -i br-a397efbb3fb4 ! -o br-a397efbb3fb4 -j ACCEPT
        • 規則翻譯: 允許經過br-a397efbb3fb4進的網絡, 從其他網卡出去。
        • 本質是:允許容器內網與外網通信。
      • 第六行規則: iptables -I FOREWARD -i br-a397efbb3fb4 -o br-a397efbb3fb4 -j ACCEPT
        • 規則翻譯: 允許經過br-a397efbb3fb4進的網絡并從該網卡出去
  4. OUTPUT 鏈: 通過網卡出去的網絡進行攔截。

    • 此處不涉及
  5. Docker 鏈: 對于br-a397efbb3fb4有一項處理

    • 第一行規則:
      • 命令: iptables -I DOCKER -p tcp --dport 80 -d 10.2.254.2 ! -i br-a397efbb3fb4 -o br-a397efbb3fb4 -j ACCEPT
      • 理解: 允許非br-a397efbb3fb4網絡到br-a397efbb3fb4網絡對目標IP 10.2.254.2 進行80端口訪問
  6. DOCKER-ISOLATION-STAGE-1規則上最終交給DOCKER-ISOLATION-STAGE-2: 有一個丟包機制

    • 第二行規則:
      • 命令:iptables -I DOCKER-ISOLATION-STAGE-2 -o br-a397efbb3fb4 -j DROP
      • 理解: 拒絕從網橋br-a397efbb3fb4出去的流量

場景1: 外部請求容器服務需要經歷哪些關卡

  1. 192.168.35.253訪問 容器HTTP 服務,請求經過eth0進入主機。
  2. PREROUTING鏈路修改目的IP地址,通過目的端口轉向到10.2.254.2
  3. 路由選擇: 匹配到10.2.254.2需要經過過br-a397efbb3fb4出去
    • 報文入口網卡是eth0
    • 分組出口網卡是br-a397efbb3fb4
  4. 目標IP不在主機網絡,經過FORWARD鏈處理,這里主要有2個處理
    • 嘗試建立鏈接: 允許 TCP 3次握手,所以滿足RELATED,ESTABLISHED進行放行。 行三規則
    • 已經建立鏈接: 允許通過來自外部對10.2.254.2的80端口訪問。 行四規則

場景2: 容器對外請求/響應需要經歷哪些關卡

  1. 容器 回復 192.168.35.253的響應報文,經過br-a397efbb3fb4到達主機
  2. POSTROUTING鏈路修改了源IP地址。
  3. 路由選擇: 匹配到192.168.35.253需要經過eth0出去
    • 流量入口網卡是br-a397efbb3fb4
    • 流量出口網卡是eth0
  4. 目標IP不在主機網絡,經過FORWARD鏈處理。
    • 允許從其他網卡出去。行五規則

場景3: 不同網絡的容器是如何隔離的

  1. 10.1.254.2 嘗試建立 10.2.254.2請求
  2. 10.1.254.2docker0進入主機,
  3. 路由選擇: 匹配到10.2.254.2需要經過過br-a397efbb3fb4出去
    • 流量入口網卡是docker0
    • 流量出口網卡是br-a397efbb3fb4
  4. 目標IP不在主機網絡,經過FORWARD, DOCKER-ISOLATION-STAGE-1, DOCKER-ISOLATION-STAGE-2 鏈處理。
    • 拒絕從br-a397efbb3fb4出去的網卡 第二行規則
    • 拒絕之后,到達RETURN規則,不再進行匹配。
    • 驗證取消容器網絡之間的隔離: iptables -F DOCKER-ISOLATION-STAGE-2

四、 模擬docker網絡通信

1. 通過shell命令實現網絡隔離, 并實現內部通信和外網通信

1. 準備工作
  1. 會用到centos網絡設備管理命令
ip help 	# 支持子命令。 所有的命令: 增(add)刪(delete/rm)改(set/put)查(show)
ip link 	# 查看網絡連接, 網卡,支持虛擬技術的。硬件虛擬,vmware就是硬件虛擬機。
ip link type bridge 虛擬網卡  /  veth: 一對虛擬網卡(常見用于docker命名空間隔離)
ip address 	# 查看IP地址
ip netns 	# 網絡命名空間,簡單理解虛擬了一個環境,與外部獨立。
ip route 	# 查看路由。 看IP怎么走的
  1. 網橋: 網絡連接的一種形式,主要連接多個局域網(lan), 進行流量接收,存儲和轉發。

  2. 虛擬以太網設備對(veth-pair): 網絡連接的另一種形式, 會產生一對網卡。常用于網絡隔離通信。

2. 設備對通信
  1. 創建一個網絡隔離的namespace: demo
[root@mking /]# ip netns add demo
# 查看網絡NS信息
[root@mking /]# ip netns ls
demo
  1. 創建一組網卡設備對: veth pair。

設備對網卡可以通過關鍵字@快速辨別。 51,51 表示網卡的編號ID , peer1peer2表示網卡別名

[root@mking /]# ip link add name peer0 type veth peer name peer1
[root@mking /]# ip link show
51: peer1@peer0: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000link/ether 6a:a0:1b:3a:58:e5 brd ff:ff:ff:ff:ff:ff
52: peer0@peer1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000link/ether 6e:90:2d:40:59:1e brd ff:ff:ff:ff:ff:ff
  1. 把peer0的網卡對設置到另一個命名空間 demo
[root@mking /]# ip link set peer0 netns demo
  1. 查不到編號52:peer0@peer1 的網卡,同時編號51網卡顯示: peer1@if52, 說明編號52網卡存在,但已被設置到其他的ns, 當前的ns查看不了
[root@mking /]# ip link show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: enp3s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000link/ether d4:5d:64:7f:92:66 brd ff:ff:ff:ff:ff:ff
51: peer1@if52: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000link/ether 6a:a0:1b:3a:58:e5 brd ff:ff:ff:ff:ff:ff link-netnsid 
  1. 分別為兩張網卡設置IP,實現通信。 veth pari創建的設備對,默認鏈路是通的。
# 操作宿主機默認的NS。 操作中需要滿足IP在一個網段內。
[root@mking /]# ip address add 10.100.0.15/16 dev peer1   
[root@mking /]# ip link set dev peer1 up# 操作隔離的NS: 設置IP地址并啟動
[root@mking /]# ip netns exec demo ip address add 10.100.15.34/16 dev peer0
[root@mking /]# ip netns exec demo ip link set dev peer0 up
  1. 相互ping測
[root@mking /]# ip netns exec demo ping 10.100.0.15
PING 10.100.0.15 (10.100.0.15) 56(84) bytes of data.
64 bytes from 10.100.0.15: icmp_seq=1 ttl=64 time=0.081 ms
64 bytes from 10.100.0.15: icmp_seq=2 ttl=64 time=0.047 ms
64 bytes from 10.100.0.15: icmp_seq=3 ttl=64 time=0.046 ms# 從宿主機進行探測ping通10.100.15.34
[root@mking /]# ping 10.100.15.34
PING 10.100.15.34 (10.100.15.34) 56(84) bytes of data.
64 bytes from 10.100.15.34: icmp_seq=1 ttl=64 time=0.066 ms
64 bytes from 10.100.15.34: icmp_seq=2 ttl=64 time=0.046 ms
64 bytes from 10.100.15.34: icmp_seq=3 ttl=64 time=0.046 ms

宿主機創建隔離網絡空間demo實現通信, 主要步驟如下

  1. 創建一個ns獨立的網絡空間
  2. 創建一組虛擬設備對 veth pair 產生兩張網絡接口滿足通信鏈路條件
  3. 其中一張網卡設置在ns
  4. 兩張網卡IP在同一個網段
  5. 啟動兩張網卡
3.多個隔離網絡之間的通信

我們實現了一個ns與主機的通信,現在模擬docker多個容器之間的通信: 即可實現多個ns內部相互通信。

  1. IP段規劃
設備命名空間IP廣播地址備注
br0default10.105.0.110.105.255.255擬gateway
vir10default10.105.1.11010.105.255.255veth-peer中的一個網卡
vir11ns110.105.3.5010.105.255.255veth-peer中的一個網卡
vir20default10.105.2.11010.105.255.255veth-peer中的一個網卡
vir21ns210.105.8.11010.105.255.255veth-peer中的一個網卡
  • IP二進制換算: 128 64 32 16 8 4 2 1
    • 10.105.0.1 == 00001010.01101001.00000000.00000001
  • 子網換算
    • 10.105.0.1/24 == 00001010.01101001.00000000.xxxxxxxx == 10.105.0.0-10.105.0.255
    • 10.105.0.1/20 == 00001010.01101001.0000xxxx.xxxxxxxx == 10.105.0.0-10.105.31.255
    • 10.105.0.1/16 == 00001010.01101001.xxxxxxxx.xxxxxxxx == 10.105.0.0-10.105.255.255
  • 需求
    • 創建橋接網卡模擬gateway的功能,實現arp
    • ns1網卡滿足通過gateway找到ns2的網卡實現通信
  • 要求
    • 隔離在ns1的網卡需要訪問到gateway
    • 隔離在ns2的網卡也需要訪問到gateway
  • 已知
    • ns1的vir11可以與vir10進行通信
    • vir10可以與gateway直接通信
    • ns2的vir21可以與vir20進行通信
    • vir11可以與gateway直接通信
  • 可得
    • vir11可以通過vir10把數據請求到gateway進行通信
    • vir21可以通過vir20把數據請求到gateway進行通信
  1. 創建兩個ns, 模擬兩個docker網絡環境
[root@mking /]# ip netns add ns1
[root@mking /]# ip netns add ns2
  1. 同上兩組網卡設備對vir10(主機)/vir11(ns1)vir20(主機)/vir21(ns2)

    同一主機內的vir10vir20是相互獨立的, 需要創建一張網卡作為其網關,將流量轉發到網關, 用于arp IP尋址

    此處設置默認網關地址10.105.0.1/16, 廣播地址得出 10.105.255.255

# 安裝相關命令
[root@mking /]# yum install bridge-utils -y# 創建一張橋接網卡
[root@mking /]# brctl addbr gateway1# 為gateway1設置網卡IP并啟動
[root@mking /]# ip address add 10.105.0.1/16 broadcast 10.105.255.255 dev gateway1
[root@mking /]# ip link set dev gateway1 up
  1. 同上兩組網卡設備對veth pair分別用于ns1ns2。 并對兩組ns設置IP,要求在同一個網段內,不需要設置vir10vir20網卡
# 創建vir1x和vir2x的veth peer網卡
[root@mking /]# ip link add name vir10 type veth peer name vir11
[root@mking /]# ip link add name vir20 type veth peer name vir21# 把vir11和vir21網卡分別放在ns1和ns2中
[root@mking /]# ip link set vir11 netns ns1
[root@mking /]# ip link set vir21 netns ns2# 分別為vri11和vir21設置IP,并啟動環路lo網卡
[root@mking /]# ip netns exec ns1 ip address add 10.105.3.50/16 broadcast 10.105.255.255 dev vir11
[root@mking /]# ip netns exec ns1 ip link set dev vir11 up
[root@mking /]# ip netns exec ns1 ip link set dev lo up[root@mking /]# ip netns exec ns2 ip address add 10.105.8.110/16 broadcast 10.105.255.255 dev vir21
[root@mking /]# ip netns exec ns2 ip link set dev vir21 up
[root@mking /]# ip netns exec ns2 ip link set dev lo up
  1. 設置同主機內的 vir10vir20 , 我們將網卡指向到gateway1
# 處理vir10和vir20, 把vir10和vir20 指向到gateway1
[root@mking /]# brctl addif gateway1 vir10
[root@mking /]# brctl addif gateway1 vir20# 啟動同一主機下的網卡
[root@mking /]# ip link set dev vir10
[root@mking /]# ip link set dev vir20# 查看網橋信息
[root@mking /]# brctl show
bridge name     bridge id               STP enabled     interfaces
gateway1                8000.7e6c5709cc5e       no              vir10vir20
  1. 至此ns1ns2可以相互ping通, 理論上不同的網絡隔離空間下的設備對網卡指向到gateway1,都可以訪問
# 從ns1 訪問 ns2
[root@mking /]# ip netns exec ns1 ping 10.105.8.110
PING 10.105.8.110 (10.105.8.110) 56(84) bytes of data.
64 bytes from 10.105.8.110: icmp_seq=1 ttl=64 time=0.043 ms
64 bytes from 10.105.8.110: icmp_seq=2 ttl=64 time=0.042 ms
64 bytes from 10.105.8.110: icmp_seq=3 ttl=64 time=0.122 ms# 從ns2 訪問 ns1
[root@mking /]# ip netns exec ns2 ping 10.105.3.50
PING 10.105.3.50 (10.105.3.50) 56(84) bytes of data.
64 bytes from 10.105.3.50: icmp_seq=1 ttl=64 time=0.068 ms
64 bytes from 10.105.3.50: icmp_seq=2 ttl=64 time=0.038 ms
64 bytes from 10.105.3.50: icmp_seq=3 ttl=64 time=0.105 ms

多個網絡空間ns實現通信, 主要步驟如下

  1. 創建多個ns 和多組 veth pair 虛擬設備對的網卡
  2. 設備對的一端網卡分別設置在多個ns1
  3. 宿主機創建一張bridge網卡
  4. 設備對的另一端網卡添加到bridge網卡。
  5. ns里面的網卡和bridge網卡的IP設置在一個網段內
4.多個NS對外通信

模擬了多個docker的網絡隔離之間通信,現在模擬docker對外的通信。

  1. 創建兩個ns, 模擬兩個docker網絡環境。 此處繼續使用之間建好的ns1ns2
# 從ns1 訪問外網,尋址失敗
[root@mking /]# ip netns exec ns2 ping 61.139.2.69
connect: Network is unreachable# 從ns2 訪問外網, 尋址失敗
[root@mking /]# ip netns exec ns1 ping 61.139.2.69
connect: Network is unreachable
  1. 創建默認路由,讓請求外網的分組請求到gateway1
# 添加默認路由
[root@mking /]# ip netns exec ns1 route add default gw  10.105.0.1
[root@mking /]# ip netns exec ns2 route add default gw  10.105.0.1# 訪問外網,但無響應。 
[root@mking /]# ip netns exec ns1 ping 61.139.2.69
PING 61.139.2.69 (61.139.2.69) 56(84) bytes of data.
2 packets transmitted, 0 received, 100% packet loss, time 1000ms[root@mking /]# ip netns exec ns2 ping 61.139.2.69
PING 61.139.2.69 (61.139.2.69) 56(84) bytes of data.
2 packets transmitted, 0 received, 100% packet loss, time 1001ms# 查看gateway1有分組經過。RX packets 接收流量, TX packets 13 傳輸流量
[root@mking /]# ifconfig gateway1
gateway1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500inet 10.105.0.1  netmask 255.255.0.0  broadcast 10.105.255.255inet6 fe80::dcb0:d8ff:fe6b:856f  prefixlen 64  scopeid 0x20<link>ether 22:4e:62:c8:f3:80  txqueuelen 1000  (Ethernet)RX packets 33  bytes 2124 (2.0 KiB)RX errors 0  dropped 0  overruns 0  frame 0TX packets 13  bytes 978 (978.0 B)TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

此時ns1ns2 可以把流量發送到網卡gateway1

  1. 分析ns1流量走勢

iptables處理分組時間軸

timelinetitle iptables時間線PREROUTING : natINPUT : filter: natFORWARD : natOUTPUT : filter: natPOSTROUTING: nat

分組從 ns1 的網卡 vir11 經過vir10 到達網卡gateway, 會經過如下判斷

  1. PREROUTING
    • 事件: 接收分組時候
    • 備注: 對分組進行修改, 此處不修改
  2. FORWARD:
    • 事件: 接收分組后
    • 執行動作: 放行
    • 觸發條件: 目標IP不在主機. 所以進行forward轉發
  3. POSTROUTING:
    • 事件: 通過轉發后觸發
    • 執行動作: 修改報文source ip, 本質是將分組交給source ipaddress發出去
# 放行FORWARD, 流入gateway1網卡分組通過。 主要是通過來自ns1,ns2請求流量
[root@mking /]# iptables -t filter -I FORWARD -i gateway1 -j ACCEPT# 放行FORWARD, 出口gateway1網卡分組通過。 主要是通過返回ns1,ns2響應流量
[root@mking /]# iptables -t filter -I FORWARD -o gateway1 -j ACCEPT# 地址轉換, 把gateway1網段的分組, 修改Source IP或指定網卡IP。實現外部請求
[root@mking /]# iptables -t nat -A POSTROUTING -s 10.105.0.0/16 -o ens33 -j MASQUERADE
  1. 自建的ns1 可以訪問外部網絡。
[root@mking /]# ip netns exec ns2 ping 61.139.2.69
PING 61.139.2.69 (61.139.2.69) 56(84) bytes of data.
64 bytes from 61.139.2.69: icmp_seq=1 ttl=57 time=3.09 ms
64 bytes from 61.139.2.69: icmp_seq=2 ttl=57 time=3.03 ms
64 bytes from 61.139.2.69: icmp_seq=3 ttl=57 time=3.44 ms
--- 61.139.2.69 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
5.外部訪問NS網絡

實現ns可以訪問外網,模擬外網如何訪問ns內部服務(訪問docker服務)

  1. 前置條件: 創建一個ns, 并要求ns網絡可以對外訪問。 此處繼續使用之間建好的ns1ns2

  2. 終端1在ns1開啟一個端口

[root@mking /]# ip netns exec ns1 nc -l -p 8000
  1. 終端2檢查ns1端口情況
[root@mking /]# ip netns exec ns1 netstat -anpt
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 0.0.0.0:8000            0.0.0.0:*               LISTEN      26130/nc            
tcp6       0      0 :::8000                 :::*                    LISTEN      26130/nc            
  1. 通過iptables的DNAT轉發,對外提供訪問

enp3s0 網卡收到外部請求端口: 8000。 可通過iptables如下鏈路

  1. PREROUTING
    • 事件: 接收分組時候。
    • 執行動作: 修改目的IP和端口。
    • 備注: 因為當前主機是沒有提供端口服務,而主機與ns1可以直接通信。 所以在入口時通過修改dst addressdst port轉發請求到目的網絡
  2. FORWARD:
    • 事件: 接收分組后
    • 執行動作: 放行
    • 觸發條件: 目標IP已改,目的不在主機. 所以進行forward轉發
# 放行FORWARD, 流入gateway1網卡分組通過。 主要是通過來自ns1,ns2請求流量
[root@mking /]# iptables -t filter -I FORWARD -i gateway1 -j ACCEPT# 放行FORWARD, 出口gateway1網卡分組通過。 主要是通過返回ns1,ns2響應流量
[root@mking /]# iptables -t filter -I FORWARD -o gateway1 -j ACCEPT# 地址轉換, 把gateway1網段的分組, 修改Source IP或指定網卡IP。實現外部請求
[root@mking /]# iptables -t nat -I PREROUTING -p tcp --dport 8000 -j DNAT --to-destination 10.105.3.50
  1. 驗證自建的ns1 對外提供端口訪問。
# 從其他設備訪問8000
[root@others /]# telnet 192.168.33.253 8000# 檢查ns1的網絡連接情況
[root@mking /]# ip netns exec ns1 netstat -anpt 
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 10.105.3.50:8000        192.168.35.253:48246    ESTABLISHED 26332/nc  

可以看到8000端口有來自192.168.35.253的訪問,基礎目標完成

2. 給docker做適配: 為容器接入自定義ns集群

目標: 把自建隔離的網絡放入到docker中

  1. 創建好一組虛擬網卡設備對, 規劃和gateway1相同的IP段.
# 創建ns3
[root@mking /]# ip netns add ns3# 創建新的虛擬網卡設備對
[root@mking /]# ip link add name vir30 type veth peer name vir31
  1. 啟動一個nginx docker,不暴露端口。
# 啟動一個nginx服務,并使用自定義網絡, 但不暴露端口。
[root@mking /]# docker run --name nginx --network demo -itd --rm nginx 

指定網絡是docker啟動會創建新的隔離網絡, 不暴露端口是咱不使用docker-proxy代理。

  1. ns的信息主要存放在路徑: /var/run/netns/, 通過創建 /proc/${pid}/ns/net 軟連接便可以管理進程的ns
# 查詢docker容器ID
[root@mking /]# docker inspect nginx --format="{{ .State.Pid }}"
6331# 創建PID下的ns軟連接到/var/run/netns/ngx, 便可以ngx去管理docker啟動nginx容器網絡
[root@mking /]# ln -s /proc/6331/ns/net /var/run/netns/ngx# 可以查看目前ngx有一張網卡
[root@mking net]# ip netns exec ngx ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500inet 10.2.254.2  netmask 255.255.255.0  broadcast 10.2.254.255ether 02:42:0a:02:fe:02  txqueuelen 0  (Ethernet)RX packets 8  bytes 656 (656.0 B)RX errors 0  dropped 0  overruns 0  frame 0TX packets 0  bytes 0 (0.0 B)TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
  1. vir31網卡設置到ngx內。把vir30網卡添加到gateway1的網橋
# 把vir31網卡設置到ngx內
[root@mking net]# ip link set vir31 netns ngx # 查看目前ngx新加入的eth1
[root@mking net]# ip netns exec ngx ifconfig vir31# 設置IP段
[root@mking /]# ip netns exec ngx ip address add 10.105.1.250/16 broadcast 10.105.255.255 dev vir31# 查看網卡信息
[root@mking net]# ip netns exec ngx ifconfig vir31
vir31: flags=4098<BROADCAST,MULTICAST>  mtu 1500inet 10.105.1.250  netmask 255.255.0.0  broadcast 10.105.255.255ether 76:f4:11:39:f9:48  txqueuelen 1000  (Ethernet)RX packets 0  bytes 0 (0.0 B)RX errors 0  dropped 0  overruns 0  frame 0TX packets 0  bytes 0 (0.0 B)TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0# 啟動容器網卡
[root@mking net]# ip netns exec ngx ip link set vir31 up
# 啟動主機網卡
[root@mking net]# ip link set vir30 up# 把外部的vir30網卡添加到gateway1.
[root@mking net]# brctl addif gateway1 vir30
  1. 內部ns訪問容器網絡
# 在ns1訪問ngx的IP。
[root@mking net]# ip netns exec ns1 curl http://10.105.1.250 #查看nginx的日志
[root@mking /]# docker logs -f nginx
10.105.3.50 - - [12/Jul/2024:07:24:36 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/8.5.0" "-"
  1. 通過iptables方式暴露對外訪問
# 先放行FORWARD(以上已做), 
[root@mking net]# iptables -I FORWARD -i gateway1 -j ACCEPT
[root@mking net]# iptables -I FORWARD -o gateway1 -j ACCEPT#添加 nat 地址映射。更改目標IP
[root@mking net]# iptables -t nat -I PREROUTING -p tcp --dport 8002 -j DNAT --to-destination 10.105.1.250:80#查看nginx的日志
[root@mking /]# docker logs -f nginx
192.168.35.253 - - [12/Jul/2024:07:55:21 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.29.0" "-"

小結

  1. docker利用橋接和虛擬設備對實現網絡隔離以及通信。
  2. docker網絡通過iptables對分組進行攔截過濾和轉發。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/45910.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/45910.shtml
英文地址,請注明出處:http://en.pswp.cn/web/45910.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

spring-boot2.x整合Kafka步驟

1.pom依賴添加 <properties><java.version>1.8</java.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</ma…

自學鴻蒙HarmonyOS的ArkTS語言<十二>wrapBuilder:組件工廠類封裝

// FactoryComponent.ets Builder function Radio1() {Column() {Text(單選組件&#xff1a;)Row() {Radio({ value: 1, group: radioGroup })Text(選項1)}Row() {Radio({ value: 2, group: radioGroup })Text(選項2)}}.margin(10) }Builder function Checkbox1() {Column() {T…

DP(5) | 完全背包 | Java | 卡碼52, LeetCode 518, 377, 70 做題總結

完全背包 感覺越寫越糊涂了&#xff0c;初始化怎么做的&#xff1f;遞推公式怎么來的&#xff1f; 卡碼52. 攜帶研究材料 https://kamacoder.com/problempage.php?pid1052 import java.util.*;public class Main {public static void main(String[] args) {Scanner sc new …

Java面試八股之Redis集群是怎么選擇數據庫的

在Redis集群中&#xff0c;數據被水平分割&#xff08;sharding&#xff09;到各個節點上&#xff0c;這意味著所有的鍵空間被分成16384個哈希槽&#xff08;hash slots&#xff09;&#xff0c;這些槽均勻地分布在集群中的各個節點上。Redis集群并不支持傳統的數據庫切換&…

xiuno兔兔超級SEO插件(精簡版)

xiuno論壇是一個一款輕論壇產品的論壇&#xff0c;但是對于這個論壇基本上都是用插件實現&#xff0c;一個論壇怎么能離開網站seo&#xff0c;本篇分享一個超級seo插件&#xff0c;自動sitemap、主動提交、自動Ping提交。 插件下載:tt_seo.zip

實驗11 數據庫日志及數據庫恢復

一、 實驗目的 了解Mysql數據庫系統中數據恢復機制和主要方法。 二、 實驗環境 操作系統&#xff1a;Microsoft Windows 7旗艦版&#xff08;32&64位&#xff09;/Linux。 硬件&#xff1a;容量足以滿足MySQL 5.7&#xff08;8.0&#xff09;安裝及后續實驗的使用。 軟件…

Python | Leetcode Python題解之第232題用棧實現隊列

題目&#xff1a; 題解&#xff1a; class MyQueue:def __init__(self):self.A, self.B [], []def push(self, x: int) -> None:self.A.append(x)def pop(self) -> int:peek self.peek()self.B.pop()return peekdef peek(self) -> int:if self.B: return self.B[-1…

什么叫圖像的中值濾波,并附利用OpenCV和MATLB實現均值濾波的代碼

圖像的中值濾波&#xff08;Median Filtering&#xff09;是一種非線性數字濾波技術&#xff0c;常用于圖像處理以減少噪聲&#xff0c;同時保留圖像邊緣細節。其基本思想是用圖像中某個窗口內像素的中值替代該窗口中心像素的值。具體步驟如下&#xff1a; 選擇窗口&#xff1a…

C++樹(二)【直徑,中心】

目錄&#xff1a; 樹的直徑&#xff1a; 樹的直徑的性質&#xff1a; 性質1&#xff1a;直徑的端點一定是葉子節點 性質2&#xff1a;任意點的最長鏈端點一定是直徑端點。 性質3&#xff1a;如果一棵樹有多條直徑,那么它們必然相交&#xff0c;且有極長連…

STM32中PC13引腳可以當做普通引腳使用嗎?如何配置STM32的TAMPER?

1.STM32中PC13引腳可以當做普通引腳使用嗎&#xff1f; 在STM32單片機中&#xff0c;PC13引腳可以作為普通IO使用&#xff0c;但需要進行一定的配置。PC13通常與RTC侵入檢測功能&#xff08;TAMPER&#xff09;復用&#xff0c;因此需要關閉TAMPER功能才能將其作為普通IO使用。…

服務端渲染框架:Nuxt.js 與 Next.js 的區別和對比

&#x1f49d;&#x1f49d;&#x1f49d;歡迎蒞臨我的博客&#xff0c;很高興能夠在這里和您見面&#xff01;希望您在這里可以感受到一份輕松愉快的氛圍&#xff0c;不僅可以獲得有趣的內容和知識&#xff0c;也可以暢所欲言、分享您的想法和見解。 推薦:「stormsha的主頁」…

2024國家護網面試小結

24年國護馬上就要開始&#xff0c;基本上大部分藍隊紅隊都已經準備入場了 今年護網第一年變成常態化護網&#xff0c;由十五天突然變成了兩個月常態化&#xff0c;導致今年護網有很多項目整的七零八落 博主今年參加了三家廠商藍隊護網面試&#xff0c;在這邊分享一下護網面試…

掌握這些技巧,讓你成為畫冊制作高手

在數字化的時代背景下&#xff0c;電子畫冊以其便捷的傳播方式、豐富的視覺表現形式&#xff0c;贏得了大眾的喜愛。它不僅能夠在個人電腦上展現&#xff0c;還能通過智能手機、平板電腦等多種移動設備隨時隨地被訪問和瀏覽。這種跨平臺的支持&#xff0c;使得無論你身處何地&a…

Html_Css問答集(12)

99、將上例的0%改為30%&#xff0c;會如何變化&#xff1f; none:延遲2秒間無色&#xff0c;3.8秒&#xff08;0%-30%占1.8秒&#xff09;前無色&#xff0c;之后變紅到5秒綠最后藍&#xff0c;動畫結束時恢復初始&#xff08;無色&#xff09;。 forward:延遲2秒間無色&am…

leetcode刷題總結——字符串匹配

KMP&#xff08;字符串匹配算法&#xff09; 主串或目標串&#xff1a;比較長的&#xff0c;我們就是在它里面尋找子串是否存在&#xff1b; 子串或模式串&#xff1a;比較短的。 前綴&#xff1a;字符串A和B&#xff0c;A BS&#xff0c;S非空&#xff0c;則B為A的前綴。 …

婚禮成本與籌備策略:一場夢幻婚禮的理性規劃

婚禮成本與籌備策略&#xff1a;一場夢幻婚禮的理性規劃 摘要 婚禮&#xff0c;作為人生中的重要儀式&#xff0c;承載著新人的愛情與夢想&#xff0c;同時也伴隨著不菲的經濟投入。本文旨在探討婚禮所需的大致成本、影響成本的主要因素以及婚禮籌備過程中的關鍵注意事項&…

【Java--數據結構】二叉樹

歡迎關注個人主頁&#xff1a;逸狼 創造不易&#xff0c;可以點點贊嗎~ 如有錯誤&#xff0c;歡迎指出~ 樹結構 樹是一種非線性的數據結構&#xff0c;它是由n&#xff08;n>0&#xff09;個有限結點組成一個具有層次關系的集合 注意&#xff1a;樹形結構中&#xff0c;子…

Transformer模型在多任務學習中的革新應用

在深度學習領域&#xff0c;多任務學習&#xff08;Multi-task Learning, MTL&#xff09;是一種訓練模型以同時執行多個任務的方法。這種方法可以提高模型的泛化能力&#xff0c;因為它允許模型在不同任務之間共享知識。近年來&#xff0c;Transformer模型因其在自然語言處理&…

【linux高級IO(三)】初識epoll

&#x1f493;博主CSDN主頁:杭電碼農-NEO&#x1f493; ? ?專欄分類:Linux從入門到精通? ? &#x1f69a;代碼倉庫:NEO的學習日記&#x1f69a; ? &#x1f339;關注我&#x1faf5;帶你學更多操作系統知識 ? &#x1f51d;&#x1f51d; Linux高級IO 1. 前言2. 初識e…

STM32 HRTIM生成PWM時遇到無法輸出PWM脈沖波形問題

在使用HRTIM生成PWM時&#xff0c;當把周期寄存器更新的設置放到while循環中時&#xff0c;無法輸出PWM脈沖波形&#xff0c;即使增加計數延時也無法輸出&#xff0c;最終只能放到中斷函數中執行后期寄存器值更新才能夠生成PWM脈沖波形。