1、暴露端口

1、端口映射

[root@Anolis2 ~]# docker run -itd --name nginx -p 80:80 nginx
00b09448f3c37116729786ed68eccca65f016f67a531a41aecc975fe1b9794f9
[root@Anolis2 ~]# docker ps -a
CONTAINER ID   IMAGE     COMMAND                   CREATED         STATUS         PORTS                               NAMES
00b09448f3c3   nginx     "/docker-entrypoint.…"   4 seconds ago   Up 3 seconds   0.0.0.0:80->80/tcp, :::80->80/tcp   nginx

2、随机端口映射

[root@Anolis2 ~]# docker run -itd --name nginxv1.0 -P nginx
b48caf2dc38aa4206f4167d26e36e9b041fabe137f3343125575c12e4bb3bfed
[root@Anolis2 ~]# docker ps -a
CONTAINER ID   IMAGE     COMMAND                   CREATED              STATUS              PORTS                                     NAMES
b48caf2dc38a   nginx     "/docker-entrypoint.…"   4 seconds ago        Up 3 seconds        0.0.0.0:32768->80/tcp, :::32768->80/tcp   nginxv1.0
00b09448f3c3   nginx     "/docker-entrypoint.…"   About a minute ago   Up About a minute   0.0.0.0:80->80/tcp, :::80->80/tcp         nginx

3、多端口映射

[root@Anolis2 ~]# docker run -itd --name nginxV3.0 -v /usr/share/nginx/nginx.conf:/etc/nginx/nginx.conf -p 80:80 -p 443:443 nginx
ba5a9a17c591ab3b0fa353081051d4717f6e255f582130cddf5c49821ece0910
[root@Anolis2 ~]# docker ps -a
CONTAINER ID   IMAGE     COMMAND                   CREATED         STATUS         PORTS                                                                      NAMES
ba5a9a17c591   nginx     "/docker-entrypoint.…"   3 seconds ago   Up 2 seconds   0.0.0.0:80->80/tcp, :::80->80/tcp, 0.0.0.0:443->443/tcp, :::443->443/tcp   nginxV3.0

[root@Anolis2 ~]# netstat -anplt | grep docker
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      24719/docker-proxy
tcp        0      0 0.0.0.0:443             0.0.0.0:*               LISTEN      24701/docker-proxy
tcp6       0      0 :::80                   :::*                    LISTEN      24725/docker-proxy
tcp6       0      0 :::443                  :::*                    LISTEN      24706/docker-proxy

Container 创建时通过-P 或 -p暴露端口

2、容器互联

        在Docker中,实现容器互联(也就是容器间通信)主要有三种核心方式。其中,使用自定义网络是现代应用的标准实践,而--link是历史遗留的产物,在大多数场景下已被前者取代。

(1)link:

        容器互联linking是除了端口映射外另一种可以与容器中应用进行交互的方式,它会在源和接收容器之间创建隧道,接收容器可以看到源容器的信息

创建mysql容器

[root@Anolis2 usr]# docker run -itd --name mysql -v /data1/mysql:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456  -p 3306:3306 mysql
cbc263c9fe082506a3279f85d878df5ab4a2b2cf26284da229fd30e76cc268bd
[root@Anolis2 usr]# docker exec -it mysql env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=cbc263c9fe08
TERM=xterm
MYSQL_ROOT_PASSWORD=123456
GOSU_VERSION=1.19
MYSQL_MAJOR=innovation
MYSQL_VERSION=9.6.0-1.el9
MYSQL_SHELL_VERSION=9.6.0-1.el9
HOME=/root
 

创建接收容器

[root@Anolis2 usr]#  docker run -d -p 80:80 -p 443:443 --name web2 --link mysql:mysql nginx
1725a4c59783b89eb3991b17ce2f0dd4667acdc41afad815d10957c2cc3cb6af


