一、背景

Docker之网桥模式 - 图1

web app业务上依赖容器外Redis服务、数据库,利用到Docker Volume机制和部分容器网络知识。以独立容器分别部署web app、nginx容器,docker-compose容器编排工具。

二、应用Docker-compose工具

使用docker镜像作为软件产品的载体,使用docker容器提供独立的软件运行上下文环境,使用docker hub等提供镜像的集中管理,使用Dockerfile定义容器的内部行为和关键属性来支持软件运行。
docker-compose.yml 文件:

  1. version: "3.4"
  2. services:
  3. app:
  4. build:
  5. context: ./app
  6. dockerfile: Dockerfile
  7. expose:
  8. - "80"
  9. extra_hosts:
  10. - "dockerhost:172.19.0.1"
  11. environment:
  12. TZ: Asia/Shanghai
  13. nginx:
  14. build:
  15. context: ./nginx
  16. dockerfile: Dockerfile
  17. ports:
  18. - "80:80"
  19. environment:
  20. TZ: Asia/Shanghai
  21. links:
  22.     - app
  23. logging:
  24. options:
  25. max-size: "200k"
  26. max-file: "10"

三、网桥模式

Docker使用linux桥接,在宿主机虚拟一个Docker容器网桥(docker0),Docker启动一个容器时会根据Docker网桥的网段分配给容器一个IP地址,称为Container-IP,同时Docker网桥是每个容器的默认网关。因为在同一宿主机内的容器都接入同一个网桥,这样容器之间就能够通过容器的Container-IP直接通信。

Docker网桥是宿主机虚拟出来的,并不是真实存在的网络设备,外部网络是无法寻址到的,这也意味着外部网络无法通过直接Container-IP访问到容器。如果容器希望外部访问能够访问到,可以通过映射容器端口到宿主机(端口映射),即docker run创建容器时通过-p或-P参数来启用,访问容器时就通过【宿主机IP】:【容器端口】访问容器

当执行docker-compose up时,会创建新的网桥设备,集合内所有容器都通过该网桥交流:
1)创建名为{project}_default的网桥
2)以服务app名加入{project}_default网络;以服务名nginx加入{project}_default网络;
每一个容器现在可使用“app”,“nginx”服务名作为主机名相互访问

为什么可以通过服务名访问容器?
是利用了Docker引擎内置的DNS,查询服务名—-》查询DNS(每个服务名:对应容器IP)

docker-compose.yml文件中【extra_hosts】的用法:
当前架构中使用的是宿主机的Redis服务,在app容器内不能再使用localhost:6379引用redis服务,因为容器内localhost指向的是容器自身。

查看容器网络:
docker-network.png
【extra_hosts】指令用于主机名映射,定义宿主机在容器内别名。,可通过docker inspect [network_id] 查看宿主机在网桥上的映射IP
docker-inspect.png
在docker-compose.yml 文件中配置了上述【extra_hosts】,在对应的app容器内我们cat /etc/hosts 会发现新增的映射记录

四、Bridge模式的拓扑

image.png
容器运行在自己单独的 network namespace 中,所以有单独的协议栈。容器中配置网关为 172.17.0.1,发出去的数据包先到达 br0,然后交给主机的协议栈,由于目的 IP 是外网 IP,且主机会开启 IP forward 功能,于是数据包通过主机的 eth0 发出去。由于 172.17.0.1 是内网 IP ,所以一般发出去之前会做 NAT 转换。由于要进过主机的协议栈并且要做 NAT 转换,所以性能上可能会差点,但是优点就是容器处于内网中,安全性相对要高点。

五、创建新的单机桥接网络

使用docker network create 命令,创建一个名为“localnet”的单机桥接网络
image.png
image.png

六、同个网络中的容器间通信

使用命令即可运行一个新的容器,并且让这个新容器加入到localnet这个网络中

  1. $ docker container run -d --name demo1 --network localnet alpine sleep 3600

image.png
如果在相同的网络中继续接入新的容器,那么新接入的容器是可以通过 demo1 这个名称来 ping 通的。如下所示,我们创建了一个新的容器(demo2),并且在这个容器中直接 ping demo1 发现可以的 ping 通的。这是因为,demo2 运行了一个本地 DNS 解析器,该解析器会将该请求转发到 Docker 内部 DNS 服务器中。DNS 服务器中记录了容器启动时通过 —name 或者 —net-alias 参数指定的名称和容器之间的和映射关系
image.png
注:Docker 默认的 bridge 网络是不支持通过 Docker DNS 服务进行域名解析的,自定义桥接网络是可以的

七、暴露端口

同一个网络中的容器之间虽然可以互相 ping 通,但是并不意味着可以任意访问容器中的任何服务。Docker 为容器增加了一套安全机制,只有容器自身允许的端口,才能被其他容器所访问。如下所示,我们可以通过 docker container ls 命令可以看到容器暴露给其他容器访问的端口是 80;

  1. [root@VM-24-15-centos logs]# docker container ls
  2. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
  3. 34d7e2d779e7 nginx:1.10 "nginx -g 'daemon of…" 19 hours ago Up 18 hours 80/tcp nginx
  4. [root@VM-24-15-centos logs]#

八、端口映射

上面提到的桥接网络中的容器只能与位于相同网络中的容器进行通信,假如一个容器想对外提供服务的话,需要进行端口映射。端口映射将容器的某个端口映射到 Docker 主机端口上。那么任何发送到该端口的流量,都会被转发到容器中。如图所示,容器内部开放端口为 80,该端口被映射到了 Docker 主机的 192.168.1.101 的 5000 端口上。最终访问 92.168.1.101:5000 的所有流量都会被转发到容器的 80 端口。
image.png

九、相关命令

  1. # 列出运行在本地 docker 主机上的全部网络
  2. docker network ls
  3. # 提供 Docker 网络的详细配置信息
  4. docker network inspect <NETWORK_NAME>
  5. # 创建新的单机桥接网络,名为 localnet,其中 -d 不指定的话,默认是 bridge 驱动。并且主机内核中也会创建一个新的网桥。
  6. docker network create -d bridge localnet
  7. # 删除 Docker 主机上指定的网络
  8. docker network rm <NETWORK_ID>
  9. # 删除主机上全部未使用的网络
  10. docker network prune
  11. # 运行一个新的容器,并且让这个容器加入 Docker localnet 这个网络中
  12. docker container run -d --name demo1 --network localnet alpine sleep 3600
  13. # 运行一个新的容器,并且让这个容器暴露 2220 两个端口
  14. docker container run -d --name web --expose 22 --expose 20 nginx
  15. # 运行一个新的容器,并且将这个容器的 80 端口映射到主机的 5000 端口
  16. docker container run -d --name web --network localnet -p 5000:80 nginx