当前位置: 首页 > news >正文

尝试理解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对分组进行拦截过滤和转发。

相关文章:

尝试理解docker网络通信逻辑

一、docker是什么 Docker本质是一个进程,宿主机通过namespace隔离机制提供进程需要运行基础环境&#xff0c;并且通过Cgroup限制进程调用资源。Docker的隔离机制包括 network隔离&#xff0c;此次主要探讨网络隔离mount隔离hostname隔离user隔离pid隔离进程通信隔离 二、doc…...

数据仓库哈哈

数据仓库 基本概念数据库&#xff08;database&#xff09;和数据仓库&#xff08;Data Warehouse&#xff09;的异同 整体架构分层架构方法论ER模型&#xff08;建模理论&#xff09;维度模型 何为分层第一层&#xff1a;数据源&#xff08;ODS ER模型&#xff09;设计要点日志…...

K最近邻(K-Nearest Neighbors, KNN)

K最近邻&#xff08;K-Nearest Neighbors, KNN&#xff09;理论知识推导 KNN算法是一个简单且直观的分类和回归方法&#xff0c;其基本思想是&#xff1a;给定一个样本点&#xff0c;找到训练集中与其最近的K个样本点&#xff0c;根据这些样本点的类别&#xff08;分类问题&am…...

深度学习损失计算

文章目录 深度学习损失计算1.如何计算当前epoch的损失&#xff1f;2.为什么要计算样本平均损失&#xff0c;而不是计算批次平均损失&#xff1f; 深度学习损失计算 1.如何计算当前epoch的损失&#xff1f; 深度学习中的损失计算&#xff0c;通常为数据集的平均损失&#xff0…...

论文翻译:通过云计算对联网多智能体系统进行预测控制

通过云计算对联网多智能体系统进行预测控制 文章目录 通过云计算对联网多智能体系统进行预测控制摘要前言通过云计算实现联网的多智能体控制系统网络化多智能体系统的云预测控制器设计云预测控制系统的稳定性和一致性分析例子结论 摘要 本文研究了基于云计算的网络化多智能体预…...

Java核心(五)多线程

线程并行的逻辑 一个线程问题 起手先来看一个线程问题&#xff1a; public class NumberExample {private int cnt 0;public void add() {cnt;}public int get() {return cnt;} }public static void main(String[] args) throws InterruptedException {final int threadSiz…...

IDEA快速生成项目树形结构图

下图用的IDEA工具&#xff0c;但我觉得WebStorm 应该也可以 文章目录 进入项目根目录下&#xff0c;进入cmd输入如下指令&#xff1a; 只有文件夹 tree . > list.txt 包括文件夹和文件 tree /f . > list.txt 还可以为相关包路径加上注释...

【CPO-TCN-BiGRU-Attention回归预测】基于冠豪猪算法CPO优化时间卷积双向门控循环单元融合注意力机制

基于冠豪猪算法CPO&#xff08;Correlation-Preservation Optimization&#xff09;优化的时间卷积双向门控循环单元&#xff08;Bidirectional Gated Recurrent Unit&#xff0c;BiGRU&#xff09;融合注意力机制&#xff08;Attention&#xff09;的回归预测需要详细的实现和…...

面试高级 Java 工程师:2024 年的见闻与思考

面试高级 Java 工程师&#xff1a;2024 年的见闻与思考 由于公司业务拓展需要&#xff0c;公司招聘一名高级java工程研发工程师&#xff0c;主要负责新项目的研发及老项目的维护升级。我作为一名技术面试官&#xff0c;参与招聘高级 Java 工程师&#xff0c;我见证了技术领域的…...

设计模式大白话之装饰者模式

想象一下&#xff0c;你走进一家咖啡馆&#xff0c;点了一杯美式咖啡。但是&#xff0c;你可能还想根据自己的口味添加一些东西&#xff0c;比如奶泡、巧克力粉、焦糖酱或是肉桂粉。每次你添加一种配料&#xff0c;你的咖啡就会变得更丰富&#xff0c;同时价格也会相应增加。 在…...

动手学深度学习6.3 填充和步幅-笔记练习(PyTorch)

以下内容为结合李沐老师的课程和教材补充的学习笔记&#xff0c;以及对课后练习的一些思考&#xff0c;自留回顾&#xff0c;也供同学之人交流参考。 本节课程地址&#xff1a;填充和步幅_哔哩哔哩_bilibili 代码实现_哔哩哔哩_bilibili 本节教材地址&#xff1a;6.3. 填充和…...

函数的形状怎么定义?

在TypeScript中&#xff0c;函数的形状可以通过多种方式定义&#xff0c;以下是几种主要的方法&#xff1a; 1、函数声明&#xff1a;使用function关键字声明函数&#xff0c;并直接在函数名后的括号内定义参数&#xff0c;通过冒号&#xff08;:&#xff09;指定参数的类型&a…...

Windows 虚拟机服务器项目部署

目录 一、部署JDK下载JDK安装JDK1.双击 jdk.exe 安装程序2.点击【下一步】3.默认安装位置&#xff0c;点击【下一步】4.等待提取安装程序5.默认安装位置&#xff0c;点击【下一步】6.等待安装7.安装成功&#xff0c;点击【关闭】 二、部署TomcatTomcat主要特点包括&#xff1a;…...

