Docker基本操作(端口?网络模式?)(五)

一、端口暴露

Docker 容器更多情况下是用来运行 Web 应用的,所以要如何访问到容器中的 Web 服务呢?比如我们现在运行一个 nginx 容器服务:

$ docker run --name webserver -d nginx
Unable to find image 'nginx:latest' locally
latest: Pulling from library/nginx
8d691f585fa8: Pull complete
5b07f4e08ad0: Pull complete
abc291867bca: Pull complete
Digest: sha256:922c815aa4df050d4df476e92daed4231f466acc8ee90e0e774951b0fd7195a4
Status: Downloaded newer image for nginx:latest
e8b034c01f4024162cefc45006738fce85b3fa1b717a6ff24520c0fcfabaf5b6

nginx 镜像没有指定 tag 标签,则默认就是拉取nginx:latest镜像;其中--name参数指定容器的名称,不指定则是随机的容器名,运行成功后可以通过docker ps命令查看容器信息:

$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED              STATUS              PORTS               NAMES
e8b034c01f40        nginx               "nginx -g 'daemon of…"   About a minute ago   Up About a minute   80/tcp              webserver

但是我们要怎么去访问这个 nginx 服务呢?实际上在我们启动容器的时候,Docker 就会为我们的容器分配一个 IP 地址,我们可以通过如下命令来获取容器的 IP 地址:

$ docker inspect webserver |grep IPAddress
            "SecondaryIPAddresses": null,
            "IPAddress": "172.17.0.4",
                    "IPAddress": "172.17.0.4",

其中的 172.17.0.4就是容器 webserver 的地址,这个时候我们可以通过该地址访问到 nginx 服务:

$ curl http://172.17.0.4
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    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 and
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>

但是这样不够方便,因为启动容器的代价很小,所以容器的 IP 这些经常变动,是否能够通过宿主机的方式去访问呢?实际上是可以的,我们可以通过如下命令重新启动一个新的容器:

$ docker rm -f webserver
$ docker run --name webserver -d -p 80:80 nginx

我们在启动容器的时候添加了一个新的参数-p 8080:80,这个参数的意思是将宿主机的 8080 端口和容器的 80 端口进行绑定,这样我们就可以通过宿主机的 8080 端口来访问容器服务了。

$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED              STATUS              PORTS                NAMES
89e105d56215        nginx               "nginx -g 'daemon of…"   About a minute ago   Up About a minute   0.0.0.0:8080->80/tcp   webserver
$ curl http://localhost:8080
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    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 and
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>

二、Bridge 模式

当 Docker 进程启动时,会在主机上创建一个名为docker0的虚拟网桥,此主机上启动的 Docker 容器会连接到这个虚拟网桥上。虚拟网桥的工作方式和物理交换机类似,这样主机上的所有容器就通过交换机连在了一个二层网络中。从 docker0 子网中分配一个 IP 给容器使用,并设置 docker0 的 IP 地址为容器的默认网关。在主机上创建一对虚拟网卡veth pair设备,Docker 将 veth pair 设备的一端放在新创建的容器中,并命名为 eth0(容器的网卡),另一端放在主机中,以vethxxx这样类似的名字命名,并将这个网络设备加入到 docker0 网桥中。可以通过brctl show命令查看:

$ brctl show
bridge name bridge id       STP enabled interfaces
docker0     8000.024286df8f39   no      veth1040b0a

bridge 模式是 docker 的默认网络模式,使用docker run -p时,实际上是通过 iptables 做了DNAT规则,实现端口转发功能。可以使用iptables -t nat -vnL查看。bridge模式如下图所示:​​

比如现在我们运行一个 busybox 容器:

$ docker run -tid --net=bridge --name docker_bri busybox top
$ brctl show
brctl show
bridge name bridge id       STP enabled interfaces
docker0     8000.024286df8f39   no      veth1040b0a

然后进入到容器内部去查看网络情况,这里我们需要使用到一个新的命令docker exec,用来进入容器内部,要记住我们要进行终端交互,所以要带上-it两个参数:

$ docker exec -it docker_bri /bin/sh
/ # ifconfig -a
eth0      Link encap:Ethernet  HWaddr 02:42:AC:11:00:05
          inet addr:172.17.0.5  Bcast:172.17.255.255  Mask:255.255.0.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:8 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:648 (648.0 B)  TX bytes:0 (0.0 B)

lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

/ # route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         172.17.0.1      0.0.0.0         UG    0      0        0 eth0
172.17.0.0      0.0.0.0         255.255.0.0     U     0      0        0 eth0

可以通过ip link show命令查看到对应的 veth pair 对名称

通过上面的命令可以验证我们前面提到的 bridge 模式原理。

三、自定义网络

另外我们可以通过自定义的 Docker 网络来连接多个容器,而不是使用--link命令,比如现在我们有一个新的容器想要和上面的 docker_bri 容器建立互连关系,之前我们可以使用 --link 命令:

$ docker run -tid --link docker_bri --name docker_bri1 busybox top
2cba17dd1326c2c82d8fa415588a0169e7291d1a44d2accaff25d50216830777
$ docker exec -it docker_bri1 /bin/sh
/ # ping docker_bri
PING docker_bri (172.17.0.5): 56 data bytes
64 bytes from 172.17.0.5: seq=0 ttl=64 time=0.194 ms
64 bytes from 172.17.0.5: seq=1 ttl=64 time=0.156 ms

我们可以看到在新创建的容器上可以访问到我们连接的容器,但是反过来却不行了,因为--link是单方面的:

$ docker exec -it docker_bri /bin/sh
/ # ping docker_bri1
ping: bad address 'docker_bri1'
/ #

这个时候我们可以通过自定义网络的方式来实现互联互通,首先创建一个自定义的网络:

$ docker network create -d bridge my-net

然后我们使用自定义的网络运行一个容器:

$ docker run -it --rm --name busybox1 --network my-net busybox sh

打开终端再运行一个容器:

$ docker run -it --rm --name busybox2 --network my-net busybox sh
                        

然后我们通过 ping 来证明 busybox1 容器和 busybox2 容器建立了互联关系。 在 busybox1 容器输入以下命令:

/ # ping busybox2
PING busybox2 (172.19.0.3): 56 data bytes
64 bytes from 172.19.0.3: seq=0 ttl=64 time=0.072 ms
64 bytes from 172.19.0.3: seq=1 ttl=64 time=0.118 ms

用 ping 来测试连接 busybox2 容器,它会解析成 172.19.0.3。 同理在 busybox2 容器执行 ping busybox1,也会成功连接到:

/ # ping busybox1
PING busybox1 (172.19.0.2): 56 data bytes
64 bytes from 172.19.0.2: seq=0 ttl=64 time=0.064 ms
64 bytes from 172.19.0.2: seq=1 ttl=64 time=0.143 ms

这样,busybox1 容器和 busybox2 容器建立了互联关系,如果你有多个容器之间需要互相连接,推荐使用后面的 Docker Compose。

四、Host 模式

如果启动容器的时候使用 host 模式,那么这个容器将不会获得一个独立的Network Namespace,而是和宿主机共用一个 Network Namespace。容器将不会虚拟出自己的网卡,配置自己的 IP 等,而是使用宿主机的 IP 和端口。但是,容器的其他方面,如文件系统、进程列表等还是和宿主机隔离的。 Host模式如下图所示:

使用 host 模式也很简单,只需要在运行容器的时候指定 --net=host 即可。

五、Container 模式

这个模式指定新创建的容器和已经存在的一个容器共享一个 Network Namespace,而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的 IP,而是和一个指定的容器共享 IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。两个容器的进程可以通过 lo 网卡设备通信。 Container 模式如下图所示:

在运行容器的时候指定 --net=container:目标容器名 即可。实际上我们后面要学习的 Kubernetes 里面的 Pod 中容器之间就是通过 Container 模式链接到 pause 容器上面的,所以容器直接可以通过 localhost 来进行访问。

六、None 模式

使用 non e模式,Docker 容器拥有自己的 Network Namespace,但是并不为Docker 容器进行任何网络配置。也就是说这个 Docker 容器没有网卡、IP、路由等信息。需要我们自己为 Docker 容器添加网卡、配置 IP 等。 None模式示意图如下所示:

选择这种模式,一般是用户对网络有自己特殊的需求,不希望 docker 预设置太多的东西。

下一篇将介绍Docker数据共享与持久化
(转发请注明出处:http://www.cnblogs.com/zhangyongli2011/ 如发现有错,请留言,谢谢)

posted @ 2022-12-27 10:16  非法小恋  阅读(127)  评论(0编辑  收藏  举报