端口映射

  • 随机映射端口从外部访问容器应用, 使用docker -P,随机映射一个49000-49900的端口到内部容器

    1
    [root@docker  ~]# docker run -d -P nginx
  • 使用ip::端口,绑定本地的任意端口到容器内部端口

    1
    [root@docker  ~]# docker run -d -p 127.0.0.1::5000 training/webapp python app.py
  • 指定映射端口从外部访问容器应用: 使用docker -p,指定一个端口映射到内部容器,例如将本机 8080 端口映射到容器的 80 端口

    1
    [root@docker  ~]# docker run -d -p 8080:80 nginx
  • 映射多个端口到容器,多次使用-p标记可以绑定多个端口

    1
    [root@docker  ~]# docker run -d -p 5000:5000 -p 3000:80 training/webapp python app.py
  • 映射指定地址的指定端口: 使用-p ip:端口: 容器端口,可以指定映射一个特定的地址。

    1
    [root@docker  ~]# docker run -d -p 127.0.0.1:5000:5000 training/webapp python app.py
  • 映射指定udp端口: 使用-p ip:端口: 容器端口/udp,可以指定映射一个udp地址。

    1
    [root@docker  ~]# docker run -d -p 127.0.0.1:5000:5000/udp training/webapp python app.py
  • 查看端口映射状态: 使用docker port来查看当前端口映射状态

    1
    2
    [root@docker  ~]# docker port 98d5c052a96
    5000/tcp -> 127.0.0.1:5000

容器互联

容器互联可以让多个容器中的应用进行快速交互,他会在源和接收容器之间创建连接关系,接收容器可以通过容器名快速访问到源容器,而不用指定具体IP

  1. 自定义容器名称

    1
    2
    3
    [root@docker  ~]# docker run -d -P --name web training/webapp python app.py
    # 删除创建的web容器
    [root@docker ~]# docker rm -f web
  2. 容器互联: 使用--link name:别名,可以让容器互联

    1
    2
    3
    4
    # 创建一个新的数据库容器 db
    [root@docker ~]# docker run -d --name db training/postgres
    # 创建一个新的web容器,并连接到db容器上
    [root@docker ~]# docker run -d -P --name web --link db:db training/webapp python app.py
  3. 使用env查看容器环境变量信息

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    [root@docker  ~]# docker exec -it web env
    PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
    HOSTNAME=fabb9c44bb04
    TERM=xterm
    DB_PORT=tcp://172.17.0.3:5432
    DB_PORT_5432_TCP=tcp://172.17.0.3:5432
    DB_PORT_5432_TCP_ADDR=172.17.0.3
    DB_PORT_5432_TCP_PORT=5432
    DB_PORT_5432_TCP_PROTO=tcp
    DB_NAME=/web/db
    DB_ENV_PG_VERSION=9.3
    HOME=/root

  4. 查看容器hosts文件信息

    1
    2
    3
    4
    5
    6
    7
    8
    9
    [root@docker  ~]# docker exec -it web 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 db 1b0ce139a2c1
    172.17.0.4 fabb9c44bb04

