在一台linux机器上,不管创建多少个docker容器,他们都有属于自己的ip地址,并且可以相互ping通访问。
为什么会ping通?其中的原理是什么?
预备工作
-
创建一个基于busybox的容器
|
1| ``` sudo docker run -d --name test1 busybox /bin/sh -c "while true; do sleep 3600; done"| ---------- | --------------------------------------------------------------------------------------------- | -
输入
ip -a命令即可查看当前容器下的网络ip地址和命名空间
通过实现linux的network namespace实现两个namespace相通
创建两个network namespace
| 1 2 3 | ```
sudo ip netns add test1 sudo ip netns add test2 sudo ip link add veth-test1 type veth peer name veth-test2
| -------------- | ------------------------------------------------------------------------------------------------------------------- |

### [](#linux%E4%B8%8B%E6%9F%A5%E7%9C%8Bnetwork-namespace "linux下查看network namespace")linux下查看network namespace
| ```
1
``` | ```
ip link
``` |
| ---------- | ---------------- |

可以看到多了两个命名空间,但是只有mac地址,没有ip地址,而且他们现在的状态都是down的
### [](#%E5%B0%86veth-test1%E6%8E%A5%E5%8F%A3%E6%B7%BB%E5%8A%A0%E5%88%B0test1%E4%B8%8A%E5%8E%BB%EF%BC%8C%E5%90%8C%E6%97%B6%E5%B0%86veth-test2%E6%8E%A5%E5%8F%A3%E6%B7%BB%E5%8A%A0%E5%88%B0test2%E4%B8%8A%E5%8E%BB "将veth-test1接口添加到test1上去,同时将veth-test2接口添加到test2上去")将veth-test1接口添加到test1上去,同时将veth-test2接口添加到test2上去
| ```
1
``` | ```
sudo ip link set veth-test1 netns test1
``` |
| ---------- | ------------------------------------------------ |
然后可以看到本地的network namespace少了一个

\
同时在test1的命名空间里,多了一个

### [](#test2%E5%90%8C%E7%90%86 "test2同理")test2同理
| ```
1
``` | ```
sudo ip link set veth-test2 netns test2
``` |
| ---------- | ------------------------------------------------ |
### [](#%E6%AD%A4%E6%97%B6%E4%B8%A4%E8%80%85%E7%9A%84%E7%8A%B6%E6%80%81 "此时两者的状态")此时两者的状态

\
还是没有ip地址,只有mac地址
### [](#%E5%88%86%E9%85%8Dip%E5%9C%B0%E5%9D%80 "分配ip地址")分配ip地址
| ```
1 2
``` | ```
sudo ip netns exec test1 ip addr add 192.168.1.1/24 dev veth-test1 sudo ip netns exec test2 ip addr add 192.168.1.2/24 dev veth-test2
``` |
| ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- |
### [](#%E5%B0%86%E4%B8%A4%E4%B8%AAveth-test%E7%AB%AF%E5%8F%A3%E5%90%AF%E5%8A%A8 "将两个veth-test端口启动")将两个veth-test端口启动
| ```
1 2
``` | ```
sudo ip netns exec test1 ip link set dev veth-test1 up sudo ip netns exec test2 ip link set dev veth-test2 up
``` |
| ------------ | ---------------------------------------------------------------------------------------------------------------------- |
### [](#%E6%9F%A5%E7%9C%8B%E4%B8%A4%E4%B8%AAnamespace%E7%9A%84ip%E5%8F%8A%E7%8A%B6%E6%80%81 "查看两个namespace的ip及状态")查看两个namespace的ip及状态

可以看到都有了ip地址,状态都为up。
可以看到test1能够ping通test2的ip地址

**这个实验与docker的container网络实现的原理类似。我们创建一个容器,随之也会创建属于这个容器的network namespace**
## [](#Docker%E7%9A%84%E7%BD%91%E7%BB%9C%E5%AE%9E%E7%8E%B0 "Docker的网络实现")Docker的网络实现
我现在有一个容器,基于busybox的一个微型image构建的容器

查看docker的network

查看docker的network的详细信息
| ```
1
``` | ```
docker network inspect a11a8b74c466(network的id)
``` |
| ---------- | -------------------------------------------------------- |
在输出的信息里面会看到关于test1的相关信息

