Docker学习第三天——Docker网络

摘要

Docker学习之旅第三天——Docker 网络。看完这篇文章将收获docker中网络相关的部分知识:

  • 单机上的Docker容器之间是如何通信的?
  • Docker容器是如何访问外网的?
  • 多个容器之间如何建立关系?
  • 多台机器上的Docker如何通信

环境准备

  • 两台安装了Docker的虚拟机,我使用的是Vagrant搭建的多台含有Docker的虚拟机。

    Vagrantfile文件内容:

    # -*- mode: ruby -*-
    # vi: set ft=ruby :
    
    Vagrant.configure("2") do |config|
      
      config.vm.define "vagrant1" do |vb|
          config.vm.provider "virtualbox" do |v|
          v.memory = 1024
          v.cpus = 1
        end
        vb.vm.host_name = "vagrant1"
        vb.vm.network :public_network, ip: "192.168.1.21"
        vb.vm.box = "centos/7"
    
        config.vm.provision "shell", inline: <<-SHELL
          sudo yum update
          sudo curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun
        SHELL
      end
    
      config.vm.define "vagrant2" do |vb|
          config.vm.provider "virtualbox" do |v|
          v.memory = 1024
          v.cpus = 1
        end
        vb.vm.host_name = "vagrant2"
        vb.vm.network :public_network, ip: "192.168.1.22"
        vb.vm.box = "centos/7"
    
        config.vm.provision "shell", inline: <<-SHELL
          sudo yum update
          sudo curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun
        SHELL
      end
    
      config.vm.define "vagrant3" do |vb|
          config.vm.provider "virtualbox" do |v|
          v.memory = 1024
          v.cpus = 1
        end
        vb.vm.host_name = "vagrant3"
        vb.vm.network :public_network, ip: "192.168.1.23"
        vb.vm.box = "centos/7"
    
        config.vm.provision "shell", inline: <<-SHELL
          sudo yum update
          sudo curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun
        SHELL
      end
    
    end
    

Linux 网络命名空间

创建一个一直运行的容器:

docker run -d --name busybox busybox /bin/sh -c "while true; do sleep 3600; done"

在这里插入图片描述

进入该容器查看该容器的网络命名空间:

docker exec -it 14f669c35a2f /bin/sh	# 14f669c35a2f为运行的容器id

在这里插入图片描述

  • 每个容器都有自己的网络命名空间,并且每个容器的是不同的。
  • 但是在同一台虚拟机上的不同容器间是可以相互ping通的。

查看已有的网络命名空间

sudo ip netns list

在这里插入图片描述

发现该虚拟机中目前并没有存在的网络命名空间。

增加网络命名空间

sudo ip netns add test1

在这里插入图片描述

发现有一个叫做net_space1的网络命名空间。

删除网络命名空间

sudo ip netns delete net_space1

在这里插入图片描述

  • 可以通过exec命令查看创建的网络命名空间具体内容:

    [vagrant@vagrant1 root]$ sudo ip netns exec test1 ip a
    1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
        link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    

    发现创建的test1网络命名空间没有ip,并且只有一个本地接口,状态为DOWN未运行。

    查看虚拟机本地的网络link, 一共有6个网络连接,5个为UP运行状态,1个为UNKNOWN状态:
    在这里插入图片描述

    刚才创建的test1的ip link是DOWN状态执行命令:sudo ip netns exec test1 ip link set dev lo up可以拉起本地网络接口, 拉起后发现状态为UNKNOWN状态而不是UP状态:
    在这里插入图片描述

    这是因为链接为UP状态必须要两端都是连接起来的,就好像两台电脑要想成功链接就必须使用一根网线来连接起来,单独启动一台电脑而不使用网线链接两台电脑,就是UNKNOWN状态。

    Linux的网络命名空间也是这个道理,我们创建了两个网络命名空间,但是都是独立的,并没有相互连接起来,要想使状态变为UP状态就必须使用叫做Veth pair的linux技术连接起来:
    在这里插入图片描述

