Marathon(4):Port

端口

在Marathon中,对应用程序端口的配置可能会造成混淆,并且有一个悬而未决的问题,需要重新设计的端口API。本文尝试更清楚的解释Marathon的端口是如何工作的。

如果你是在DC/OS集群中运行Marathon,你可以使用虚拟地址(VIP:Virtual addresses) ,使端口的管理更容易。VIP简化应用间的通讯,并实现可靠的面向服务的架构。VIP从单个虚拟地址向多个IP地址和端口映射的流量。

定义

containerPort:用来指定容器内部的一个端口。当使用BRIDGEUSER网络模式连接Docker容器时,必须将这个属性设置为port mapping的一部分。

hostPort:用来指定绑定到主机上的一个端口。当使用BRIDGEUSER网络模式,你可以指定一个port mapping将一个主机端口映射到容器端口。在HOST网络模式下,默认的请求端口就是主机的端口。请注意,主机端口只可以通过环境变量提供给一个任务。

BRIDGE网络模式:指定Docker应用程序使用BRIDGE网络模式。在这种模式下,容器端口(容器内部的端口)被映射到主机端口(主机上的端口)。在这种模式下,应用程序被绑定到容器内的指定端口,容器的端口被绑定到主机上的指定端口。

USER网络模式:指定Docker应用程序使用USER网络模式。在这种模式下,容器端口(容器内部的端口)被映射到主机端口(主机上的端口)。在这种模式下,应用程序被绑定到容器内的指定端口,容器的端口被绑定到主机上的指定端口。在与“用户自定义”Docker网络集成时,USER网络模式将会非常有用。在Mesos世界,这种网络通常是通过使用与Mesos CNI网络隔离的 CNI 插件访问。

HOST网络模式:该种模式在Marathon应用为非容器化而其它应用为容器化的情况下使用。在这种模式下,应用程序直接绑定到主机上的一个或多个端口。

portMapping:在Docker BRIDGE模式下,在容器外部可被访问到的端口都需要做端口映射。端口映射是一个包含host port, container port, service port和协议的元组。可以为Marathon应用指定多个端口映射; 未指定hostPort,则其默认值为0(意味着Marathon将随机分配一个)。在Docker USER模式下,hostPort的语义为稍有点变化:USER模式不需要指定hostPort,如果未指定Marathon不会自动分配一个随机的。这允许在USER网络模式下部署容器,包括containerPort和发现信息,但不暴露主机网络上的这些端口(意味着将不消耗主机端口资源)。

port:该端口数组被用来定义在HOST网络模式下应被视为资源提供的一部分的端口。在没有指定端口映射的情况下这个属性是必须的。一个应用程序只需要定义端口或portDefinitions两者其中之一。

portDefinitionsportDefinitions数组用于定义应被认为是资源提供一部分的端口。在使用HOST网络模式和没有指定端口映射的情况下,需要定义portDefinitions数组。这个数组是为了替换port数组,并可以为其指定一个端口名,协议和标签。一个应用程序只需要定义端口或portDefinitions两者其中之一。

protocol:protocol用于指定端口使用的因特网协议(例如tcpudp或者udp,tcp)。这只需要在Docker容器环境下使用BRIDGEUSER网络模式时,在port mapping属性下设置。

requirePortsrequirePorts是指定Marathon是否应该在收到资源提供请求时,专门寻找指定端口的一个属性。这确保了这些端口是可用的,可以绑定到上Mesos Agent上的。这不支持BRIDGEUSER网络模式。

servicePort:当您在Marathon上(不管是通过REST API或界面)创建一个新的应用程序,你可以指定一个或多个服务端口给它。可以指定所有有效的端口号为服务端口,也可以用0表示Marathon应该自动分配的可用服务端口给应用程序使用。如果你选择自己的服务端口,你必须自己确保,这个端口在所有应用程序中是唯一的。

随机端口分配
使用值0,表示你想让Marathon随机分配一个端口。但是,如果在portMapping属性内部,将containerPort的值设置为0,意味着使用与hostPort相同的端口。

环境变量

