net使用哪个端口_DC/OS:揭开Marathon网络端口的神秘面纱

631b11249658cee0cfa8e5662b170f75.png

503c5de8f5a0a25b22c388a922038af0.gif

对于许多组织而言,构建和部署单体应用的日子已是过去时,将开发工作集中在微服务成为新主流。微服务架构将应用程序分解为小的、互相连接的微服务,其原子性被定义为技术边界、业务服务边界或两者的组合,通常使用提供数据持久性的共享基础设施服务,并通过RPC等方式实现通信。

操作支持微服务架构的基础设施具有一定的挑战性,但利大于弊(如可扩展性、生产力的提高以及技术选择的灵活性)。

本质上,这一方法是从新旧操作系统的基本原理中衍生出来的。容器化的兴起是这一新方法的催化剂,它由先进的Linux内核特性支持,随后又经Docker推广。然而,我们仍受缚于TCP/IP网络堆栈所提供的功能及其特性,当决定在部署应用程序中选择哪种网络模式和端口时,这一点便在Marathon中得到了体现。

幸运的是,如DC/OS这样的平台,可以较为容易的运行此类服务。 接下来,让我们一起来看看以云原生方式运行同一应用程序的多个副本时,后台会发生什么。 在本文中,我们将研究各种可选选项及其含义,并通过研究主机操作系统层面的实践来理解其中的含义。

1b36c8676a6bb960ada934a61d4e9b7e.gif

0 1 应用程序示例 为了让您对示例有一个更加清晰的了解,我们将使用一个简单的应用程序。 当通过HTTP访问时,这个应用程序会将Pi返回到小数点后十位。 它用Go语言编译一个二进制文件,再通过容器以无状态微服务的形式运行。 它只需一个选项-“-端口”,用于控制我们的应用程序侦听HTTP请求。 查看此演示程序代码请访问 https://github.com/dcos-labs/httpi02 主机模式网络 Marathon有三种网络模式分别是: 主机、容器/桥接、容器。主机模式是最为人熟知的网络模式,在主机模式下,运行在容器中的网络服务会像在主机本身上运行时一样凸显出来,如同一个“传统的”守护进程或服务。   让我们使用以下配置部署一个简单的应用程序,以作示例:
{
  "id": "httpi",
  "networks": [ { "mode": "host" } ],
  "portDefinitions": [
    {"port": 0, "name": "http"}
  ],
  "container": {
    "type": "DOCKER",
    "docker": {
      "image": "dcoslabs/httpi:latest",
      "forcePullImage": true
    }
  },
  "instances": 1,
  "cpus": 0.1,
  "mem": 32
}
以上的应用程序定义示例会指示Marathon使用Docker containerizer来调度我们的应用程序,且其网络应为默认 - “主机”。 我们有一个标记为“http”的单端口定义,它的值设置为0,这意味着Marathon将替我们进行选择。 我们可以通过执行以下操作来部署并查看已分配的端口:
$ dcos marathon app add httpi_host.json
$ dcos marathon app show /httpi | jq -r '.tasks[].ports[]'
16327
我们正在部署的应用程序考虑到一个环境变量,这个环境变量是由Marathon为我们设置的,名为PORT_HTTP,如在portDefinitions部分中所命名的一样。 这将被传递给小型Golang应用程序,并指示它监测该环境变量中指定的值。   这意味着我们能够使用HTTP客户端连接到运行容器的主机,并看到返回的Pi值:
$ dcos marathon app show /httpi | jq -r '.tasks[].ipAddresses[]'
{
  "ipAddress": "172.16.9.84",
  "protocol": "IPv4"
}
然后在我们的DC/OS 主节点上:
$ dcos node ssh --master-proxy --leader --user=centos
[centos@ip-172-16-9-84 ~]$ curl http://172.16.9.84:16327
3.1415926536
以客户端请求的角度来看,如下方示意图:

617014a3c20be61cf6d6a7e583d1aae7.png

