如何使用舰队单元文件为 CoreOS 集群创建灵活的服务

金钱(警告)

状态: 被贬值

原因: 2016 年 12 月 22 日,CoreOS 宣布将不再维护车队;车队将收到安全更新和错误修复,直到 2017 年 2 月,该项目将从CoreOS 中移除。

参见相反: 若要了解如何在没有舰队的 CoreOS 上使用 Kubernetes,请参阅 Kubernetes on CoreOS Documentation

介绍

CoreOS 安装利用一系列工具,使集群和 Docker 服务易于管理.虽然etcd涉及连接单独节点并为全球数据提供一个区域,但实际的服务管理和管理任务中的大多数都涉及与舰队大师一起工作。

在一个(https://andsky.com/tech/tutorials/how-to-use-fleet-and-fleetctl-to-manage-your-coreos-cluster)指南中,我们讨论了使用fleetl命令来操纵服务和群集成员的基本用法。

在本指南中,我们将深入探索舰队单元文件,了解如何创建它们以及一些技术,使您的服务在生产中更加强大。

前提条件

为了完成本教程,我们将假设你有一个CoreOS集群配置,如在我们的 集群指南

代码: coreos-1 代码: coreos-2 代码: coreos-3

虽然本教程的大部分内容将集中在单元文件创建上,但这些机器将在以后被用来展示某些指令的编程影响。

我们还会假设你已经阅读了我们的指南在 如何使用 fleetctl。你应该有工作知识的 fleetctl,所以你可以提交和使用这些单元文件与集群。

当您完成这些要求后,继续与其余的指南。

单元文件部分和类型

由于舰队的服务管理方面主要依赖于每个本地系统的systemd init系统,因此systemd单元文件用于定义服务。

虽然服务是与CoreOS配置的最常见的单元类型,但实际上还有其他单元类型可以定义,这些是传统的systemd单元文件可用的子集。

*服务 :这是最常见的单位文件类型. 它用于定义一个可以在集群中的一款机器上运行的服务或应用程序. *socket :定义套接字或类似套接字的文件的细节。 其中包括网络插座、IPC插座和FIFO缓冲器。 这些用于调用服务,以便在文件看到流量时开始. *device :定义udev设备树上可用的设备信息. Systemd将在单个主机上根据需要为基于udev规则的内核设备创建这些. 这些通常用于订购问题,以确保在试图上架之前有设备可用. *挂载 :定义设备挂载点的信息。 这些以它们提到的挂起点来命名,被划线所取代。 *自动挂载 :定义自动挂载点。 它们遵循与挂载单位相同的命名惯例,必须伴有相关的挂载单位. 这些用于描述需求和平行增长。 *计时器 :定义与另一个单位有关的计时器。 当达到此文件中定义的时间点时,关联单位就会被启动. *path :定义一条可被监视用于路径激活的路径. 当修改到某个路径时,这可以用来启动另一个单元. .

虽然所有这些选项都可用,但服务单元将最常使用,在本指南中,我们只会讨论服务单元配置。

单元文件是简单的文本文件,以点和上述后缀之一结束。 内部,它们是按部分组织的。 对于 fleet,大多数单元文件将具有以下一般格式:

 1[Unit]
 2generic_unit_directive_1
 3generic_unit_directive_2
 4
 5[Service]
 6service_specific_directive_1
 7service_specific_directive_2
 8service_specific_directive_3
 9
10[X-Fleet]
11fleet_specific_directive

单元文件中的部分标题和其他所有内容都是案例敏感的。[单元]部分用于定义有关单元的通用信息。

「[服務]」部分被用來設定特定服務單位的指令. 上述單位類型中的大多數(但並非全部)都有相關的單位類型相關的部分,以獲取單位類型特定的資訊。 請參閱 generic systemd unit file man page以獲取不同單位類型的連結。

「[X-Fleet]」部分用于为单元设置与「 fleet」使用的日程要求. 使用此部分,您可以要求某些条件是正确的,以便单元在主机上进行日程安排。

建立主要服务

对于本节,我们将从我们在CoreOS上运行服务的基本指南中描述的单元文件的变体开始(https://andsky.com/tech/tutorials/how-to-create-and-run-a-service-on-a-coreos-cluster)。

 1[Unit]
 2Description=Apache web server service
 3
 4# Requirements
 5Requires=etcd.service
 6Requires=docker.service
 7Requires=apache-discovery.1.service
 8
 9# Dependency ordering
10After=etcd.service
11After=docker.service
12Before=apache-discovery.1.service
13
14[Service]
15# Let processes take awhile to start up (for first run Docker containers)
16TimeoutStartSec=0
17
18# Change killmode from "control-group" to "none" to let Docker remove
19# work correctly.
20KillMode=none
21
22# Get CoreOS environmental variables
23EnvironmentFile=/etc/environment
24
25# Pre-start and Start
26## Directives with "=-" are allowed to fail without consequence
27ExecStartPre=-/usr/bin/docker kill apache
28ExecStartPre=-/usr/bin/docker rm apache
29ExecStartPre=/usr/bin/docker pull username/apache
30ExecStart=/usr/bin/docker run --name apache -p ${COREOS_PUBLIC_IPV4}:80:80 \
31username/apache /usr/sbin/apache2ctl -D FOREGROUND
32
33# Stop
34ExecStop=/usr/bin/docker stop apache
35
36[X-Fleet]
37# Don't schedule on the same machine as other Apache instances
38X-Conflicts=apache.*.service

我们从[单位]部分开始,这里的基本想法是描述单元并列出依赖性信息,我们从一组要求开始,我们为这个例子使用了硬要求,如果我们希望舰队试图启动额外的服务,但不会停止失败,我们可以使用想要指令。

随后,我们明确列出要求的订单应该是什么。这很重要,以便前提服务在需要时可用。这也是我们自动启动我们将建设的服务的方式。

对于[服务]部分,我们关闭了服务启动时间。第一次在主机上运行服务时,必须将容器从Docker注册表中拉下来,这会计算到启动时间。

然后我们将 killmode 设置为 none. 这是因为正常的 kill 模式(控制组)有时会导致容器删除命令失败(特别是当Docker 的 `--rm' 选项尝试时)。

我们将环境文件拉进去,以便我们可以访问COREOS_PUBLIC_IPV4,如果创建过程中启用了私人网络,则环境变量COREOS_PRIVATE_IPV4

使用ExecStartPre行来切除以前运行中的任何剩余残骸,以确保执行环境是干净的。我们使用=-在前两行中表示systemd应该忽略并继续如果这些命令失败。

实际启动命令会启动 Docker 容器并将其绑定到主机的公共 IPv4 接口中,从而利用环境文件中的信息,使切换接口和端口变得微不足道。

「[X-Fleet]」部分包含一個簡單的條件,強迫「艦隊」在尚未執行其他Apache服務的機器上安排服務。

建设主要服务的基本取舍

在上面的例子中,我们谈到了一个相当基本的配置,但是,我们可以从中吸取很多教训,以帮助我们在整体上构建服务。

在构建主服务时要记住的一些行为:

*依附关系和顺序的分离逻辑 : 列出你的附属单位,说明要求'或要求'的附属单位,取决于如果附属单位不能得到满足,则是否应当失效。 将单行after QQ beefore QQ分开,以便在要求发生变化时容易调整。 将依赖性列表从命令中分离,有助于您在发生依赖性问题时调试。 *单独办理的手工服务登记 : 您的服务应注册为 " etcd " ,以利用服务发现和允许的动态配置特征。 然而,这应当由单独的"sidekick"容器处理,以保持逻辑分离. 这使您能够从外部角度更准确地报告服务的健康情况,这是其他部分所需要的。 *注意您服务时间的可能 : 考虑调整 " TimeoutStartSec " 指令,以便允许更长的起动时间。 设为"0"将禁用启动超时 。 这常常是必要的,因为有时多克必须拉出一幅图像(在先跑时或发现更新时),这可以给服务初始化增加大量时间. 如果你的服务不停止的话 就用"杀人模式"吧 如果您的服务或集装箱似乎不干净地停止了,请注意 " 杀死工具 " 选项。 设置为"无"有时可以解决您的容器在停止后不被移除的问题. 当您指定您的容器时, 这一点尤为重要, 因为 Docker 将会失败, 如果先前的运行中留下了同名的容器 。 请检查 KillMode 上的文档以获取更多信息 (http://www.freedesktop.org/software/systemd/man/systemd.kill.html) *在开始前清除环境 : 与上项相关, 确保每次启动时清理之前的多克容器 。 您不应该假设之前的服务运行已如预期的那样退出。 如果不需要清理,这些清理线应使用XQ -- -- " 规格,允许它们默默地失败。 虽然通常应当用 " 垃圾站 " 来停放容器,但在清理过程中,也许应当使用 " 垃圾站 " 。 *为服务可移植性而输入和使用特定主机的信息 : 如果您需要将服务绑定在一个特定的网络接口上,请在)。 在[X-Fleet]节,只有n'、N'、i'和`p'的规格才能奏效。 .

构建Sidekick公告服务

现在我们已经知道在构建主服务时要注意什么,我们可以开始寻找传统的sidekick服务,这些sidekick服务与主服务有关,并被用作etcd注册服务的外部点。

这个文件,正如它在主单元文件中所提到的,被称为apache-discovery.1.service,它看起来像这样:

 1[Unit]
 2Description=Apache web server etcd registration
 3
 4# Requirements
 5Requires=etcd.service
 6Requires=apache.1.service
 7
 8# Dependency ordering and binding
 9After=etcd.service
10After=apache.1.service
11BindsTo=apache.1.service
12
13[Service]
14
15# Get CoreOS environmental variables
16EnvironmentFile=/etc/environment
17
18# Start
19## Test whether service is accessible and then register useful information
20ExecStart=/bin/bash -c '\
21  while true; do \
22    curl -f ${COREOS_PUBLIC_IPV4}:80; \
23    if [ $? -eq 0 ]; then \
24      etcdctl set /services/apache/${COREOS_PUBLIC_IPV4} \'{"host": "%H", "ipv4_addr": ${COREOS_PUBLIC_IPV4}, "port": 80}\' --ttl 30; \
25    else \
26      etcdctl rm /services/apache/${COREOS_PUBLIC_IPV4}; \
27    fi; \
28    sleep 20; \
29  done'
30
31# Stop
32ExecStop=/usr/bin/etcdctl rm /services/apache/${COREOS_PUBLIC_IPV4}
33
34[X-Fleet]
35# Schedule on the same machine as the associated Apache service
36X-ConditionMachineOf=apache.1.service

我们以与主要服务相同的方式开始侧重服务,在转向依赖信息和订单逻辑之前,我们描述了单元的目的。

这里的第一个新项目是BindsTo=指令. 该指令导致该单元遵循发送给列出的单元的启动、停止和重新启动命令. 基本上,这意味着我们可以通过操纵主单元来管理这两个单元,一旦它们都被加载到舰队中。

对于[服务]部分,我们再次源/etc/environment文件,因为我们需要它所包含的变量。在这种情况下,ExecStart=指令基本上是一个短的bash脚本,它试图通过被曝光的接口和端口连接到主要服务。

如果连接成功,则使用etcdctl命令在etcd内的/services/apache中设置主机的公共 IP 地址的密钥。 此值为包含服务信息的 JSON 对象。 密钥设置为在 30 秒内到期,因此如果该单位意外下跌,则不会在etcd中留下停滞服务信息。

这个循环包括一个20秒的睡眠命令,这意味着每20秒(在30秒的),这个单元会重新检查主单元是否可用,并重置密钥。

在这种情况下,停止命令只会导致手动删除密钥,这会导致服务登记被删除,当主单元的停止命令因BindsTo=指令而反映到该单元时。

对于[X-Fleet]部分,我们需要确保该单元在与主单元相同的服务器上启动,虽然这不允许该单元向远程机器报告服务的可用性,但BindsTo=指令的正确运作很重要。

建立Sidekick服务的基本取舍

在构建这一侧面,我们可以看到一些事情,我们应该记住作为这些类型的单位的一般规则:

*检查主要单元的实际可用性 : 要切实检查主要单位的状态. 不要假设主单元只是因为侧式已经初始化而可用. 这取决于主要单位的设计和功能是什么,但你的支票越坚固,你的注册状态就越可信. 检查对单位来说是有意义的,从检查 " /健康 " 终点到试图与客户连接数据库。 *允许定期重新检查的登记逻辑 : 检查服务启动时的可用性很重要,但您必须定期重新检查。 这可能会发现出乎意料的服务故障,特别是如果这些故障导致集装箱没有停止。 通过权衡快速发现的重要性 与你主单位的额外负载 来调整周期之间的暂停 *在对故障自动注销登记时使用TTL旗 : 侧式单元的意外故障可能导致 " etcd " 中陈旧的发现信息。 为了避免您服务的注册状态与实际状态发生冲突,您应当让您的密钥超时. 通过上面的循环构造,您可以在超时间隔前刷新出每个密钥,以确保在侧键运行期间,密钥不会实际过期. 循环中的睡眠间隔应当设置为略小于超时间隔,以确保此函数正确.

  • 登记有用信息,而不仅仅是确认 : 在您第一次接见伴侣时,您可能只有兴趣在单位启动时与 " etcd " 准确登记。 然而,这是一个错失机会,无法为其他服务提供大量有用的信息。 尽管你现在可能不需要这种信息,但随着你建立能够从etcd读取自己配置值的其他组件,这种信息将变得更加有用。 " etcd " 服务是一个全球性的钥匙价值商店,所以不要忘记通过提供关键信息来利用它。 在JSON对象中存储细节是传递多条信息的好方法. .

通过记住这些考虑,你可以开始建立强大的注册单元,能够智能地确保etcd有正确的信息。

舰队特定的考虑

虽然舰队单元文件大多与传统的systemd单元文件没有什么不同,但有一些额外的功能和陷阱。

最明显的区别是添加一个名为[X-Fleet]的部分,可以用来指导舰队如何做出计划决策。

  • **QQ- ContitionMachineID ** :可用于指定装入单元的精确机器. 提供的值是一个完整的机器ID. 通过审查/etc/机器-id ' 文件,或通过发布名单-机器-l ' 命令的`fleetctl ' ,可从该组的单个成员中取出这一数值。 需要整个ID字符串. 如果您正在运行一个数据库, 并且数据目录保存在特定的机器上, 可能需要这个功能 。 除非您有具体的理由使用,否则尽量避免,因为它会降低单位的灵活性.
  • 条件中心 : 此指令可用于在装入指定单位的同一机器上安排此单位. 这对伴侣单位或将相关单位合并在一起很有帮助。
  • QQ- 冲突 : 与上述声明相反, 因为它指定了单位文件, 此单位不能与该单位同时排列 。 这对于通过启动多个版本的同一种服务来方便地配置高可用性是有用的,每个版本在不同的机器上.
  • QQ- ContitionMachineMetadata :用于根据可用机器的元数据指定调度要求。 在'fleetctl list-机器'输出的"MEDATA"一栏中,可以看到为每个主机设定的元数据. 要设置元数据, 请在初始化服务器实例时, 在您的 云形配置 文件中通过 。 *Global :这是一个特别指令,它使用布尔参数,说明是否应该在集群中的所有机器上安排。 只有元数据条件可以与此指令并用. .

这些附加指令为管理员提供了更大的灵活性和权力来定义如何在可用的机器上运行服务,这些在fleetctl load阶段将其转移到特定机器的systemd实例之前进行评估。

这导致我们在fleet中使用相关单元时需要注意的下一个问题。fleetctl实用程序不会对单元文件的X-Fleet部分以外的依赖性要求进行评估。

这意味着,虽然fleetctl工具将采取必要的步骤,使目标单位进入所需状态,根据所给出的命令进行提交、加载和启动过程,但它不会对该单位的依赖进行这种操作。

因此,如果您同时提交了主单元和副单元,但没有加载,则输入起步单元。 服务将装入,然后试图启动。 服务单位。 然而,自从"侧翼"。 由于 " fleetctl " 将不评估依赖性信息,从而将依赖性单位通过装载和启动过程,即 " 主体 " 带入依赖性单元。 服务单位将失败。 这是因为一旦机器的系统化实例处理主要。 服务单位,将无法找到侧翼。 当_it_评价依赖关系时。 那个"侧翼" 服务单位从未装入机器.

为了避免这种情况,在处理伴侣单元时,您可以同时手动启动服务,而不是依赖‘BindsTo=’指令将 sidekick 带入运行状态:

1fleetctl start main.service sidekick.service

另一种选择是确保在主单元运行时至少加载侧方单元。加载阶段是选择一台机器并将单元文件提交给本地systemd实例。

1fleetctl load main.service sidekick.service
2fleetctl start main.service

请记住,如果您的相关单元不正确响应您的fleetctl命令。

实例和模板

在使用舰队时最强大的概念之一是单元模板。

单元模板依赖于一个名为实例systemd功能,这些实例单元是通过处理一个模板单元文件在运行时创建的。

模板文件可通过其文件名中的 @ 来识别,而传统服务则采用这种形式:

1unit.service

一个 template 文件可以看起来像这样:

当一个单元从模板中实例化时,其实例标识符被放置在@.service字符串之间,该标识符是一个由管理员选择的唯一字符串:

1unit@instance_id.service

可以通过%p标识符从单位文件内部访问基础单位名称,同样,可以通过%i访问给定的实例标识符。

主单元文件作为模板

这意味着,而不是用我们之前看到的内容创建名为apache.1.service的主单元文件,您可以创建一个名为[email protected]的模板,它看起来像这样:

 1[Unit]
 2Description=Apache web server service on port %i
 3
 4# Requirements
 5Requires=etcd.service
 6Requires=docker.service
 7Requires=apache-discovery@%i.service
 8
 9# Dependency ordering
10After=etcd.service
11After=docker.service
12Before=apache-discovery@%i.service
13
14[Service]
15# Let processes take awhile to start up (for first run Docker containers)
16TimeoutStartSec=0
17
18# Change killmode from "control-group" to "none" to let Docker remove
19# work correctly.
20KillMode=none
21
22# Get CoreOS environmental variables
23EnvironmentFile=/etc/environment
24
25# Pre-start and Start
26## Directives with "=-" are allowed to fail without consequence
27ExecStartPre=-/usr/bin/docker kill apache.%i
28ExecStartPre=-/usr/bin/docker rm apache.%i
29ExecStartPre=/usr/bin/docker pull username/apache
30ExecStart=/usr/bin/docker run --name apache.%i -p ${COREOS_PUBLIC_IPV4}:%i:80 \
31username/apache /usr/sbin/apache2ctl -D FOREGROUND
32
33# Stop
34ExecStop=/usr/bin/docker stop apache.%i
35
36[X-Fleet]
37# Don't schedule on the same machine as other Apache instances
38X-Conflicts=apache@*.service

如你所见,我们已经将apache-discovery 1.service'的依赖性修改为apache-discovery i.service'。 这将意味着,如果我们有一个名为[email protected]'的单位文件实例,这将需要一个名为[email protected]'的侧翼。 " i " 已取而代之的是实例标识符。 在这种情况下,我们正在使用标识符来保存关于我们服务运行方式的动态信息,具体来说就是Apache服务器将可以使用的端口.

为了做到这一点,我们正在改变将集装箱的港口暴露在主机上的一个港口上的docker run参数。 在静态单位文件中,我们使用的参数是"${COREOS_PUBLIC_IPV4}:80:80",它将容器的80端口映射到公共IPv4接口上主机的80端口. 在本模板文件中,我们用${COREOS_PUBLIC_IPV4}:%i:80替换了这个文件,因为我们正在使用实例标识符来告诉我们要使用什么端口。 实例标识符的聪明选择可能意味着您模板文件中的灵活性更大 .

Docker 名称本身也被修改,以便它也使用基于实例 ID 的独特容器名称. 请记住,Docker 容器不能使用 @ 符号,所以我们必须从单元文件中选择不同的名称. 我们修改了在 Docker 容器上运行的所有指令。

X-Fleet部分中,我们还修改了日程信息,以便识别这些实例单元,而不是我们以前使用的静态类型。

Sidekick Unit 作为一个模板

我们可以通过类似的程序来调整我们的 sidekick 单元来模板。

我們的新 sidekick 單位將被命名為「[email protected]」並將看起來如下:

 1[Unit]
 2Description=Apache web server on port %i etcd registration
 3
 4# Requirements
 5Requires=etcd.service
 6Requires=apache@%i.service
 7
 8# Dependency ordering and binding
 9After=etcd.service
10After=apache@%i.service
11BindsTo=apache@%i.service
12
13[Service]
14
15# Get CoreOS environmental variables
16EnvironmentFile=/etc/environment
17
18# Start
19## Test whether service is accessible and then register useful information
20ExecStart=/bin/bash -c '\
21  while true; do \
22    curl -f ${COREOS_PUBLIC_IPV4}:%i; \
23    if [ $? -eq 0 ]; then \
24      etcdctl set /services/apache/${COREOS_PUBLIC_IPV4} \'{"host": "%H", "ipv4_addr": ${COREOS_PUBLIC_IPV4}, "port": %i}\' --ttl 30; \
25    else \
26      etcdctl rm /services/apache/${COREOS_PUBLIC_IPV4}; \
27    fi; \
28    sleep 20; \
29  done'
30
31# Stop
32ExecStop=/usr/bin/etcdctl rm /services/apache/${COREOS_PUBLIC_IPV4}
33
34[X-Fleet]
35# Schedule on the same machine as the associated Apache service
36X-ConditionMachineOf=apache@%i.service

我们已经通过了要求和绑定到主单元流程的实例版本的相同步骤,而不是静态版本,这将与正确的实例主单元匹配。

弯曲命令中,当我们检查服务的实际可用性时,我们将静态端口80代替即时ID,以便它连接到正确的位置。

我们还修改了被登录到etcd端口,以便使用相同的实例 ID. 随着此变化,在etcd中设置的JSON数据完全是动态的。

最后,我们需要确保这个过程在与主单元实例相同的机器上启动。

从模板中实时化单位

要实际地从模板文件中实例化单位,您有几种不同的选项。

fleetsystemd都可以处理符号链接,这使我们有机会创建具有完整实例ID的链接到模板文件,如下:

这将创建两个链接,称为[email protected][email protected]。每个链接都有fleetsystemd现在运行这些单元所需的所有信息。

然后,我们可以提交、加载或使用fleetctl开始这些服务:

如果您不想创建符号链接来定义您的实例,另一种选择是将模板本身提交到fleetctl,如下:

您可以从fleetctl中直接实例化这些模板中的单元,只需在运行时分配实例标识符。

这消除了需要有符号链接的需求,但一些管理员更喜欢链接机制,因为这意味着您可以随时使用实例文件,还允许您将目录转移到fleetl,以便一切都立即开始。

例如,在您的工作目录中,您可能有一个名为模板的子目录为您的模板文件和一个名为实例的子目录为即时链接的版本。

1mkdir templates instances static

然后你可以将你的静态文件移动到静态和你的模板文件到模板:

1mv apache.1.service apache-discovery.1.service static
2mv [email protected] [email protected] templates

从这里,您可以创建所需的实例链接。让我们在端口5555,66667777上运行我们的服务:

1cd instances
2ln -s ../templates/[email protected] [email protected]
3ln -s ../templates/[email protected] [email protected]
4ln -s ../templates/[email protected] [email protected]
5ln -s ../templates/[email protected] [email protected]
6ln -s ../templates/[email protected] [email protected]
7ln -s ../templates/[email protected] [email protected]

然后,您可以通过键入类似的东西同时启动所有实例:

1cd ..
2fleetctl start instances/*

这可以非常有用,快速开始您的服务。

结论

通过利用部分单元文件中可用的动态功能,您可以确保您的服务均匀分布,靠近其依赖性,并使用etcd注册有用的信息。

在一个(https://andsky.com/tech/tutorials/how-to-use-confd-and-etcd-to-dynamically-reconfigure-services-in-coreos)指南中,我们将介绍如何配置您的集装箱以使用您在etcd注册的信息。

Published At
Categories with 技术
comments powered by Disqus