每个host port值是通过 PORT0 PORT1等等的环境变量暴露给正在运行的应用程序实例。每个Marathon应用默认被分配一个端口,因此 PORT0MarathonDocker使NAME PORT_NAME访问。

当使用BRIDGEUSER模式的网络,确保你的应用程序绑定到你在portMapping中声明的containerPort的端口上。但是,如果设置containerPort的值为0,那么这个端口将与hostPort的端口一样,你可以使用$PORT环境变量。

示例配置

Host模式

Host模式网络是Docker容器默认的组网方式,是非Docker应用程序的唯一网络模式。注意,没有必要在Dockerfile文件中EXPOSE这个端口。

启用Host模式

Host模式默认情况下在Docker中是启用状态。如果你想明确的指定,你也可以手工在network属性上指定:

  "container": {
    "type": "DOCKER",
    "docker": {
      "image": "my-image:1.0",
      "network": "HOST"
    }
  },

对于非Docker应用程序,你不需要指定任何东西。

指定端口

您可以通过ports数组指定端口:

    "ports": [
        0, 0, 0
    ],

或通过portDefinitions数组:

  "portDefinitions": [
      {"port": 0}, {"port": 0}, {"port": 0}
    ],

在这个例子中,我们指定三个随机分配主机端口,这可以通过环境变量 PORT0 PORT1$PORT2提供给我们的命令行使用。Marathon也会在这三个主机端口之外,随机分配三个服务端口(service port)。

您也可以指定特定的服务端口(service port):

 "ports": [
        2001, 2002, 3000
    ],

或者:

    "portDefinitions": [
        {"port": 2001}, {"port": 2002}, {"port": 3000}
    ],

在这种情况下,主机端口 PORT0 PORT1$PORT3保持随机分配。但是,这个应用程序的三个服务端口现在是2001,2002和3000。

在本例中,像上面前一个,有必要使用一个服务发现解决方案,如HAProxy,代理从服务端口到主机端口的请求。

如果你想要的应用程序服务端口等于其主机端口,可以设置requirePortstrue(默认情况下requirePortsfalse)。这将告诉Marathon在Agent上只调度具有这些可用端口的应用程序:

   "ports": [
        2001, 2002, 3000
    ],
    "requirePorts" : true

服务端口和主机端口(包括环境变量 PORT0 PORT1$PORT2),现在都是200120023000

如果你不使用服务发现解决方案,代理从服务端口主机端口的请求,这个属性很有用。

定义portDefinitions数组允许你为每个端口指定一个协议,一个名字和标签。当开始新的任务,Marathon将传递这些元数据给Mesos。Mesos会在任务的discovery域中暴露这些信息。自定义网络发现解决方案可以使用这个值。

下例的端口定义中,请求一个名字是http、标签VIP_0的为10.0.0.1:80的 动态tcp端口:

   "portDefinitions": [
        {
            "port": 0,
            "protocol": "tcp",
            "name": "http",
            "labels": {"VIP_0": "10.0.0.1:80"}
        }
    ],

该port字段是强制性的。protocolnamelabels字段是可选的。其中有port字段被设置为等于ports数组。

注意,只有ports数组和portDefinitions数组不应该一起使用,除非他们所有的元素是一样的。

引用端口

您可以易用为我们虚构的应用程序在Dockerfile中定义的端口:

CMD ./my-app --http-port=$PORT0 --https-port=$PORT1 --monitoring-port=$PORT2

另外,如果你不使用Docker或在马拉松应用程序中指定了一个cmd,运行效果一样:

"cmd": "./my-app --http-port=$PORT0 --https-port=$PORT1 --monitoring-port=$PORT2"

桥接(Bridge)模式

桥接模式网络允许你将主机端口映射到容器内的端口,并且只适用于Docker容器。如果想为容器镜像分配一个固定的端口,这个办法将会非常有用。注意,没有必要在Dockerfile文件中EXPOSE这个端口。

启用桥接模式

你需要通过network属性指定桥接模式:

  "container": {
    "type": "DOCKER",
    "docker": {
      "image": "my-image:1.0",
      "network": "BRIDGE"
    }
  },

启用用户模式

