聊聊docker那些端口问题

今天来系统聊一聊docker的端口,常见的有容器内程序端口、容器端口、主机端口、Dockerfile中EXPOSE端口、docker-compose和docker run中的port等。

貌似很多端口,但连接时真实的端口仅程序端口和主机端口,其他都是对端口的声明,并不会实际开启端口的服务。

下面以nginx和redis镜像为例介绍下这些端口的关系:nginx程序端口80,映射主机端口30080;redis程序端口6379,映射主机端口36379。实际通信如下图

程序端口即容器端口

程序端口即实际容器端口,它们是同一个端口。不需要任何其他配置,容器启动后,本机或同网络其他容器通过容器内程序端口(即容器端口)访问该容器的服务。

以nginx为例,启动容器(名称web)

[root@localhost ~]# docker run -d --name web nginx 
3cb58fd8e585e0b015e7e42e9587df95359e1d133ff5371c6838fd303132d2ef
[root@localhost ~]# docker ps
CONTAINER ID   IMAGE     COMMAND                  CREATED          STATUS          PORTS     NAMES
3cb58fd8e585   nginx     "/docker-entrypoint.…"   17 seconds ago   Up 15 seconds   80/tcp    web

 本地服务器验证访问容器端口,先查看容器ip,再用telnet 检查端口80通讯,返回如下高亮信息表示端口访问正常

[root@localhost ~]# docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' web
172.17.0.2
[root@localhost ~]# curl //172.17.0.2:80
<!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>
。。。。。

外网验证访问,连接不通

$ curl http://192.168.8.190:80
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:--  0:00:01 --:--:--     0
curl: (7) Failed to connect to 192.168.8.190 port 80: Connection refused

Dockerfile中EXPOSE端口

  • 结论:Dockerfile中的EXPOSE仅是对端口的声明,在ps或inspect时显示,但并不会实际开启端口的服务,实际端口有容器中程序的端口决定

1. 修改容器web内nginx的端口为88,重启生效

[root@localhost ~]# docker exec -it web /bin/bash
root@9ac59b2d523f:/# vi /etc/nginx/conf.d/default.conf 
  修改listen参数值为88
[root@localhost ~]# docker restart web
web

2.检查镜像的Dockerfile文件中Expose的端口:80

[root@localhost ~]# docker history nginx
IMAGE          CREATED       CREATED BY                                      SIZE      COMMENT
605c77e624dd   9 days ago    /bin/sh -c #(nop)  CMD ["nginx" "-g" "daemon…   0B        
<missing>      9 days ago    /bin/sh -c #(nop)  STOPSIGNAL SIGQUIT           0B        
<missing>      9 days ago    /bin/sh -c #(nop)  EXPOSE 80                    0B        

3.检查容器信息中显示的端口:80

[root@localhost ~]# docker ps
CONTAINER ID   IMAGE         COMMAND                  CREATED          STATUS          PORTS      NAMES
c24aa7848e38   nginx         "/docker-entrypoint.…"   39 seconds ago   Up 37 seconds   80/tcp     web

4.验证实际可访问端口。80拒绝连接,88访问正常

[root@localhost ~]# curl http://172.17.0.2:80
curl: (7) Failed connect to 172.17.0.2:80; 拒绝连接
[root@localhost ~]# curl http://172.17.0.2:88
<!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 and
working. Further configuration is required.</p>

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

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

容器端口映射主机端口

  •  结论:若容器未指定主机映射端口,则主机外网络无法访问容器,仅本机网络范围可访问。

1. 启动容器,未指定主机映射端口时

[root@localhost ~]# docker run -d --name web nginx 
3cb58fd8e585e0b015e7e42e9587df95359e1d133ff5371c6838fd303132d2ef
[root@localhost ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 3cb58fd8e585 nginx "/docker-entrypoint.…" 17 seconds ago Up 15 seconds 80/tcp web

  外网访问主机80端口,不通

 

 

 2. 启动容器,指定主机映射端口30080映射容器80端口

[root@localhost ~]# docker run -d -p 30080:80 --name web nginx
04ef8440c357b81da934e91e786612b6027d20ea2e7cb3bd81ae91a9ca15a807

[root@localhost ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
04ef8440c357 nginx "/docker-entrypoint.…" 8 minutes ago Up 8 minutes 0.0.0.0:30080->80/tcp, :::30080->80/tcp web

  外网访问主机30080,成功

 

 

 补充:在docker run中使用-p映射端口,在docker-compose中使用-port映射端口,效果相同。

容器间连接

访问上方已启动容器web,容器端口80,映射宿主机端口30080,容器ip:172.17.0.2,主机ip:192.168.8.190

  • 总结:容器IP:容器Port,主机IP:主机Port,当ip与port匹配时才能访问通。

启动一个redis容器,从redis容器访问web容器服务。

[root@localhost ~]# docker run -d --name redis redis:6.0.8 
[root@localhost ~]# docker exec -it redis /bin/bash
root@25d8d484aa5b:/data# curl http://172.17.0.2:80 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> ...........
root@25d8d484aa5b:/data# curl //172.17.0.2:30080
curl: (7) Failed to connect to 172.17.0.2 port 30080: Connection refused

root@25d8d484aa5b:/data# curl //192.168.8.190:30080
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
............

  root@25d8d484aa5b:/data# curl //192.168.8.190:80
  curl: (7) Failed to connect to 192.168.8.190 port 80: Connection refused