docker network

  • 由于Docker 所有容器都连接于默认的桥接网络上,因此默认情况下所有容器都是可以互联的,没有隔离,当然这样安全性不好。而服务发现,是在这种环境下发展出来的,通过修改容器内的 /etc/hosts 文件来完成的。凡是 --link 的主机的别名就会出现于 /etc/hosts 中,其地址由 Docker 引擎维护。因此容器间才可以通过别名互访。而不在同一网络中的容器,不可以互联。

  • 但是这种办法并不是好的解决方案

    • 首先是因为使用 --link 就很可能还在用默认桥接网络,这很不安全,所有容器都没有适度隔离,用自定义网络才比较方便互联隔离。其次,修改 /etc/hosts 文件有很多弊病。比如,高频繁的容器启停环境时,容易产生竞争冒险,导致 /etc/hosts 文件损坏,出现访问故障;或者有些应用发现是来自于 /etc/hosts 文件后,就假定其为静态文件,而缓存结果不再查询,从而导致容器启停 IP 变更后,使用旧的条目而无法连接到正确的容器等。
    • Docker 早在一年多以前就已经使用自定义网络了。在同一个网络中的容器,可以互联,并且,Docker 内置了 DNS,容器内的应用可以使用服务名、容器名、别名来进行服务发现,名称会经由内置的 DNS 进行解析,其结果是动态的;
  • 查看docker默认的network网络

    1
    2
    3
    4
    5
    [root@node1 docker]# docker network ls
    NETWORK ID NAME DRIVER SCOPE
    af09f33de965 bridge bridge local
    73a79b3950dd host host local
    99ccf146e61e none null local
  • 创建自定义网络

    1
    2
    3
    4
    5
    6
    7
    8
    [root@node1 docker]# docker network create net
    521dd530656137081816f85ec3b7978020f8aff43fb0446ce9edb4505f41804e
    [root@node1 docker]# docker network ls
    NETWORK ID NAME DRIVER SCOPE
    af09f33de965 bridge bridge local
    73a79b3950dd host host local
    521dd5306561 net bridge local
    99ccf146e61e none null local
  • 启动两个测试容器,测试连通性

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45

    [root@node1 docker]# docker run -d -it --name=test1 --network net busybox /bin/sh
    6ed3c9cb40195564f5895f906684db505578b75258efe1fef1c7bb30f6f88ac6
    [root@node1 docker]# docker run -d -it --name=test2 --network net busybox /bin/sh
    31fa1bbe74af38ab37eef3508b2d90e178314b7911c62dd4f509f02860799cce



    [root@node1 docker]# docker ps
    CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
    31fa1bbe74af busybox "/bin/sh" 26 seconds ago Up 24 seconds test2
    6ed3c9cb4019 busybox "/bin/sh" 40 seconds ago Up 38 seconds test1

    [root@node1 docker]# docker exec -it test1 sh
    / # ping test2
    PING test2 (172.18.0.3): 56 data bytes
    64 bytes from 172.18.0.3: seq=0 ttl=64 time=0.094 ms
    64 bytes from 172.18.0.3: seq=1 ttl=64 time=0.156 ms
    64 bytes from 172.18.0.3: seq=2 ttl=64 time=0.197 ms
    64 bytes from 172.18.0.3: seq=3 ttl=64 time=0.202 ms
    ^C
    --- test2 ping statistics ---
    4 packets transmitted, 4 packets received, 0% packet loss
    round-trip min/avg/max = 0.094/0.162/0.202 ms

    / # 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.18.0.2 6ed3c9cb4019

    / # nslookup -type=a test2
    Server: 127.0.0.11
    Address: 127.0.0.11:53

    Non-authoritative answer:
    Name: test2
    Address: 172.18.0.3

    / # cat /etc/resolv.conf
    nameserver 127.0.0.11
    options ndots:0
  • 通过上述实验可知,通过network互联的容器,并没有像–link那样,在/etc/hosts中记录解析信息,而是通过Docker内置的DNS进行动态解析。

  • 在默认的bridge网络创建容器,测试能否与net网络容器连接

    • 即便bridge和net共用一个DRIVER,由于他们属于不同的隔离网络,即使通过IP也不能互联访问
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    [root@node1 docker]# docker run --name test3 -d  -it busybox /bin/sh
    18a691bf2f77fe7a11505ea3f7db55b464358c8ea10b822f85d827b4dadb85fe
    [root@node1 docker]# docker ps
    CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
    18a691bf2f77 busybox "/bin/sh" 13 seconds ago Up 11 seconds test3
    31fa1bbe74af busybox "/bin/sh" 10 minutes ago Up 10 minutes test2
    6ed3c9cb4019 busybox "/bin/sh" 10 minutes ago Up 10 minutes test1

    [root@node1 docker]# docker exec -it test3 ping 172.18.0.3
    PING 172.18.0.3 (172.18.0.3): 56 data bytes
    ^C
    --- 172.18.0.3 ping statistics ---
    9 packets transmitted, 0 packets received, 100% packet loss
  • 创建新容器,使用net网络,测试能否暴露端口提供访问

    • 得知创建的自定义网络,通过bridge网桥,可以挂载主机端口对外提供访问
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    [root@tiaoban ~]# docker run -d --name=nginx -p 80:80 --network net nginx
    09f31a3e73cacf9951ad763777f2d16e745d1f679b8d380e6a63218bc69c8ce7
    [root@tiaoban ~]# docker ps
    CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
    09f31a3e73ca nginx "/docker-entrypoint.…" 8 seconds ago Up 6 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp nginx
    18a691bf2f77 busybox "/bin/sh" About a minute ago Up About a minute test3
    31fa1bbe74af busybox "/bin/sh" 11 minutes ago Up 11 minutes test2
    6ed3c9cb4019 busybox "/bin/sh" 11 minutes ago Up 11 minutes test1

    [root@node1 docker]# curl 127.0.0.1
    <!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, the nginx web server is successfully installed an d
    working. Further configuration is required.</p>

    <p>For online documentation and support please refer to
    <a href="http://nginx.org/">nginx.org</a>.<br/>
    Commercial support is available at
    <a href="http://nginx.com/">nginx.com</a>.</p>

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

添加SSH服务

基于commit

  1. 首先,获取centos镜像,并创建一个容器:

    1
    2
    [root@node1 docker]# docker pull centos
    [root@docker sshd_centos]# docker run -it centos:latest bash
  2. 安装和配置SSH服务

    1
    [root@71e353322f0c /]# yum install passwd openssh-server -y
  3. 修改root密码

    1
    [root@71e353322f0c ~]# passwd
  4. 生成秘钥

    1
    2
    3
    [root@71e353322f0c ~]# ssh-keygen -t rsa -f  /etc/ssh/ssh_host_rsa_key
    [root@71e353322f0c ~]# ssh-keygen -t rsa -f /etc/ssh/ssh_host_ecdsa_key
    [root@71e353322f0c ~]# ssh-keygen -t rsa -f /etc/ssh/ssh_host_ed25519_key
  5. 修改配置文件

    1
    2
    3
    [root@71e353322f0c ~]# vi /etc/ssh/sshd_config
    #禁用 PAM
    UsePAM no
  6. 编写服务启动脚本

    1
    2
    3
    4
    [root@71e353322f0c ~]# vi /run.sh
    #!/bin/bash
    /usr/sbin/sshd -D
    [root@71e353322f0c ~]# chmod +x /run.sh
  7. 退出容器,保存镜像

    1
    2
    3
    4
    [root@71e353322f0c /]# exit

    [root@node1 docker]# docker commit 71e353322f0c ssh:centos
    sha256:ad951b83040c77e322b7fbb6453cd1bdfa0b23c95604ebfb547e83c4d7e88209
  8. 查看镜像

    1
    2
    3
    4
    [root@node1 docker]# docker images ssh
    REPOSITORY TAG IMAGE ID CREATED SIZE
    ssh centos ad951b83040c 27 seconds ago 276MB

  9. 使用镜像,运行容器

    1
    2
    [root@node1 docker]# docker run -p 10086:22 -d ssh:centos /run.sh
    9513f14498c0db4a3b44bbdebe3f78450147a44eaf43c4a6a6981662ce8dbf5b
  10. ssh链接docker容器

    1
    [root@node1 docker]#  ssh root@127.0.0.1 -p 10086