接下来,让我们看一下Docker在相关代理上为我们配置的内容。 首先,让我们找出我们的单例模式运行在哪个代理上:
$ dcos node ssh --master-proxy --user centos --mesos-id $(dcos marathon app show /httpi | jq -r '.tasks[].slaveId')
现在我们可以检查一些特定于Docker容器的设置,确认Marathon是否正确完成任务。 例如,以下是从运行的容器转储某些显著选项的两个命令(注意,为了简洁起见,没有包含输出):
[centos@ip-172-16-9-84 ~]$ sudo docker inspect mesos-4d89f563-28ee-4bf8-ba1b-7690503cdb8c | jq -r '.[].NetworkSettings'
[centos@ip-172-16-9-84 ~]$ sudo docker inspect mesos-4d89f563-28ee-4bf8-ba1b-7690503cdb8c | jq -r '.[].Config.Env'
当我们运行应用程序的多个实例时会发生什么? 每个实例都使用一个唯一的端口,那么其他应用程序如何知道连接到哪里? DC/OS有几个选项可以管理流量如何发现并到达其预期目的地,其中包括通过dcos-net实现第4层负载均衡和通过Marathon-LB实现第7层负载均衡。 接下来,我们将通过一个示例向您介绍上述情况。03 使用dcos-net的容器/桥接模式和负载均衡 让我们使用以下配置重新部署我们的应用程序:
{
  "id": "/httpi",
  "cpus": 0.1,
  "instances": 5,
  "mem": 32,
  "networks": [ { "mode": "container/bridge" } ],
  "requirePorts": false,
  "container": {
    "portMappings": [
      {
        "labels": {
          "VIP_0": "/httpi:3141"
        },
        "protocol": "tcp",
        "name": "http"
      }
    ],
    "type": "DOCKER",
    "volumes": [],
    "docker": {
      "image": "dcoslabs/httpi:latest",
      "forcePullImage": true,
      "privileged": false,
      "parameters": []
    }
  }
}
这将指示Marathon部署我们服务的五个副本,并且通过使用VIP_0标签集替我们配置单个VIP或虚拟IP。 我们还指示了我们的containerizer使用容器/桥接模式网络。 这意味着部署的容器将具有连接主机网桥的虚拟以太网接口。   “portMappings”部分为我们提供了一种方法,可告知Marathon我们将要部署的服务。 通过这样做,它可以配置基本的containerizer和我们在容器中运行的应用程序,并确定哪个服务应该暴露哪个端口,以及如何将其映射以提供外部连接。 尽管我们可以在使用主机或容器端口时指定其他设置,但Marathon也可以替我们进行选择和分配。 这将通过PORT_HTTP的环境变量向我们的应用程序暴露。   通过此配置和上述标签,DC/OS还可以创建DNS资源记录,作为客户端可以定位的VIP的别名。 默认情况下,使用的协议是: ..l4lb.thisdcos.directory:,因此在我们的示例中,这将变成httpi.marathon.l4lb.thisdcos.directory:3141。同样,从主节点或任何代理节点中,我们现在可以使用以下方法测试:
$ curl http://httpi.marathon.l4lb.thisdcos.directory:3141
3.1415926536
那么如何确保它能够运行我们服务的任何一个后端节点? 如果我们解析'httpi.marathon.l4lb.thisdcos.directory',得到的IP地址是11.249.78.219,但是,此集群中的主机实际上没有分配该IP的接口。 dcos-net使用了另一个Linux内核网络功能,称为IPVS或IP虚拟服务器。 要查询已放入主机的IPVS配置,我们需要安装一个提供必要命令的包:
sudo yum install -y ipvsadm
[..]
Installed:
  ipvsadm.x86_64 0:1.27-7.el7

完成!

接下来:
$ sudo ipvsa
dm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port       Forward Weight ActiveConn InActConn
TCP  11.249.78.219:3141 wlc
  -> 172.16.15.168:9896       Masq 1  0      0
  -> 172.16.15.168:26186      Masq 1  0      0
  -> 172.16.17.198:8584       Masq 1  0      0
  -> 172.16.40.67:7143        Masq 1  0      0
  -> 172.16.40.67:14666       Masq 1  0      0
