Nomad系列-Nomad网络模式

news2024/11/28 16:48:47

系列文章

  • Nomad 系列文章

概述

Nomad 的网络和 Docker 的也有很大不同, 和 K8s 的有很大不同. 另外, Nomad 不同版本(Nomad 1.3 版本前后)或是否集成 Consul 及 CNI 等不同组件也会导致网络模式各不相同. 本文详细梳理一下 Nomad 的主要几种网络模式

在Nomad 1.3发布之前,它自身并不支持发现集群中运行的其他应用程序。在集群中调度任务时,这是一个非常基本的要求。Nomad依赖于Consul来发现其他“服务”,并为注册和获取服务记录提供一流的支持,这使得事情变得更容易。Consul通过各种机制提供记录,例如REST API,DNS和Consul模板,这些模板在可以注入到应用程序中的Go模板中呈现服务的确切IP/端口。

学习 Nomad 的一个难点在于, Nomad 往往和 Consul 一起运行, 那么对于这种情况来说,一个主要的学习曲线是,我们必须首先了解Consul是如何工作的,部署一个Consul集群, 同时要融会贯通 2 个软件就很难了。Nomad 1.3 解决了这个问题的一部分(即不需要运行Consul就可以进行基本的服务发现),非常适合刚刚开始使用基于Nomad的网络。

场景一: 在主机上公开应用

Host Dynamic Port

从最简单的用例开始:你有一个 redis 容器,你想把它暴露给主机。 相当于我们想要做的docker run是 :

docker run --rm -p=6379 redis

此命令公开主机上的动态端口。要查看端口号到底是什么,您可以执行 docker ps 并在 PORTS 下找到类似于 0.0.0.0:49153->6379/tcp 的输出。

$ redis-cli -p 49153                
127.0.0.1:49153> ping
PONG

那么, 在 Nomad 中相同的操作如何实现?

job "redis" {
  type        = "service"

  group "redis" {
    network {
      mode = "host"
      port "redis" {
        to = 6379
      }
    }

    task "redis" {
      driver = "docker"

      config {
        image = "redis"
        ports = ["redis"]
      }
    }
  }
}

在几行配置中,我们有一个正在运行的Docker容器,它公开了一个动态端口 30627:

Nomad Redis Job Map Port

我们可以通过主机上的 redis-cli 连接到它:

$ redis-cli -p 30627
127.0.0.1:30627> ping
PONG

🐾Warning

task.config 部分中有 ports 很重要。Nomad将此信息传递给主机上运行的 docker 守护进程。因此,除非您指定在容器中通告哪些端口,否则它不会知道是否要公开6379。

暴露静态端口

一种不太常见的情况是将应用程序绑定到主机上的静态端口, 只需在 port 块中添加一个 static 行:

    network {
      port "redis" {
        static = 6379
      }
    }

Host Static Port

当我们再次部署相同的文件时,我们可以看到端口分配已经从动态端口更改为我们分配的静态端口。但是注意需要确保没有其他应用程序侦听同一接口和端口,否则必然会导致冲突。

静态端口典型的使用场景就是: Ingress. 比如 Traefik 可以使用静态端口监听 80 和 443.

场景二: 与同一 Group 内的 Redis 通信

对于这个场景,我们假设有一个应用程序需要与Redis通信。在这个场景中,Redis用途是临时缓存,所以可以将它们部署在同一个 Group 中。

一个 Group 可以包含多个 Task。这里需要知道的重要一点是,同一 Group 将始终具有自己的共享网络命名空间(类似K8s中Pod中的多个Container具有共享网络命名空间)。这意味着,如果您在组中有2个 Task,则它们都可以访问相同的网络命名空间。这允许两个 Task 在同一网络接口上相互通信。