Dockerfile创建

  • 创建工作目录及相关文件

    1
    2
    [root@docker ~]# mkdir sshd_centos
    [root@docker ~]# touch sshd_centos/Dockerfile run.sh
  • 编写run.sh脚本,创建authorized_keys文件

    1
    2
    3
    [root@docker ~]# vim run.sh
    #! /bin/bash
    /usr/sbin/sshd -D
  • 在宿主主机上生成SSH密钥对,并创建authorized_keys文件:

    1
    2
    [root@docker ~]# ssh-keygen -t rsa
    [root@docker ~]# cat /root/.ssh/id_rsa.pub > authorized_keys
  • 编写Dockerfile

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    #设置继承镜像
    FROM centos:latest
    #提供一些作者的信息
    MAINTAINER docker_user (user@docker.com)
    #安装 ssh 服务
    RUN yum install openssh-server -y
    #修改root用户密码
    RUN /bin/echo "123.com" | passwd --stdin root
    #修改配置信息
    RUN /bin/sed -i 's/.*session.*required.*pam_loginuid.so.*/session optional pam_loginuid.so/g' /etc/pam.d/sshd \
    && /bin/sed -i 's/UsePAM yes/UsePAM no/g' /etc/ssh/sshd_config \
    && /bin/sed -i "s/#UsePrivilegeSeparation.*/UsePrivilegeSeparation no/g" /etc/ssh/sshd_config
    #生成密钥
    RUN ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key \
    && ssh-keygen -t rsa -f /etc/ssh/ssh_host_ecdsa_key \
    && ssh-keygen -t rsa -f /etc/ssh/ssh_host_ed25519_key
    #开放端口
    EXPOSE 22
    #设置自启动命令
    CMD ["/usr/sbin/sshd","-D"]
  • 创建镜像, 在sshd_ubuntu目录下,使用docker build命令来创建镜像。这里用户需要注意在最后还有一个“.”,表示使用当前目录中的Dockerfile:

    1
    [root@docker sshd_centos]# docker  build -t sshd:dockerfile .
  • 查看生成的docker镜像

    1
    [root@docker sshd_centos]# docker images sshd:dockerfile
  • 测试镜像,运行容器

    1
    [root@docker sshd_centos]# docker run -it -p 10010:22  sshd:dockerfile

Docker网络管理

启动过程

  • 在主机上自动创建一个docker0虚拟网桥,实际上是一个Linux网桥。网桥可以理解为一个软件交换机,负责挂载其上的接口之间进行包转发。

  • 创建一对虚拟接口,分别放到本地主机和新容器的命名空间中;

    • 本地主机一端的虚拟接口连接到默认的docker0网桥或指定网桥上,并具有一个以veth开头的唯一名字,如veth1234;
    • 另一端的虚拟接口将放到新创建的容器中,并修改名字作为eth0。这个接口只在容器的命名空间可见;
  • 从网桥可用地址段中获取一个空闲地址分配给容器的eth0(例如172.17.0.2/16),并配置默认路由网关为docker0网卡的内部接口docker0的IP地址(例如172.17.42.1/16)。

网络相关参数

  • 在Docker服务启动的时候才能配置,修改后重启生效

    命令 说明
    -b BRIDGE or –bridge=BRIDGE 指定容器挂载的网桥
    –bip=CIDR 定制docker0的掩码
    -H SOCKET… or –host=SOCKET Docker服务端接收命令的通道
    –icc=true|false 是否支持容器之间进行通信
    –ip-forward=true|false 启用net.ipv4.ip_forward,即打开转发功能
    –iptables=true|false 禁止Docker添加iptables规则
    –mtu=BYTES 容器网络中的MTU
  • 既可以在启动服务时指定,也可以Docker容器启动(使用docker [con-tainer] run命令)时候指定。在Docker服务启动的时候指定则会成为默认值,后续执行该命令时可以覆盖设置的默认值:

    命令 说明
    –dns=IP_ADDRESS 使用指定的DNS服务器
    –dns-opt=”” 指定DNS选项
    –dns-search=DOMAIN 指定DNS搜索域
  • 只能在docker [container] run命令执行时使用,因为它针对容器的配置

    命令 说明
    -h HOSTNAME or –hostname=HOSTNAME 配置容器主机名
    -ip 指定容器内接口的IP地址
    –link=CONTAINER_NAME:ALIAS 添加到另一个容器的连接
    –net=bridge|none|container:NAME_or_ID|host|user_defined_network 配置容器的桥接模式
    –network-alias 容器在网络中的别名
    -p SPEC or –publish=SPEC 映射容器端口到宿主主机
    -P or –publish-all=true|false 映射容器所有端口到宿主主机

–net选项

  • –net选项支持以下五种模式:

    模式 说明
    –net=bridge 默认配置。为容器创建独立的网络命名空间,分配网卡、IP地址等网络配置,并通过veth接口对将容器挂载到一个虚拟网桥(默认为docker0)上
    –net=none 为容器创建独立的网络命名空间,但不进行网络配置,即容器内没有创建网卡、IP地址等
    –net=container:NAME_or_ID 新创建的容器共享指定的已存在容器的网络命名空间,两个容器内的网络配置共享,但其他资源(如进程空间、文件系统等)还是相互隔离的
    –net=host 不为容器创建独立的网络命名空间,容器内看到的网络配置(网卡信息、路由表、Iptables规则等)均与主机上的保持一致。注意其他资源还是与主机隔离的
    –net=user_defined_network 用户自行用network相关命令创建一个网络,同一个网络内的容器彼此可见,可以采用更多类型的网络插件。

Docker网络命令

命令 说明
create 创建一个网络
connect 将容器接入到网络
disconnect 把容器从网络上断开
inspect 查看网络的详细信息
ls 列出所有的网络
prune 清理无用的网络资源
rm 删除一个网络

创建网络

  • creat命令用于创建一个新的容器网络。Docker内置了bridge(默认使用)和overlay两种驱动,分别支持单主机和多主机场景。Docker服务在启动后,会默认创建一个bridge类型的网桥bridge。不同网络之间默认相互隔离。

  • 创建网络命令格式为

    1
    docker network create [OPTIONS] NETWORK
    参数 说明
    -attachable[=false] 支持手动容器挂载
    -aux-address=map[] 辅助的IP地址
    -config-from=”” 从某个网络复制配置数据
    -config-only[=false] 启用仅可配置模式
    -d, -driver=”bridge” 网络驱动类型,如bridge或overlay
    -gateway=[] 网关地址
    -ingress[=false] 创建一个Swarm可路由的网状网络用于负载均衡,可将对某个服务的请求自动转发给一个合适的副本
    -internal[=false] 内部模式,禁止外部对所创建网络的访问
    -ip-range=[] 指定分配IP地址范围
    -ipam-driver=”default” IP地址管理的插件类型
    -ipam-opt=map[] IP地址管理插件的选项
    -ipv6[=false] 支持IPv6地址
    -label value 为网络添加元标签信息
    -o, -opt=map[] 网络驱动所支持的选项
    -scope=”” 指定网络范围
    -subnet=[] 网络地址段,CIDR格式,如172.17.0.0/16。

