尝试理解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 …...

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

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

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

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

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

南京邮电大学统计学课程实验3 用EXCEL进行方差分析 指导
一、实验描述 实验目的 1、学会在计算机上利用EXCEL进行单因素方差分析; 2、学会在计算机上利用EXCEL进行无重复的双因素方差分析。 二、实验环境 实验中使用以下软件和硬件设备 (1)Windows XP操作系统; (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机器并执行命令
需求:我们需要从11.0.1.17远程到11.0.1.16上执行命令 实现: 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服务启动与停止(Windows)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,可以按照以下步骤进行操作: 创建一个Spring Boot项目,并添加所需的依赖。在pom.xml文件中添加Spring Boot、Spring Security、Redis、MySQL和MyBatis的依赖项。 …...