job "hello" {
  type        = "service"

  group "app" {
    network {
      mode = "host"
      port "app" {
        static = 8080
      }
      port "redis" {
        static = 6379
      }
    }

    task "redis" {
      driver = "docker"

      config {
        network_mode = "host"
        image        = "redis"
        ports        = ["redis"]
      }
    }


    task "app" {
      driver = "docker"
      env {
        DEMO_REDIS_ADDR = "${NOMAD_ADDR_redis}"
      }

      config {
        network_mode = "host"
        image        = "mrkaran/hello-app:1.0.0"
        ports        = ["app"]
      }
    }
  }
}

详细说明如下:

  • 您可以看到我们在同一 Group 下定义了 task app 和task redis 。这意味着Nomad将在同一客户端上共同定位这两个Task(因为它们不仅倾向于共享相同的网络命名空间,而且还共享公共分配目录-这使得跨任务共享文件变得非常容易)。
  • 我们使用 NOMAD_ADDR_redis 来获取 redis task 的 IP:Port 组合。这在运行时由Nomad注入。您可以在这里找到运行时环境变量的列表。
  • 这是快速测试/开发设置的理想选择,因为您不希望服务发现等问题,并且希望以最小的代价连接到您的应用程序。

如果您要从基于 docker-compose 的环境迁移,以上配置非常适合(但是实现还是不同, Nomad利用了主机网络),您可以将此模板用于您的服务。这种方法的最大限制是它使用主机网络

场景三: 跨不同的 Group 进行通信

如上所述, 如果您有相关的 Task(如init task,您希望在 task 开始前获取文件),同一个 Group 很有用(类似K8s Pod 的 init container)。但是使用 group 的缺点是您不能独立地扩展 task。在上面的例子中,我们将Redis和App放在同一个 Group 中,但这意味着如果你增加同一个 Group 的 count 来扩展 app,你最终也会扩展Redis容器。这是不可取的,因为Redis可能不需要与应用程序成比例地扩展。

创建多个 Group 的方法是将任务拆分到各自的组中:

job "hello" {
  type        = "service"

  group "app" {
    count = 1

    network {
      mode = "host"
      port "app" {
        static = 8080
      }
    }

    task "app" {
      driver = "docker"
      env {
        DEMO_REDIS_ADDR = "localhost:6379"
      }

      config {
        image = "mrkaran/hello-app:1.0.0"
        ports = ["app"]
      }
    }
  }

  group "redis" {
    count = 1

    network {
      mode = "host"
      port "redis" {
        static = 6379
      }
    }

    task "redis" {
      driver = "docker"

      config {
        image = "redis"
        ports = ["redis"]
      }
    }
  }
}

提交此 Job 后,您将获得2个分配ID(每个 Group 会创建一个 alloc )。这里的关键点是这两个 Group 都有自己的网络命名空间。因此,我们实际上没有任何方法可以访问其他应用程序(我们不能向上面这样依赖主机网络,因为无法保证这两个 Group 都部署在同一个节点上)。

现在由于组是分开的, app 容器不知道 redis (反之亦然):

env | grep NOMAD
NOMAD_REGION=global
NOMAD_CPU_LIMIT=4700
NOMAD_IP_app=127.0.0.1
NOMAD_JOB_ID=hello
NOMAD_TASK_NAME=app
NOMAD_SECRETS_DIR=/secrets
NOMAD_CPU_CORES=1
NOMAD_NAMESPACE=default
NOMAD_ALLOC_INDEX=0
NOMAD_ALLOC_DIR=/alloc
NOMAD_JOB_NAME=hello
NOMAD_HOST_IP_app=127.0.0.1
NOMAD_SHORT_ALLOC_ID=a9da72dc
NOMAD_DC=dc1
NOMAD_ALLOC_NAME=hello.app[0]
NOMAD_PORT_app=8080
NOMAD_GROUP_NAME=app
NOMAD_PARENT_CGROUP=nomad.slice
NOMAD_TASK_DIR=/local
NOMAD_HOST_PORT_app=8080
NOMAD_MEMORY_LIMIT=512
NOMAD_ADDR_app=127.0.0.1:8080
NOMAD_ALLOC_PORT_app=8080
NOMAD_ALLOC_ID=a9da72dc-94fc-6315-bb37-63cbeef153b9
NOMAD_HOST_ADDR_app=127.0.0.1:8080