接入网络

  • connect命令将一个容器连接到一个已存在的网络上。连接到网络上的容器可以跟同一网络中其他容器互通,同一个容器可以同时接入多个网络。也可以在执行docker run命令时候通过-net参数指定容器启动后自动接入的网络。

  • 接入网络命令格式为

    1
    docker network connect [OPTIONS] NETWORK CONTAINER
  • 支持参数包括:

    参数 说明
    -alias=[] 为容器添加一个别名,此别名仅在所添加网络上可见;
    -ip=”” 指定IP地址,需要注意不能跟已接入的容器地址冲突;
    -ip6=”” 指定IPv6地址;
    -link value 添加链接到另外一个容器;
    -link-local-ip=[] 为容器添加一个链接地址。

断开网络

  • disconnect命令将一个连接到网络上的容器从网络上断开连接。

  • 命令格式为

    1
    docker network disconnect [OPTIONS] NETWORK CONTAINER
  • 支持参数包括-f, -force: 强制把容器从网络上移除。

查看网络信息

  • inspect命令用于查看一个网络的具体信息(JSON格式),包括接入的容器、网络配置信息等。

  • 命令格式为

    1
    docker network inspect [OPTIONS] NETWORK [NETWORK...]
  • 支持参数包括:

    参数 说明
    -f, -format="" 给定一个Golang模板字符串,对输出结果进行格式化,如只查看地址配置可以用-f '{{.IPAM.Config}}'d
    -v, -verbose[=false] 输出调试信息

列出网络

  • ls命令用于列出网络。命令格式为docker network ls [OPTIONS],其中支持的选项主要有:

    选项 说明
    -f, -filter=”” 指定输出过滤器,如driver=bridge;
    -format=”” 给定一个golang模板字符串,对输出结果进行格式化;
    -no-trunc[=false] 不截断地输出内容;
    -q, -quiet[=false] 安静模式,只打印网络的ID。

清理无用网络

  • prune命令用于清理已经没有容器使用的网络。

  • 命令格式为docker network prune [OPTIONS] [flags],支持参数包括:

    选项 说明
    -filter=”” 指定选择过滤器
    -f, -force 强制清理资源

删除网络

  • rm命令用于删除指定的网络。当网络上没有容器连接上时,才会成功删除。
  • 命令格式为docker network rm NETWORK [NETWORK...]

配置DNS和主机名

  • Docker服务启动后会默认启用一个内嵌的DNS服务,来自动解析同一个网络中的容器主机名和地址,如果无法解析,则通过容器内的DNS相关配置进行解析。用户可以通过命令选项自定义容器的主机名和DNS配置

相关配置文件

  • 容器中主机名和DNS配置信息可以通过三个系统配置文件来管理:

    • /etc/resolv.conf
    • /etc/hostname
    • /etc/hosts
  • 启动一个容器,在容器中使用mount命令可以看到这三个文件挂载信息:

    1
    2
    3
    4
    5
    [root@node1 ~]# docker run -it centos bash
    [root@51adaac0dedf /]# mount | grep etc
    /dev/mapper/centos-root on /etc/resolv.conf type xfs (rw,relatime,seclabel,attr2,inode64,noquota)
    /dev/mapper/centos-root on /etc/hostname type xfs (rw,relatime,seclabel,attr2,inode64,noquota)
    /dev/mapper/centos-root on /etc/hosts type xfs (rw,relatime,seclabel,attr2,inode64,noquota)
  • /etc/resolv.conf记录了DNS服务器

    1
    2
    3
    [root@51adaac0dedf /]# cat /etc/resolv.conf
    # Generated by NetworkManager
    nameserver 192.168.61.2
  • /etc/hostname文件则记录了容器的主机名

    1
    2
    [root@51adaac0dedf /]# cat /etc/hostname
    51adaac0dedf
  • /etc/hosts文件中默认只记录了容器自身的地址和名称:

    1
    2
    3
    4
    5
    6
    7
    8
    [root@51adaac0dedf /]# 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.4 51adaac0dedf

容器内修改配置文件

  • 容器运行时,可以在运行中的容器里直接编辑/etc/hosts、/etc/hostname和/etc/resolve. conf文件。
  • 但是这些修改是临时的,只在运行的容器中保留,容器终止或重启后并不会被保存下来,也不会被docker commit提交。

通过参数指定

注意一般不推荐与-net=host一起使用,会破坏宿主机上的配置信息

  • 如果用户想要自定义容器的配置,可以在创建或启动容器时利用下面的参数指定
  • 指定主机名-h HOSTNAME或者--hostname=HOSTNAME: 设定容器的主机名。
    • 容器主机名会被写到容器内的/etc/hostname和/etc/hosts。
    • 但这个主机名只有容器内能中看到,在容器外部则看不到,既不会在docker ps中显示,也不会在其他容器的/etc/hosts中看到;
  • --link=CONTAINER_NAME:ALIAS: 记录其他容器主机名。
    • 在创建容器的时候,添加一个所连接容器的主机名到容器内/etc/hosts文件中。
    • 这样,新建容器可以直接使用主机名与所连接容器通信;
  • --dns=IP_ADDRESS: 指定DNS服务器。添加DNS服务器到容器的/etc/resolv.conf
    • 容器会用指定的服务器来解析所有不在/etc/hosts中的主机名;
  • --dns-option list: 指定DNS相关的选项;
  • --dns-search=DOMAIN: 指定DNS搜索域。
    • 设定容器的搜索域,当设定搜索域为 .example.com 时
    • 在搜索一个名为host的主机时,DNS不仅搜索host,还会搜索host.example.com。

防火墙访问控制