JDBC(2)基础篇2——增删改查及常见问题

目录 一、基于PreparedStatement实现CRUD 1.查询单行单列 2.查询单行多列 3.查询多行多列 4.新增 5.修改 6.删除 7.总结 二、常见问题 1.资源的管理 2.SQL语句问题 3.SQL语句未设置参数问题 4.用户名或密码错误问题 5.通信异常 总结 一、基于PreparedStatement实…...

JVM知识点梳理

目录标题 1.类加载机制1.1 Java 运行时一个类是什么时候被加载的?1.2 JVM 一个类的加载过程?1.3 一个类被初始化的过程?1.4 继承时父子类的初始化顺序是怎样的?1.5 究竟什么是类加载器?1.6 JVM 有哪些类加载器?1.7 JVM 中不同的类加载器加载哪些文件?1.8 JVM 三层类加载…...

产品经理-一份标准需求文档的8个模块(14)

一份标准优秀的产品需求文档包括&#xff1a; ❑ 封面&#xff1b; ❑ 文档修订记录表&#xff1b; ❑ 目录&#xff1b; ❑ 引言&#xff1b; ❑ 产品概述&#xff1a;产品结构图 ❑ 详细需求说明&#xff1a;产品逻辑图、功能与特性简述列表、交互/视觉设计、需求详细描述&am…...

如何用一个例子向10岁小孩解释高并发实时服务的单线程事件循环架构

I/O密集型进程和CPU密集型进程 聊天应用程序、MMO&#xff08;大型多人在线&#xff09;游戏、金融交易系统、等实时服务需要处理大量并发流量和实时数据。 这些服务是I/O密集型的&#xff0c;因为它们花费大量资源处理输入输出操作&#xff0c;例如高吞吐量、低延迟网络通信…...

如何为帕金森病患者选择合适的步行辅助设备?

选择步行辅助设备的步骤和建议 为帕金森病患者选择合适的步行辅助设备时&#xff0c;应考虑以下几个关键因素&#xff1a; 患者的具体症状和需求&#xff1a;帕金森病患者的步行困难可能包括冻结步态、平衡能力下降和肌肉僵硬。选择设备时&#xff0c;应考虑这些症状&#xff…...

【排序算法】1.冒泡排序-C语言实现

冒泡排序&#xff08;Bubble Sort&#xff09;是最简单和最通用的排序方法&#xff0c;其基本思想是&#xff1a;在待排序的一组数中&#xff0c;将相邻的两个数进行比较&#xff0c;若前面的数比后面的数大就交换两数&#xff0c;否则不交换&#xff1b;如此下去&#xff0c;直…...

Unity最新第三方开源插件《Stateful Component》管理中大型项目MonoBehaviour各种序列化字段 ,的高级解决方案

上文提到了UIState, ObjectRefactor等,还提到了远古的NGUI, KBEngine-UI等 这个算是比较新的解决方法吧,但是抽象出来,问题还是这些个问题 所以你就说做游戏是不是先要解决这些问题? 而不是高大上的UiImage,DoozyUI等 Mono管理引用基本用法 ① 添加Stateful Component …...

[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?

&#x1f9e0; 智能合约中的数据是如何在区块链中保持一致的&#xff1f; 为什么所有区块链节点都能得出相同结果&#xff1f;合约调用这么复杂&#xff0c;状态真能保持一致吗&#xff1f;本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里&#xf…...

Cursor实现用excel数据填充word模版的方法

cursor主页&#xff1a;https://www.cursor.com/ 任务目标&#xff1a;把excel格式的数据里的单元格&#xff0c;按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例&#xff0c;…...

DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径

目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...

在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能

下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能&#xff0c;包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...

安宝特方案丨XRSOP人员作业标准化管理平台:AR智慧点检验收套件

在选煤厂、化工厂、钢铁厂等过程生产型企业&#xff0c;其生产设备的运行效率和非计划停机对工业制造效益有较大影响。 随着企业自动化和智能化建设的推进&#xff0c;需提前预防假检、错检、漏检&#xff0c;推动智慧生产运维系统数据的流动和现场赋能应用。同时&#xff0c;…...

高危文件识别的常用算法:原理、应用与企业场景

高危文件识别的常用算法&#xff1a;原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件&#xff0c;如包含恶意代码、敏感数据或欺诈内容的文档&#xff0c;在企业协同办公环境中&#xff08;如Teams、Google Workspace&#xff09;尤为重要。结合大模型技术&…...

Element Plus 表单(el-form)中关于正整数输入的校验规则

目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入&#xff08;联动&#xff09;2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...

什么是Ansible Jinja2

理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具&#xff0c;可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板&#xff0c;允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板&#xff0c;并通…...

学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”

2025年#高考 将在近日拉开帷幕&#xff0c;#AI 监考一度冲上热搜。当AI深度融入高考&#xff0c;#时间同步 不再是辅助功能&#xff0c;而是决定AI监考系统成败的“生命线”。 AI亮相2025高考&#xff0c;40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕&#xff0c;江西、…...

React---day11

14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store&#xff1a; 我们在使用异步的时候理应是要使用中间件的&#xff0c;但是configureStore 已经自动集成了 redux-thunk&#xff0c;注意action里面要返回函数 import { configureS…...