前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Docker入门——网络相关

Docker入门——网络相关

原创
作者头像
cocoon_yh
发布2019-09-09 21:04:40
1.4K0
发布2019-09-09 21:04:40
举报
文章被收录于专栏:小哈维的学习日记

背景

某次在试图从容器内访问到本地的数据库时,发现在本机上并没有 docker0 这个网桥。学习了一波 Docker 网络相关的知识后作出了以下总结。

相信下面这张图大家没少看过,Docker 相较于 VM 更加轻量,不需要虚拟出自己的操作系统层,具有更多的优点。

docker.png
docker.png

上图来源于网络,后文中也将对来源于网络的图做出说明

然而笔者想当然的就以为 Docker 在所有系统下都是这种架构。查阅相关资料后发现,Dockerlinux 环境下确实是这样运行的,但是在 WindowsmacOS 系统下,Docker 会先启一个 VM ,然后在该 VM 上运行 Docker 环境。新版的 Docker for Mac 已经不再使用 VirtualBox 提供虚拟机环境,而是使用更轻量的 HyperKit ,设计上更为巧妙,本文就不展开讲这个点了。下图是 Docker for Mac 的架构图。

-1.png
-1.png

因此 docker0 网桥是在虚拟机内,而不是在宿主机内。可想而知,运行在虚拟机中与运行在本机上肯定是有区别的,因此 Docker for Mac 实际上自带了 k8s 环境,并且通过端口转发到宿主机,以屏蔽用户对 VM 的感知。然而要实现容器到宿主机的通信,还需要进行另一番调研。

下面先介绍一下 Docker 支持的几种网络模式。

Docker 支持的网络模式

Network

Description

none

no networking in the container

bridge

Connect the container to the bridge via veth interfaces

host

Use the host's network stack inside the container

Container:<name\id>

Use the network stack of another container, specified via its name or id

Network

Connects the container to a user created network(using docker network create command)

NETWORK: NONE

这种网络模式下无法联网,仅有一个 loopback 回环网络,没有其他网卡。这种类型的网络无法联网,但可以通过 link 容器来实现容器间通信,这种网络模式可以很好的保证容器的安全性。同时,这种网络模式下用户自己自行创建网络,可以实现更为灵活复杂的网络。

0.png
0.png

NETWORK: BRIDGE

这是容器默认的网络模式。桥接模式会为 Docker Container 创建独立的网络堆栈,保证容器内的进程组使用独立的网络环境,从而实现容器间、容器与宿主机之间的网络栈隔离。同时,桥接模式可以通过宿主机上的 docker0 网桥来实现宿主机与容器之间的网络通信。

桥接模式会在主机上创建两个虚拟网络接口设备,一个附加在宿主机上的 docker0 网桥内,并命名为 veth0 ,另一个附加在 Docker Container 所属的 namespace 的下,并命名为 eth0

1.png
1.png

通过 veth pair 技术,其特性可以保证无论哪一个 veth 接收到网络报文,都会将报文传输给另一方,这就实现了从容器到宿主机的网络连通性。然而上面也提到了 Docker 需要运行在 linux 环境下,所以我们无法在主机上看到 docker0 网桥,这个网桥位于虚拟机中。

NETWORK: HOST

这种网络模式下容器将跟主机共享网络堆栈,因此容器可以直接使用宿主机的 eh0 实现与外界的通信,并且主机所有的接口都可以被容器访问及使用。

host.png
host.png

在这种模式下,容器将获取更高的网络性能,因为它使用主机的网络栈,不需要通过 Docker 守护进程进行一层虚拟化。但是,这种模式也会导致容器网络环境隔离性弱化,即容器不再拥有隔离的、独立的网络栈。容器会与宿主机竞争网络栈的使用,同时容器也不再拥有所有的端口资源,因为部分端口已经被宿主机本身的服务占用,还有部分端口用于桥接模式容器的端口映射。

NETWORK: CONTAINER

这种网络模式下,容器将和另一个容器共享网络堆栈,因此,同样需要注意端口冲突等问题。该模式下,两个容器与其他容器以及宿主机之间存在网络隔离。

container.png
container.png

USER-DEFINED NETWORK