我们可以知道test1的container连接到了bridge的网络上面。
查看本机的ip信息

再来查看test1的container的ip相关信息

给结论:**test1的eth0\@if6与本机的veth821ee0b\@if5相对应,container最终要映射到本机的docker0的network namespace上去**
### [](#%E9%AA%8C%E8%AF%81 "验证")验证
#### [](#%E5%AE%89%E8%A3%85bridge-utils "安装bridge-utils")安装bridge-utils
| ```
1
``` | ```
sudo yum install bridge-utils
``` |
| ---------- | -------------------------------------- |
#### [](#%E8%BF%90%E8%A1%8C%E5%91%BD%E4%BB%A4%EF%BC%9Abrctl-show "运行命令:brctl show")运行命令:`brctl show`

可以看到我们的veth821ee0b接口连接到了docker0的namespace上
#### [](#%E7%8E%B0%E5%9C%A8%E6%88%91%E4%BB%AC%E5%86%8D%E5%88%9B%E5%BB%BA%E4%B8%80%E4%B8%AA%E5%AE%B9%E5%99%A8%EF%BC%9A "现在我们再创建一个容器:")现在我们再创建一个容器:
| ```
1
``` | ```
sudo docker run -d --name test2 busybox /bin/sh -c "while true; do sleep 3600; done"
``` |
| ---------- | --------------------------------------------------------------------------------------------- |
再次查看网络信息,可以看到多了一个接口

然后看到bridge可以对应到两个container

再来运行命令:`brctl show`

可以看到我们的veth821ee0b接口以及vethbbbd3b9都连接到了docker0的namespace上
#### [](#%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86%E7%9A%84%E6%8B%93%E6%89%91%E5%9B%BE "实现原理的拓扑图")实现原理的拓扑图

两个容器分别是两个不同的network namespace,两者通过docker0进行连接。
单个容器如何访问外网?**通过本机的docker0的network namespace**

## [](#%E5%AE%B9%E5%99%A8%E4%B9%8B%E9%97%B4%E7%9A%84link "容器之间的link")容器之间的link
在实际项目中我们不能总是依赖于ip地址,如果容器之间需要相互访问还可以通过另外一种方式:**link**
| ```
1
``` | ```
sudo docker run -d --name test2 --link test1 busybox /bin/sh -c "while true; do sleep 3600; done"
``` |
| ---------- | ---------------------------------------------------------------------------------------------------------- |
在我们创建容器的时候加参数:`--link [容器名]`\
即可在我们的容器中不仅可以通过ip地址访问我们想要访问的ip,还可以通过容器名访问

\
我们在容器创建的时候就已经指定了对应的容器了。\
反过来,在test1里面,如果想访问test2的话是不行的,link是单向的,不是双向的\
link用的其实并不多。
## [](#%E8%AE%A9%E5%AE%B9%E5%99%A8%E4%B8%8D%E8%BF%9E%E6%8E%A5bridge%EF%BC%8C%E8%BF%9E%E6%8E%A5%E8%87%AA%E5%AE%9A%E4%B9%89%E7%9A%84network-namespace "让容器不连接bridge,连接自定义的network namespace")让容器不连接bridge,连接自定义的network namespace
重新构建test2容器

### [](#%E6%96%B0%E5%BB%BAnetwork "新建network")新建network
| ```
1
``` | ```
sudo docker network create -d bridge my-bridge
``` |
| ---------- | ------------------------------------------------------- |
可以看到多了一个my-bridge

### [](#%E6%96%B0%E5%BB%BA%E5%AE%B9%E5%99%A8%E6%8C%87%E5%AE%9A%E8%BF%9E%E6%8E%A5%E6%88%91%E8%87%AA%E5%AE%9A%E4%B9%89%E7%9A%84network "新建容器指定连接我自定义的network")新建容器指定连接我自定义的network
| ```
1
``` | ```
sudo docker run -d --name test3 --network my-bridge busybox /bin/sh -c "while true; do sleep 3600; done"
``` |
| ---------- | ----------------------------------------------------------------------------------------------------------------- |

