尝试理解docker网络通信逻辑
一、docker是什么
- Docker本质是一个进程,
- 宿主机通过namespace隔离机制提供进程需要运行基础环境,并且通过Cgroup限制进程调用资源。
- Docker的隔离机制包括
- network隔离,此次主要探讨网络隔离
- mount隔离
- hostname隔离
- user隔离
- pid隔离
- 进程通信隔离
二、docker 网络模式
host
: 主机模式- 不会创建网络隔离机制,直接宿主机的网络。
bridge
: 默认桥接, 新建虚拟桥接网卡用于docker
容器之间的通信。- 桥接网卡通过
iptables
对分组进行转发到主网卡,通常使用主机外网IP与外部网络通信。 - 桥接网卡会关联多张
interface
网络接口。 实现内网通信。 interface
主要是通过veth pair
虚拟设备对,一端用于容器,一端用于绑定网桥。
- 桥接网卡通过
macvlan
: 为容器穿件一张虚拟网卡,该网卡用于独立的MAC地址和相同的网卡操作逻辑。- 相比
bridge
和hosts
模式。 即满足了网络隔离需求,也满足了和宿主机网络相同地位。
- 相比
ipvlan
: 需要要求内核版本 4.2+, 暂不复现。 可以通过ip link vlan
相关命令了解用法overlay
: 未复现,主要用于swarm
集群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-a397efbb3fb4
在nat
中处理逻辑。
- PREROUTING: 外部流量到达主机时处理,该链路主要动作是修改报文目的IP地址,通过DNAT动作实现请求转发
- 链路展示所有报文会通过
DOCKER CHAIN
链, 该链也恰好有处理DNAT
动作
- 链路展示所有报文会通过
- OUTPUT: 流量出去的时候,也会经过
DOCKER CHAIN
。 - DOCKER: 关于网桥
br-a397efbb3fb4
有两处处理。- RETURN处: 流量从网卡
br-a397efbb3fb4
进入的, 就不再继续向下匹配,也就不修改目标IP - DNAT: 流量从其他网卡进入且访问80端口的,通过修改了目的地址指向nginx容器的IP:
10.2.254.2
。
- RETURN处: 流量从网卡
- 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的
- 假设外部通过
eth0
访问80端口,先通过iptable
规则进行报文分析处理- 报文的入口网卡是
eth0
- 报文的入口网卡是
- 在
PREROUTING
进行了目的端口转发,那么报文就要发送到10.2.254.2
, 见docker
链的DNAT
规则- 要求不是入口网卡
br-a397efbb3fb4
的报文进行DNAT转换 - 网桥
br-a397efbb3fb4
作为一个网关角色,是保持内部通信的基础,所以其IP不能进行改动
- 要求不是入口网卡
- 通过路由寻址。
route -n
可以看到目标网段在10.2.254.0
是通过br-a397efbb3fb4
出去。- 此时报文的入口是
eth0
, 报文的出口br-a397efbb3fb4
br-a397efbb3fb4
和容器IP10.2.254.2
在同一个网段下,且满足通信。
- 此时报文的入口是
场景2: 容器是如何通过SNAT实现对外通信的
-
假设容器请求其他主机:
192.168.35.253
,删除a397efbb3fb4
的SNAT规则后。进行ping测试 -
通过简单的分析拿到容器在主机的设备对网卡是
vetha276645
- 在容器查看命令
ip link show
可以看容器网卡的ID@外部的网卡ID。eth0@if125
- 外部查看
ip link show
查到125网卡vetha276645
- 在容器查看命令
-
ping
请求通过vetha276645
到达主机后,通过路由会从网卡eth0
发出,查看eth0
抓包信息。可以看到是有报文出去
抓包容器网卡vetha276645
向192.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
-
通过抓包可以简单分
- 容器
10.2.254.3
向192.168.35.253
发起了ICMP
请求。 - 主机
192.168.33.253
通过路由指定网卡进行外部通信 - 目标
192.168.35.253
也收到来自IP10.2.254.3
的请求 - 目标
192.168.35.253
向10.2.254.3
进行回应, - 目标
192.168.35.253
找不到10.2.254.3
, 因为他们本身不具备直接通信的条件。 - 容器
10.2.254.3
不能收到192.168.35.253
响应。 导致网络不能互通
- 容器
-
通过
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
规则目的
- 外部对内请求,会在PREROUTING链DNAT转发请求到容器端口。
- 外部对内过程没有修改源IP地址。所以容器是可以拿到客户端IP地址。
- 内部对外访问,会在POSTROUTING链SNAT转发请求到外部网络。
- 内部对外修改了源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
处理拦截。
-
PREROUTING 链路: 进入主机后规则匹配成功,修改目标IP地址。
-
INPUT 链: 对所有目的IP存在本机的流量进行处理
- 因为容器对网络环境进行了隔离,所以当前主机 (
ip address
和ifconfig
) 没有容器IP地址。
- 因为容器对网络环境进行了隔离,所以当前主机 (
-
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
进的网络并从该网卡出去
- 规则翻译: 允许经过
- 第三行规则
-
OUTPUT 链: 通过网卡出去的网络进行拦截。
- 此处不涉及
-
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
网络对目标IP10.2.254.2
进行80端口访问
- 命令:
- 第一行规则:
-
DOCKER-ISOLATION-STAGE-1规则上最终交给DOCKER-ISOLATION-STAGE-2: 有一个丢包机制
- 第二行规则:
- 命令:
iptables -I DOCKER-ISOLATION-STAGE-2 -o br-a397efbb3fb4 -j DROP
- 理解: 拒绝从网桥
br-a397efbb3fb4
出去的流量
- 命令:
- 第二行规则:
场景1: 外部请求容器服务需要经历哪些关卡
- 从
192.168.35.253
访问 容器HTTP 服务,请求经过eth0
进入主机。 - 在
PREROUTING
链路修改目的IP地址,通过目的端口转向到10.2.254.2
- 路由选择: 匹配到
10.2.254.2
需要经过过br-a397efbb3fb4
出去- 报文入口网卡是
eth0
- 分组出口网卡是
br-a397efbb3fb4
- 报文入口网卡是
- 目标IP不在主机网络,经过
FORWARD
链处理,这里主要有2个处理- 尝试建立链接: 允许 TCP 3次握手,所以满足
RELATED,ESTABLISHED
进行放行。 行三规则 - 已经建立链接: 允许通过来自外部对
10.2.254.2
的80端口访问。 行四规则
- 尝试建立链接: 允许 TCP 3次握手,所以满足
场景2: 容器对外请求/响应需要经历哪些关卡
- 容器 回复
192.168.35.253
的响应报文,经过br-a397efbb3fb4
到达主机 - 在
POSTROUTING
链路修改了源IP地址。 - 路由选择: 匹配到
192.168.35.253
需要经过eth0
出去- 流量入口网卡是
br-a397efbb3fb4
。 - 流量出口网卡是
eth0
- 流量入口网卡是
- 目标IP不在主机网络,经过
FORWARD
链处理。- 允许从其他网卡出去。行五规则
场景3: 不同网络的容器是如何隔离的
10.1.254.2
尝试建立10.2.254.2
请求10.1.254.2
从docker0
进入主机,- 路由选择: 匹配到
10.2.254.2
需要经过过br-a397efbb3fb4
出去- 流量入口网卡是
docker0
- 流量出口网卡是
br-a397efbb3fb4
- 流量入口网卡是
- 目标IP不在主机网络,经过
FORWARD
,DOCKER-ISOLATION-STAGE-1
,DOCKER-ISOLATION-STAGE-2
链处理。- 拒绝从
br-a397efbb3fb4
出去的网卡 第二行规则 - 拒绝之后,到达
RETURN
规则,不再进行匹配。 - 验证取消容器网络之间的隔离:
iptables -F DOCKER-ISOLATION-STAGE-2
- 拒绝从
四、 模拟docker网络通信
1. 通过shell
命令实现网络隔离
, 并实现内部通信和外网通信
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怎么走的
-
网桥: 网络连接的一种形式,主要连接多个局域网(lan), 进行流量接收,存储和转发。
-
虚拟以太网设备对(veth-pair): 网络连接的另一种形式, 会产生一对网卡。常用于
网络隔离
通信。
2. 设备对通信
- 创建一个网络隔离的namespace: demo
[root@mking /]# ip netns add demo
# 查看网络NS信息
[root@mking /]# ip netns ls
demo
- 创建一组网卡设备对: veth pair。
设备对网卡可以通过关键字@
快速辨别。 51,51 表示网卡的编号ID , peer1
和peer2
表示网卡别名
[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
- 把peer0的网卡对设置到另一个命名空间
demo
[root@mking /]# ip link set peer0 netns demo
- 查不到编号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
- 分别为两张网卡设置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
- 相互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
实现通信, 主要步骤如下
- 创建一个
ns
独立的网络空间 - 创建一组虚拟设备对
veth pair
产生两张网络接口满足通信链路条件 - 其中一张网卡设置在
ns
- 两张网卡IP在同一个网段
- 启动两张网卡
3.多个隔离网络之间的通信
我们实现了一个ns与主机的通信,现在模拟docker多个容器之间的通信: 即可实现多个ns内部相互通信。
- IP段规划
设备 | 命名空间 | IP | 广播地址 | 备注 |
---|---|---|---|---|
br0 | default | 10.105.0.1 | 10.105.255.255 | 拟gateway |
vir10 | default | 10.105.1.110 | 10.105.255.255 | veth-peer中的一个网卡 |
vir11 | ns1 | 10.105.3.50 | 10.105.255.255 | veth-peer中的一个网卡 |
vir20 | default | 10.105.2.110 | 10.105.255.255 | veth-peer中的一个网卡 |
vir21 | ns2 | 10.105.8.110 | 10.105.255.255 | veth-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进行通信
- 创建两个ns, 模拟两个docker网络环境
[root@mking /]# ip netns add ns1
[root@mking /]# ip netns add ns2
-
同上两组网卡设备对
vir10(主机)/vir11(ns1)
和vir20(主机)/vir21(ns2)
。同一主机内的
vir10
和vir20
是相互独立的, 需要创建一张网卡
作为其网关
,将流量转发到网关
, 用于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
- 同上两组网卡设备对
veth pair
分别用于ns1
和ns2
。 并对两组ns
设置IP,要求在同一个网段内,不需要设置vir10
和vir20
网卡
# 创建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
- 设置同主机内的
vir10
和vir20
, 我们将网卡指向到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
- 至此
ns1
和ns2
可以相互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
实现通信, 主要步骤如下
- 创建多个
ns
和多组veth pair
虚拟设备对的网卡 - 设备对的一端网卡分别设置在多个
ns1
- 宿主机创建一张
bridge
网卡 - 设备对的另一端网卡添加到
bridge
网卡。 ns
里面的网卡和bridge
网卡的IP设置在一个网段内
4.多个NS对外通信
模拟了多个docker的网络隔离之间通信,现在模拟docker对外的通信。
- 创建两个ns, 模拟两个docker网络环境。 此处继续使用之间建好的
ns1
和ns2
。
# 从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
- 创建默认路由,让请求外网的分组请求到
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
此时ns1
和 ns2
可以把流量发送到网卡gateway1
。
- 分析
ns1
流量走势
iptables处理分组时间轴
timelinetitle iptables时间线PREROUTING : natINPUT : filter: natFORWARD : natOUTPUT : filter: natPOSTROUTING: nat
分组从 ns1
的网卡 vir11
经过vir10
到达网卡gateway
, 会经过如下判断
- PREROUTING
- 事件: 接收分组时候
- 备注: 对分组进行修改, 此处不修改
- FORWARD:
- 事件: 接收分组后
- 执行动作: 放行
- 触发条件: 目标IP不在主机. 所以进行
forward
转发
- 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
- 自建的
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服务)
-
前置条件: 创建一个
ns
, 并要求ns
网络可以对外访问。 此处继续使用之间建好的ns1
和ns2
。 -
终端1在
ns1
开启一个端口
[root@mking /]# ip netns exec ns1 nc -l -p 8000
- 终端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
- 通过
iptables
的DNAT转发,对外提供访问
enp3s0
网卡收到外部请求端口: 8000。 可通过iptables如下链路
- PREROUTING
- 事件: 接收分组时候。
- 执行动作: 修改目的IP和端口。
- 备注: 因为当前主机是没有提供端口服务,而主机与
ns1
可以直接通信。 所以在入口时通过修改dst address
和dst port
转发请求到目的网络
- 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
- 验证自建的
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中
- 创建好一组虚拟网卡设备对, 规划和
gateway1
相同的IP段.
# 创建ns3
[root@mking /]# ip netns add ns3# 创建新的虚拟网卡设备对
[root@mking /]# ip link add name vir30 type veth peer name vir31
- 启动一个nginx docker,不暴露端口。
# 启动一个nginx服务,并使用自定义网络, 但不暴露端口。
[root@mking /]# docker run --name nginx --network demo -itd --rm nginx
指定网络是docker
启动会创建新的隔离网络, 不暴露端口是咱不使用docker-proxy
代理。
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
- 将
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
- 内部
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" "-"
- 通过
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" "-"
小结
- docker利用桥接和虚拟设备对实现网络隔离以及通信。
- docker网络通过iptables对分组进行拦截过滤和转发。
相关文章:
尝试理解docker网络通信逻辑
一、docker是什么 Docker本质是一个进程,宿主机通过namespace隔离机制提供进程需要运行基础环境,并且通过Cgroup限制进程调用资源。Docker的隔离机制包括 network隔离,此次主要探讨网络隔离mount隔离hostname隔离user隔离pid隔离进程通信隔离 二、doc…...

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

K最近邻(K-Nearest Neighbors, KNN)
K最近邻(K-Nearest Neighbors, KNN)理论知识推导 KNN算法是一个简单且直观的分类和回归方法,其基本思想是:给定一个样本点,找到训练集中与其最近的K个样本点,根据这些样本点的类别(分类问题&am…...
深度学习损失计算
文章目录 深度学习损失计算1.如何计算当前epoch的损失?2.为什么要计算样本平均损失,而不是计算批次平均损失? 深度学习损失计算 1.如何计算当前epoch的损失? 深度学习中的损失计算,通常为数据集的平均损失࿰…...

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

Java核心(五)多线程
线程并行的逻辑 一个线程问题 起手先来看一个线程问题: 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工具,但我觉得WebStorm 应该也可以 文章目录 进入项目根目录下,进入cmd输入如下指令: 只有文件夹 tree . > list.txt 包括文件夹和文件 tree /f . > list.txt 还可以为相关包路径加上注释...
【CPO-TCN-BiGRU-Attention回归预测】基于冠豪猪算法CPO优化时间卷积双向门控循环单元融合注意力机制
基于冠豪猪算法CPO(Correlation-Preservation Optimization)优化的时间卷积双向门控循环单元(Bidirectional Gated Recurrent Unit,BiGRU)融合注意力机制(Attention)的回归预测需要详细的实现和…...
面试高级 Java 工程师:2024 年的见闻与思考
面试高级 Java 工程师:2024 年的见闻与思考 由于公司业务拓展需要,公司招聘一名高级java工程研发工程师,主要负责新项目的研发及老项目的维护升级。我作为一名技术面试官,参与招聘高级 Java 工程师,我见证了技术领域的…...
设计模式大白话之装饰者模式
想象一下,你走进一家咖啡馆,点了一杯美式咖啡。但是,你可能还想根据自己的口味添加一些东西,比如奶泡、巧克力粉、焦糖酱或是肉桂粉。每次你添加一种配料,你的咖啡就会变得更丰富,同时价格也会相应增加。 在…...
动手学深度学习6.3 填充和步幅-笔记练习(PyTorch)
以下内容为结合李沐老师的课程和教材补充的学习笔记,以及对课后练习的一些思考,自留回顾,也供同学之人交流参考。 本节课程地址:填充和步幅_哔哩哔哩_bilibili 代码实现_哔哩哔哩_bilibili 本节教材地址:6.3. 填充和…...
函数的形状怎么定义?
在TypeScript中,函数的形状可以通过多种方式定义,以下是几种主要的方法: 1、函数声明:使用function关键字声明函数,并直接在函数名后的括号内定义参数,通过冒号(:)指定参数的类型&a…...

Windows 虚拟机服务器项目部署
目录 一、部署JDK下载JDK安装JDK1.双击 jdk.exe 安装程序2.点击【下一步】3.默认安装位置,点击【下一步】4.等待提取安装程序5.默认安装位置,点击【下一步】6.等待安装7.安装成功,点击【关闭】 二、部署TomcatTomcat主要特点包括:…...
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)
一份标准优秀的产品需求文档包括: ❑ 封面; ❑ 文档修订记录表; ❑ 目录; ❑ 引言; ❑ 产品概述:产品结构图 ❑ 详细需求说明:产品逻辑图、功能与特性简述列表、交互/视觉设计、需求详细描述&am…...

如何用一个例子向10岁小孩解释高并发实时服务的单线程事件循环架构
I/O密集型进程和CPU密集型进程 聊天应用程序、MMO(大型多人在线)游戏、金融交易系统、等实时服务需要处理大量并发流量和实时数据。 这些服务是I/O密集型的,因为它们花费大量资源处理输入输出操作,例如高吞吐量、低延迟网络通信…...

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

【排序算法】1.冒泡排序-C语言实现
冒泡排序(Bubble Sort)是最简单和最通用的排序方法,其基本思想是:在待排序的一组数中,将相邻的两个数进行比较,若前面的数比后面的数大就交换两数,否则不交换;如此下去,直…...

Unity最新第三方开源插件《Stateful Component》管理中大型项目MonoBehaviour各种序列化字段 ,的高级解决方案
上文提到了UIState, ObjectRefactor等,还提到了远古的NGUI, KBEngine-UI等 这个算是比较新的解决方法吧,但是抽象出来,问题还是这些个问题 所以你就说做游戏是不是先要解决这些问题? 而不是高大上的UiImage,DoozyUI等 Mono管理引用基本用法 ① 添加Stateful Component …...
[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?
🧠 智能合约中的数据是如何在区块链中保持一致的? 为什么所有区块链节点都能得出相同结果?合约调用这么复杂,状态真能保持一致吗?本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里…...
Cursor实现用excel数据填充word模版的方法
cursor主页:https://www.cursor.com/ 任务目标:把excel格式的数据里的单元格,按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例,…...
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中实现类似抖音的点赞功能,包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...

安宝特方案丨XRSOP人员作业标准化管理平台:AR智慧点检验收套件
在选煤厂、化工厂、钢铁厂等过程生产型企业,其生产设备的运行效率和非计划停机对工业制造效益有较大影响。 随着企业自动化和智能化建设的推进,需提前预防假检、错检、漏检,推动智慧生产运维系统数据的流动和现场赋能应用。同时,…...

高危文件识别的常用算法:原理、应用与企业场景
高危文件识别的常用算法:原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件,如包含恶意代码、敏感数据或欺诈内容的文档,在企业协同办公环境中(如Teams、Google Workspace)尤为重要。结合大模型技术&…...
Element Plus 表单(el-form)中关于正整数输入的校验规则
目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入(联动)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 是一款功能强大的开源自动化工具,可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板,允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板,并通…...

学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”
2025年#高考 将在近日拉开帷幕,#AI 监考一度冲上热搜。当AI深度融入高考,#时间同步 不再是辅助功能,而是决定AI监考系统成败的“生命线”。 AI亮相2025高考,40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕,江西、…...
React---day11
14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store: 我们在使用异步的时候理应是要使用中间件的,但是configureStore 已经自动集成了 redux-thunk,注意action里面要返回函数 import { configureS…...