当前位置: 首页 > 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 …...

Spark SQL----INSERT TABLE

Spark SQL----INSERT TABLE 一、描述二、语法三、参数四、例子4.1 Insert Into4.2 Insert Overwrite 一、描述 INSERT语句将新行插入表中或覆盖表中的现有数据。插入的行可以由值表达式指定&#xff0c;也可以由查询结果指定。 二、语法 INSERT [ INTO | OVERWRITE ] [ TABL…...

socket功能定义和一般模型

1. socket的功能定义 socket是为了使两个应用程序间进行数据交换而存在的一种技术&#xff0c;不仅可以使同一个主机上两个应用程序间可以交换数据&#xff0c;而且可以使网络上的不同主机间上的应用程序间进行通信。 2. 图解socket的服务端/客户端模型...

如何在linux中给vim编辑器添加插件

在Linux系统中给Vim编辑器添加插件通常通过插件管理器来完成&#xff0c;以下是一般的步骤&#xff1a; 1.使用插件管理器安装插件 安装插件管理器&#xff08;如果尚未安装&#xff09;&#xff1a; 常见的插件管理器包括 Vundle、vim-plug 和 Pathogen 等。你可以根据个人喜…...

Web 中POST为什么会发送两次请求

文章目录 前言一、浏览器的重试机制二、跨域请求与预检请求三、表单的自动提交四、服务器配置问题五、前端代码的重复执行六、同源策略与CORS总结 前言 我们在做Web开发时&#xff0c;经常会使用浏览器F12查看请求参数是否正确&#xff0c;但是会发现POST请求&#xff0c;一个地…...

C语言经典程序100案例

C语言经典程序100题(完整版) 【程序1】题目&#xff1a;有1、2、3、4个数字&#xff0c;能组成多少个互不相同且无重复数字的三位数都是多少 程序分析&#xff1a;可填在百位、十位、个位的数字都是1、2、3、4。组成所有的排列后再去掉不满足条件的排列。 #include "stdio…...

南京邮电大学统计学课程实验3 用EXCEL进行方差分析 指导

一、实验描述 实验目的 1、学会在计算机上利用EXCEL进行单因素方差分析&#xff1b; 2、学会在计算机上利用EXCEL进行无重复的双因素方差分析。 二、实验环境 实验中使用以下软件和硬件设备 &#xff08;1&#xff09;Windows XP操作系统&#xff1b; &#xff08;2&am…...

2024-07-13 Unity AI状态机2 —— 项目介绍

文章目录 1 项目介绍2 模块介绍2.1 BaseState2.2 ...State2.2.1 PatrolState2.2.2 ChaseState / AttackState / BackState 2.3 StateMachine2.4 Monster 3 其他功能4 类图 项目借鉴 B 站唐老狮 2023年直播内容。 点击前往唐老狮 B 站主页。 1 项目介绍 ​ 本项目使用 Unity 2…...

shell脚本-linux如何在脚本中远程到一台linux机器并执行命令

需求&#xff1a;我们需要从11.0.1.17远程到11.0.1.16上执行命令 实现&#xff1a; 1.让11.0.1.17 可以免密登录到11.0.1.16 [rootlocalhost ~]# ssh-keygen Generating public/private rsa key pair. Enter file in which to save the key (/root/.ssh/id_rsa): Created d…...

Spring Data Redis + Redis数据缓存学习笔记

文章目录 1 Redis 入门1.1 简介1.2 Redis服务启动与停止&#xff08;Windows&#xff09;1.2.1 服务启动命令1.2.2 客户端连接命令1.2.3 修改Redis配置文件1.2.4 Redis客户端图形工具 2. Redis数据类型2.1 五种常用数据类型介绍 3. Redis常用命令3.1 字符串操作命令3.2 哈希操作…...

在项目中,如何使用springboot+vue+springsecurity+redis缓存+Axios+MySQL数据库+mybatis

要在项目中使用springbootvuespringsecurityredis缓存AxiosMySQL数据库mybatis&#xff0c;可以按照以下步骤进行操作&#xff1a; 创建一个Spring Boot项目&#xff0c;并添加所需的依赖。在pom.xml文件中添加Spring Boot、Spring Security、Redis、MySQL和MyBatis的依赖项。 …...