可以看到有了interface,在创建test3之前是没有interface的
### [](#%E4%BF%AE%E6%94%B9test1%E5%92%8Ctest2%E8%BF%9E%E6%8E%A5%E7%9A%84network "修改test1和test2连接的network")修改test1和test2连接的network
| ```
1
``` | ```
sudo docker network connect my-bridge test2
``` |
| ---------- | ---------------------------------------------------- |
查看my-bridge的信息

\
查看bridge的信息

\
可以看到test2既连接到了bridge上,又连接到了my-bridge上

在test2上既可以通过ip地址访问test3,又可以通过test3的名称访问test3,这是因为在用户自定义的bridge上的所有容器之间都是默认加了link去进行相互之间的访问的,而且是双向的。这就是系统默认的bridge和用户自定义bridge的区别,在系统默认的bridge上的container默认是不支持link连接的。
## [](#%E5%AE%B9%E5%99%A8%E7%9A%84%E7%AB%AF%E5%8F%A3%E6%98%A0%E5%B0%84 "容器的端口映射")容器的端口映射
### [](#%E5%88%9B%E5%BB%BA%E4%B8%80%E4%B8%AAnginx%E5%AE%B9%E5%99%A8 "创建一个nginx容器")创建一个nginx容器
| ```
1
``` | ```
sudo docker run --name web -d nginx
``` |
| ---------- | -------------------------------------------- |
### [](#%E6%9F%A5%E7%9C%8Bngingx%E7%9A%84ip%E5%9C%B0%E5%9D%80 "查看ngingx的ip地址")查看ngingx的ip地址
| ```
1
``` | ```
sudo docker network inspect bridge
``` |
| ---------- | ------------------------------------------- |

为172.17.0.4
### [](#%E8%AE%BF%E9%97%AEnginx "访问nginx")访问nginx

可以访问到
**如何让我的docker-node1对外提供nginx服务呢?就是端口映射**
### [](#%E5%81%9C%E6%AD%A2%E5%B9%B6%E5%88%A0%E9%99%A4web%EF%BC%88nginx%EF%BC%89%E5%AE%B9%E5%99%A8 "停止并删除web(nginx)容器")停止并删除web(nginx)容器
| ```
1 2
``` | ```
sudo docker stop web sudo docker rm web
``` |
| ------------ | ------------------------------------------------ |
### [](#%E9%87%8D%E6%96%B0%E6%9E%84%E5%BB%BA%EF%BC%8C%E4%BD%86%E6%98%AF%E8%A6%81%E5%8A%A0%E5%8F%82%E6%95%B0 "重新构建,但是要加参数")重新构建,但是要加参数
| ```
1
``` | ```
sudo docker run --name web -d -p 80:80 nginx
``` |
| ---------- | ----------------------------------------------------- |