连接两个网络命名空间

  1. 使用命令sudo ip link veth-test1 type veth peer name veth-test2创建veth网络接口:
    在这里插入图片描述

    执行完后发现本地的网络命名空间多了两个接口, 这两个接口都有MAC地址但是没有ip, 状态都为DOWN。

  2. 使用命令sudo ip link set veth-test1 netns test1将veth-test1接口添加到test1网络命名空间的link中:

    执行完查看test1的网络link发现多了一个网络接口(就是我们刚才设置的veth-test1):
    在这里插入图片描述

    在查看下本地的ip link 发现veth-test1接口不见了:
    在这里插入图片描述

  3. 同理使用命令sudo ip link set veth-test2 netns test2给网络命名空间test2添加link。添加完后查看下本地link,发现veth-test2也没有了。

  4. 分配ip地址

    这样两个网络命名空间就已经连接了,查看下test1和test2:
    在这里插入图片描述

    发现这几个接口都没有ip地址而且都是DOWN的状态(test1是UNKNOWN是因为前边拉起过)。

    现在需要分别为veth-test1和veth-test2分配ip地址:

    [vagrant@vagrant1 root]$ sudo ip netns exec test1 ip addr add 192.168.1.1/24 dev veth-test1
    [vagrant@vagrant1 root]$ sudo ip netns exec test2 ip addr add 192.168.1.2/24 dev veth-test2
    

    分配完后查看下test1和test2的ip详情,发现已经有了ip地址:
    在这里插入图片描述

  5. 拉起状态

    有了ip地址后需要做的就是拉起状态了:

    [vagrant@vagrant1 root]$ sudo ip netns exec test1 ip link set dev veth-test1 up
    [vagrant@vagrant1 root]$ sudo ip netns exec test2 ip link set dev veth-test2 up
    

    查看下test1的link内容,发现已经是UP状态并且有了ip地址,test2是一样的:
    在这里插入图片描述

到此为止,两个网络命名空间test1和test2就已经互通了:
在这里插入图片描述

Docker bridge网络

注:接下来使用到的命令brctl show 和 docker inspect network 查看桥接网络与建立联系的容器间的关系都是容器在运行中才会显示的。

上边实现的两个连通的自定义网络命名空间只能相互间访问,无法访问外网,接下来研究docker中的容器是如何访问外网的。

首先使用命令sudo docker network ls查看docker中的网络:

[vagrant@vagrant1 ~]$ sudo docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
e284c339aa0b        bridge              bridge              local
ecd6a29a11fe        host                host                local
9ac287424e80        none                null                local

我们前边创建的busybox容器是可以访问外网的,这个容器其实是桥接到了docker的bridge网络才能访问外网的:
在这里插入图片描述

通过命令sudo docker network inspect bridge查看下docker的桥接网络详情,发现两个docker容器都连接到了bridge网络:
在这里插入图片描述

docker bridge网络原理

为什么桥接到了docker的bridge网络就可以访问外网了呢?

首先使用命令ip a查看下虚拟机的网络命名空间:
在这里插入图片描述

每一个网络接口都有自己的作用,但是却多出了两个并不知道是干嘛的网络接口,想想如果容器想要访问外网就必须和docker0接口搭上关系,我想你大概已经猜到了:通过桥接的方式。

通过一个命令brctl show可以查看linux桥接网络情况,可以通过sudo yum install bridge-utils下载:
在这里插入图片描述

现在多出来的这两个接口和docker0接口是桥接的搞清楚了,那么busybox和registry容器是如何和这两个接口搭上关系的呢?这和上边连接自定义网络命名空间的原理是一样的:都是需要两个eth接口来连通容器和多出来的接口的。

这样关系就明白了:

docker0桥接到虚拟机多出来的接口,容器在构建一个额外的接口连接多出来的接口,这样容器就与虚拟主机建立了连接,并且容器间也通过docker0间接建立了连接:所以此时容器间可以相互通信。
在这里插入图片描述

但是单独的容器是如何访问外网的呢?
在这里插入图片描述

要想访问外网只有通过eth0网络接口,docker0通过网络地址转换(NAT)将内网地址转换为外网地址,就可以通过eth0访问外网了。

容器之间的Link

容器之间是可以相互建立连接的(不仅仅是相互通信),比如说一个后端程序需要用到SQL数据库和Redis数据库,而这几个数据库和后端程序是分离的,放在不同的容器之中。