容器的访问控制主要通过Linux上的iptables防火墙软件来进行管理和实现。

容器访问外部网络

  • 容器默认指定了网关为docker0网桥上的docker0内部接口。docker0内部接口同时也是宿主机的一个本地接口。因此,容器默认情况下可以访问到宿主机本地网络。如果容器要想通过宿主机访问到外部网络,则需要宿主机进行辅助转发。

  • 在宿主机Linux系统中,检查转发是否打开,代码如下:

    1
    2
    3
    4
    5
    [root@node1 ~]#  sysctl net.ipv4.ip_forward
    net.ipv4.ip_forward = 1
    # 如果为0,说明没有开启转发,则需要手动打开:
    [root@node1 ~]# sysctl -w net.ipv4.ip_forward=1
    net.ipv4.ip_forward = 1
  • Docker服务启动时会默认开启--ip-forward=true,自动配置宿主机系统的转发规则

容器之间访问

  • 容器之间相互访问需要两方面的支持:

    • 网络拓扑是否已经连通。默认情况下,所有容器都会连接到docker0网桥上,这意味着默认情况下拓扑是互通的;
    • 本地系统的防火墙软件iptables是否允许访问通过。这取决于防火墙的默认规则是允许(大部分情况)还是禁止。
  • 访问所有端口

    • 当启动Docker服务时候,默认会添加一条“允许”转发策略到iptables的FORWARD链上。通过配置--icc=true|false(默认值为true)参数可以控制默认的策略。

    • 为了安全考虑,可以在Docker配置文件中配置DOCKER_OPTS="--icc=false"来默认禁止容器之间的相互访问。

      1
      2
      [root@node1 ~]#  vim /etc/default/docker
      DOCKER_OPTS="--icc=false"
    • 同时,如果启动Docker服务时手动指定--iptables=false参数,则不会修改宿主机系统上的iptables规则。

  • 访问指定端口

    • 在通过-icc=false禁止容器间相互访问后,仍可以通过--link=CONTAINER_NAME:ALIAS选项来允许访问指定容器的开放端口。

    • 例如,在启动Docker服务时,可以同时使用--icc=false --iptables=true参数来配置容器间禁止访问,并允许Docker自动修改系统中的iptables规则。此时,系统中的iptables规则可能是类似如下规则,禁止所有转发流量:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      # sudo iptables -L -n    查看iptables规则的情况
      # sudo iptables -F 清空iptables规则设置
      # sudo service docker restart 重新启用docker的服务
      # sudo iptables -L -n 再来查看iptables的设置,docker的规则链已经在第一位
      $ sudo iptables -nL
      ...
      Chain FORWARD (policy ACCEPT)
      target prot opt source destination
      DROP all -- 0.0.0.0/0 0.0.0.0/0
      ...
    • 启动容器时使用--link=CONTAINER_NAME:ALIAS选项。Docker会在iptable中为两个互联容器分别添加一条ACCEPT规则,允许相互访问开放的端口(取决于 Dockerfile 中的 EXPOSE 行)。

    • 当添加了 --link=CONTAINER_NAME:ALIAS 选项后,添加了 iptables 规则。

      • 注意: --link=CONTAINER_NAME:ALIAS 中的 CONTAINER_NAME 目前必须是 Docker 分配的名字,或使用 --name 参数指定的名字。主机名则不会被识别。
      1
      2
      3
      4
      5
      6
      7
      8
      [root@docker  ~]# docker run -it --link=3782df38f864:centoslink centos
      [root@docker ~]# sudo iptables -nL
      ...
      Chain FORWARD (policy ACCEPT)
      target prot opt source destination
      ACCEPT tcp -- 172.17.0.2 172.17.0.3 tcp spt:80
      ACCEPT tcp -- 172.17.0.3 172.17.0.2 tcp dpt:80
      DROP all -- 0.0.0.0/0 0.0.0.0/0

网络分析

容器访问外部网络

  • centos 位于 docker0 这个私有bridge 网络中(172.17.0.0/16),当 centos 从容器向外 ping 时,数据包通过NAT地址转换到达 baidu.com

  • 查看一下 docker host 上的 iptables 规则:

    1
    2
    3
    4
    [root@node1 oracle]# iptables -t nat -S | grep docker0
    -A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
    -A DOCKER -i docker0 -j RETURN
    -A DOCKER ! -i docker0 -p tcp -m tcp --dport 10086 -j DNAT --to-destination 172.17.0.3:22
  • 在 NAT 表中,有这么一条规则:

    • -A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
  • 其含义是: 如果网桥 docker0 收到来自 172.17.0.0/16 网段的外出包,把它交给 MASQUERADE 处理。而 MASQUERADE 的处理方式是将包的源地址替换成 host 的地址发送出去,即做了一次网络地址转换(NAT)。

  • 通过 tcpdump 查看地址是如何转换的。先查看 docker host 的路由表:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    [root@node1 oracle]# ip r
    default via 192.168.61.2 dev ens33 proto static metric 100
    172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
    172.18.0.0/16 dev br-521dd5306561 proto kernel scope link src 172.18.0.1
    172.20.0.0/16 dev br-852e88d73bf9 proto kernel scope link src 172.20.0.1
    192.168.61.0/24 dev ens33 proto kernel scope link src 192.168.61.10 metric 100
    [root@node1 oracle]# tcpdump -i docker0 -n icmp
    tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
    listening on docker0, link-type EN10MB (Ethernet), capture size 262144 bytes
  • 默认路由通过 ens33 发出去,所以我们要同时监控 ens33和 docker0 上的 icmp(ping)数据包。

  • 当 docker的容器ping baidu.com 时,tcpdump 输出如下:

    1
    2
    3
    4
    5
    6
    7
    [root@node1 oracle]# tcpdump -i docker0 -n icmp
    tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
    listening on docker0, link-type EN10MB (Ethernet), capture size 262144 bytes
    13:47:23.179156 IP 172.17.0.4 > 50.28.32.8: ICMP echo request, id 1, seq 1, length 64
    13:47:23.438049 IP 50.28.32.8 > 172.17.0.4: ICMP echo reply, id 1, seq 1, length 64
    13:47:24.232102 IP 172.17.0.4 > 50.28.32.8: ICMP echo request, id 1, seq 2, length 64
    13:47:24.499137 IP 50.28.32.8 > 172.17.0.4: ICMP echo reply, id 1, seq 2, length 64
  • docker0 收到 centos 的 ping包,源地址为容器 IP 172.17.0.4,这没问题,交给 MASQUERADE 处理。这时,在 ens33 上我们看到了变化:

    1
    2
    3
    4
    5
    6
    7
    8
    # ping 包的源地址变成了 ens33 的 IP      192.168.61.10
    [root@node1 oracle]# tcpdump -i ens33 -n icmp
    tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
    listening on ens33, link-type EN10MB (Ethernet), capture size 262144 bytes
    13:53:55.595133 IP 192.168.61.10 > 50.28.32.8: ICMP echo request, id 2, seq 1, length 64
    13:53:55.823131 IP 50.28.32.8 > 192.168.61.10: ICMP echo reply, id 2, seq 1, length 64
    13:53:56.633403 IP 192.168.61.10 > 50.28.32.8: ICMP echo request, id 2, seq 2, length 64

  • 这就是 iptable NAT 规则处理的结果,从而保证数据包能够到达外网。这个过程:

    1. centos 发送 ping 包: 172.17.0.4 > www.baidu.com。
    2. docker0 收到包,发现是发送到外网的,交给 NAT 处理。
    3. NAT 将源地址换成 ens33 的 IP: 192.168.61.10 > www.baidu.com。
    4. ping 包从ens33 发送出去,到达 www.baidu.com。