开发者可以使用 Docker 网络驱动程序或者外部的网络驱动程序来创建网络,也可以把多个容器连接到同一个网络下。在这里不展开介绍。

解决方法

方案一(仅在 macOS/Windows 下可用)

Docker 本身针对这种场景已经提出了一些解决方案。从版本 18.03 开始,Docker for Mac 提供一个特殊的 DNS name 以便用户从容器内访问到本机, 这个 DNS name 被解析至主机在 docker 内使用的内部 IP 。同时其他版本的 Docker Desktop for Mac 也有对应的 host ,如下所示:

  • Docker for Mac v 18.03 and above
  • host.docker.internal
  • Docker for Mac v 17.12 to v 18.02
  • docker.for.mac.host.internal
  • Docker for Mac v 17.06 to v 17.11
  • docker.for.mac.localhost
  • Docker for Mac 17.05 and below
  • Docker 不提供,需要自己进行配置,具体配置方法可以参考 Janne Annala 的回答
代码语言:txt
复制
https://stackoverflow.com/questions/24319662/from-inside-of-a-docker-container-how-do-i-connect-to-the-localhost-of-the-mach
1的副本.png
1的副本.png

如上图所示,可以通过 host.docker.internal 直接访问到宿主机内的服务

方案二(仅在 Linux 下可用)

使用桥接模式。

可以看到宿主机下已经有了 docker0 网桥,且地址为 172.17.0.1

2.png
2.png

进入容器并查看容器内的网络接口信息和路由表可以发现 eth0 和宿主机中 docker0 的网段相同,且已经将 docker0 的接口地址设置为了默认网关,即匹配到的请求将通过 172.17.0.1 转发到目标地址。

3.png
3.png

那么通过该地址即可实现从容器到宿主机的访问。

4.png
4.png

方案三(仅在 Linux 下可用)

使用 host 模式启动服务就可以直接访问本机上的服务。利弊如上所述,若在生产环境使用该模式还需要自己再多做相关调研。

5.png
5.png

延展阅读

读到这的读者可能就会想,那容器内部需要访问外界,或者外界需要访问服务内部该咋办?好像刚刚提到的知识点不足以支撑这一流程呀?docker0 网段和宿主机的网段不同,外界无法得知容器 IP 更无法直接访问到容器内部。

6.png
6.png

这里就要引入另一个概念 NAT(Network Address Translation) ,是一种用于重写源IP地址或目的IP地址的技术。

外界访问容器内部

前提条件:容器运行时通过 -P 或 -p 指令主动暴露端口并将端口映射至主机上

to_docker.png
to_docker.png
  1. 外界直接请求 host_ip:port_0
  2. 通过 DNAT 将请求的目的地址修改为 container_ip:port_1
  3. 宿主机将请求转发给 veth pair
  4. veth pair 将请求通过 veth 转发至容器内部的 eth0
  5. 回包时也通过 docker0 转发至宿主机的 eth0 发送回包

通过检测数据包可以看到请求的目的 IP 被修改为了对应的容器 IP ,以完成外界对容器内部的访问

7.png
7.png
8.png
8.png

容器内部访问外界

from_docker.png
from_docker.png
  1. 容器内发出请求,此时会随机选取一个未被占用的端口 port0 作为源端口
  2. 请求通过 eth0 转发至 docker0 网桥处的 veth
  3. docker0 网球将请求转发至宿主机的 eth0
  4. 宿主机处理请求时通过 SNAT 将请求源地址修改为 host_ip:port_1 并转发出去
  5. 外界回包时发送至宿主机的 eth0
  6. 按照 iptables 规则,宿主机将请求转发至容器内部

查看 iptables 规则可知,从 172.17.0.0 网段出去访问外网的请求都会交由 MASQUERADE 处理。而 MASQUERADE 的处理就是将请求的源 ip 替换成宿主机的 ip 并发出去,也就是做了一次 NAT 处理。

9.png
9.png
通过检测数据包可知,请求的源 ip 确实被从 docker0 网段的容器 ip:172.17.0.2 修改为 eth1:10.12.91.17
10.png
10.png
11.png
11.png

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景
  • Docker 支持的网络模式
  • 解决方法
  • 延展阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档