草庐IT

【Docker】关于Docker网络隔离与通信详解

踏雪无痕 2023-03-28 原文

一、Docker的网络概念

docker受一个github上的issue启发,引入了容器网络模型(container network model,CNM),容器网络模型主要包含了3个概念

  • network:网络,可以理解为一个Driver,是一个第三方网络栈,包含多种网络模式:单主机网络模式(none、host、bridge,joined container),多主机网络模式(overlay、macvlan、flannel)

  • sandbox:沙盒,它定义了容器内的虚拟网卡、DNS和路由表,是network namespace的一种实现,是容器的内部网络栈

  • endpoint:端点,用于连接sandbox和network

我们可以类比传统网络模型,将network比作交换机,sandbox比作网卡,endpoint比作接口和网线,另外,docker在创建容器时,先调用控制器创建sandbox对象,再调用容器运行时为容器创建network namespace

二、Docker的网络模式

这里我们先讨论docker的单主机网络模式,它包括以下4类:host、bridge、none、joined-containe

2.1、Host模式

docker不会为容器创建独有的network namespace;使用宿主机的默认网络命名空间,共享一个网络栈;表现为容器内和宿主机的IP一致;这种模式用于网络性能较高的场景,但安全隔离性相对差一些。

2.2、Bridge模式

桥接模式,有点类型VM-NAT,dockerd进程启动时会创建一个docker0网桥,容器内的数据通过这个网卡设备与宿主机进行数据传输。

虚拟网桥的工作方式和物理交换机类似,这样主机上的所有容器就通过交换机连在了一个二层网络中。从docker0子网中分配一个 IP 给容器使用,并设置 docker0 的 IP 地址为容器的默认网关。在主机上创建一对虚拟网卡veth pair设备,Docker 将 veth pair 设备的一端放在新创建的容器中,并命名为eth0(容器的网卡),另一端放在主机中,以vethxxx这样类似的名字命名,并将这个网络设备加入到 docker0 网桥中。bridge模式是 docker 的默认网络模式,不写–net参数,就是bridge模式。使用docker run -p时,docker 实际是在iptables做了DNAT规则,实现端口转发功能。可以使用iptables -vnL查看。

docker会为容器创建独有的network namespace,也会为这个命名空间配置好虚拟网卡,路由,DNS,IP地址与iptables规则(也就是sandbox的内容)。

2.3、None模式

none模式可以说是桥接模式的一种特例,docker会为容器创建独有的network namespace ,但不会为这个命名空间准备虚拟网卡,IP地址,路由等,需要用户自己配置。

2.4、joined-container 模式

容器共享模式,这种模式是host模式的一种延伸,一组容器共享一个network namespace;对外表现为他们有共同的IP地址,共享一个网络栈;kubernetes的pod就是使用的这一模式。

关于跨主机的docker网络通信,包含overlay、macvaln,又包含calico、flannel、weave等方案,不过跨主机的docker网络管理更多的是交给kubernetes或swarm等编排工具去实现了。

三、Docker网络对象和网络模式的关系

回顾docker网络对象和网络模式的关系其实就是下面这一张表格,每个容器在network namespace中的占比决定了采用哪种网络模式

四、Iptables的使用

4.1、iptables语法

[root@localhost ~]# iptables -h
iptables v1.4.21

Usage: iptables -[ACD] chain rule-specification [options]
       iptables -I chain [rulenum] rule-specification [options]
       iptables -R chain rulenum rule-specification [options]
       iptables -D chain rulenum [options]
       iptables -[LS] [chain [rulenum]] [options]
       iptables -[FZ] [chain] [options]
       iptables -[NX] chain
       iptables -E old-chain-name new-chain-name
       iptables -P chain target [options]
       iptables -h (print this help information)