[root@Anolis2 usr]# docker exec -it web2 /bin/bash
root@1725a4c59783:/# env
MYSQL_PORT_33060_TCP_ADDR=172.17.0.3
MYSQL_PORT=tcp://172.17.0.3:3306
MYSQL_PORT_3306_TCP_ADDR=172.17.0.3
MYSQL_NAME=/web2/mysql
MYSQL_ENV_MYSQL_ROOT_PASSWORD=123456
MYSQL_PORT_3306_TCP_PORT=3306
HOSTNAME=1725a4c59783
MYSQL_ENV_MYSQL_MAJOR=innovation
MYSQL_PORT_3306_TCP=tcp://172.17.0.3:3306
PWD=/
ACME_VERSION=0.3.1
PKG_RELEASE=1~trixie
HOME=/root
MYSQL_ENV_GOSU_VERSION=1.19
MYSQL_PORT_33060_TCP_PROTO=tcp
DYNPKG_RELEASE=1~trixie
MYSQL_ENV_MYSQL_SHELL_VERSION=9.6.0-1.el9
NJS_VERSION=0.9.6
TERM=xterm
MYSQL_PORT_33060_TCP_PORT=33060
MYSQL_PORT_3306_TCP_PROTO=tcp
SHLVL=1
MYSQL_PORT_33060_TCP=tcp://172.17.0.3:33060
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
NGINX_VERSION=1.29.6
NJS_RELEASE=1~trixie
MYSQL_ENV_MYSQL_VERSION=9.6.0-1.el9
_=/usr/bin/env


root@1725a4c59783:/# cat /etc/hosts
127.0.0.1       localhost
::1     localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.3      mysql 53b3b7414f16
172.17.0.2      1725a4c59783
 

解决的就是容器ip地址会发生变化,导致不能访问,容器互联可以通过环境变量访问容器地址,环境变量会随着ip地址的变化而更改

(2)自定义网络

        这是目前实现同一主机内容器互联的首选和最佳实践。它最大的优势在于提供了自动服务发现功能。

工作原理:

        当你创建一个自定义网络(通常是bridge驱动)并将多个容器连接到该网络后,Docker会为这个网络启动一个内置的DNS服务器。这个DNS服务器会记录网络中所有容器的名称和IP地址。

如何互联:

        连接到该网络的任何容器,都可以直接用其他容器的名称(--name指定的名字)进行通信,而无需关心它们的IP地址是否会变化。这让容器间的依赖关系从“硬编码IP”变成了“服务名”,大大增强了应用的弹性和可维护性。

示例:

创建一个my-net自定义网络

[root@Anolis2 usr]# docker network create my-net
fece4dc44e5b50ef0d10f4262c4c827a65eeda57789eb2dba4655168e19bd87c

加入自定义网络
[root@Anolis2 usr]# docker run -d --name my-db --network my-net mysql
6b612b9c836549c9eeed4f247ca1e519d877e43693e384896728c15a2344e482

再创建一个容器,同样加入网络
[root@Anolis2 usr]# docker run -d --name my-nginx --network my-net nginx
17c02c1c0dd3a100875726a8f8a99c3164f54ce891e1bc492a24ff40be7ca6ba

登录mysql访问nginx,也可以用ping命令直接测试

[root@Anolis2 usr]# docker exec -it my-db bash
bash-5.1# curl my-nginx
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, nginx is successfully installed and working.
Further configuration is required for the web server, reverse proxy,
API gateway, load balancer, content cache, or other features.</p>

<p>For online documentation and support please refer to
<a href="https://nginx.org/">nginx.org</a>.<br/>
To engage with the community please visit
<a href="https://community.nginx.org/">community.nginx.org</a>.<br/>
For enterprise grade support, professional services, additional
security features and capabilities please refer to
<a href="https://f5.com/nginx">f5.com/nginx</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

(3)container模式

        这种模式让一个新创建的容器共享一个已存在容器的网络命名空间。这意味着它们拥有完全相同的网络配置(IP、端口、主机名等)。

工作原理:

        当你使用 --network container:<另一个容器名> 启动容器时,新容器不会创建自己的网卡和IP,而是直接使用指定容器的网络栈

如何互联:

        这两个容器之间可以通过 localhost 进行通信,就像两个进程在同一台机器上一样。这是因为它们共享了同一个网络环回接口。

典型应用场景:边车模式

        例如,一个Web服务容器和一个日志收集容器。让日志收集容器通过container模式共享Web容器的网络,它就可以直接通过localhost抓取Web服务的访问日志,无需配置复杂的网络发现

先启动一个容器

[root@Anolis2 usr]# docker run -d --name nginx nginx
9f91abd7eec2b4d941186e040946ce62225ebaf909f884c1426d407116b429ec

再启动一个辅助容器,共享nginx的网络,在这个辅助容器里,可以通过curl localhost访问nginxz

[root@Anolis2 usr]# docker run -it --rm --network container:nginx curlimages/curl curl http://localhost
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, nginx is successfully installed and working.
Further configuration is required for the web server, reverse proxy,
API gateway, load balancer, content cache, or other features.</p>