服务发现

app Group 需要在连接到 redis 之前发现它。有多种方法可以做到这一点,但我们将介绍两种更常见的标准方法。

使用 Nomad Native Service Discovery

Nomad Native Service Discovery

这是在Nomad 1.3中推出的功能。在这次发布之前,Nomad 不得不依靠 Consul 来完成这一任务。但是有了Nomad中内置的原生服务发现,事情就简单多了。让我们对作业文件进行以下更改。在每个 Group 中,我们将添加一个 service 定义:

  group "app" {
    count = 1

    network {
      mode = "host"
      port "app" {
        to = 8080
      }
    }

    service {
      name     = "app"
      provider = "nomad"
      port     = "app"
    }
    // task is the same
  }

  group "redis" {
    count = 1

    network {
      mode = "host"
      port "redis" {
        to = 6379
      }
    }

    service {
      name     = "redis"
      provider = "nomad"
      port     = "redis"
    }
    // task is the same
  }

如上,我们添加了一个新的 service 块,并删除了 static 端口。当我们使用服务发现时,不需要绑定到静态端口

提交作业后,我们可以使用 nomad service list 命令确保服务已注册到Nomad。

nomad service list    
Service Name  Tags
app           []
redis         []

要了解特定服务的详细信息,我们可以使用 nomad service info

$ nomad service info app      
Job ID  Address          Tags  Node ID   Alloc ID
hello   127.0.0.1:29948  []    d92224a5  5f2ac51f
$ nomad service info redis
Job ID  Address          Tags  Node ID   Alloc ID
hello   127.0.0.1:22300  []    d92224a5  8078c9a6

如上, 我们可以看到每个服务中的动态端口分配。要在我们的应用程序中使用此配置,我们将其模板化:

    task "app" {
      driver = "docker"

      template {
        data = <<EOH
{{ range nomadService "redis" }}
DEMO_REDIS_ADDR={{ .Address }}:{{ .Port }}
{{ end }}
EOH

        destination = "secrets/config.env"
        env         = true
      }

      config {
        image = "mrkaran/hello-app:1.0.0"
        ports = ["app"]
      }
    }

我们添加了 template 节,它将在容器中插入环境变量。我们遍历 nomadService 并获取 redis 服务的地址和端口。这使得其他节点上的任务可以方便地发现彼此。

使用 Consul 服务发现

Consul Service Discovery