外部网络访问容器

  • docker 可将容器对外提供服务的端口映射到 host 的某个端口,外网通过该端口访问容器。容器启动时通过-p参数映射端口:

  • 容器启动后,可通过 docker ps 或者 docker port 查看到 host 映射的端口。在上面的例子中,httpd 容器的 80 端口被映射到 host 49154上,这样就可以通过<host ip>:<49154>访问容器的 web 服务了。

    1
    2
    3
    4
    5
    6

    doc[root@node1 ~]# docker run -d --rm -P httpd
    4d227b0ab1099b3cf2b857d042f4415975fe922e094dcd7e83d5466ac9f953f7
    docker [root@node1 ~]# docker ps | grep 4d227
    4d227b0ab109 httpd "httpd-foreground" 15 seconds ago Up 11 seconds 0.0.0.0:49154->80/tcp, :::49154->80/tcp interesting_thompson

  • 除了映射动态端口,也可在 -p 中指定映射到 host某个特定端口,例如可将 80 端口映射到 host 的 8080 端口:

    1
    2
    3
    doc[root@node1 ~]# docker run -d --rm -p 80:80 httpd
    # 指定一个端口范围
    doc[root@node1 ~]#docker run -d -p 8000-9000:80 httpd
  • 每一个映射的端口,host 都会启动一个docker-proxy 进程来处理访问容器的流量

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14

    [root@node1 ~]# ps -ef |grep docker-proxt
    root 83152 127169 0 18:51 pts/1 00:00:00 grep --color=auto docker-proxt
    [root@node1 ~]# ps -ef |grep docker-proxy
    root 47106 20786 0 Sep23 ? 00:00:00 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 80 -container-ip 172.18.0.4 -container-port 80
    root 47112 20786 0 Sep23 ? 00:00:00 /usr/bin/docker-proxy -proto tcp -host-ip :: -host-port 80 -container-ip 172.18.0.4 -container-port 80
    root 55098 20786 0 Sep23 ? 00:00:00 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 10086 -container-ip 172.17.0.3 -container-port 22
    root 55104 20786 0 Sep23 ? 00:00:00 /usr/bin/docker-proxy -proto tcp -host-ip :: -host-port 10086 -container-ip 172.17.0.3 -container-port 22
    root 83168 127169 0 18:51 pts/1 00:00:00 grep --color=auto docker-proxy
    root 84278 20786 0 Sep23 ? 00:00:00 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 1521 -container-ip 172.20.0.2 -container-port 1521
    root 84292 20786 0 Sep23 ? 00:00:00 /usr/bin/docker-proxy -proto tcp -host-ip :: -host-port 1521 -container-ip 172.20.0.2 -container-port 1521
    root 127976 20786 0 15:34 ? 00:00:00 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 49154 -container-ip 172.17.0.5 -container-port 80
    root 127982 20786 0 15:34 ? 00:00:00 /usr/bin/docker-proxy -proto tcp -host-ip :: -host-port 49154 -container-ip 172.17.0.5 -container-port 80

  • 以 0.0.0.0:32770->80/tcp为例分析整个过程

    1. docker-proxy 监听 host 的 32770 端口。
    2. 当 curl 访问 10.0.2.15:32770 时,docker-proxy 转发给容器172.17.0.2:80。
    3. httpd 容器响应请求并返回结果。

网络类型

  • 查看网络

    1
    2
    3
    4
    5
    6
    7
    [root@node1 ~]# docker network ls
    NETWORK ID NAME DRIVER SCOPE
    81cc705d9ae4 bridge bridge local
    dbefd1fb2d23 host host local
    cbb819178e6e mysql_default bridge local
    85844dc32a26 none null local

none网络

  • none 网络就是什么都没有的网络。挂在这个网络下的容器除了 lo,没有其他任何网卡。

  • 容器创建时,可以通过 –network=none 指定使用 none 网络。

  • 一些对安全性要求高并且不需要联网的应用可以使用 none 网络。比如某个容器的唯一用途是生成随机密码,就可以放到 none 网络中避免密码被窃取。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    [root@node1 ~]# docker run -it --network=none busybox
    Unable to find image 'busybox:latest' locally
    latest: Pulling from library/busybox
    5cc84ad355aa: Pull complete
    Digest: sha256:5acba83a746c7608ed544dc1533b87c737a0b0fb730301639a0179f9344b1678
    Status: Downloaded newer image for busybox:latest
    / # ip a
    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 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

