HashiCorp Nomad和遗留系统

容器看起来似乎无处不在。的确,它们使得将应用程序部署到开发、测试和生产环境中更加容易。然而,当容器不能工作时怎么办?为了充分利用目前可用的容器编排平台,必须对应用程序和部署流程进行大量更改。对于绿色领域应用程序来说,这不是问题,因为它们通常是在考虑容器的情况下构建的。但是如果您想要在遗留系统拥有类似容器的调度器优势,该如何做呢?

进入 HashiCorp Nomad.

Nomad是一个集群调度引擎,它有几种驱动类型,允许您在跨工作负载节点的分布式网络调度应用程序工作负载。这些驱动程序允许调度运行工作负载的容器、虚拟机或任何其他可执行文件。

工作负载可能由一个应用程序进程或一组我们希望在Nomad节点上运行的进程组成。Nomad在运行时处理动态配置注入,以设置不同的服务依赖关系,如数据库连接、http端口、服务凭证和注册,并使用服务发现工具,如HashiCorp Consul。所有这些工作负载配置元素都可以由一个Nomad作业文件定义。

Nomad作业文件是Nomad集群在Nomad集群中调度、配置和执行所需作业的工具。使用此作业文件,您可以根据以下几个因素定义如何跨集群调度工作负载:

  • 操作系统和版本
  • 硬件配置
  • 标签定义
  • 数据中心位置
  • 资源消耗因素

据此,Nomad确定工作负载的位置以及如何配置它以运行。

用于运行遗留系统的Nomad

对于一些遗留应用程序来说,运行它们的唯一方法是使用二进制文件或二进制文件组来执行工作负载。对于这些类型的应用程序,您可以使用Nomad的exec驱动程序。Nomad exec驱动程序允许对作业进行调度,并将其链接到已经存在于机器上或在运行时动态安装的可执行文件上。让我们来看一个例子。

遗留Tomcat应用程序

对于本例,我们将查看Tomcat应用程序。虽然这只是一个简单的示例,却包含了本文中涉及的许多概念,可以很容易地应用于其他可执行类型。

先决条件

要使这个示例工作起来,首先需要一个正在运行的Nomad集群。在这个示例中,我有一个集群,它由三个Nomad服务器节点和六个Nomad客户端节点组成。在每个客户端节点上,我都在/opt/ Tomcat目录中安装了Apache Tomcat。最佳实践是不要使用包管理器在Nomad集群中安装依赖项,而是使用本地安装脚本或配置工具(如Chef或Puppet)单独安装它们。

这是有充分理由的。首先,发行版和各版本之间的安装各不相同,您希望在Nomad集群之间保持互操作性。其次,相当多的安装将二进制文件注册为已经在运行的服务。Nomad希望在作业和本地安装的服务之间保持一定程度的隔离,通常每台机器只有一个配置。这使得使用相同的服务运行不同的作业配置变得困难。

一旦您安装并运行了集群并配置了Tomcat,我们就可以使用LegacyApp.nomad将作业部署到集群中。

编写 LegacyApp.nomad 作业文件

让我们一步步浏览Nomad作业文件,这样您就可以了解它的语法格式和如何工作的。与所有HashiCorp工具一样,Nomad遵循HCL格式。这允许声明式格式具有插入功能,就像HashiCorp Terraform一样。

每个Nomad文件需要的第一件事就是作业名称。对于这个作业,我们取名为“LegacyWebApp”:

job “LegacyWebApp” {

在job下,您可以定义关于作业可以在何处运行的特定属性。对于我们的应用程序示例,我们希望它在所有节点上运行。但是您可以使用特定属性来确定位置、反亲和性、蓝色/绿色或其他许多节点。

  region      = “global”
  datacenters = [“dc1”]

我们需要的下一个定义是将要运行的应用程序类型。您可以为Nomad定义三种 type 的工作负载:batch、system和service。batch就是在给定的时间间隔排队运行的工作负载。然后可以使用Nomad作为分布式批处理程序。system作业告诉Nomad,无论你定义什么作业,你都希望它运行在集群中的每个节点上。我们将使用的是service。这适用于长时间运行的流程,该流程应该始终基于以下节中概述的作业规范运行。

    type = “service”

接下来我们要定义一个 group。Nomad中的一个group是一组任务的集合,它们都需要一起运行才能使工作负载正常工作。一个组中可以有一个任务,或者多个任务。位于group节中的任何任务都将在同一个Nomad节点上一起启动。

    group “tomcat” {

group 节下,您将定义工作负载的所有属性。我们要确定的第一件事是需要运行多少相同的工作负载。这是由 count 属性定义的。

        count = 3

要定义的第二件事是如何更新工作负载任务。这是在update节中完成的。在本节中,您可以精确地定义您希望如何升级。在这个例子中,我们想要模拟一个蓝/绿部署,这样在开始终止旧版本之前,我们将拥有与当前版本相同的canary部署数量,其健康状态最低(min_healthy_time)为30s。健康的最后期限(healthy_deadline)规定,如果所有的canary部署在10分钟内没有启动,就摧毁canary部署并保持稳定。

    update {
        canary = 3
       min_healthy_time = “30s”
       healthy_deadline = “10m”
       auto_revert      = true
       max_parallel     = “1”
    }

Task 节

运行遗留二进制文件的真正技巧来自task节。第一部分是使用 artifact 节。此配置允许您指定要将哪些组件下载到节点提供的临时存储中,以及将它们放在何处。您可以从HTTP端点或VCS系统(如GitHub或GitLab)中提取文件。您甚至可以下载压缩文件并将它们解压到磁盘上。如果您需要下载一个由包装器脚本启动的二进制文件包,那么这将非常有用。您还可以下载不同的配置文件,并通过环境变量或使用模板通过Nomad的本地插值来填充它们。

对于我们的Tomcat示例,需要下载两个文件。第一个是我们想要部署在上面的war文件。第二个是定制的servcer.xml。我们将用Nomad节点本身提供的环境变量填充。在artifact 节中,您必须有一个源位置(source)。目标(destination)是可选的,如果未声明,则默认为节点上的本地 / 目录。

        task “WebApp” {
        artifact {
         source = “http://location.of.my.file/myfile.war" 
               destination = “/local/webapps”
       }
       artifact {
         source = “http://location.of.my.file/server.xml”
         destination = “/local/tomcat/conf”
       }

ENV 节

我们要使用的下一节是env调用。这允许您设置将在运行时填充的环境变量。对于我们的应用程序示例,我们将从命令中填充运行Tomcat所需的内容,比如JAVA_HOME、CATALINA_HOME,以及特定于应用程序的内容,比如数据库凭证,以及要映射到Tomcat进程的http端口。这允许您使用不同的端口在同一个主机上运行多个Tomcat实例。您将把它与resources节一起使用,其中http端口将由节点动态分配。

          env {
        DBHOST = “your.db.host”
        DBUSER = “dbuser”
        DBPASS = “password”
        DATABASE = “demodb”
        CATALINA_OPTS = -Dport.http=$NOMAD_PORT_http -Ddefault.context=$NOMAD_TASK_DIR”
        JAVA_HOME = “/location/to/java”
        CATALINA_HOME = “/opt/tomcat”
       }

注意,我正在为http端口和Nomad任务目录使用已填充的Nomad环境变量,以填充要注入到Tomcat服务启动中的java命令行参数。这两个位于CATALINA_OPTS环境变量中的参数被传递给server.xml用于建立监听http端口以及从根目录到war文件位置的位置。

<?xml version='1.0' encoding='utf-8'?>
<Server port="-1" shutdown="SHUTDOWN">

  <Service name="Catalina">

    <Connector port="${port.http}" protocol="HTTP/1.1"
               connectionTimeout="20000" />
    <Engine name="Catalina" defaultHost="localhost">

      <Host name="localhost"  appBase="${default.context}/webapps/"
            unpackWARs="true" autoDeploy="true"
            xmlValidation="false" xmlNamespaceAware="false">

      </Host>
    </Engine>
  </Service> 

EXEC 驱动

driver配置用于运行此工作负载的驱动程序类型。对于这个工作负载,我们使用exec驱动程序。这允许任何命令作为nomad进程运行的用户执行。我们再次注入一个环境变量,以告诉tomcat server.xml配置文件在哪里,以便在启动时使用。catalina.sh是一个包装器脚本,用于构建和运行java命令以启动tomcat。您可以使用java驱动程序来完成这项工作,但是构建所有选项可能会比较麻烦,而且tomcat已经有了运行tomcat的好方法。

大多数命令都有传递运行时参数的方法。config节允许您指定那些参数是什么。对于本例,我们希望将-config选项连同server.xml的位置一起传递。

需要注意的一点是,exec不能执行作为守护进程运行进程的命令。它必须在前台运行,才能在进程结束时终止进程。为了在tomcat中实现这一点,我们传递了“run”参数,它将在前台维持这个进程。

        driver = “raw_exec”
      config {
           command = “/opt/tomcat/bin/catalina.sh”  
           args = [“run”, “-config”, “$NOMAD_TASK_DIR/tomcat/conf/server.xml”]
      }

因为我们需要使用动态http端口,所以需要像Consul这样的服务发现工具来声明这个应用程序的位置。Consul registration功能将允许我们的动态负载均衡器Fabio自动提取运行中的服务并将其映射到一个外部端口9999。为了做到这一点,我们使用service节,并将它注册到Consul。

          service {
            tags = ["urlprefix-/LegacyApp"]
             port = "http"

             check {
                  name     = "version_check"
                  type     = "http"
                  path     = "/LegacyApp/version/version.jsp"
                 interval = "10s"
                  timeout  = "2s"
             }
        }

我们要做的最后一件事是提供资源约束。这将告诉调度器我们的工作需要哪些资源。它还将定义将http映射到的端口。如果你看一下http属性,我在它旁边放了花括号。这告诉Nomad使用一个端口范围为20000-40000来分配http端口。然后,在环境变量(用于传递给tomcat)中,对要绑定到的对象进行插值。

       resources {
            cpu    = 500 # MHz
            memory = 128 # MB

            network {
               mbits = 1
              port "http" {}
          }
       }

把它们放在一起

一旦创建了这个作业文件,剩下要做的就是将其提交给Nomad进行调度。通过在命令行上执行的nomad run命令来实现。

(master) $: nomad run LegacyWebapp.nomad
==> Monitoring evaluation "c0dde6be"
    Evaluation triggered by job "LegacyWebApp"
    Evaluation within deployment: "a3e63ddd"
    Allocation "af427171" modified: node "c8288d23", group "tomcat"
    Allocation "d7af2ec8" modified: node "33cd403c", group "tomcat"
    Allocation "ea1c2194" modified: node "c56b21d7", group "tomcat"
    Evaluation status changed: "pending" -> "complete"
==> Evaluation "c0dde6be" finished with status “complete"

运行此命令后,您可以通过运行job status命令查看作业的状态。

(master) $: nomad job status LegacyWebApp
ID            = LegacyWebApp
Name          = LegacyWebApp
Submit Date   = 04/02/18 19:50:27 EDT
Type          = service
Priority      = 50
Datacenters   = dc1
Status        = running
Periodic      = false
Parameterized = false

Summary
Task Group  Queued  Starting  Running  Failed  Complete  Lost
tomcat      0       0         3        2       55        0

Latest Deployment
ID          = a3e63ddd
Status      = successful
Description = Deployment completed successfully

Deployed
Task Group  Desired  Placed  Healthy  Unhealthy
tomcat      3        3       3        0

Allocations
ID        Node ID   Task Group  Version  Desired  Status   Created    Modified
af427171  c8288d23  tomcat      40       run      running  4h31m ago  4h28m ago
d7af2ec8  33cd403c  tomcat      40       run      running  4h31m ago  4h28m ago
ea1c2194  c56b21d7  tomcat      40       run      running  4h31m ago  4h28m ago

Consul将显示您的服务已经启动并运行,注册并通过所有健康检查。

现在,如果您将浏览器指向URL,就应该得到您的应用程序。

如果您想要从1.0版本升级到2.0版本,只需对job文件进行修改即可。在这种情况下,只需将artifact 值由v.1的地址改为v.2的地址。重新提交作业,Nomad就会发现发生了什么变化。您可以首先执行plan命令,然后查看Nomad将如何处理更新后的工作。

(master) $: nomad plan LegacyWebapp.nomad
+/- Job: "LegacyWebApp"
+/- Task Group: "tomcat" (3 canary, 3 ignore)
  +/- Update {
    +/- AutoRevert:      "false" => "true"
    +/- Canary:          "0" => "3"
        HealthCheck:     "checks"
    +/- HealthyDeadline: "300000000000" => "600000000000"
        MaxParallel:     "2"
    +/- MinHealthyTime:  "10000000000" => "30000000000"
      }
  +/- Task: "WebApp" (forces create/destroy update)
    + Artifact {
      + GetterMode:   "any"
      + GetterSource: "https://s3.us-east-2.amazonaws.com/nomad-demo-legacyapp/v.2/LegacyApp.war"
      + RelativeDest: "/local/webapps/"
      }
    - Artifact {
      - GetterMode:   "any"
      - GetterSource: "https://s3.us-east-2.amazonaws.com/nomad-demo-legacyapp/v.1/LegacyApp.war"
      - RelativeDest: "/local/webapps/"
      }

重新提交作业,并查看Nomad将应用程序的三个新实例作为canary部署。

(master) $: nomad job status LegacyWebApp
ID            = LegacyWebApp
Name          = LegacyWebApp
Submit Date   = 04/03/18 00:26:38 EDT
Type          = service
Priority      = 50
Datacenters   = dc1
Status        = running
Periodic      = false
Parameterized = false

Summary
Task Group  Queued  Starting  Running  Failed  Complete  Lost
tomcat      0       0         6        2       55        0

Latest Deployment
ID          = ea1564a0
Status      = running
Description = Deployment is running but requires promotion

Deployed
Task Group  Auto Revert  Promoted  Desired  Canaries  Placed  Healthy  Unhealthy
tomcat      true         false     3        3         3       0        0

Allocations
ID        Node ID   Task Group  Version  Desired  Status   Created    Modified
0b0ef2a4  206bfc4f  tomcat      41       run      running  9s ago     8s ago
916307f1  46102c23  tomcat      41       run      running  9s ago     8s ago
ca82eb52  89797ed9  tomcat      41       run      running  9s ago     9s ago
af427171  c8288d23  tomcat      40       run      running  4h39m ago  4h35m ago
d7af2ec8  33cd403c  tomcat      40       run      running  4h39m ago  4h36m ago
ea1c2194  c56b21d7  tomcat      40       run      running  4h39m ago  4h36m ago

正如您所看到的,Nomad为我们的应用程序创建了三个新的canary实例,以确保所有的东西都能投入使用。

在我们测试以确保部署工作正常之后,我们现在可以升级新代码并终止旧代码。您可以通过执行deployment promote命令来实现这一点。

(master) $: nomad deployment promote ea1564a0
==> Monitoring evaluation "58e44443"
    Evaluation triggered by job "LegacyWebApp"
    Evaluation within deployment: "ea1564a0"
    Evaluation status changed: "pending" -> "complete"
==> Evaluation "58e44443" finished with status “complete"

现在,当您执行 job status时,您可以看到我们的三个旧版本已经结束,现在只运行新版本。

(master) $: nomad job status LegacyWebApp
ID            = LegacyWebApp
Name          = LegacyWebApp
Submit Date   = 04/03/18 00:26:38 EDT
Type          = service
Priority      = 50
Datacenters   = dc1
Status        = running
Periodic      = false
Parameterized = false

Summary
Task Group  Queued  Starting  Running  Failed  Complete  Lost
tomcat      0       0         3        2       58        0

Latest Deployment
ID          = ea1564a0
Status      = successful
Description = Deployment completed successfully

Deployed
Task Group  Auto Revert  Promoted  Desired  Canaries  Placed  Healthy  Unhealthy
tomcat      true         true      3        3         3       3        0

Allocations
ID        Node ID   Task Group  Version  Desired  Status    Created    Modified
0b0ef2a4  206bfc4f  tomcat      41       run      running   5m44s ago  5m9s ago
916307f1  46102c23  tomcat      41       run      running   5m44s ago  5m7s ago
ca82eb52  89797ed9  tomcat      41       run      running   5m44s ago  5m8s ago
af427171  c8288d23  tomcat      40       stop     complete  4h45m ago  1m55s ago
d7af2ec8  33cd403c  tomcat      40       stop     complete  4h45m ago  1m55s ago
ea1c2194  c56b21d7  tomcat      40       stop     complete  4h45m ago  1m55s ago

结论

对于那些还没有准备好进行容器化的遗留应用程序,Nomad是一个很好的选择,它允许对那些还没有准备好迁移到新部署框架中的工作负载类型灵活地进行动态作业调度

要了解更多关于Nomad的信息,请访问 https://www.hashicorp.com/products/nomad.

猜你喜欢

转载自blog.csdn.net/zl1zl2zl3/article/details/83024305