IPVS负责将目标为11.249.78.219端口3141的TCP流量重定向到上面列出的五个主机中的任何一个,并且DC/OS将替我们应用此服务,而无需我们做任何额外的工作。   另一方面,我们还可以通过DNS检索IP地址和端口列表; Mesos-DNS创建了一个SRV资源记录,我们可以查询该记录返回我们服务的类似列表。 描述该协议的文件可在此处找到(https://github.com/mesosphere/mesos-dns/blob/master/docs/docs/naming.md),或者您可以执行以下操作:
$ dig +short _httpi._tcp.marathon.mesos SRV
0 1 9896 httpi-7n7a6-s2.marathon.mesos.
0 1 26186 httpi-9kxtg-s3.marathon.mesos.
0 1 8584 httpi-kdkgr-s3.marathon.mesos.
0 1 7143 httpi-y14gy-s1.marathon.mesos.
0 1 14666 httpi-ekjsh-s1.marathon.mesos.
请注意,第三列对应于运行我们服务的容器所分配的端口。   就容器本身而言,我们可以检查给定代理节点上的网桥配置,如下所示。 首先,选择运行我们服务的代理:
$ dcos marathon app show /httpi | jq -r '.tasks[].slaveId'
602c0829-41e6-4910-91e5-16a38fa82c0a-S2
602c0829-41e6-4910-91e5-16a38fa82c0a-S3
602c0829-41e6-4910-91e5-16a38fa82c0a-S4
602c0829-41e6-4910-91e5-16a38fa82c0a-S3
602c0829-41e6-4910-91e5-16a38fa82c0a-S4
然后是SSH:
$ dcos node ssh --master-proxy --user centos --mesos-id 602c0829-41e6-4910-91e5-16a38fa82c0a-S3
此节点包含两个在Docker容器中运行的应用程序副本:
$ sudo docker ps --format "table {{.Names}}\t{{.Status}}"
NAMES                                    STATUS
mesos-1e44c87b-4c24-479b-a3ce-70bb33ddfc25   Up 32 minutes
mesos-881e3841-b58f-49dc-b20c-31253aa2cafb   Up 32 minutes
我们可以看到,还有两个与网桥(称为docker0)关联的接口,一端是一对VETH设备,另一端是我们的容器。
$ sudo bridge li
69: veth10f72e2 state UP @(null):  mtu 1500 master docker0 state forwarding priority 32 cost 2
71: vethd365035 state UP @(null):  mtu 1500 master docker0 state forwarding priority 32 cost 2
这也可以通过使用“docker”命令检查与给定容器关联的网络进行验证。 我们使用“jq”过滤我们关心的属性:
$ sudo docker inspect mesos-1e44c87b-4c24-479b-a3ce-70bb33ddfc25 | jq -r '.[].NetworkSettings.Networks.bridge.NetworkID'
cc5a44426e0c1f7b9605e03eb5c2205ae0726a7f6b666f068424f8ab0f2fe293
$ sudo docker network inspect cc5a44426e0c1f7b9605e03eb5c2205ae0726a7f6b666f068424f8ab0f2fe293 | jq -r '.[].Containers'
{
  "1bd64c0d55ae69254c0284213782b946f6c4bea30629152e37023df0d614089b": {
    "Name": "mesos-881e3841-b58f-49dc-b20c-31253aa2cafb",
    "EndpointID": "4db549817f410c5e44f0ff3b9c7d88738248fcfbd245fda33fd6818c49566e40",
    "MacAddress": "02:42:ac:11:00:02",
    "IPv4Address": "172.17.0.2/16",
    "IPv6Address": ""
  },
  "e99fd806dc8e4e01ea4cba8ccc5139bac1f7104595344e2ae7987b23d485d8e9": {
    "Name": "mesos-1e44c87b-4c24-479b-a3ce-70bb33ddfc25",
    "EndpointID": "a8d3084179e339f14aeef1ea771db6eefb232cb4b7837c4e1577f9fbf8095cfb",
    "MacAddress": "02:42:ac:11:00:03",
    "IPv4Address": "172.17.0.3/16",
    "IPv6Address": ""
  }
}
容器本身有一个IP地址,该地址是从定义为Docker配置部分的网络中自动分配的。 最后,网络地址转换(NAT)将客户端连接与目标IP和端口连接起来,一旦该请求到达我们的DC/OS节点,它将负责将其定向到适当的容器:
$ sudo iptables -n -t nat -L DOCKER
Chain DOCKER (2 references)
target prot opt source           destination 
RETURN all  -- 0.0.0.0/0        0.0.0.0/0 
DNAT   tcp  -- 0.0.0.0/0        0.0.0.0/0        tcp dpt:16106 to:172.17.0.2:16106
DNAT   tcp  -- 0.0.0.0/0        0.0.0.0/0        tcp dpt:3651 to:172.17.0.3:3651
在端口1606上访问我们的DC/OS节点的流量将被重定向到172.17.0.2:16106,而对于: 3651,将被重定向到172.17.0.3:3651。 从上面的命令可以看出,这两个地址与我们的容器分配的地址相对应。 对于给定的DC/OS节点,它看起来类似于下面的内容,其中蓝色框表示的是我们容器化服务的实例:

7ce9a4214da290bc94c15c04bd52983f.png

因此端到端,忽略HTTP协议的细节:
  • 客户端HTTP请求访问http://httpi.marathon.l4lb.thisdcos.directory:3141;
  • httpi.marathon.l4lb.thisdcos.directory解析为IP地址11.249.78.219;
  • 每个主机上的IPVS规则都会转换并透明地重定向我们的请求,并从与集群中节点对应的关联地址列表中进行选择。在本例中,我们将使用172.16.15.168:9896;
  • 或继续向IP对应172.16.15.168的节点请求,并hit TCP端口9896;
  • 目标主机上的目标NAT规则将此IP和端口元组映射到与容器中运行的服务相对应的IP和端口元组;
  • 建立TCP连接,我们的HTTP请求最终可以得到响应。
这种方法的缺陷在于,它需要为我们的服务提供适当的IPVS入口。 如果我们需要向DC/OS集群之外的客户端露出某些内容,会发生什么情况? 这时候就得使用Marathon-LB或EdgeLB了。04 使用Marathon-LB实现负载均衡 对于流量来自集群之外的客户端,我们需要将负载均衡能力移到堆栈稍高的位置。

767e3bff276922392bbc6aebb2b5444c.png

dcos-net网络作用于第4层,即传输层,使用需要客户端配置的机制。 换句话说,它不适用于需要从其他地方将流量路由到集群的网络。 此时,我们就需要Marathon-LB或Edge-LB了,它们可作用于第4层(TCP)和第7层(HTTP/HTTPS)。 同样,Marathon允许我们通过应用程序服务定义来表示元数据,这样负载均衡器就可以动态和按需配置网络访问。   让我们来看一个使用容器/桥接模式网络的示例。 首先,我们需要通过运行以下命令将Marathon-LB安装到集群中: $ dcos package install marathon-lb --yes 现在,我们可以更新我们的服务定义并按如下方式重新部署:
{
  "id": "/httpi",
  "cpus": 0.1,
  "instances": 5,
  "mem": 32,
  "networks": [ { "mode": "container/bridge" } ],
  "requirePorts": false,
  "labels":{
    "HAPROXY_GROUP": "external",
    "HAPROXY_0_MODE": "http",
    "HAPROXY_0_PORT": "3141"
  },
  "container": {
  "portMappings": [
   {
     "labels": {
       "VIP_0": "/httpi:3141"
     },
     "protocol": "tcp",
     "name": "http"
   }
  ],
  "type": "DOCKER",
  "volumes": [],
  "docker": {
     "image": "dcoslabs/httpi:latest",
     "forcePullImage": true,
     "privileged": false,
     "parameters": []
    }
  }
}
这里的变化基本上可归结为一组新“标签”,前缀为“HAPROXY”。 这些标签由Marathon-LB读取,并用于替我们配置负载均衡器。 一旦部署完毕,我们就可以点击分配到我们的公共代理节点(运行Marathon-LB的HAPROXY服务的节点)的IP地址,并从我们的微服务接收到响应:
$ http 93.186.40.121:3141
HTTP/1.1 200 OK
Content-Length: 12
Content-Type: text/plain; charset=utf-8
Date: Mon, 22 Jul 2019 16:00:09 GMT
 
3.1415926536
由于我们在部署服务时应用了正确的配置选项,所以这一切都已经为我们自动配置,这要归功于DC/OS的功能。 每次我们通过添加或删除容器来扩展或缩小我们的服务时,Marathon-LB都会为所有成员更新其配置。05 容器模式网络 第三个也是最高级的方案——为每个容器分配专用IP地址的配置。 使用此模式,容器可以获得自己的Linux网络命名空间(因此也获得专用网络堆栈),且连接由基础软件定义网络(SDN)提供程序技术进行管理。 通过为每个容器分配专用IP,它消除了对任何端口映射安排的需要。 它还允许网络管理员使用防火墙规则隔离和控制流量,从而促进服务隔离。 最后,容器的网络与主机隔离,在发生危机情况时提供额外的安全保障。   DC/OS同时支持CNI(容器-网络接口)和CNM(容器网络模型)标准,这意味着现有的SDN解决方案(如Calico、Cilium和Flannel)都可以适用。 DC/OS还提供了自己的虚拟网络解决方案,称为DC/OS Overlay,我们将使用该解决方案来演示容器模式网络的功能。   让我们再次部署我们的应用程序,但这次我们将指定“容器”网络模式,并使用名为“dcos”的默认覆盖网络:
{
  "id": "/httpi",
  "cpus": 0.1,
  "instances": 5,
  "mem": 32,
  "networks": [ { "mode": "container", "name": "dcos" } ],
  "requirePorts": false,
  "labels":{
    "HAPROXY_GROUP": "external",
    "HAPROXY_0_MODE": "http",
    "HAPROXY_0_PORT": "3141"
  },
  "container": {
    "portMappings": [
      {
        "containerPort": 3141,
        "labels": {
          "VIP_0": "/httpi:3141"
        },
        "protocol": "tcp",
        "name": "http"
      }
    ],
    "type": "DOCKER",
    "volumes": [],
    "docker": {
      "image": "dcoslabs/httpi:latest",
      "forcePullImage": true,
      "privileged": false,
      "parameters": []
    }
  }
}
部署后,检查“dcos marathon app show”命令的输出,以查看以下内容:
$ dcos marathon app show /httpi | jq -r '.tasks[].ipAddresses[].ipAddress'
9.0.2.130
9.0.3.130
9.0.3.132
9.0.4.130
9.0.3.131
我指定了最初要部署的应用程序的5个副本,因此分配了5个地址,即每个容器一个。 我们可以在给定代理的容器配置中作进一步探讨:
sudo docker inspect mesos-c2e5a5a4-e3e3-45a2-ae15-3eb696d5b122 | jq -r '.[].NetworkSettings.Networks.dcos'
{
  "IPAMConfig": null,
  "Links": null,
  "Aliases": [
"f90839231484"
  ],
  "NetworkID": "14d322c9f1896002eafed61d466c74d3ab667a1d9f8bf1431fd63ac2fe1b3320",
  "EndpointID": "900f49089bbbe5eb7f0ec2d1899fe5e4f03863198985dd8ed787b495d616b7a2",
  "Gateway": "9.0.3.129",
  "IPAddress": "9.0.3.131",
  "IPPrefixLen": 25,
  "IPv6Gateway": "",
  "GlobalIPv6Address": "",
  "GlobalIPv6PrefixLen": 0,
  "MacAddress": "02:42:09:00:03:83",
  "DriverOpts": null
}
用与先前“容器/桥接模式”示例相同的方式暴露应用程序的网络端口和服务功能。 使用应用程序服务定义中包含的同组标签,Marathon-LB将替我们创建适当的负载均衡器配置。 这一次,它将指向自动分配给每个容器的IP地址和端口,而不是服务运行代理的IP地址。   如果您想全面了解基础技术是如何组合在一起的,请参阅文档《DS/OS覆盖设计指南》(https://docs.d2iq.com/mesosphere/dcos/1.13/overview/design/overlay/)。 或者,您可以参考以下的快速解析:
  • 启动服务时,容器在规定的覆盖网络上分配一个专用IP地址;
  • 可选的网络在安装DC/OS时就已定义好。在刚才的示例中,我们使用了默认的“dcos”,即9.0.0.0/8;
  • 覆盖网络使用VXLAN在集群中承载DC/OS节点之间的流量;
  • 只要集群中的所有节点能够相互连接,在任意节点上运行的任意应用程序(无论地理位置或云平台提供商如何),都可以使用其专用IP地址连接到另一个容器上的服务。

  当您在微服务环境中部署和扩展服务时,许多复杂性问题都可以被容器编排引擎(如DC/OS)迎刃而解。 希望本文可以帮助您了解如何通过DC/OS和Marathon选择应用程序的部署方式和其服务暴露方式。   推荐阅读:

通过D2iQ和Hedvig实现企业级容器基础设施

D2iQ+Rafay:统一的应用程序和基础设施管理

未来已来 携手前行——Mesosphere正式更名D2iQ

96f0bc034c2fc38c91a550b6ced6656e.png c5270bf85a99bc2132e6918bdc321bb8.png 点击在看,与更多人分享本文 ???
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值