只需调整 service 块中的 provider ,我们就可以使用Consul代理进行服务发现。

    service {
      name     = "app"
      provider = "consul"
      port     = "app"
    }


    task "app" {
      driver = "docker"

      template {
        data = <<EOH
{{ range service "redis" }}
DEMO_REDIS_ADDR={{ .Address }}:{{ .Port }}
{{ end }}
EOH

🐾Warning:

注意 range nomadService 也改为了 range service

前提是要确保正在运行Consul并已将Nomad连接到它。具体请参阅该文档。

其余的事情几乎保持不变。只用两行代码就可以在Nomad/Consul之间切换来发现服务。

另外, 使用Consul会有更多的优势

  • 可以使用DNS查询服务的地址:
doggo redis.service.consul @tcp://127.0.0.1:8600
NAME                    TYPE    CLASS   TTL ADDRESS     NAMESERVER     
redis.service.consul.   A       IN      0s  172.20.10.3 127.0.0.1:8600  
  • 可由Nomad以外的应用程序访问。如果 consul 被Nomad集群外的其他应用程序使用,它们仍然可以获得对应的地址(使用DNS或REST API)

当然,Nomad Native Service Discovery 非常适合本地/边缘环境设置,甚至是生产中的较小用例,因为它不再需要 Consul!

场景四: 限制对某些 Namespace 的访问

Consul Service Mesh

在上述所有场景中,我们发现服务会暴露给本地Nomad客户端。如果您在集群上运行多个 Namespace,您可能希望根本不公开它们。此外,您可能希望表达应用程序可以访问特定服务的细粒度控制。所有这些都可以通过服务网格实现。Nomad提供了一种通过Consul Connect建立“服务网格”的方法。Consul Connect可以进行mTLS和服务授权。在引擎盖下,它是一个与您的应用程序一起运行的Envoy代理(或sidecar)。 Consul 代理为您配置Envoy配置,因此这一切都非常无缝。

要做到这一点,我们首先需要的是 bridge 网络模式。此网络模式实际上是一个CNI插件,需要在 /opt/cni/bin 中单独安装。按照这里提到的步骤:

    network {
      mode = "bridge"
      port "redis" {
        to = 6379
      }
    }

Redis 中的服务被 Consul Connect Ingress 所调用:

    service {
      name     = "redis"
      provider = "consul"
      port     = "6379"
      connect {
        sidecar_service {}
      }
    }

这是一个空块,因为我们不需要在这里定义任何上游。其余值将为默认值。

接下来,我们为 app 创建一个服务,这是一个Consul Connect Egress:

    service {
      name     = "app"
      provider = "consul"
      port     = "app"
      connect {
        sidecar_service {
          proxy {
            upstreams {
              destination_name = "redis"
              local_bind_port  = 6379
            }
          }
        }
      }
    }

这里我们为 redis 定义一个上游。在这里,当 app 想要与redis通信时,它会与 localhost:6379 对话,这是Envoy sidecar正在监听的本地端口。我们可以使用 netstat 来验证:

$ netstat -tulpvn
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 127.0.0.2:19001         0.0.0.0:*               LISTEN      -                   
tcp        0      0 0.0.0.0:23237           0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.1:6379          0.0.0.0:*               LISTEN      -                   
tcp6       0      0 :::8080                 :::*                    LISTEN      1/./hello.bin

流量从这个端口发送到它通告的端口上的另一个Envoy代理(并且Consul自动配置)。该Envoy代理进一步将流量发送到端口6379上的 redis 容器。代理流量通过mTLS进行安全加密并授权(通过Consul Intentions -本文不做介绍)。

场景五: 向最终用户公开服务

LB + Ingress

在第一个场景中,我们讨论了如何使用静态端口。事实证明,如果你想定义一个Traffic Ingress服务,它非常有用。与K8s不同的是,Nomad没有任何Ingress Controller,所以最好的方法是将这些Web代理作为 system job 部署在每个节点上(这意味着它可以确保在每个客户端节点上运行),并将它们绑定到静态端口(比如443/80)。然后,配置 LB 并将所有Nomad节点注册为 Target IP,其端口将是您定义的静态端口。这些Ingress代理(比如Traefik/Nginx)可以通过上面提到的任何模式与您的应用程序通信。

📝Notes:

在上一篇文章中, 我们并没有配置 LB 后面对接所有 Traefik. 相反, 我们直接访问某一个特定节点的 Traefik 的 80/443 端口.

通常,您希望为入口代理使用“基于主机”的路由模式来做出路由决策。

例如,如果您有一个指向ALB的 a.example.org DNS记录。现在,当请求到达ALB时,它会转发到任何一个Traefik/NGINX。为了使 NGINX 正确地将流量路由到a service,您可以使用“Host”报头。

总结

这些是我所知道的一些常见的网络模式。由于其中一些概念并不是非常简单,我希望解释有助于带来一些清晰。

关于这个主题还有很多,比如 Consul Gateway 和多种CNI,它们可以调整集群中的网络的底层细节,但这些都是一些非常高级的主题,超出了本文的范围。后续有机会可以再做展开.

📚️参考文档

  • Understanding Nomad Networking Patterns - YouTube
  • Understanding Networking in Nomad | Karan Sharma (mrkaran.dev)

三人行, 必有我师; 知识共享, 天下为公. 本文由东风微鸣技术博客 EWhisper.cn 编写.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/992294.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CSS_文字渐变

/* 定义渐变背景样式 */ .gradient-text {background-image: linear-gradient(to right, #ff0000, #00ff00); /* 渐变色范围 */background-clip: text; /* 应用渐变背景到文本 */-webkit-background-clip: text; /* Safari 和 Chrome 的前缀 */color: transparent; /* 将文本颜…

ADS1115 模拟IIC

ADS1115是16位ADC&#xff0c;基准源内部可选&#xff0c;PGA 可提供从 256mV 到 6.144V 的输入范围。 地址可由ADDR引脚决定&#xff0c;一般接地&#xff0c;地址为0x90 写寄存器地址为0x90&#xff0c;读寄存器地址为0x91 ADS1115有4个控制寄存器&#xff0c;0x00,0x01,0x0…

debian apt安装mysqlodbc

mysql的deb包下载地址 下载后上传到linux后&#xff0c; #安装deb包 apt install ./mysql-apt-config_0.8.26-1_all.deb #更新源 apt-get update #搜索包 apt search odbc #安装包 apt-get install mysql-connector-odbc

3. 自定义datasource

一、自定义DataSource ​ 自定义DataSource有两大类&#xff1a;单线程的DataSource和多线程的DataSource 单线程&#xff1a;继承 SourceFunction 多线程&#xff1a;继承 ParallelSourceFunction&#xff0c;继承 RichParallelSourceFunction&#xff08;可以有其他的很多操…

origin中optimal cluster安装报错解决

1.在安装之后程序运行出错&#xff0c;报错信息为缺少numpy包。解决办法&#xff1a;打开窗口-脚本窗口&#xff0c;用pip安装numpy&#xff0c;其他缺少的包可用同样方法解决。 2.有的包在外部python中才有&#xff0c;通过origin无法下载。解决办法&#xff1a;在连接-python…

WIFI版本云音响设置教程阿里云平台版本

文章目录 WIFI本云音响设置教程介绍一、申请设备三元素1.登录阿里云物联网平台2.创建产品3.设置产品参数4.添加设备5.获取三元素 二、设置设备三元素1.打开MQTTConfigTools2.计算MQTT参数3.使用windows电脑的WIFI连接到设备热点4.设置参数5.配置设备连接路由器 三、阿里云物联网…

有始有终!

作者 | 磊哥 来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09; 转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09; 开始是立秋之日&#xff08;8.8 号&#xff09;&#xff0c;结束是白露之时&#xff08;9.8 号&#xff09;。 为期一月&…

Day60|leetcode 84.柱状图中最大的矩形

leetcode 84.柱状图中最大的矩形 题目链接&#xff1a;84. 柱状图中最大的矩形 - 力扣&#xff08;LeetCode&#xff09; 视频链接&#xff1a;单调栈&#xff0c;又一次经典来袭&#xff01; LeetCode&#xff1a;84.柱状图中最大的矩形_哔哩哔哩_bilibili 题目概述 给定 n 个…

CSS3技巧36:backdrop-filter 背景滤镜

CSS3 有 filter 滤镜属性&#xff0c;能给内容&#xff0c;尤其是图片&#xff0c;添加各种滤镜效果。 filter 滤镜详见博文&#xff1a;CSS3中强大的filter(滤镜)属性_css3滤镜_stones4zd的博客-CSDN博客 后续&#xff0c;CSS3 又新增了 backdrop-filter 背景滤镜。 backdr…

卷积概念理解

卷积(convolution)最容易理解的解释_一点一点的进步的博客-CSDN博客 图像处理之卷积模式及C实现_利用卷积模型分类图片 c_扫地工的博客-CSDN博客 卷积的重要的物理意义是&#xff1a;一个函数&#xff08;如&#xff1a;单位响应&#xff09;在另一个函数&#xff08;如&…

进程

目录 进程定义 进程与程序对比 进程分类 系统进程 用户进程 交互进程 批处理进程 守护进程 进程状态 进程组成 ​编辑正文段&#xff08;text&#xff09;和用户数据段 用户数据段 正文段 PCB进程控制块 进程标识信息 处理机状态 进程调度信息 进程控制信息 …

通达信自定义副图行业指标K线指标 HYZS_QD

行业指数:HY_INDEXC,NODRAW; DRAWKLINE(HY_INDEXH,HY_INDEXO,HY_INDEXL,HY_INDEXC); MA5:MA(HY_INDEXC,5),COLORWHITE; {MA10:MA(HY_INDEXC,10),COLORYELLOW,LINETHICK2}; DRAWTEXT_FIX(1,1,1,1,STRCAT(STRCAT(CON2STR(HY_INDEXADV,0),/),STRCAT(CON2STR(HY_INDEXDEC,0), ))),…

06_瑞萨GUI(LVGL)移植实战教程之驱动EC11旋转编码器(GPIO)

本系列教程配套出有视频教程&#xff0c;观看地址&#xff1a;https://www.bilibili.com/video/BV1gV4y1e7Sg 6. 驱动EC11旋转编码器(GPIO) 本次实验我们驱动EC11旋转编码器。 6.1 复制工程 上次实验得出的工程我们可以通过复制在原有的基础上得到一个新的工程。 如果你不清…

XCE18T4K1P40-FJJP40、F4Z1P40规格书(泰兴创航)

关于XCE18T4K1P40-FJJP40、F4Z1P40电连接器规格书 主要性能指标 工作温度&#xff1a;-55℃~200℃相对湿度&#xff1a;温度40℃2℃时达98%振动&#xff1a;频率10-2000Hz&#xff0c;加速度196m/s2冲击&#xff1a;加速度980m/s2机械寿命&#xff1a;5000次壳体材料&#xff1…

05_瑞萨GUI(LVGL)移植实战教程之添加LVGL库,对接显示和触摸驱动

本系列教程配套出有视频教程&#xff0c;观看地址&#xff1a;https://www.bilibili.com/video/BV1gV4y1e7Sg 5. 添加LVGL库&#xff0c;对接显示和触摸驱动 本次实验我们会融合前面实验的成果&#xff0c;添加LVGL库&#xff0c;对接显示和触摸驱动&#xff0c;让屏幕能显示…

金蝶云星空与泛微OA集成的方案落地与实践

打破信息孤岛&#xff0c;泛微OA集成的方案落地与实践 在现代企业内部&#xff0c;不同类型的业务系统和泛微OA平台层出不穷。企业需要找到一种高效的方法来整合和协同这些多样化的系统&#xff0c;同时将它们与泛微OA平台融合&#xff0c;以实现资源整合和高度协同的办公环境…

Win10下python的命令行启动和调用问题

Win10下python的命令行启动和调用问题 Win10下Python的启动问题解决办法 Win10下Python的启动问题 Win10下安装了python&#xff0c;但是命令行启动仍然显示Windows商店界面 同时在C:\Users\用户名\AppData\Local\Microsoft\WindowsApps目录下发现空的python3.exe文件 即便在…

【Java】基于SSM的单位人事管理系统

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

向量数据库,能让AI再次起飞吗?

9月7-8日&#xff0c;深圳国际会展中心18号馆 来了&#xff0c;来了&#xff0c;腾讯面向产业互联网领域规格最高、规模最大、覆盖最广的年度科技盛会 -——- 腾讯全球数字生态大会。 9 月 7 日&#xff0c;我们将聚焦产业未来发展新趋势&#xff0c;针对云计算、大数据、人工…

Kafka3.0.0版本——消费者(分区的分配以及再平衡)

目录 一、分区的分配以及再平衡1.1、消费者分区及消费者组的概述1.2、如何确定哪个consumer来消费哪个partition的数据1.3、消费者分区分配策略 一、分区的分配以及再平衡 1.1、消费者分区及消费者组的概述 一个consumer group中有多个consumer组成&#xff0c;一个 topic有多…