在查看某些官方镜像的 Dockerfile 时,经常会看到在 Dockerfile 中使用 EXPOSE 指令,比如 MySQL 5.7。
如果 EXPOSE 指令真的是暴露端口的你不觉得奇怪吗?比如我有一个 SpringBoot 的 Java 服务,在配置文件中定义的服务端口(server.port)是 8080。难道我在 Dockerfile 中使用 EXPOSE 暴露的端口是 9090 也能够访问该服务?显然是不可能的事情。
别瞎猜了,看下 Docker 官方对 EXPOSE 指令的解释:
The EXPOSE instruction does not actually publish the port.It functions as a type of documentation between the personwho builds the image and the person who runs the container,about which ports are intended to be published.To actually publish the port when running the container, usethe -p flag on docker run to publish and map one or more ports,or the -P flag to publish all exposed ports and map them to high-order ports.
用大白话翻译就是 EXPOSE 指令仅仅是开发文档说明,别人一看到这个 Dockerfile 就:哦,该 Docker 服务对外暴露的是这个端口啊。
另外一点就是,当我们在 Dockerfile 中使用该指令构建了一个镜像。使用者又没有构建该镜像的 Dockerfile,那他该怎么知道暴露的端口是哪个呢?这就是 EXPOSE 指令的作用了。使用 docker image inspect $image/$container 就能在 ContainerConfig 中看到暴露的端口信息了:

这么一看,EXPOSE 的作用还是蛮大的。
那实际上真正暴露端口的命令是什么呢?是 -p!
端口映射
在初次运行 Docker 容器都是使用 docker run 命令。该命令实际上有许多可选参数,-p 就是其中一个,它是 publish 的缩写。该命令格式一般如下:
docker run -p host_port:container_port image_name
一定要注意,第一个是宿主机的端口,第二个是容器的端口。当运行该命令之后宿主机端口就会与容器端口做个映射。
我们都知道,docker 容器也是有自己的网络网卡的,默认情况下容器是无法与宿主机通信的。想要通信必须将容器端口与宿主机端口建立映射关系,这样当我们访问宿主机上的端口时实际上访问的是容器的端口。
下面看一个示例:
FROM openjdk:8WORKDIR appADD docker-web-demo.jar app.jarEXPOSE 8080/TCPENTRYPOINT ["/bin/bash", "-c", "java -jar app.jar"]
写一个简单的 Dockerfile 文件,并使用 EXPOSE 指令对外标注暴露该服务对外暴露的是 8080/TCP 端口。现在构建该镜像:
$ docker build -t publish-demo:v1 .
构建完成之后使用 docker image inspect 看下这个镜像的元信息:
$ docker image inspect publish-demo:v1
找到 ExposedPorts 信息:

这么一来,即使我忘记了 Dockerfile 文件中的内容,我也可以使用 inspect 命令查看该镜像构建的服务对外暴露的端口是什么。
现在运行成一个容器,并与宿主机的 7070 端口映射:
$ docker run -p 7070:8080 publish-demo:v1
现在想要访问该服务就直接访问宿主机的 7070 端口即可,如果访问 8080 是访问不到该服务的:
$ curl -XGET localhost:7070# 输出信息{"name" : "张三","age" : 18,"i18nTitle" : "本地化","date" : "2021-07-03","time" : "09:54:07","dateTime" : "2021-07-03 09:54:07"}
完结,撒花~
