Docker网络配置
一、Docker网络概念
1、网络驱动
Docker 网络子系统使用可插拔的驱动,默认情况下有多个驱动程序,并提供核心联网功能。
- bridge:桥接网络,这是默认的网络驱动程序(不指定驱动程序创建的容器默认是bridge驱动)。
- host:主机网络。消除容器和主机的网络隔离,直接使用主机的网络。
- overlay:覆盖网络。可以将多个Docker守护进程连接,实现跨主机容器通讯(swarm集群)。
- macvlan:将MAC地址分配给容器,使容器作为网络上的物理设备。不通过Docker主机网络栈进行路由,直接通过MAC地址路由到容器。
- none:表示关闭容器的所有网络连接。常与自定义网络驱动一起使用,不适用于swarm。
- 网络插件:可以通过Docker安装和使用第三方网络插件。
2、Docker 网络驱动选用原则
- bridge桥接网络:最适合用于同一个Docker主机上运行的多个容器之间需要通信的场景。(单主机)
- host主机网络:最适用于当网络栈不能与Docker主机隔离,而容器的其他方面需要被隔离的场景。(解除容器和主机隔离)
- overlay网络:适用于不同Docker主机上运行的容器需要通信的场景,或者多个应用程序通过Swarm集群服务一起工作的场景。(多主机、集群)
- macvlan网络:适用于从虚拟机迁移过来的场景,或者容器需要像网络上的物理机一样,拥有独立MAC地址的场景。(容器需要mac)
- 第三方网络插件适用于将Docker与专用网络栈进行集成的场景。(订制化)
3、容器的网络模式
docker内置了四种网络模式,也可以使用自定义网络。
(1)bridge模式
桥接网络分为默认桥接网络和用户自定义桥接网络两种类型。
桥接网络用于同(单)主机运行的容器间通信。
实现原理:桥接网络使用软件网桥,让连接到同一桥接网络的容器可以通信,没连接该网桥的容器被隔离。
工作流程:Docker守护进程启动,会在主机上创建一个名为 docker0
的虚拟网桥,启动容器时如果没有特别指定,自动连接到这个虚拟网桥。
[root@localhost ~]# ifconfig
docker0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
inet 172.17.0.1 netmask 255.255.0.0 broadcast 172.17.255.255
ether 02:42:b2:81:22:9d txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.100.111 netmask 255.255.255.0 broadcast 192.168.100.255
# 案例:默认桥接网络和用户自定义桥接网络
[root@huangqiushi ~]# docker network create --driver bridge hqs-net
c330cf15a47d29057a89a4313e5734348799bde32fba4dd71376382b049f1c86
[root@huangqiushi ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
73ab60681376 bridge bridge local
76d9e06a3606 host host local
c330cf15a47d hqs-net bridge local
9f44873071f4 none null local
Docker 守护进程为每个启动的容器创建一个 VETH对设备(总是成对出现,组成数据通道)。
VETH对是直接连接的一对虚拟网络接口,一个接口设置为新创建容器的接口(位于容器的网络名称空间中);另一个接口连接到虚拟网桥docker0(位于Docker的网络名称空间中)。
(2)host模式
host模式的容器使用host驱动,直接连接Docker主机网络栈。实质:关闭 Docker 网络,让容器直接使用主机操作系统的网络。
host模式没有为容器创建一个隔离的网络环境,容器没有隔离的网络名称空间,也不会获得一个独立的网络名称空间,而是和Docker主机共用一个网络名称空间。
容器和主机在同一个网络中,使用主机的物理网络接口,没有独立的IP地址,直接使用主机的IP地址和端口。
由于主机名、地址都一样,因此同一主机上的容器要做好协调。各容器通过端口来进行区分。
(3)container模式
Docker中一种较为特别的网络模式,主要用于容器和容器直接频繁交流的情况。
特点:
- 该模式指定新建的容器和现有的一个容器共享网络名称空间。
- 新创建的容器和一个现有的指定容器共享IP地址、端口范围,不创建自己的网络接口、IP地址。
- 两个容器间网络不隔离,进程可通过回环网络进行通信。
- 这两个容器和主机和其他容器存在网络隔离。
(4)none模式
none模式将容器放置在它自己的网络栈中,但是并不进行任何配置,实际上关闭了容器的网络功能。
特性:none模式,容器有自己的网络名称空间,但未进行任何网络配置,未构建任何网络环境,容器内部只能使用回环网络接口(127.0.0.1)。
可用场景:
- 有些容器并不需要网络,例如只需要写入磁盘卷的批处理任务。
- 安全性要求高并且不需要联网的应用可以使用none模式。
- 要创建自定义网络。
(5)用户自定义网络模式
管理员可以使用Docker网络驱动(bridge、overlay、macvlan)或第三方网络驱动插件创建一个自定义的网络,然后将多个容器连接到同一个自定义网络。
特点:
- 连接到用户自定义网络的容器,可以使用IP地址或名称相互通信。
- 可以根据需要创建任意数量的自定义网络。
- 可以在任何时间将容器连接到这些网络。
- 对运行中的容器,可连接、断开自定义网络,无须重启容器。
用户自定义桥接网络(自定义网络使用bridge网络驱动):单机环境常用。生产环境推荐使用。
生产环境不推荐使用默认桥接网络,推荐使用用户自定义桥接网络,原因在以下区别:
- 用户自定义桥接网络能提供容器化应用程序之间更好的隔离和互操作性。如果在默认桥接网络上运行应用栈,则Docker主机需要通过其他方式来限制对端口的访问。
- 用户自定义桥接网络提供容器之间自动DNS解析功能,可以通过名称或别名互相访问。而默认桥接网络上的容器只能通过IP地址互相访问。
- 容器可以在运行时与用户自定义网络连接和断开。要断开与默认桥接网络的连接,需要停止容器并使用不同的网络选项重新创建该容器。
- 每个用户可通过自定义网络创建一个可配置的网桥。而默认桥接网络会自动穿件一个名为docker0的虚拟网桥。
- 用户自定义网络中所连接容器不能共享环境变量,不过有更好的方式实现共享环境变量(docker卷挂载、compose文件定义、集群)。默认桥接网络中所连接的容器共享环境变量。
4、容器间的通信方案
容器间通信方案:
- bridge模式让同一个Docker网络上的所有容器在所有端口上都可以相互连接。
- 默认桥接网络不支持基于名称的服务发现和用户指定的IP地址,所连接的容器只能通过IP地址互相访问,除非创建容器时使用--link选项建立容器连接。
- 在用户自定义桥接网络中,容器之间可以通过名称或别名互相访问。
- host模式让所有容器都位于同一个主机网络空间中,并共用主机的IP地址栈,在该主机上的所有容器都可通过主机的网络接口相互通信。
- 容器通过端口映射对外部提供连接。
- container模式让容器共用一个IP网络,两个容器之间可通过回环网络接口相互通信。
- 容器之间使用
--link
选项建立传统的容器互联。 - 容器之间通过挂载主机目录来实现相互之间的数据通信。
5、传统容器连接
创建容器时使用--link选项可以在容器之间建立连接,这是Docker传统的容器互联解决方案。
特性:
- 这种连接方式用来将多个容器连接在一起,并在容器之间发送连接信息。
- 当容器被连接时,在源容器和 接收容器 之间建立一个
安全通道
,关于源容器的信息能够被发送到接收容器,让接收容器可以访问源容器所指定的数据。
为容器设置自定义名称的好处:
- 表示特定用途的名称更易记忆,如将一个Web应用的容器命名为web。
- 便于Docker通过该名称引用其他容器,弥补默认桥接网络不支持容器名称解析的不足
# 语法
[root@localhost ~]# docker create --help
Usage: docker create [OPTIONS] IMAGE [COMMAND] [ARG...]
Create a new container
Options:
--link list Add link to another container # 添加连接到另一个容器
# 格式
--link <源容器的名称或ID>
--link <源容器的名称或ID>:容器别名
# 案例
# 创建容器时,添加到第一个容器的连接,并配置别名alp
[root@localhost ~]# docker run -dti --name alpine2 --link alpine1:alp alpine ash
21c0ddd94cb4d7190701d003868caef7cd9f82aded0ae0c5a2e9e68ebb45a03f
6、容器访问外部网络
默认情况下,容器可以主动访问到外部网络的连接,但是外部网络无法访问到容器。
使用bridge模式(默认桥接网络)的容器通过NAT方式实现外部访问,具体通过iptables(Linux的包过滤防火墙)的源地址伪装操作实现。
iptables源地址伪装:容器所有到外部网络的连接,源地址都会被 NAT 装换成本地系统的 IP 地址(即docker0地址)。
查看主机的 NAT 规则:
[root@localhost ~]# iptables -t nat -vnL
Chain PREROUTING (policy ACCEPT 142 packets, 31842 bytes)
pkts bytes target prot opt in out source destination
1 52 DOCKER all -- * * 0.0.0.0/0 0.0.0.0/0 ADDRTYPE match dst-type LOCAL
Chain INPUT (policy ACCEPT 140 packets, 31384 bytes)
pkts bytes target prot opt in out source destination
Chain OUTPUT (policy ACCEPT 4 packets, 288 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 LOCAL
Chain POSTROUTING (policy ACCEPT 4 packets, 288 bytes)
pkts bytes target prot opt in out source destination
0 0 MASQUERADE all -- * !docker0 172.17.0.0/16 0.0.0.0/0 《————这一条规则将所有源地址在 172.17.0.0/16 网段,目标地址为其他网段(外部网络)的流量动态伪装为从系统网卡发出。
MASQUERADE 相比传统 SNAT 的好处是它能动态从网卡获取地址。
Chain 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
7、从外部网络访问容器
默认情况下,创建的容器不会将其任何端口对外发布,从容器外部无法访问容器内部。
从外部访问容器内的应用程序必须要有明确的授权,通过内部端口映射来实现。
实现:将容器的一个端口映射到 docker 主机上的一个端口,允许从外部网络通过该端口访问容器。这种端口映射也是一种NAT实现,即目标地址转换(Destination NAT DNAT)。
# 案例
[root@localhost dockerfile-test]# docker run --rm -d -p 8000:80 --name my-nginx centos-nginx-hqs:1.0
8、容器的网络配置语法
使用 docker run
和 docker create
命令来设置容器的网络配置的语法。
(1)设置容器的网络连接
容器启动时,可以使用 --network
选项设置容器要连接的网络,即网络模式。
none:容器为none模式,容器不使用任何网络连接,能完全禁用网络连接。
bridge:容器为bridge模式,连接到默认桥接网络,也是默认设置。
host:容器为host模式,使用主机网络栈。
container:容器为container模式,容器使用某一个容器(通过id或name来标识)的网络栈。
网络名or网络id:容器连接自定义网络,可使用自定义网络的名称或id。
# 语法
[root@localhost ~]# docker run --help
Usage: docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
--network network Connect a container to a network # 连接容器到网络中
# 案例
[root@hqs ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
9b3f5f5076b9 bridge bridge local
76d9e06a3606 host host local
9f44873071f4 none null local
[root@hqs ~]# docker run --network none -dit --name centos-test centos /bin/bash
24110a1ca4da0f6d7659d7d2e8a1f57a8d980031ab54ac4730a3b89e44679e46
[root@hqs ~]# docker inspect --format='{{json .HostConfig.NetworkMode}}' 24110a1ca4da
"none"
(2)为容器添加网络作用域的别名
容器在网络作用域中允许有别名,别名在所在网络中可以直接访问。使用 --network-alias
选项指定容器在网络中的别名。
注意:网络作用域别名只支持用户自定义的网络。
# 语法
[root@localhost ~]# docker run --help
Usage: docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
--network-alias list Add network-scoped alias for the container # 给容器添加网络作用域别名
# 失败案例
[root@hqs ~]# docker run -d -p 80:80 --name testweb --network host --network-alias websrv centos
docker: network-scoped aliases are only supported for user-defined networks. # 网络作用域别名只支持用户自定义的网络
# 案例
[root@hqs ~]# docker network create --driver bridge mynet
a7b402ee062eaf27c50fb303ae6e764f384cd7b2ebd0acf61fdeeba68bddb5f6
[root@hqs ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
9b3f5f5076b9 bridge bridge local
76d9e06a3606 host host local
a7b402ee062e mynet bridge local
9f44873071f4 none null local
# 启动容器
[root@huangqiushi ~]# docker run -d -p 8082:80 --name test-web --network mynet --network-alias webtesthqs centos
99e3e91c82122b70fb53adebb120dc423f9d91e8a08696645fd2a7f5441e0417
# 查看网络模式
[root@huangqiushi ~]# docker inspect --format='{{json .HostConfig.NetworkMode}}' 99e3e91c82
"mynet"
# 查看自定义网络信息
[root@huangqiushi ~]# docker inspect --format='{{json .NetworkSettings.Networks.mynet}}' 99e3e91c82
{"IPAMConfig":null,"Links":null,"Aliases":["webtesthqs","99e3e91c8212"],"NetworkID":"f5d3fe3303c75faccc4ecbdb740d65865e2c05364608dc14602d110daa32bce6","EndpointID":"","Gateway":"","IPAddress":"","IPPrefixLen":0,"IPv6Gateway":"","GlobalIPv6Address":"","GlobalIPv6PrefixLen":0,"MacAddress":"","DriverOpts":null}
# 查看网络别名
[root@huangqiushi ~]# docker inspect --format='{{json .NetworkSettings.Networks.mynet.Aliases}}' 99e3e91c82
["webtesthqs","99e3e91c8212"]
# 查看端口映射
[root@huangqiushi ~]# docker inspect --format='{{json .HostConfig.PortBindings}}' 99e3e91c82
{"80/tcp":[{"HostIp":"","HostPort":"8082"}]}
(3)设置容器的IP地址
使用--network
选项启动容器连接自定义网络时,可以使用 --ip
或 --ip6
选项明确指定分配给该网络容器的ip地址。
使用前面创建的mynet,创建新容器指定ip时报错:Error response from daemon: user specified IP address is supported only when connecting to networks with user configured subnets.
注意有两个前置使用条件:
- 只适用用户自定义网络;
- 用户自定义网络配置了子网。
# 语法
[root@localhost ~]# docker run --help
Usage: docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
--ip string IPv4 address (e.g., 172.30.100.104)
--ip6 string IPv6 address (e.g., 2001:db8::33)
# 案例:使用默认网络--ip配置地址不生效
[root@hqs ~]# docker run -dti --name test-ip --ip 172.17.1.100 centos
04ffb3d192dd681f07ba53d828f515f6376376ae3dbc29b69c6f8fa5f06b6f17
[root@hqs ~]# docker inspect --format='{{json .NetworkSettings.IPAddress}}' test-ip
"172.17.0.2"
# 案例
[root@hqs ~]# docker network create --driver bridge --subnet 10.10.0.0/24 --gateway 10.10.0.254 hqsnet2
bb6f77c8b7f0525d0563ae9e85ef4b8a829ac5fee5a4f666c0caff4fcbc1b67b
[root@hqs ~]# docker network inspect hqsnet2
[
{
"Name": "hqsnet2",
"Id": "bb6f77c8b7f0525d0563ae9e85ef4b8a829ac5fee5a4f666c0caff4fcbc1b67b",
"Created": "2022-04-15T03:26:05.242174757+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "10.10.0.0/24",
"Gateway": "10.10.0.254"
}
]
}
}
]
[root@hqs ~]# docker run -dti --name test-ip02 --network hqsnet2 --ip 10.10.0.101 centos
1377b1fe89549cf87833f6188c3a6355c39cec86d6237e7141a958befb8a176a
[root@hqs ~]# docker inspect --format='{{json .NetworkSettings.Networks.hqsnet2.IPAddress}}' test-ip02
"10.10.0.101"
(4)设置容器的网络接口MAC地址
默认情况下,容器mac地址是基于其IP地址生成。
可以通过 --mac-address
选项为容器指定一个MAC地址。
注意:手动指定mac地址,docker不会检查地址的唯一性。
# 语法
[root@localhost ~]# docker run --help
Usage: docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
--mac-address string Container MAC address (e.g., 92:d0:c6:0a:29:33)
# 案例1:(默认网络)
[root@hqs ~]# docker run -dti --name test-dns01 --mac-address 56:78:ab:bc:8c:1d centos
f4e0babb3072cc2a1484e68b7fcbd733194a462660f5ee571072c6fb575f7683
[root@hqs ~]# docker inspect --format='{{json .Config.MacAddress}}' test-dns01
"56:78:ab:bc:8c:1d"
# 案例2:
[root@hqs ~]# docker run -d -p 8080:80 --name testweb03 --network mynet --network-alias web03 --mac-address 12:34:56:78:9a:bc centos
4bc34a65f48f46b95c021194dec216f640d8974f98c6c297dcc37c64f924436b
[root@hqs ~]# docker inspect --format='{{json .Config.MacAddress}}' 4bc34a65
"12:34:56:78:9a:bc"
(5)设置容器的DNS配置和主机名
默认情况下,容器继承Docker守护进程的DNS配置,包括 /etc/hosts
和 /etc/resolv.conf
配置文件。
可以使用以下选项配置DNS,覆盖默认配置。
# 语法
[root@localhost ~]# docker run --help
Usage: docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
--dns list Set custom DNS servers # 为容器设置DNS服务器的IP地址
可以使用多个--dns为容器指定多个DNS服务器。
容器连不上指定DNS服务器,会自动使用google的公共dns:8.8.8.8。
--dns-option list Set DNS options # 为容器设置表示DNS选项及值的键值对
--dns-search list Set custom DNS search domains # 为容器指定一个DNS搜索域
搜索非全称的主机名。
可以使用多个--dns-search的选项
-h, --hostname string Container host name # 为容器指定自定义的主机名
# --dns案例
[root@hqs ~]# docker run -d -p 8081:80 --name testdns --network mynet --dns 202.103.24.68 centos
101cf268006a5780c9e69e147d302933aa91bace5922c5527d41f5dcec4a23f9
[root@hqs ~]# docker inspect --format='{{json .HostConfig.Dns}}' 101cf268
["202.103.24.68"]
# --dns-option案例
# 参照resolv.conf文件来确定选项
# 如果配置错误DNS,测试发现一开始会找不到地址,过一段时间后才能转换域名为ip
[root@hqs ~]# docker run --name test-dns-opt02 -dti --dns-option 202.103.24.68 centos
c51e9e87b4e6e401868ece408a8a5726aaf67ec5ae34e8bbae4e9e1309f5e34e
# 登录测试
[root@hqs ~]# docker exec -ti test-dns-opt02 /bin/bash
[root@c51e9e87b4e6 /]# ping www.baidu.com
PING www.wshifen.com (103.235.46.39) 56(84) bytes of data.
64 bytes from 103.235.46.39 (103.235.46.39): icmp_seq=4 ttl=127 time=293 ms
# 查看配置
[root@hqs ~]# docker inspect --format='{{json .HostConfig.DnsOptions}}' test-dns-opt02
["202.103.24.68"]
# --dns-search案例
[root@hecs-hqs-01 ~]# docker run -dti -p 8082:80 --name test_dns_search --network mynet --dns-search openstacklocal centos
b23a978d072acf45a90a093883c3494d0b3383c817ffe76708c9fd132df00358
[root@hecs-hqs-01 ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b23a978d072a centos "/bin/bash" 5 seconds ago Up 4 seconds 0.0.0.0:8082->80/tcp test_dns_search
[root@hecs-hqs-01 ~]# docker inspect --format='{{json .HostConfig.DnsSearch}}' test_dns_search
["openstacklocal"]
# --hostname案例
[root@hecs-hqs-01 ~]# docker run -dti -p 8083:80 --name test_hostname --network mynet --hostname hostname-test centos
5f49da1802ea461de7133beb4c8460a75a8cf687648ef1d135757b79f7e2c30b
[root@hecs-hqs-01 ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5f49da1802ea centos "/bin/bash" 4 seconds ago Up 3 seconds 0.0.0.0:8083->80/tcp test_hostname
[root@hecs-hqs-01 ~]# docker inspect --format='{{json .Config.Hostname}}' test_hostname
"hostname-test"
二、Docker网络管理命令
docker network
是Docker 网络本身的管理命令。
# 语法
[root@localhost ~]# docker network --help
Usage: docker network COMMAND
Manage networks 网络管理
Commands:
connect Connect a container to a network # 将容器连接到网络
create Create a network # 创建网络
disconnect Disconnect a container from a network # 断开容器和某网络的连接
inspect Display detailed information on one or more networks # 显示网络的详细信息
ls List networks # 显示网络列表
prune Remove all unused networks # 删除未被使用网络
rm Remove one or more networks # 删除指定网络
Run 'docker network COMMAND --help' for more information on a command.
1、查看网络列表
执行 docker network ls
命令,可查看当前网络列表。
网络作用域为local,则仅在docker主机范围内提供连接和网络服务。
# 语法
[root@localhost ~]# docker network ls --help
Usage: docker network ls [OPTIONS]
List networks
Aliases:
ls, list
Options:
-f, --filter filter Provide filter values (e.g. 'driver=bridge')
--format string Pretty-print networks using a Go template
--no-trunc Do not truncate the output
-q, --quiet Only display network IDs
# 案例
[root@localhost ~]# docker network ls
网络ID 网络名称 网络驱动 作用域
NETWORK ID NAME DRIVER SCOPE
989e0968a6fe bridge bridge local
76d9e06a3606 host host local
9f44873071f4 none null local
[root@localhost ~]# docker network ls -q
989e0968a6fe
76d9e06a3606
9f44873071f4
[root@localhost ~]# docker network ls --filter driver=bridge
NETWORK ID NAME DRIVER SCOPE
989e0968a6fe bridge bridge local
[root@localhost ~]# docker network ls --format asdasd
asdasd
asdasd
asdasd
[root@localhost ~]# docker network ls --no-trunc
NETWORK ID NAME DRIVER SCOPE
989e0968a6fe9b95dd76238e4707f40bc0cf288fd6b764acbd537954fd0e6fcf bridge bridge local
76d9e06a360678e6c0b12b13d9cdde88cb63a77807012cb7e4e8b66bfd47e88b host host local
9f44873071f4baa0f97756fd0b904cde51a718455689ccc58252d374b4d47992 none null local
2、查看网络详细信息
执行 docker network inspect
命令查看指定网络的详细信息。
参数可以是网络名称或网络ID。
# 语法
[root@localhost ~]# docker network inspect --help
Usage: docker network inspect [OPTIONS] NETWORK [NETWORK...]
Display detailed information on one or more networks
Options:
-f, --format string Format the output using the given Go template
-v, --verbose Verbose output for diagnostics # 诊断的详细输出
# 案例
[root@localhost ~]# docker network inspect bridge
[
{
"Name": "bridge",
"Id": "989e0968a6fe9b95dd76238e4707f40bc0cf288fd6b764acbd537954fd0e6fcf",
"Created": "2022-04-19T21:56:32.999550849+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.17.0.0/16",
"Gateway": "172.17.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"15d605de252549571b613ec08b397af49060e1d6f1a45548f884a7d2de896c56": {
"Name": "test-dns02",
"EndpointID": "a474c3a28141e679b93f7c4d44616452114e310f5bf8f40560f3f73eb90a1f7f",
"MacAddress": "02:42:ac:11:00:03",
"IPv4Address": "172.17.0.3/16",
"IPv6Address": ""
},
"24574957d30aab88b2b4ebcb98c0878470a78a05df03dfb86ad4078fabed1fe5": {
"Name": "test-dns03",
"EndpointID": "745a7c6b314b5963c6d134c148205fc0a215f1f3afb7948246a423088863ab4e",
"MacAddress": "02:42:ac:11:00:04",
"IPv4Address": "172.17.0.4/16",
"IPv6Address": ""
},
"60e63a6f891f2b8a25847c410f35ca0c88f172c15edbb39fd3631a61b7fc5ad0": {
"Name": "test-dns",
"EndpointID": "08574b9714bb18374c24cddb340a932a332a9aeda71037548e5ea87966c904f8",
"MacAddress": "02:42:ac:11:00:02",
"IPv4Address": "172.17.0.2/16",
"IPv6Address": ""
}
},
"Options": {
"com.docker.network.bridge.default_bridge": "true",
"com.docker.network.bridge.enable_icc": "true",
"com.docker.network.bridge.enable_ip_masquerade": "true",
"com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
"com.docker.network.bridge.name": "docker0",
"com.docker.network.driver.mtu": "1500"
},
"Labels": {}
}
]
# --format筛选输出
[root@localhost ~]# docker network inspect --format='{{json .Options}}' bridge
{"com.docker.network.bridge.default_bridge":"true","com.docker.network.bridge.enable_icc":"true","com.docker.network.bridge.enable_ip_masquerade":"true","com.docker.network.bridge.host_binding_ipv4":"0.0.0.0","com.docker.network.bridge.name":"docker0","com.docker.network.driver.mtu":"1500"}
[root@localhost ~]# docker network inspect --format='{{json .Containers}}' bridge
3、创建容器网络
使用 docker network create
命令创建容器网络。
# 语法
[root@localhost ~]# docker network create --help
Usage: docker network create [OPTIONS] NETWORK
Create a network
Options:
--attachable Enable manual container attachment
--aux-address map Auxiliary IPv4 or IPv6 addresses used by Network driver (default map[])
--config-from string The network from which to copy the configuration # 网络从哪复制配置
--config-only Create a configuration only network # 创建配置唯一网络
-d, --driver string Driver to manage the Network (default "bridge") # 指定网络驱动
--gateway strings IPv4 or IPv6 Gateway for the master subnet # 指定子网网关
--ingress Create swarm routing-mesh network
--internal Restrict external access to the network
--ip-range strings Allocate container ip from a sub-range # 指定子网中容器的IP地址范围
--ipam-driver string IP Address Management Driver (default "default")
--ipam-opt map Set IPAM driver specific options (default map[])
--ipv6 Enable IPv6 networking
--label list Set metadata on a network
-o, --opt map Set driver specific options (default map[])
--scope string Control the network's scope # 控制网络作用域
--subnet strings Subnet in CIDR format that represents a network segment # 子网
# 案例
[root@localhost ~]# docker network create --driver bridge hqs_net01
00c4ff496a7d44dda7e1913e6119d45064aba010b5210b0afee04c6eedbae6a3
[root@localhost ~]# docker network create --driver bridge --subnet 10.10.1.0/24 --gateway 10.10.1.254 hqs_net02
7fe5768c8e84d3befef561a8632c49bdeb194b440836bf7223d8d37e51ec2c8c
[root@localhost ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
989e0968a6fe bridge bridge local
76d9e06a3606 host host local
00c4ff496a7d hqs_net01 bridge local
7fe5768c8e84 hqs_net02 bridge local
9f44873071f4 none null local
4、删除容器网络
使用 docker network rm
删除容器网络。
# 语法
[root@localhost ~]# docker network rm --help
Usage: docker network rm NETWORK [NETWORK...]
Remove one or more networks
Aliases:
rm, remove
# 案例
[root@localhost ~]# docker network rm c3f9bb71d92e
c3f9bb71d92e
[root@localhost ~]# docker network rm hqs_net01 hqs_net02
hqs_net01
hqs_net02
5、连接容器网络
使用 docker network connect
连接容器网络。
# 语法
[root@localhost ~]# docker network connect --help
Usage: docker network connect [OPTIONS] NETWORK CONTAINER
Connect a container to a network
Options:
--alias strings Add network-scoped # 添加容器网络域关联
alias for the container
--driver-opt strings driver options for # 网络驱动选项
the network
--ip string IPv4 address (e.g., # IPV4地址
172.30.100.104)
--ip6 string IPv6 address (e.g., # IPV6地址
2001:db8::33)
--link list Add link to another # 添加到另一个容器的链接
container
--link-local-ip strings Add a link-local # 添加容器本地链接地址
address for the
container
# 案例
[root@localhost ~]# docker network connect bridge alpine4
三、实验案例
1、容器连接默认的桥接网络
创建或启动容器时不指定网络,则该容器会被连接到默认桥接网络。
本实验,测试同一Docker主机两个容器间的通信。
# 1.查看当前已有的网络
[root@localhost ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
989e0968a6fe bridge bridge local 《————默认桥接网络
76d9e06a3606 host host local
9f44873071f4 none null local
# 2.启动两个运行ash的alpine容器
# alpine镜像下载
[root@localhost ~]# docker pull alpine
Using default tag: latest
latest: Pulling from library/alpine
df9b9388f04a: Pull complete
Digest: sha256:4edbd2beb5f78b1014028f4fbb99f3237d9561100b6881aabbf5acce2c4f9454
Status: Downloaded newer image for alpine:latest
docker.io/library/alpine:latest
# 启动容器
[root@localhost ~]# docker run -dti --name alpine1 alpine ash
f5116b60027b745495ad090de3e47e18b7b4ef27dd9529c0fc658f66febac044
[root@localhost ~]# docker run -dti --name alpine2 alpine ash
aa54330101c2bf5b09909ebbfbad18e316e6649305176e57db07842f3735e03f
# 3.检查容器是否启动
[root@localhost ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
55f9241be324 alpine "ash" 4 seconds ago Up 3 seconds alpine2
a654ec01f3eb alpine "ash" 7 seconds ago Up 6 seconds alpine1
# 4.查看bridge网络的详细信息
[root@localhost ~]# docker network inspect bridge
[
{
"Name": "bridge",
"Id": "989e0968a6fe9b95dd76238e4707f40bc0cf288fd6b764acbd537954fd0e6fcf",
"Created": "2022-04-19T21:56:32.999550849+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.17.0.0/16",
"Gateway": "172.17.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"55f9241be324d8a2aba1c96c18fd762aa8c5940ff395a01ead8cb9824719b032": {
"Name": "alpine2",
"EndpointID": "c1b5d5ea6e1446b8f934e54a3461197f544eb13ef7f1e7350f6afe8836b7b3a7",
"MacAddress": "02:42:ac:11:00:03",
"IPv4Address": "172.17.0.3/16", 《————第二个容器
"IPv6Address": ""
},
"a654ec01f3eb117fbc4f4248b5e08aceb7ad37efbe7a228d998ac86a13b5a17a": {
"Name": "alpine1",
"EndpointID": "874a9857659ff2ea7d0004826b34058923806cae592951276c78a4f2bb72a391",
"MacAddress": "02:42:ac:11:00:02",
"IPv4Address": "172.17.0.2/16", 《————第一个容器
"IPv6Address": ""
}
},
"Options": {
"com.docker.network.bridge.default_bridge": "true",
"com.docker.network.bridge.enable_icc": "true",
"com.docker.network.bridge.enable_ip_masquerade": "true",
"com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
"com.docker.network.bridge.name": "docker0",
"com.docker.network.driver.mtu": "1500"
},
"Labels": {}
}
]
...
# 5.docker attach命令连接到alpine1容器
[root@localhost ~]# docker attach alpine1 《————连接到alpine容器
/ # ip addr show 《———— ‘#’说明当前容器用户以root登录
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo 《————回环网络
valid_lft forever preferred_lft forever
19: eth0@if20: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0 《————地址为172.17.0.2
valid_lft forever preferred_lft forever
/ #
# 6.在alpine1容器ping外网
/ # ping -c 2 www.163.com
PING www.163.com (221.233.240.107): 56 data bytes
64 bytes from 221.233.240.107: seq=0 ttl=127 time=4.929 ms
64 bytes from 221.233.240.107: seq=1 ttl=127 time=5.103 ms
--- www.163.com ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 4.929/5.016/5.103 ms
# 7.在alpine1容器ping第二个容器
# (1)ping地址可以通
/ # ping -c 2 172.17.0.3
PING 172.17.0.3 (172.17.0.3): 56 data bytes
64 bytes from 172.17.0.3: seq=0 ttl=64 time=0.065 ms
64 bytes from 172.17.0.3: seq=1 ttl=64 time=0.057 ms
--- 172.17.0.3 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.057/0.061/0.065 ms
# (2)ping名称不能通
/ # ping -c 2 alpine2
ping: bad address 'alpine2'
# 8.离开但不停止容器————输入 Ctrl+P 和 Ctrl+Q
/ # read escape sequence
[root@localhost ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
55f9241be324 alpine "ash" 9 minutes ago Up 9 minutes alpine2
a654ec01f3eb alpine "ash" 9 minutes ago Up 9 minutes alpine1
# 9.停止并删除容器
[root@localhost ~]# docker stop alpine1 alpine2
alpine1
alpine2
[root@localhost ~]# docker rm alpine1 alpine2
alpine1
alpine2
2、使用传统的容器连接
默认桥接网络的容器之间只能通过IP地址通信。
如要通过名称通信,可使用传统的 --link
选项添加到容器的连接。
本实验,在两个容器间建立连接,然后通过连接名称或别名访问源容器。
# 1.创建两个连接默认桥接网络的容器
[root@localhost ~]# docker run -dti --name alpine1 alpine ash
667caa0d0690c95de34e41bfebaf983e897f3a7e028fda892fca884f1f94dc49
# 创建容器时,添加到第一个容器的连接,并配置别名alp
[root@localhost ~]# docker run -dti --name alpine2 --link alpine1:alp alpine ash
21c0ddd94cb4d7190701d003868caef7cd9f82aded0ae0c5a2e9e68ebb45a03f
# 2.进入alpine2容器测试网络连通性
[root@localhost ~]# docker attach alpine2
/ # ping -c 2 alpine1
PING alpine1 (172.17.0.2): 56 data bytes
64 bytes from 172.17.0.2: seq=0 ttl=64 time=0.048 ms
64 bytes from 172.17.0.2: seq=1 ttl=64 time=0.056 ms
--- alpine1 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.048/0.052/0.056 ms
# 测试后,再通过ctrl+P+Q退出容器。
# 3.进入alpine1容器测试网络连通性
[root@localhost ~]# docker attach alpine1
/ # ping -c 2 alpine2
ping: bad address 'alpine2'
# 失败原因:--link只添加了单向通信,接受容器可以通过名称访问源容器,源容器不能通过名称访问接收容器。
# 测试后,再通过ctrl+P+Q退出容器。
# 4.删除容器恢复环境
[root@localhost ~]# docker rm -f $(docker ps -aq)
21c0ddd94cb4
667caa0d0690
3、创建用户自定义桥接网络并连接容器
实验:示范如何将容器连接到用户自定义桥接网络,并验证容器间的连通性。
在自定义桥接网络中,容器不仅能通过IP地址进行通信,还能将容器名称解析到IP地址,此功能称为自动服务发现(Automatic Service Discovery)。
# 1.创建用户自定义桥接网络
[root@localhost ~]# docker network create --driver bridge alpine-net
9e8a13047a172f4d3c8cdb10d16291f6889f40c4ea7e790e49e11dd6e6e7d00c
# 2.查看当前docker主机的网络
[root@localhost ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
9e8a13047a17 alpine-net bridge local
# 查看新网络的详细信息
[root@localhost ~]# docker network inspect alpine-net
[
{
"Name": "alpine-net",
"Id": "9e8a13047a172f4d3c8cdb10d16291f6889f40c4ea7e790e49e11dd6e6e7d00c",
"Created": "2022-04-20T16:59:22.055790409+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.18.0.0/16", 《————子网
"Gateway": "172.18.0.1" 《————网关
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {},
"Options": {},
"Labels": {}
}
]
# 3.创建四个容器
# (1)2个只连接用户自定义桥接网络
[root@localhost ~]# docker run -dti --name alpine1 --network alpine-net alpine ash
22af8e376fef34108cf2058810b7edd05c148314354341c067ff45d6e7437f46
[root@localhost ~]# docker run -dti --name alpine2 --network alpine-net alpine ash
26f211048d9d857029ee9e038918c38b9ae19f698e1806adbc78ed0aca6f1aff
# (2)1个只连接默认桥接网络
[root@localhost ~]# docker run -dti --name alpine3 alpine ash
1078e18ff3a2d84b2ea97a612fd577ef142e5a954753169e95c4d9b2bd337c41
# (3)1个同时连接默认桥接网络和用户自定义网络
# docker run 命令执行只能连接一个网络
[root@localhost ~]# docker run -dti --name alpine4 --network alpine-net alpine ash
4b63cd8a7a1efcbeabcdfd12fdb8881d35ffe72e66ddec15fdf64825dcb1cf6a
# connet命令连接第二个网络
[root@localhost ~]# docker network connect bridge alpine4
# (4)查看当前正在运行的容器
[root@localhost ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4b63cd8a7a1e alpine "ash" 2 minutes ago Up 2 minutes alpine4
1078e18ff3a2 alpine "ash" 3 minutes ago Up 3 minutes alpine3
26f211048d9d alpine "ash" 4 minutes ago Up 4 minutes alpine2
22af8e376fef alpine "ash" 4 minutes ago Up 4 minutes alpine1
# 4.查看bridge网络和alpine-net网络信息
# 查看bridge网络
[root@localhost ~]# docker network inspect bridge
[
{
"Name": "bridge",
"Id": "aefaa402b60d4b6f7f2515575ba2720f98eb8fd0c1ead0a8572442c098e52ed5",
"Created": "2022-04-20T15:52:23.173167512+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.17.0.0/16",
"Gateway": "172.17.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"1078e18ff3a2d84b2ea97a612fd577ef142e5a954753169e95c4d9b2bd337c41": {
"Name": "alpine3",
"EndpointID": "1e19495d48d8e83cff4add4d8ad1570b24068fec17824cf4f499c56010831e52",
"MacAddress": "02:42:ac:11:00:02",
"IPv4Address": "172.17.0.2/16",
"IPv6Address": ""
},
"4b63cd8a7a1efcbeabcdfd12fdb8881d35ffe72e66ddec15fdf64825dcb1cf6a": {
"Name": "alpine4",
"EndpointID": "d2cb2c81fe71d54c3fc39782755f0b0c86ce415c67205c159bf16fe3ebd76c24",
"MacAddress": "02:42:ac:11:00:03",
"IPv4Address": "172.17.0.3/16",
"IPv6Address": ""
}
},
"Options": {
"com.docker.network.bridge.default_bridge": "true",
"com.docker.network.bridge.enable_icc": "true",
"com.docker.network.bridge.enable_ip_masquerade": "true",
"com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
"com.docker.network.bridge.name": "docker0",
"com.docker.network.driver.mtu": "1500"
},
"Labels": {}
}
]
# 查看alpine-net网络
[root@localhost ~]# docker network inspect alpine-net
[
{
"Name": "alpine-net",
"Id": "9e8a13047a172f4d3c8cdb10d16291f6889f40c4ea7e790e49e11dd6e6e7d00c",
"Created": "2022-04-20T16:59:22.055790409+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.18.0.0/16",
"Gateway": "172.18.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"22af8e376fef34108cf2058810b7edd05c148314354341c067ff45d6e7437f46": {
"Name": "alpine1",
"EndpointID": "b2f1d6d2ebf8cce71dddf0b23fb941d17a4d3e0c757bf33604d6d57f8b801665",
"MacAddress": "02:42:ac:12:00:02",
"IPv4Address": "172.18.0.2/16",
"IPv6Address": ""
},
"26f211048d9d857029ee9e038918c38b9ae19f698e1806adbc78ed0aca6f1aff": {
"Name": "alpine2",
"EndpointID": "01a8b99524d4db9a820ffede79fbcc0b95ccc1c00ab2d87a9a236c9f67c04dec",
"MacAddress": "02:42:ac:12:00:03",
"IPv4Address": "172.18.0.3/16",
"IPv6Address": ""
},
"4b63cd8a7a1efcbeabcdfd12fdb8881d35ffe72e66ddec15fdf64825dcb1cf6a": {
"Name": "alpine4",
"EndpointID": "c79921b791e45fc22dc8b3b3df06ffe45ffe97f8999a724c51bd8c53d358ebb0",
"MacAddress": "02:42:ac:12:00:04",
"IPv4Address": "172.18.0.4/16",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {}
}
]
# 5、alpine1网络测试
[root@localhost ~]# docker attach alpine1
/ # ping -c 2 alpine2
PING alpine2 (172.18.0.3): 56 data bytes
64 bytes from 172.18.0.3: seq=0 ttl=64 time=0.063 ms 《————用户自定义网络,支持自动服务发现
64 bytes from 172.18.0.3: seq=1 ttl=64 time=0.054 ms
--- alpine2 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.054/0.058/0.063 ms
/ # ping -c 2 alpine4
PING alpine4 (172.18.0.4): 56 data bytes
64 bytes from 172.18.0.4: seq=0 ttl=64 time=0.060 ms 《————用户自定义网络,支持自动服务发现
64 bytes from 172.18.0.4: seq=1 ttl=64 time=0.055 ms
--- alpine4 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.055/0.057/0.060 ms
/ # ping -c 2 alpine3
ping: bad address 'alpine3' 《————alpine3不在用户自定义网络alpine-net中
/ # ping -c 2 172.17.0.2 《————不在一个网络中,名称和IP都不能ping通
PING 172.17.0.2 (172.17.0.2): 56 data bytes
--- 172.17.0.2 ping statistics ---
2 packets transmitted, 0 packets received, 100% packet loss
/ # ping www.baidu.com
PING www.baidu.com (103.235.46.39): 56 data bytes
64 bytes from 103.235.46.39: seq=0 ttl=127 time=248.724 ms
64 bytes from 103.235.46.39: seq=2 ttl=127 time=259.902 m
# 测试后,再通过ctrl+P+Q退出容器alpine1。
# 6.alpine4网络测试
# alpine4既连接了默认桥接网络,也连接了alpine-net网络,可以访问所有的容器。
[root@localhost ~]# docker attach alpine4
/ # ping -c 2 alpine1
PING alpine1 (172.18.0.2): 56 data bytes
64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.046 ms 《————用户自定义网络,支持自动服务发现
64 bytes from 172.18.0.2: seq=1 ttl=64 time=0.052 ms
--- alpine1 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.046/0.049/0.052 ms
/ # ping -c 2 alpine2
PING alpine2 (172.18.0.3): 56 data bytes
64 bytes from 172.18.0.3: seq=0 ttl=64 time=0.058 ms 《————用户自定义网络,支持自动服务发现
64 bytes from 172.18.0.3: seq=1 ttl=64 time=0.053 ms
--- alpine2 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.053/0.055/0.058 ms
/ # ping -c 2 alpine3 《————alpine3为默认桥接网络,不支持自动服务发现
ping: bad address 'alpine3'
/ # ping -c 2 172.17.0.2
PING 172.17.0.2 (172.17.0.2): 56 data bytes
64 bytes from 172.17.0.2: seq=0 ttl=64 time=0.065 ms 《————alpine3为默认桥接网络,支持地址ping通
64 bytes from 172.17.0.2: seq=1 ttl=64 time=0.060 ms
--- 172.17.0.2 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.060/0.062/0.065 ms
/ # ping -c 2 www.baidu.com
PING www.baidu.com (14.215.177.38): 56 data bytes
64 bytes from 14.215.177.38: seq=0 ttl=127 time=29.962 ms 《————能访问外网
64 bytes from 14.215.177.38: seq=1 ttl=127 time=30.115 ms
--- www.baidu.com ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 29.962/30.038/30.115 ms
7.清理环境
[root@localhost ~]# docker rm -f $(docker ps -qa)
4b63cd8a7a1e
1078e18ff3a2
26f211048d9d
22af8e376fef
[root@localhost ~]# docker network rm alpine-net
alpine-net
本文来自博客园,作者:Cloudservice,转载请注明原文链接:https://www.cnblogs.com/whwh/p/16171454.html,只要学不死,就往死里学!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!