Commands:
Either long or short options are allowed.
  --append  -A chain        Append to chain
  --check   -C chain        Check for the existence of a rule
  --delete  -D chain        Delete matching rule from chain
  --delete  -D chain rulenum
                Delete rule rulenum (1 = first) from chain
  --insert  -I chain [rulenum]
                Insert in chain as rulenum (default 1=first)
  --replace -R chain rulenum
                Replace rule rulenum (1 = first) in chain
  --list    -L [chain [rulenum]]
                List the rules in a chain or all chains
  --list-rules -S [chain [rulenum]]
                Print the rules in a chain or all chains
  --flush   -F [chain]      Delete all rules in  chain or all chains
  --zero    -Z [chain [rulenum]]
                Zero counters in chain or all chains
  --new     -N chain        Create a new user-defined chain
  --delete-chain
            -X [chain]      Delete a user-defined chain
  --policy  -P chain target
                Change policy on chain to target
  --rename-chain
            -E old-chain new-chain
                Change chain name, (moving any references)
Options:
    --ipv4  -4      Nothing (line is ignored by ip6tables-restore)
    --ipv6  -6      Error (line is ignored by iptables-restore)
[!] --protocol  -p proto    protocol: by number or name, eg. `tcp'
[!] --source    -s address[/mask][...]
                source specification
[!] --destination -d address[/mask][...]
                destination specification
[!] --in-interface -i input name[+]
                network interface name ([+] for wildcard)
 --jump -j target
                target for rule (may load target extension)
  --goto      -g chain
                              jump to chain with no return
  --match   -m match
                extended match (may load extension)
  --numeric -n      numeric output of addresses and ports
[!] --out-interface -o output name[+]
                network interface name ([+] for wildcard)
  --table   -t table    table to manipulate (default: `filter')
  --verbose -v      verbose mode
  --wait    -w [seconds]    maximum wait to acquire xtables lock before give up
  --wait-interval -W [usecs]    wait time to try to acquire xtables lock
                default is 1 second
  --line-numbers        print line numbers when listing
  --exact   -x      expand numbers (display exact values)
[!] --fragment  -f      match second or further fragments only
  --modprobe=<command>      try to insert modules using this command
  --set-counters PKTS BYTES set the counter during insert/append
[!] --version   -V      print package version.

4.2、阻止其他主机的ping请求

iptables -A INPUT -p icmp -j REJECT

4.3、开放本机的9501端口

iptables -I INPUT -p tcp --dport 9501 -j ACCEPT
iptables -I INPUT -p udp --dport 9501 -j ACCEPT

4.4、禁止本机访问外部web服务

iptables -A OUTPUT -p tcp --dport 80 -j REJECT

使用 iptables -L 可以查看已设置的规则,iptables -D 可以删除规则,iptables 命令执行完是即时生效的,但是如果主机重启,已设置的规则就会丢失,这里可以使用 iptables-saveiptables-restore 。iptables-save 将现有规则保存成文件,iptables-restore 从文件中恢复规则。

4.5、docker容器

docker run -d --name redis01 -p 6380:6379 redis

该命令执行后,docker 会在 iptables 自定义链 DOCKER 中定义转发规则,如果此时系统的 net.ipv4.ip_forward 为0,该命令执行完会提示:WARNING: IPv4 forwarding is disabled. Networking will not work,只需打开该配置就行了,无需重启容器。此时查看 DOCKER 链可以看到添加了一条允许所有来源转发到6379端口的流量,用 redis-cli 也可以顺利连上

开发中,经常会遇到容器里面放问宿主机的情况,除了使用 host.docker.internal 之外,还可以配置 extra_hosts 解决,因为 docker0 与 宿主机是相通的,直接用 ifconfig 查看宿主机 en0 网卡的ip地址,配置到 extra_hosts 即可,如:

version: '3'

networks:
  web-network:
    driver: bridge

services:
  fpm:
    build:
      context: ./fpm
    ports:
      - '8080:8080'
    networks:
      - web-network
    extra_hosts:
      - "test.com:192.168.1.100"

五、Docker与Iptables