在使用数据库容器的时候可以通过传递命令(传递ip地址等信息)的方式去建立连接,但是有时候ip等信息可能会变动,而且这些东西又不好记。

这个时候就需要容器之间相互关联,只需要起个见名知意的容器名就可以了,使用到的时候直接用容器名字就可以了。

在运行一个容器是可以通过参数--link来连接两个容器:

[vagrant@vagrant1 ~]$ sudo docker run -d --name busybox2 --link busybox busybox /bin/sh -c "while true; do sleep 3600; done"5fdd1706757b7b182ee1e257b9b015e512d1e900eb5503e27837c52c9f8b9772

这里我们新创了一个busybox2容器来和上次创的busybox容器建立联系。

我们进入这个buxybox2容器直接ping容器busybox的容器名是可以ping通的:
在这里插入图片描述

但是需要注意的是link是单向连接,我们进入到busybox中去ping容器busybox2是ping不通的:
在这里插入图片描述

但是通常不直接使用link来建立容器之间的连接,而是通过构建一个单独的桥接网络,在将两个容器都与这个桥接网络建立连接,这样两个容器间就建立了连接。

  1. 构建自己的bridge网络

    [vagrant@vagrant1 ~]$ sudo docker network create -d bridge my-bridge
    9e19ca6756cdab106e3b10d35e424cf852107e34969599d15c16f8a4eced59c4
    
    

    -d指定驱动为bridge, 构建完后查看下已有的docker网络,发现多了个my-bridge:

    [vagrant@vagrant1 ~]$ docker network ls
    NETWORK ID          NAME                DRIVER              SCOPE
    e284c339aa0b        bridge              bridge              local
    ecd6a29a11fe        host                host                local
    9e19ca6756cd        my-bridge           bridge              local
    9ac287424e80        none                null                local
    

    目前该桥接网络还没有与任何接口进行桥接,使用命令brctl show查看桥接情况:

    [vagrant@vagrant1 ~]$ brctl show
    bridge name     bridge id               STP enabled     interfaces
    br-9e19ca6756cd         8000.0242921ba6dc       no              
    docker0         8000.0242f4f2d8d3       no              veth7674b4b
                                                            veth978d23c
                                                            vethfe7385e
    
    
  2. 建立bridge网络与容器之间联系

    • 新建立的容器与my-bridge网络建立联系:使用--network命令指定网络

      [vagrant@vagrant1 ~]$ sudo docker run -d --name busybox3 --network my-bridge       busybox /bin/sh -c "while true; do sleep 3600; done"
      967ebe56dadcf08cc274beaa10ee98de35ea84a1b36f6c8a2d31fd87a946c818
      

      使用brctl show命令查看下桥接网络情况:

      [vagrant@vagrant1 ~]$ brctl show
      bridge name     bridge id               STP enabled     interfaces
      br-9e19ca6756cd         8000.024293486c85       no              veth3b27801
      docker0         8000.02425c4d17a5       no              vethc6f1d6b
      
      

      可以发现新建立的my-bridge网络br-9e19ca6756cd已经与网络接口veth3b27801桥接。

      也可以使用命令sudo docker inspect network my-bridge来查看建立连接的容器:
      在这里插入图片描述

    • 已有的容器与my-bridge网络建立联系:

      对于已经存在的容器可以使用docker network connect命令来建立容器与网络的联系:

      [vagrant@vagrant1 ~]$ sudo docker network connect my-bridge busybox2
      

      使用命令docker inspect network my-bridge查看与my-bridge桥接网络建立联系的容器,可以发现busybox2和busybox3容器都与该网络建立了桥接:
      在这里插入图片描述

      需要注意的是一个容器可以与多个网络建立联系,busybox2在建立容器时就使用的是默认的bridge桥接网络,使用命令docker inspect network bridge查看下:
      在这里插入图片描述

  3. 测试容器之间是否已经连通

    使用命令docker exec -it busybox2 /bin/shdocker exec -it busybox3 /bin/sh分别ping对方的名字都是可以ping通的。
    在这里插入图片描述

  • 因此,如果想要多个容器之间可以互通,一般可以建立一个桥接网络,然后将这多个容器与该桥接网络建立联系即可。

容器的端口映射