host网络

  • 连接到 host 网络的容器共享 Docker host的网络栈,容器的网络配置与 host 完全一样。

  • 通过 –network=host 指定使用 host 网络。

  • 直接使用 Docker host 的网络最大的好处就是性能,如果容器对网络传输效率有较高要求,则可以选择 host 网络。当然不便之处就是牺牲一些灵活性,比如要考虑端口冲突问题,Docker host 上已经使用的端口就不能再用了。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    [root@node1 ~]# docker run -it --network=host busybox
    / # ip a
    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 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
    inet6 ::1/128 scope host
    valid_lft forever preferred_lft forever
    2: ens32: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast qlen 1000
    link/ether 00:0c:29:fe:4d:57 brd ff:ff:ff:ff:ff:ff
    inet 192.168.61.10/24 brd 192.168.61.255 scope global noprefixroute ens32
    valid_lft forever preferred_lft forever
    inet6 fe80::20c:29ff:fefe:4d57/64 scope link
    valid_lft forever preferred_lft forever
    3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue
    link/ether 02:42:e3:ed:a0:cf brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
    valid_lft forever preferred_lft forever

bridge网络

  • Docker 安装时会创建一个命名为docker0 的 linux bridge。如果不指定–network,创建的容器默认都会挂到 docker0 上。

  • 创建一个容器后,查看容器网络信息。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    [root@node1 ~]# docker run -it busybox
    / # ip a
    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 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
    7: eth0@if8: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
    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
    valid_lft forever preferred_lft forever
    / # ip route
    default via 172.17.0.1 dev eth0
    172.17.0.0/16 dev eth0 scope link src 172.17.0.2
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    # 查看服[root@node1 ~]# ip a
    [root@node1 ~]# ip a
    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 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
    inet6 ::1/128 scope host
    valid_lft forever preferred_lft forever
    2: ens32: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 00:0c:29:fe:4d:57 brd ff:ff:ff:ff:ff:ff
    inet 192.168.61.10/24 brd 192.168.61.255 scope global noprefixroute ens32
    valid_lft forever preferred_lft forever
    inet6 fe80::20c:29ff:fefe:4d57/64 scope link
    valid_lft forever preferred_lft forever
    3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:e3:ed:a0:cf brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
    valid_lft forever preferred_lft forever
    inet6 fe80::42:e3ff:feed:a0cf/64 scope link
    valid_lft forever preferred_lft forever
    ...
    8: vethe63fdb0@if7: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
    link/ether 9a:e6:32:fe:5b:ba brd ff:ff:ff:ff:ff:ff link-netnsid 1
    inet6 fe80::98e6:32ff:fefe:5bba/64 scope link
    valid_lft forever preferred_lft forever
    ...
  • 一个新的网络接口vethe63fdb0被挂到了 docker0 上,vethe63fdb0就是新创建容器的虚拟网卡。eth0@if8和vethe63fdb0@if7是一对 veth pair。veth pair 是一种成对出现的特殊网络设备,可以把它们想象成由一根虚拟网线连接起来的一对网卡,网卡的一头(eth0@if8)在容器中,另一头(vethe63fdb0@if7)挂在网桥 docker0 上,其效果就是将 eth0@if8 也挂在了 docker0 上。

  • 每次创建一个新容器的时候,Docker从可用的地址段中选择一个空闲的IP地址分配给容器的eth0端口,并且使用本地主机上docker0接口的IP作为容器的默认网关

自定义网络

修改默认网桥

  • https://www.jb51.net/article/169294.htm

  • 安装brctl软件包

    1
    [root@node1 ~]# yum -y install bridge-utils
  • 停止docker服务并移除docker0网桥

    1
    2
    3
    4
    [root@node1 ~]# systemctl stop docker
    [root@node1 ~]# ip link set dev docker0 down
    [root@node1 ~]# brctl delbr docker0
    [root@node1 ~]# iptables -t nat -F POSTROUTING
  • 创建自定义网桥,bridge0 可以换成其他名称, 192.168.0.0/24 也可以换成你喜欢的其它网段

    1
    2
    3
    4
    5
    6
    # 添加新网桥,bridge0为新网桥的名称
    [root@node1 ~]# brctl addbr bridge0
    # 为网桥配置IP地址,此处可以将IPv4地址替换为IPv6地址,也可分两次为该网桥分别添加IPv4和IPv6地址
    [root@node1 ~]# ip addr add 192.168.0.0/24 dev bridge0
    # 启动网桥
    [root@node1 ~]# ip link set dev bridge0 up
  • 查看网桥信息

    1
    2
    3
    4
    5
    6
    7
    8
    [root@node1 ~]# ip addr show bridge0
    11: bridge0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default qlen 1000
    link/ether ee:30:6a:61:9f:db brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.0/24 scope global bridge0
    valid_lft forever preferred_lft forever
    inet6 fe80::ec30:6aff:fe61:9fdb/64 scope link
    valid_lft forever preferred_lft forever

  • 修改配置,设置docker默认使用新的网桥

    1
    [root@node1 ~]# echo 'DOCKER_OPTS="-b=bridge0" ' >> /etc/sysconfig/docker
  • 修改systemctl启动配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    #修改配置文件 vim /lib/systemd/system/docker.service

    [Service]
    Type=notify
    EnvironmentFile=-/etc/sysconfig/docker # (<-- #添加配置文件 -代表ignore error)
    # the default is not to use systemd for cgroups because the delegate issues still
    # exists and systemd currently does not support the cgroup feature set required
    # for containers run by docker
    # ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock (<-- 注释ExecStart)
    ExecStart=/usr/bin/dockerd $DOCKER_OPTS # (<-- 设定网卡参数)
    ExecReload=/bin/kill -s HUP $MAINPID
    TimeoutSec=0
    RestartSec=2
    Restart=always
  • 重启docker

    1
    2
    [root@node1 ~]# systemctl daemon-reload
    [root@node1 ~]# systemctl start docker
  • 运行容器,测试效果

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    [root@node1 ~]# docker run -it busybox
    / # ip a
    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 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
    38: eth0@if39: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
    link/ether 02:42:c0:a8:00:02 brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.2/24 brd 192.168.0.255 scope global eth0
    valid_lft forever preferred_lft forever