<p>For online documentation and support please refer to
<a href="https://nginx.org/">nginx.org</a>.<br/>
To engage with the community please visit
<a href="https://community.nginx.org/">community.nginx.org</a>.<br/>
For enterprise grade support, professional services, additional
security features and capabilities please refer to
<a href="https://f5.com/nginx">f5.com/nginx</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

重要限制:

        如果主容器停止了,共享其网络的辅助容器也会失去网络连接,甚至可能无法正常工作

3、Overlay网络

        这是Docker容器网络中最具挑战性但也最强大的部分,它是构建跨主机容器集群(如Docker Swarm, Kubernetes)的基石。可以把它理解成:在物理网络(Underlay)之上,再搭建一层虚拟的逻辑网络。 对于容器来说,它感知不到底层物理网络的存在,感觉自己就像连在一个巨大的本地交换机上。

为什么需要Overlay网络?

在单机环境下,bridge网络已经足够好。但在多主机环境下,会遇到一个核心难题:

  • 问题:主机A上的容器(IP: 172.17.0.2)想要访问主机B上的容器(IP: 172.18.0.3)。由于这两个IP属于不同的私有网段,并且分布在不同的物理机器上,宿主机的物理网络并不知道如何路由这些包,直接发送会被丢弃。

  • Overlay网络的解决方案:它将原始的容器数据包“打包”进一个能在宿主机网络(Underlay)中传输的新数据包里。就像寄快递时,把内部物品(容器数据包)先用盒子装好(封装),然后在外面贴上寄件人和收件人的物理地址(宿主机IP),就可以在现实的物流网络(物理网络)中运输了。

核心工作原理深度解析

Overlay网络的核心思想就是封装解封装。下面我们通过一个例子来逐步拆解:

场景:主机A上的容器C1(IP: 10.0.0.2)想向主机B上的容器C2(IP: 10.0.0.3)发送一个HTTP请求。它们都在同一个名为mynet的Overlay网络中。

1. 发送端 (主机A)
  1. 容器内出发:C1发起请求,数据包的源IP是10.0.0.2,目标IP是10.0.0.3。C1查看路由表,发现目标IP在另一个网段,于是将这个包发送到自己的网关(通常是连接C1的虚拟网桥)。

  2. 内核识别:数据包到达主机A的内核。内核意识到这个包的目标IP 10.0.0.3 属于一个Overlay网络。内核不会将其发送到物理网卡,而是交给处理Overlay网络的模块。

  3. 封装 (Encapsulation):这是最关键的一步。Overlay网络驱动(如VXLAN)会创建一个新的数据包,这个新数据包的结构如下:

    • 外部IP头部源IP = 主机A的物理IP(例如 192.168.1.10),目标IP = 主机B的物理IP(例如 192.168.1.20)。这个头部的作用是让数据包能在物理网络上传输。

    • 外部UDP头部:目标端口通常是VXLAN专用端口 4789

    • 原始数据包:将C1发出的整个原始二层数据帧(包含MAC地址、源IP 10.0.0.2、目标IP 10.0.0.3等)作为负载,放在新数据包的内部。

  4. 发送:封装好的新UDP数据包通过主机A的物理网卡,被发送到物理网络中。

2. 传输 (Underlay网络)

这个封装后的数据包在物理网络(Underlay)中就像一个普通的UDP包,路由器、交换机等设备只关心它的外部IP头部(192.168.1.10 -> 192.168.1.20),并将其路由到主机B。它对内部的容器数据完全无感知。

3. 接收端 (主机B)
  1. 接收:主机B的物理网卡收到这个UDP数据包。

  2. 内核识别:内核检查发现目标UDP端口是 4789(VXLAN端口),知道这是一个Overlay网络的包,于是将其交给Overlay网络驱动处理。

  3. 解封装 (Decapsulation):Overlay网络驱动剥去外部的IP和UDP头部,提取出内部的原始容器数据帧。

  4. 交付:驱动根据内部数据帧的目标IP 10.0.0.3,通过主机B上的虚拟网络组件,将数据包准确地发送给容器C2。

整个过程对容器C1和C2是完全透明的。它们以为自己直接通信,实际上数据包在中间被“包装”和“拆包”了一次。

关键技术:VXLAN