运行容器时使用-p参数可以进行端口映射。

例如运行一个nginx容器让浏览器访问时输入的是8888端口而不是默认的80端口:

docker run --name nginx_8888 -d -p 8888:80 nginx

其中80是nginx容器的端口,8888是容器建立的桥接网络的端口。
在这里插入图片描述

查看浏览器:
在这里插入图片描述

容器网络之host和none

none网络

首先运行一个网络驱动为none的容器:

docker run -d --name busybox1 --network none busybox /bin/sh -c "while true; do sleep 3600;done"

然后查看下none网络详情:

docker inspect network none

在这里插入图片描述

发现busybox1确实和none网路建立了联系,但是none网络没有mac地址也没有ip地址。

进入容器内部查看ip也是只有本地接口没有别的接口:
在这里插入图片描述

这意味着none网络只能通过本地(127.0.0.1)访问,无法通过其他方式访问。

host网络

首先运行一个网络驱动为host的容器:

docker run -d --name busybox1 --network none busybox /bin/sh -c "while true; do sleep 3600;done"

然后查看下host网络详情:

docker inspect network host

在这里插入图片描述

发现host网络也没有给容器分配ip地址和mac地址。

再进入busybox2内部查看下ip详情,发现和本地(虚拟机)的网络命名空间是一样的:

busybox2的网络命名空间:
在这里插入图片描述

本地的网络命名空间:
在这里插入图片描述

  • host网络模式就是使容器和本地共用一套网络命名空间,这样做既有优点也有缺点:
    • 优点:这样我们在运行容器的时候不需要考虑端口映射的问题,因为使用的是同一套端口。
    • 缺点:但是容易造成端口冲突,比如说部署两个nginx容器默认使用的都是80端口就会造成端口冲突。

多容器复杂应用的部署

应用场景

部署一个flask项目,该项目使用了redis数据库,redis和flask程序分别部署到不同的容器中。

Dockerfile内容

FROM python:2.7
LABEL maintaner="Peng Xiao xiaoquwl@gmail.com"
COPY . /app
WORKDIR /app
RUN pip install flask redis
EXPOSE 5000
CMD [ "python", "app.py" ]

app.py内容

from flask import Flask
from redis import Redis
import os
import socket

app = Flask(__name__)
redis = Redis(host=os.environ.get('REDIS_HOST', '127.0.0.1'), port=6379)


@app.route('/')
def hello():
    redis.incr('hits')
    return 'Hello Container World! I have been seen %s times and my hostname is %s.\n' % (redis.get('hits'),socket.gethostname())


if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000, debug=True)
  1. build镜像

    docker build -t flask_5000_redis_6379/flask-redis .
    
  2. 运行容器

    [vagrant@vagrant2 flask-redis]$ docker run -d --name flask-redis --link redis -e REDIS_HOST=redis -p 5000:5000 flask_5000_redis_6379/flask-redis
    

    -e参数是传递环境变量参数,进入容器查看下环境变量, 可以看到我们设置的环境变量:

    docker exec -it flask-redis /bin/sh
    

在这里插入图片描述

  1. 验证程序
    在这里插入图片描述

多机器通信

上边的docker实验都是在单机上进行的,接下来进行多机器间的docker实验。

首先在两台虚拟机上搭建etcd存储集群,两台虚拟机的ip分别为192.168.43.21192.168.43.22,hostname分别为docker-node1和docker-node2。

搭建步骤

  1. 在docker-node1上:
vagrant@docker-node1:~$ wget https://github.com/coreos/etcd/releases/download/v3.0.12/etcd-v3.0.12-linux-amd64.tar.gz
vagrant@docker-node1:~$ tar zxvf etcd-v3.0.12-linux-amd64.tar.gz
vagrant@docker-node1:~$ cd etcd-v3.0.12-linux-amd64
vagrant@docker-node1:~$ nohup ./etcd --name docker-node1 --initial-advertise-peer-urls http://192.168.43.21:2380 \
--listen-peer-urls http://192.168.43.21:2380 \
--listen-client-urls http://192.168.43.21:2379,http://127.0.0.1:2379 \
--advertise-client-urls http://192.168.43.21:2379 \
--initial-cluster-token etcd-cluster \
--initial-cluster docker-node1=http://192.168.43.21:2380,docker-node2=http://192.168.43.22:2380 \
--initial-cluster-state new&