bridge创建自定义网桥

  • 创建自定义网桥

    1
    2
    3
    4
    5
    [root@node1 ~]# docker network create docker1 --subnet=192.17.1.0/16 -o com.docker.network.bridge.name=docker1
    8fdfc08340243e1b6cbf0d0d3b0fb08ee60f611dd267675511dae5d73c3b8998
    # --subnet=192.17.1.0/16: 指定IP网段
    # -o com.docker.network.bridge.name=docker1: 指定设备名称
    #Error response from daemon: Pool overlaps with other one on this address space 那就换一个网段
  • 查看网络

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    [root@node1 ~]# docker network ls
    NETWORK ID NAME DRIVER SCOPE
    5d7b7033ec88 bridge bridge local
    8fdfc0834024 docker1 bridge local
    dbefd1fb2d23 host host local
    cbb819178e6e mysql_default bridge local
    85844dc32a26 none null local


    [root@node1 ~]# ip addr show docker1
    40: docker1: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
    link/ether 02:42:4e:ea:a5:94 brd ff:ff:ff:ff:ff:ff
    inet 192.17.0.1/16 brd 192.17.255.255 scope global docker1
    valid_lft forever preferred_lft forever

  • 运行容器,使用docker1网络

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    [root@node1 ~]# docker run -it --network docker1 busybox
    / # ip a
    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 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
    41: eth0@if42: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
    link/ether 02:42:c0:11:00:02 brd ff:ff:ff:ff:ff:ff
    inet 192.17.0.2/16 brd 192.17.255.255 scope global eth0
    valid_lft forever preferred_lft forever

    #运行容器,指定ip地址
    [root@docker ~]# docker run --rm -it --network docker1 --ip 192.17.0.100 busybox
  • 删除自定义网桥

    1
    [root@node1 ~]# docker network rm docker1
  • 自定义网桥与默认docker0通信

    1
    2
    3
    4
    # docker 在设计上就是要隔离不同的 netwrok。
    # 由于iptables DROP 掉了网桥 docker0 之间双向的流量。只能采取为 容器添加一块docker1的网卡来实现通信
    # docker network connect [OPTIONS] NETWORK CONTAINER
    [root@node1 ~]# docker network connect docker1 506505

    点到点连接

    • 用户有时候需要两个容器之间可以直连通信,而不用通过主机网桥进行桥接。

    • 解决办法很简单: 创建一对peer接口,分别放到两个容器中,配置成点到点链路类型即可。

    • 启动两个容器

      1
      2
      [root@node1 ~]# docker run -it --net=none --name=box1 busybox
      [root@node1 ~]# docker run -it --net=none --name=box2 busybox
    • 创建网络命名空间的跟踪文件

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      # 找到容器进程号,并创建网络命名空间的跟踪文件
      [root@node1 ~]# docker inspect -f '{{.State.Pid}}' box1
      68829
      [root@node1 ~]# docker inspect -f '{{.State.Pid}}' box2
      69215

      [root@node1 ~]# mkdir -p /var/run/netns
      [root@node1 ~]# ln -s /proc/69215/ns/net /var/run/netns/68829
      [root@node1 ~]# ln -s /proc/69215/ns/net /var/run/netns/69215

    • 创建一对peer接口

      1
      2
      # 创建一对peer接口 创建虚拟网络接口A,并为A创建一个映射端设备B
      [root@docker ~]# ip link add A1 type veth peer name B1
    • 添加IP地址和路由信息

      1
      2
      3
      4
      5
      6
      7
      8
      9
      # 将设备A加入到创建的网络
      [root@docker ~]# ip link set A1 netns 68829
      [root@docker ~]# ip netns exec 68829 ip addr add 10.1.1.1/32 dev A1
      [root@docker ~]# ip netns exec 68829 ip link set A1 up
      [root@docker ~]# ip netns exec 68829 ip route add 10.1.1.2/32 dev A1
      [root@docker ~]# ip link set B1 netns 69215
      [root@docker ~]# ip netns exec 69215 ip addr add 10.1.1.2/32 dev B1
      [root@docker ~]# ip netns exec 69215 ip link set B1 up
      [root@docker ~]# ip netns exec 69215 ip route add 10.1.1.1/32 dev B1

docker网络模型

  1. 容器网络模型包括三种基本元素:

    • 沙盒(Sandbox): 代表一个容器(准确地说,是其网络命名空间);
    • 接入点(Endpoint): 代表网络上可以挂载容器的接口,会分配IP地址;
    • 网络(Network): 可以连通多个接入点的一个子网。
  2. 对于使用CNM的容器管理系统来说,具体底下网络如何实现,不同子网彼此怎么隔离,有没有QoS,都不关心。只要插件能提供网络和接入点,只需把容器给接上或者拔下,剩下的都是插件驱动自己去实现,这样就解耦了容器和网络功能,十分灵活。

  3. CNM的典型生命周期

    • 首先,驱动注册自己到网络控制器,网络控制器使用驱动类型,来创建网络;然后在创建的网络上创建接口;最后把容器连接到接口上即可。销毁过程则正好相反,先把容器从接入口上卸载,然后删除接入口和网络即可。

  4. CNM的典型生命周期目前CNM支持的驱动类型有四种:

    • Null: 不提供网络服务,容器启动后无网络连接;
    • Bridge: 就是Docker传统上默认用Linux网桥和Iptables实现的单机网络;
    • Overlay: 是用vxlan隧道实现的跨主机容器网络;
    • Remote: 扩展类型,预留给其他外部实现的方案,比如有一套第三方的SDN方案(如OpenStack Neutron)就可以接进来。
  5. 从位置上看,libnetwork往上提供容器支持,往下隐藏实现差异,自身处于十分关键的中间层。libnetwork可以类比为最核心的TCP/IP层。目前,已有大量的网络方案开始支持libnetwork。