Dockerfile中的EXPOSE和Kubernetes服务/容器端口之间的关系是什么?

10
我曾认为在Kubernetes中,暴露应用程序端口的方法是通过首先在Dockerfile中使用EXPOSE来进行暴露,然后在部署yaml文件中设置containerPort,最后在服务yaml文件中设置targetPort。我认为这些值都必须相同,例如7214
然而,我刚刚注意到,在我的一个应用程序的Dockerfile中,我错误地暴露了一个端口为7124(但是在另外两个文件中设置了正确的端口),如下所示: Dockerfile
expose 7124 #This is incorrect

Deployment.yaml

ports:
  - containerPort: 7214

Service.yaml

apiVersion: v1
kind: Service
metadata:
 name: my-service
spec:
 type: NodePort
 ports:
  - port: 7214
    targetPort: 7214

然而,我的其他应用程序可以很好地访问该服务,并且请求无问题地转发到端口 7214 的应用程序上。
为什么会这样?难道我不需要在 Dockerfile 中公开端口吗?containerPort 起作用了吗?一个类似问题的用户回答称暴露的端口不需要匹配,但未解释原因。

这是因为你说了 expose 7124,我想要在端口7124上暴露我的应用程序的所有流量。 - Hackerman
我明白。但这并不能解释为什么我的其他应用程序能够在端口7214上访问它?7214没有从Dockerfile中公开,那么为什么可以访问它呢? - user3275784
1
containerPort: 7214 覆盖了 Dockerfile 的端口...你可以看到同样的行为也出现在环境变量上。例如,在 Dockerfile 中添加一些环境变量,然后在 yaml 文件中定义相同的变量并赋予其他值,你应该会看到它们具有新值而不是 Dockerfile 的值。 - Hackerman
1
好的,没错,这正是我所怀疑的,但只是想确认一下!谢谢!那么在这种情况下,在Dockerfile中暴露任何东西都没有意义了吗? - user3275784
2个回答

7
一个进程正在监听的端口只有该进程的开发人员知道,因为端口绑定发生在代码中。 EXPOSEcontainerPort是向外界传达这一信息的方法。
Dockerfile中的EXPOSE指令除了作为文档记录容器中进程可能监听的端口以供阅读Dockerfile的人理解,以及在运行docker ps命令时用于一些UI显示之外,没有其他作用。就像是Dockerfile作者和可能使用您的映像或修改您的Dockerfile的其他人之间的交流。
即使您在deployment.yaml中的containerPort部分也不会影响任何事情。它也作为文档,供阅读您的清单的人知道pod中的进程可能在哪些端口上侦听。另一个用途是使用name字段为端口命名,并且您可以在其他地方(如服务对象)中按名称引用此端口。
唯一重要的是容器中进程实际上监听的端口,并确保该端口在服务端口的targetPort字段中使用。

谢谢,但我还是有点困惑。如果我没有一个服务坐在我的 pod 前面,那么端口怎么暴露呢?比如说我想使用另一个应用程序从集群内直接访问端口 7214 上的 pod。从你的措辞中,我无法理解端口是否总是暴露或从不暴露,因为像“expose”和“containerPort”这样的东西被忽略了。你能澄清一下吗? - user3275784
您可以始终在集群内部通过pod-host-name:7214或pod-ip:7214与pod进行通信。但是,由于节点不可用或资源紧缺而导致的驱逐情况下,pod是短暂的,因此pod名称和pod IP可能会发生变化。 pod名称中还包含随机字符。使用服务对象,您可以使用所需的服务名称访问后端pod。创建服务对象时,它只是编程iptables,以便将服务Ip:port上的任何请求转发到任何支持pod-ip:targetPort之一。 - Shashank V
是的,我明白服务是正确的选择,因为Pod是短暂的。我只是认为端口默认情况下不可访问,除非你暴露它们?因为我认为默认情况下Docker容器不会暴露它们的端口,除非你使用“--publish”标志。Kubernetes也是这种情况吗?因为除非我误解了,否则如果我可以直接访问Pod,那么端口似乎是默认暴露的? - user3275784
当您在Docker中使用--publish标志时,实际上是将容器端口绑定到主机网络上的端口。但这并不是Pod所发生的情况。端口不会绑定到主机网络上,而是绑定到Pod网络上,即pod-ip:port。如果要在Pod中实现与--publish相同的行为,则需要使用hostPort字段。 - Shashank V

6
在 Dockerfile 中,EXPOSE 选项仅用作文档,正如官方文档所述,它并没有公开端口:

EXPOSE 指令实际上不会发布端口。它可以作为构建镜像的人和运行容器的人之间有关要公开哪些端口的类型的文档。

在 Kubernetes 中,与 EXPOSE 等效的是 spec.containers.ports.containerPort。您可以将两个值设置为任何您想要的值,并且不会改变任何内容。将其视为注释。
使用 Service 对象有一点不同,那里的值确实很重要。 Service 使用 spec.ports.portspec.ports.targetPort。如果您没有指定 targetPort,则 Kubernetes 将其值设置为与 port(必需)中指定的相同。
然而,targetPortport 并不需要相同,实际上它们有不同的目的:
  • port 指定 Service 的端口
  • targetPort 指定 Pod 的端口
因此,一般流程如下: 调用 <service-name>:port -> 转发到标记为 foo:bar 的 Pod -> Pod 在 targetPort 上接收呼叫。
以下是一个示例:
apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 12345

导致的结果是:

curl my-service:80 -> 转发到标签为 app: MyApp 的 Pod 上 -> Pod 在端口 12345 上接收请求。

在您的情况下,您在端口 7214 上与您的服务进行通信,并且它将请求转发到 Pod 时也会使用端口 7214,这就是为什么无论您为 EXPOSEcontainerPort 设置什么,它都会继续工作的原因。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接