Overlay网络有很多实现技术,在Docker Swarm和Linux环境中,最主流的是 VXLAN (Virtual Extensible LAN,虚拟可扩展局域网)

  • 它是什么:VXLAN是一种将二层数据帧封装到三层UDP数据包中的网络虚拟化技术。

  • 主要优势

    • 扩展性:传统的VLAN ID只有12位,最多支持4096个网络,而VXLAN的网络标识符(VNI)有24位,可以支持超过1600万个相互隔离的虚拟网络,非常适合大规模多租户环境。

    • 跨三层网络:VXLAN基于三层IP网络传输,这意味着你的容器集群可以跨越不同的数据中心或机房,只要宿主机之间IP网络可达即可。

创建和使用

        在Docker环境中,要使用Overlay网络,首先需要将Docker Swarm模式初始化(即使你只使用一个节点,也需要开启Swarm模式)。

1.初始化Swarm模式 (在你想作为管理员的节点上执行)

[root@Anolis2 ~]# docker swarm init
Swarm initialized: current node (vlopxl9wal8z638o7vg2fn95f) is now a manager.

To add a worker to this swarm, run the following command:

    docker swarm join --token SWMTKN-1-03px4850lhu9bfmk46u09du7hq6zjekoq9x71sptpuxtyzbcvs-9sspscazjfsi2qcb08q9tzcjq 192.168.137.141:2377

To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.

2.创建Overlay网络,--driver overlay 指定使用overlay驱动,--attachable 允许非Swarm服务的独立容器也连接到这个网络(可选)

[root@Anolis2 ~]# docker network create -d overlay --attachable my-cluster-net
itmya1a88ajxtfiupmgeza9h2

3.其它节点加入,在其他主机上加入Swarm集群 (使用第一步init后给出的token),加入集群后,它们会自动拥有这个Overlay网络的信息

[root@Anolis ~]# docker swarm join --token SWMTKN-1-03px4850lhu9bfmk46u09du7hq6zjekoq9x71sptpuxtyzbcvs-9sspscazjfsi2qcb08q9tzcjq 192.168.137.141:2377
This node joined a swarm as a worker.

4.查看

[root@Anolis2 usr]# docker node ls
ID                            HOSTNAME   STATUS    AVAILABILITY   MANAGER STATUS   ENGINE VERSION
ul5sg7gzzxt2l3fehbplr6qto     Anolis     Ready     Active                          26.1.3
vlopxl9wal8z638o7vg2fn95f *   Anolis2    Ready     Active         Leader           26.1.3
 

5.在不同的主机上启动服务,并连接到这个网络,服务可以通过服务名互相通信,Docker内置的DNS负载均衡会自动处理

[root@Anolis2 usr]# docker service create --name my-web --network my-cluster-net --replicas 2 nginx

pq2190z3y92t9oeieu4bimtwm
overall progress: 2 out of 2 tasks
1/2: running   [==================================================>]
2/2: running   [==================================================>]
verify: Service pq2190z3y92t9oeieu4bimtwm converged

[root@Anolis2 ~]# docker service create --name db --network my-cluster-net --replicas 1 mysql

6.(如果创建时加了--attachable)你甚至可以运行独立容器并连接到这个跨主机网络

[root@Anolis2 ~]# docker run -it --rm --network my-cluster-net alpine ping my-web
PING my-web (10.0.1.2): 56 data bytes
64 bytes from 10.0.1.2: seq=0 ttl=64 time=1.213 ms

特性 Overlay网络 主机端口映射 (-p) 自定义Bridge网络
适用场景 跨主机容器集群 对外暴露服务,或临时连接 单主机内多容器通信
通信方式 通过服务名/容器名 通过宿主机IP + 映射端口 通过容器名
网络隔离 ⭐⭐⭐ 优秀 (VXLAN隧道) ⭐ 差 (依赖宿主机防火墙) ⭐⭐ 较好 (独立网段)
性能 ⭐⭐ 有封装/解封装开销 ⭐⭐⭐ 原生性能 (但需NAT) ⭐⭐⭐ 原生性能
复杂度 高 (需配置集群)

总结

  • Overlay网络是一种隧道技术,它在现有物理网络之上构建了一个逻辑网络,解决了跨主机容器通信的根本问题。

  • 核心技术是封装,VXLAN是目前最主流的实现方式。

  • 它是容器编排的基础,无论是Docker Swarm还是Kubernetes(通过CNI插件,如Calico、Flannel等),都依赖Overlay或类似的网络模型来构建统一的集群网络平面。

  • 选择建议:如果你是单机环境,用bridge就足够了;如果你需要管理一个集群,让不同机器上的容器能像在同一个内网里一样通信,那么Overlay网络是标准解决方案

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