你需要通过network属性指定用户模式:

"container": {
    "type": "DOCKER",
    "docker": {
      "image": "my-image:1.0",
      "network": "USER"
    }
  },
  "ipAddress": {
    "networkName": "someUserNetwork"
  }

指定端口

端口映射可以简单地在命令行中通过-p参数传递给Docker,并在容器内端口和主机端口之间创建关联关系。在这种情况下,用portMappings数组来替换在主机模式下使用的portsportDefinitions数组。

端口映射在container对象中的portMappings对象中指定:

 "container": {
    "type": "DOCKER",
    "docker": {
      "image": "my-image:1.0",
      "network": "BRIDGE",
      "portMappings": [
        { "containerPort": 0, "hostPort": 0 },
        { "containerPort": 0, "hostPort": 0 },
        { "containerPort": 0, "hostPort": 0 }
      ]
    }
  },

在这个例子中,我们指定了3个映射。0值让Marathon给hostPort随机分配一个值。在这种情况下,设置containerPort为0将让它有和hostPort相同的值。这些值作在容器内分别通过 PORT0 PORT1并$PORT2访问。

另外,如果我们的应用在容器中使用固定端口,我们可以像下面这样的做:

 "container": {
    "type": "DOCKER",
    "docker": {
      "image": "my-image:1.0",
      "network": "BRIDGE",
      "portMappings": [
        { "containerPort": 80, "hostPort": 0 },
        { "containerPort": 443, "hostPort": 0 },
        { "containerPort": 4000, "hostPort": 0 }
      ]
    }
  },

在这种情况下,Marathon随机分配主机端口并将这些端口分别映射到804434000。值得注意的是,这是很重要, PORT PORT0**将被设置为hostPort的第一个值,其它的顺序设置。

指定协议

您也可以指定这些端口映射的协议。默认值是tcp:

"container": {
    "type": "DOCKER",
    "docker": {
      "image": "my-image:1.0",
      "network": "BRIDGE",
      "portMappings": [
        { "containerPort": 80, "hostPort": 0, "protocol": "tcp" },
        { "containerPort": 443, "hostPort": 0, "protocol": "tcp" },
        { "containerPort": 4000, "hostPort": 0, "protocol": "udp" }
      ]
    }
  },

指定服务端口

默认情况下,Marathon会为每个端口创建服务端口,并赋予它们随机值。服务端口用于服务发现解决方案,这是通常设置这些值为众所周知的值。你可以通过为每一个映射设置一个servicePort做到这一点:

 "container": {
    "type": "DOCKER",
    "docker": {
      "image": "my-image:1.0",
      "network": "BRIDGE",
      "portMappings": [
        { "containerPort": 80, "hostPort": 0, "protocol": "tcp", "servicePort": 2000 },
        { "containerPort": 443, "hostPort": 0, "protocol": "tcp", "servicePort": 2001 },
        { "containerPort": 4000, "hostPort": 0, "protocol": "udp", "servicePort": 3000}
      ]
    }
  },

在这个例子中,主机端口 PORT0 PORT1$PORT3保持随机分配。但是,这个应用程序的服务端口现在200120023000。外部代理,如HAProxy,应配置为从服务端口到主机端口路由。

引用端口

如果设置containerPort为0,那么你应该在Dockerfile文件中为我们虚构的应用程序指定的端口,像下面这样:

CMD ./my-app --http-port=$PORT0 --https-port=$PORT1 --monitoring-port=$PORT2

但是,如果指定了containerPort的值,只需使用在Dockerfile相同的值代替:

CMD ./my-app --http-port=80 --https-port=443 --monitoring-port=4000

另外,也可以指定在Marathon应用程序中指定一个cmd,它的会和上面一样以相同的工作方式运行:

"cmd": "./my-app --http-port=$PORT0 --https-port=$PORT1 --monitoring-port=$PORT2"

或者,如果你使用固定的值:

"cmd": "./my-app --http-port=80 --https-port=443 --monitoring-port=4000"

参考资料:

[1]: http://mesosphere.github.io/marathon/docs/ports.html

你可能感兴趣的:(Mesos)