wget太慢可以通过这个网站下载后通过sftp传上去。

  1. 在docker-node2上
vagrant@docker-node2:~$ wget https://github.com/coreos/etcd/releases/download/v3.0.12/etcd-v3.0.12-linux-amd64.tar.gz
vagrant@docker-node2:~$ tar zxvf etcd-v3.0.12-linux-amd64.tar.gz
vagrant@docker-node2:~$ cd etcd-v3.0.12-linux-amd64/
vagrant@docker-node2:~$ nohup ./etcd --name docker-node2 --initial-advertise-peer-urls http://192.168.43.22:2380 \
--listen-peer-urls http://192.168.43.22:2380 \
--listen-client-urls http://192.168.43.22:2379,http://127.0.0.1:2379 \
--advertise-client-urls http://192.168.43.22:2379 \
--initial-cluster-token etcd-cluster \
--initial-cluster docker-node1=http://192.168.43.21:2380,docker-node2=http://192.168.43.22:2380 \
--initial-cluster-state new&

搭建完毕后检查集群是否搭建成功:

vagrant@docker-node2:~/etcd-v3.0.12-linux-amd64$ ./etcdctl cluster-health

出现如下为成功:

member 21eca106efe4caee is healthy: got healthy result from http://192.168.43.21:2379
member 8614974c83d1cc6d is healthy: got healthy result from http://192.168.43.22:2379
cluster is healthy
  1. 重启docker服务

    在docker-node1上:

    $ sudo service docker stop
    $ sudo /usr/bin/dockerd -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock --cluster-store=etcd://192.168.43.21:2379 --cluster-advertise=192.168.43.21:2375&
    

    在docker-node2上

    $ sudo service docker stop
    $ sudo /usr/bin/dockerd -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock --cluster-store=etcd://192.168.43.22:2379 --cluster-advertise=192.168.43.22:2375&
    
  2. 创建overlay network

    vagrant@docker-node1:~$ sudo docker network ls
    NETWORK ID          NAME                DRIVER              SCOPE
    0e7bef3f143a        bridge              bridge              local
    a5c7daf62325        host                host                local
    3198cae88ab4        none                null                local
    vagrant@docker-node1:~$ sudo docker network create -d overlay demo
    3d430f3338a2c3496e9edeccc880f0a7affa06522b4249497ef6c4cd6571eaa9
    vagrant@docker-node1:~$ sudo docker network ls
    NETWORK ID          NAME                DRIVER              SCOPE
    0e7bef3f143a        bridge              bridge              local
    3d430f3338a2        demo                overlay             global
    a5c7daf62325        host                host                local
    3198cae88ab4        none                null                local
    

    我们会看到在node2上,这个demo的overlay network会被同步创建:

    vagrant@docker-node2:~$ sudo docker network ls
    NETWORK ID          NAME                DRIVER              SCOPE
    c9947d4c3669        bridge              bridge              local
    3d430f3338a2        demo                overlay             global
    fa5168034de1        host                host                local
    c2ca34abec2a        none                null                local
    

到此我们的简单集群算搭建成功了,现在在docker-node1虚拟机上使用overlay网络创建一个名称为test1的busybox容器:

[vagrant@vagrant1 etcd-v3.0.12-linux-amd64]$ sudo docker run -d --name test1 --net demo busybox sh -c "while true ; do sleep 3600; done"

再到另一台虚拟机上也创建一个名为test1的busybox容器,会报错test1名字已经被使用了,这就是ectd会在集群中查找到test1被使用了就不能再用了:
在这里插入图片描述

换个名字就可以创建了:

docker run -d --name test2 --net demo busybox sh -c "while true; do sleep 3600; done"

查看下test1容器和test2容器的ip:
在这里插入图片描述
在这里插入图片描述

在查看下overlay网络的详情,发现containers中包含了test1和test2的ip:
在这里插入图片描述

这个时候两个不同机器上的不同容器间是可以相互通信的:

test1容器ping test2容器:
在这里插入图片描述
同样的test2ping test1容器也是可以ping通的。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一切如来心秘密

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值