由于我在构建的时候指定了我的docker-node1的ip地址为192.168.205.10,所以在我本地的浏览器中访问[http://192.168.205.10/,即可](http://192.168.205.10/%EF%BC%8C%E5%8D%B3%E5%8F%AF)

## [](#none-network "none network")none network
### [](#%E6%96%B0%E5%BB%BA%E4%B8%80%E4%B8%AA%E8%BF%9E%E6%8E%A5%E5%88%B0none%E7%9A%84network%E7%9A%84%E5%AE%B9%E5%99%A8 "新建一个连接到none的network的容器")新建一个连接到none的network的容器
| ```
1
``` | ```
sudo docker run -d --name none-test --network none busybox /bin/sh -c "while true;do sleep 3600;done"
``` |
| ---------- | -------------------------------------------------------------------------------------------------------------- |
### [](#%E6%9F%A5%E7%9C%8Bnone%E7%9A%84%E7%8A%B6%E6%80%81 "查看none的状态")查看none的状态
| ```
1
``` | ```
sudo docker network inspect none
``` |
| ---------- | ----------------------------------------- |

可以看到没有任何的mac的地址和IP地址
### [](#%E8%BF%9B%E5%85%A5%E5%AE%B9%E5%99%A8 "进入容器")进入容器
| ```
1 2
``` | ```
sudo docker exec -it none-test /bin/sh ip a
``` |
| ------------ | ---------------------------------------------------- |

没有任何接口和地址
### [](#%E5%AE%83%E7%9A%84%E4%BD%9C%E7%94%A8%EF%BC%9F "它的作用?")它的作用?
可能是在存储一些密码等敏感信息的时候出于安全性考虑只有在容器内部才能进行访问的时候才用这种模式(只是猜测)。对外提供不了服务
## [](#host%E6%96%B9%E5%BC%8F "host方式")host方式
### [](#%E6%96%B0%E5%BB%BA%E4%B8%80%E4%B8%AA%E8%BF%9E%E6%8E%A5%E5%88%B0none%E7%9A%84network%E7%9A%84%E5%AE%B9%E5%99%A8-1 "新建一个连接到none的network的容器")新建一个连接到none的network的容器
| ```
1
``` | ```
sudo docker run -d --name host-test --network host busybox /bin/sh -c "while true;do sleep 3600;done"
``` |
| ---------- | -------------------------------------------------------------------------------------------------------------- |
### [](#%E6%9F%A5%E7%9C%8Bnone%E7%9A%84%E7%8A%B6%E6%80%81-1 "查看none的状态")查看none的状态
| ```
1
``` | ```
sudo docker network inspect host
``` |
| ---------- | ----------------------------------------- |

同样,也没有任何ip地址和mac地址
### [](#%E8%BF%9B%E5%85%A5%E5%AE%B9%E5%99%A8-1 "进入容器")进入容器
| ```
1 2
``` | ```
sudo docker exec -it none-test /bin/sh ip a
``` |
| ------------ | ---------------------------------------------------- |

可以看到与容器的ip状态是一致的
这个容器没有自己的独立的network namespace,它是与我的主机所在的network namespace共享一套。
通过这种方式构建的容器带来的问题:**由于是与容器主机共享的network namespace,意味着端口可能会冲突。比如创建两个nginx的container,都绑定到host的network上去,就会出问题**
## [](#%E6%9E%84%E5%BB%BA%E5%A4%8D%E6%9D%82app "构建复杂app")构建复杂app
### [](#%E5%88%9B%E5%BB%BA%E4%B8%80%E4%B8%AAredis%E5%AE%B9%E5%99%A8 "创建一个redis容器")创建一个redis容器
| ```
1
``` | ```
docker run -d --name redis redis
``` |
| ---------- | ----------------------------------------- |
为什么没有指定端口映射?
* 是因为我这个redis不是对外提供服务的,而是在我的容器内部进行访问的,没必要暴露到外面
### [](#%E5%90%AF%E5%8A%A8%E6%9C%8D%E5%8A%A1 "启动服务")启动服务
| ```
1
``` | ```
sudo docker run -d --link redis -p 5000:5000 --name fast-redis -e REDIS_HOST=redis jinping/flask-redis
``` |
| ---------- | --------------------------------------------------------------------------------------------------------------- |
### [](#%E8%BF%9B%E5%85%A5%E6%9C%8D%E5%8A%A1%EF%BC%8C%E5%8F%AF%E4%BB%A5%E7%9C%8B%E5%88%B0%E7%8E%AF%E5%A2%83%E5%8F%98%E9%87%8F "进入服务,可以看到环境变量")进入服务,可以看到环境变量
| ```
1 2
``` | ```
docker exec -it fast-redis /bin/sh env
``` |
| ------------ | ----------------------------------------------- |
**-e 是设置环境变量的**

在我们当前容器的内部,是能够ping通redis的
所以当我们执行curl 127.0.0.1:5000时,可以输出相应的pv
当我回到我的vagrant时,由于在容器中指定了端口映射,所以一样可以输出pv

**推荐:一个模块一个容器,只要搞清楚他们之间的部署关系就可以了**

## [](#%E5%A4%84%E4%BA%8E%E4%B8%8D%E5%90%8C%E7%9A%84linux%E6%9C%BA%E5%99%A8%E9%97%B4%E7%9A%84docker%E9%80%9A%E4%BF%A1 "处于不同的linux机器间的docker通信")处于不同的linux机器间的docker通信

VXLAN的方式\
分布式存储:etcd<https://github.com/docker/labs/blob/master/networking/concepts/06-overlay-networks.md>