Docker提供了bridge, host, overlay等多种网络,同一个Docker宿主机上同时存在多个不同类型的网络。位于不同网络中的容器,彼此之间是无法通信的。Docker容器的跨网络隔离与通信,是借助了iptables的机制。我们知道,iptables的filter表中默认划分为IPNUT, FORWARD和OUTPUT共3个链,详情请参考 iptables及其过滤规则。Docker在FORWARD链中,还额外提供了自己的链,以实现bridge网络之间的隔离与通信。

5.1、 Docker在iptables的filter表中的链

在2015.12之前,Docker只额外提供了DOCKER链。在此之后,直到Docker 17.06.0(2017.6)之前的版本中,Docker提供了如下2个链:

DOCKER
DOCKER-ISOLATION

在Docker 17.06.0(2017.6)及之后,Docker 18.03.1(2018.4)及之前的版本中,Docker提供了如下3个链:

DOCKER
DOCKER-ISOLATION
DOCKER-USER

查看Docker的iptables如下:

Chain FORWARD (policy ACCEPT)
target prot opt source destination
DOCKER-USER all -- 0.0.0.0/0 0.0.0.0/0
DOCKER-ISOLATION-STAGE-1 all -- 0.0.0.0/0 0.0.0.0/0
DOCKER all -- 0.0.0.0/0 0.0.0.0/0

在Docker 18.05.0(2018.5)及之后的版本中,提供如下4个chain:

DOCKER
DOCKER-ISOLATION-STAGE-1
DOCKER-ISOLATION-STAGE-2
DOCKER-USER

目前,Docker默认对宿主机的iptables设置规则完整一览:

iptables -N DOCKER
iptables -N DOCKER-ISOLATION-STAGE-1
iptables -N DOCKER-ISOLATION-STAGE-2
iptables -N DOCKER-USER
iptables -A FORWARD -j DOCKER-USER
iptables -A FORWARD -j DOCKER-ISOLATION-STAGE-1
iptables -A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
iptables -A FORWARD -o docker0 -j DOCKER
iptables -A FORWARD -i docker0 ! -o docker0 -j ACCEPT
iptables -A FORWARD -i docker0 -o docker0 -j ACCEPT
iptables -A DOCKER-ISOLATION-STAGE-1 -i docker0 ! -o docker0 -j DOCKER-ISOLATION-STAGE-2
iptables -A DOCKER-ISOLATION-STAGE-1 -j RETURN
iptables -A DOCKER-ISOLATION-STAGE-2 -o docker0 -j DROP
iptables -A DOCKER-ISOLATION-STAGE-2 -j RETURN
iptables -A DOCKER-USER -j RETURN

5.2、Docker的DOCKER链

仅处理从宿主机到docker0的IP数据包。

5.3、Docker的DOCKER-ISOLATION链

可以看到,为了隔离在不同的bridge网络之间的容器,Docker提供了两个DOCKER-ISOLATION阶段实现。DOCKER-ISOLATION-STAGE-1链过滤源地址是bridge网络(默认docker0)的IP数据包,匹配的IP数据包再进入DOCKER-ISOLATION-STAGE-2链处理,不匹配就返回到父链FORWARD。在DOCKER-ISOLATION-STAGE-2链中,进一步处理目的地址是bridge网络的IP数据包,匹配的IP数据包表示该IP数据包是从一个bridge网络的网桥发出,到另一个bridge网络的网桥,这样的IP数据包来自其他bridge网络,将被直接DROP;不匹配的IP数据包就返回到父链FORWARD继续进行后续处理。

5.4、Docker的DOCKER-USER链

Docker启动时,会加载DOCKER链和DOCKER-ISOLATION(现在是DOCKER-ISOLATION-STAGE-1)链中的过滤规则,并使之生效。绝对禁止修改这里的过滤规则。

如果用户要补充Docker的过滤规则,强烈建议追加到DOCKER-USER链。DOCKER-USER链中的过滤规则,将先于Docker默认创建的规则被加载,从而能够覆盖Docker在DOCKER链和DOCKER-ISOLATION链中的默认过滤规则。例如,Docker启动后,默认任何外部source IP都被允许转发,从而能够从该source IP连接到宿主机上的任何Docker容器实例。如果只允许一个指定的IP访问容器实例,可以插入路由规则到DOCKER-USER链中,从而能够在DOCKER链之前被加载。示例如下:

只允许192.168.1.1访问容器

iptables -A DOCKER-USER -i docker0 ! -s 192.168.1.1 -j DROP

只允许192.168.1.0/24网段中的IP访问容器

iptables -A DOCKER-USER -i docker0 ! -s 192.168.1.0/24 -j DROP

只允许192.168.1.1-192.168.1.3网段中的IP访问容器(需要借助于iprange模块)

iptables -A DOCKER-USER -m iprange -i docker0 ! --src-range 192.168.1.1-192.168.1.3 -j DROP

5.5、Docker在iptables的nat表中的规则

为了能够从容器中访问其他Docker宿主机,Docker需要在iptables的nat表中的POSTROUTING链中插入转发规则,示例如下:

iptables -t nat -A POSTROUTING -s 172.18.0.0/16 -j MASQUERADE

上述配置,还进一步限制了容器实例的IP范围,这是为了区分Docker宿主机上有多个bridge网络的情况。

5.6、Docker中禁止修改iptables过滤表

dockerd启动时,参数--iptables默认为true,表示允许修改iptables路由表。要禁用该功能,可以有两个选择:设置启动参数--iptables=false

修改配置文件/etc/docker/daemon.json,设置"iptables": "false";然后执行systemctl reload docker重新加载

 

有关【Docker】关于Docker网络隔离与通信详解的更多相关文章

  1. ruby-on-rails - Rails 应用程序之间的通信 - 2

    我构建了两个需要相互通信和发送文件的Rails应用程序。例如,一个Rails应用程序会发送请求以查看其他应用程序数据库中的表。然后另一个应用程序将呈现该表的json并将其发回。我还希望一个应用程序将存储在其公共(public)目录中的文本文件发送到另一个应用程序的公共(public)目录。我从来没有做过这样的事情,所以我什至不知道从哪里开始。任何帮助,将不胜感激。谢谢! 最佳答案 无论Rails是什么,几乎所有Web应用程序都有您的要求,大多数现代Web应用程序都需要相互通信。但是有一个小小的理解需要你坚持下去,网站不应直接访问彼此

  2. ruby - 用 Ruby 编写一个简单的网络服务器 - 2

    我想在Ruby中创建一个用于开发目的的极其简单的Web服务器(不,不想使用现成的解决方案)。代码如下:#!/usr/bin/rubyrequire'socket'server=TCPServer.new('127.0.0.1',8080)whileconnection=server.acceptheaders=[]length=0whileline=connection.getsheaders想法是从命令行运行这个脚本,提供另一个脚本,它将在其标准输入上获取请求,并在其标准输出上返回完整的响应。到目前为止一切顺利,但事实证明这真的很脆弱,因为它在第二个请求上中断并出现错误:/usr/b

  3. Get https://registry-1.docker.io/v2/: net/http: request canceled while waiting - 2

    1.错误信息:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:requestcanceledwhilewaitingforconnection(Client.Timeoutexceededwhileawaitingheaders)或者:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:TLShandshaketimeout2.报错原因:docker使用的镜像网址默认为国外,下载容易超时,需要修改成国内镜像地址(首先阿里

  4. 网络编程套接字 - 2

    网络编程套接字网络编程基础知识理解源`IP`地址和目的`IP`地址理解源MAC地址和目的MAC地址认识端口号理解端口号和进程ID理解源端口号和目的端口号认识`TCP`协议认识`UDP`协议网络字节序socket编程接口`sockaddr``UDP`网络程序服务器端代码逻辑:需要用到的接口服务器端代码`udp`客户端代码逻辑`udp`客户端代码`TCP`网络程序服务器代码逻辑多个版本服务器单进程版本多进程版本多线程版本线程池版本服务器端代码客户端代码逻辑客户端代码TCP协议通讯流程TCP协议的客户端/服务器程序流程三次握手(建立连接)数据传输四次挥手(断开连接)TCP和UDP对比网络编程基础知识

  5. MIMO-OFDM无线通信技术及MATLAB实现(1)无线信道:传播和衰落 - 2

     MIMO技术的优缺点优点通过下面三个增益来总体概括:阵列增益。阵列增益是指由于接收机通过对接收信号的相干合并而活得的平均SNR的提高。在发射机不知道信道信息的情况下,MIMO系统可以获得的阵列增益与接收天线数成正比复用增益。在采用空间复用方案的MIMO系统中,可以获得复用增益,即信道容量成倍增加。信道容量的增加与min(Nt,Nr)成正比分集增益。在采用空间分集方案的MIMO系统中,可以获得分集增益,即可靠性性能的改善。分集增益用独立衰落支路数来描述,即分集指数。在使用了空时编码的MIMO系统中,由于接收天线或发射天线之间的间距较远,可认为它们各自的大尺度衰落是相互独立的,因此分布式MIMO

  6. ruby-on-rails - 关于 Ruby 的一般问题 - 2

    我在我的rails应用程序中安装了来自github.com的acts_as_versioned插件,但有一段代码我不完全理解,我希望有人能帮我解决这个问题class_eval我知道block内的方法(或任何它是什么)被定义为类内的实例方法,但我在插件的任何地方都找不到定义为常量的CLASS_METHODS,而且我也不确定是什么here,并且有问题的代码从lib/acts_as_versioned.rb的第199行开始。如果有人愿意告诉我这里的内幕,我将不胜感激。谢谢-C 最佳答案 这是一个异端。http://en.wikipedia

  7. ruby - 检查网络文件是否存在,而不下载它? - 2

    是否可以在不实际下载文件的情况下检查文件是否存在?我有这么大的(~40mb)文件,例如:http://mirrors.sohu.com/mysql/MySQL-6.0/MySQL-6.0.11-0.glibc23.src.rpm这与ruby​​不严格相关,但如果发件人可以设置内容长度就好了。RestClient.get"http://mirrors.sohu.com/mysql/MySQL-6.0/MySQL-6.0.11-0.glibc23.src.rpm",headers:{"Content-Length"=>100} 最佳答案

  8. ruby - 404 未找到,但可以从网络浏览器正常访问 - 2

    我在这方面尝试了很多URL,在我遇到这个特定的之前,它们似乎都很好:require'rubygems'require'nokogiri'require'open-uri'doc=Nokogiri::HTML(open("http://www.moxyst.com/fashion/men-clothing/underwear.html"))putsdoc这是结果:/Users/macbookair/.rvm/rubies/ruby-2.0.0-p481/lib/ruby/2.0.0/open-uri.rb:353:in`open_http':404NotFound(OpenURI::HT

  9. ruby - 我怎样才能更好地了解/了解更多关于 Ruby 的知识? - 2

    按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭9年前。我最近开始学习Ruby,这是我的第一门编程语言。我对语法感到满意,并且我已经完成了许多只教授相同基础知识的教程。我已经写了一些小程序(包括我自己的数组排序方法,在有人告诉我谷歌“冒泡排序”之前我认为它非常聪明),但我觉得我需要尝试更大更难的东西来理解更多关于Ruby.关于如何执行此操作的任何想法?

  10. ruby - 关于 Ruby 中 Dir[] 和 File.join() 的混淆 - 2

    我在Ruby中遇到了一个关于Dir[]和File.join()的简单程序,blobs_dir='/path/to/dir'Dir[File.join(blobs_dir,"**","*")].eachdo|file|FileUtils.rm_rf(file)ifFile.symlink?(file)我有两个困惑:首先,File.join(@blobs_dir,"**","*")中的第二个和第三个参数是什么意思?其次,Dir[]在Ruby中有什么用?我只知道它等价于Dir.glob(),但是,我对Dir.glob()确实不是很清楚。 最佳答案

随机推荐