部署指南

本文档提供 Swift 部署和配置的一般指导。关于配置选项的详细描述,请参阅 配置文档

硬件考虑事项

Swift 设计用于在通用硬件上运行。不要求在存储驱动器上使用 RAID,也不推荐使用。Swift 的磁盘使用模式是 RAID 的最坏情况,使用 RAID 5 或 6 会导致性能迅速下降。

部署选项

Swift 服务完全自主运行,这为构建 Swift 的硬件部署提供了很大的灵活性。主要的 4 个服务是

  1. 代理服务

  2. 对象服务

  3. 容器服务

  4. 账户服务

代理服务对 CPU 和网络 I/O 需求更高。如果使用 10g 网络连接到代理,或者在代理处终止 SSL 流量,则需要更大的 CPU 算力。

对象、容器和账户服务(存储服务)对磁盘和网络 I/O 需求更高。

最简单的部署是在每个服务器上安装所有服务。这样做没有问题,因为它可以横向扩展每个服务。

或者,可以有一组服务器专门用于代理服务,另一组服务器专门用于存储服务。这样可以配置更快地网络连接到代理,而不是存储服务器,并使代理负载均衡更易于管理。存储服务可以通过添加存储服务器进行横向扩展,并且可以通过添加更多代理来扩展整体 API 吞吐量。

如果需要更高的账户或容器服务吞吐量,可以将它们各自部署到自己的服务器上。例如,您可以使用更快的(但更昂贵的)SAS 甚至 SSD 驱动器来获得更快的磁盘 I/O 到数据库。

Swift 的高可用性 (HA) 部署要求部署多个代理服务器,并在它们之间进行负载均衡。每个代理服务器实例都是无状态的,能够响应整个集群的请求。

负载均衡和网络设计留作读者练习,但这是集群中非常重要的一部分,因此应该花时间设计 Swift 集群的网络。

Web 前端选项

Swift 带有集成的 Web 前端。但是,它也可以作为使用 mod_wsgi 的 Apache2 的请求处理器部署,如 Apache 部署指南 中所述。

准备环

第一步是确定环中的分区数。我们建议每个驱动器至少有 100 个分区,以确保驱动器之间的均匀分布。一个好的起点可能是确定集群将包含的最大驱动器数量,然后乘以 100,然后向上取整到最接近的 2 的幂。

例如,假设我们正在构建一个集群,该集群将包含不超过 5,000 个驱动器。这意味着我们将总共有 500,000 个分区,这非常接近 2^19,向上取整。

保持分区数量较小(相对而言)也是一个好主意。分区越多,复制器和其他后端作业需要完成的工作就越多,并且环在进程中消耗的内存就越多。目标是在小环和最大集群大小之间找到一个好的平衡。

下一步是确定要存储的数据的副本数。目前建议使用 3(因为这是唯一经过测试的值)。数字越高,使用的存储空间越多,但丢失数据的可能性越小。

确定集群应具有的区域数量也很重要。建议从至少 5 个区域开始。您可以从更少的区域开始,但我们的测试表明,在发生故障时,至少有五个区域是最佳的。我们还建议尝试尽可能高地配置区域,以创建尽可能多的隔离。一些需要考虑的示例包括物理位置、电源可用性和网络连接。例如,在小型集群中,您可能会决定按机柜划分区域,每个机柜都有自己的电源和网络连接。区域概念非常抽象,因此请随意以最能将数据与故障隔离的方式使用它。每个区域都存在于一个区域中。

区域也是一个抽象概念,可用于区分地理上分隔的区域以及在同一数据中心内使用。区域和区域由正整数引用。

现在可以使用以下命令开始构建环

swift-ring-builder <builder_file> create <part_power> <replicas> <min_part_hours>

这将启动环构建过程,创建带有 2^<part_power> 分区的 <builder_file>。<min_part_hours> 是特定分区可以在连续时间内移动的小时数(24 是一个很好的值)。

可以使用以下命令将设备添加到环中

swift-ring-builder <builder_file> add r<region>z<zone>-<ip>:<port>/<device_name>_<meta> <weight>

这将向环中添加一个设备,其中 <builder_file> 是之前创建的生成器文件的名称,<region> 是区域所在的区域编号,<zone> 是设备所在的区域编号,<ip> 是服务器的 IP 地址,<port> 是服务器正在运行的端口号,<device_name> 是服务器上的设备名称(例如:sdb1),<meta> 是设备的元数据字符串(可选),<weight> 是一个浮点权重,用于确定相对于集群中其余设备的有多少分区放置在该设备上(一个好的起点是驱动器上的 TB 的 100.0 倍)。添加将最初位于集群中的每个设备。

将所有设备添加到环后,运行

swift-ring-builder <builder_file> rebalance

这将跨环中的驱动器分配分区。在对环进行任何更改时,务必在运行重新平衡之前进行所有必需的更改。这将确保环保持尽可能平衡,并且移动的分区尽可能少。

上述过程应完成以创建每个存储服务(账户、容器和对象)的环。生成器文件将在未来更改环时需要,因此务必保存和备份这些文件。生成的 .tar.gz 环文件应推送到集群中的所有服务器。有关构建环的更多信息,使用 swift-ring-builder 不带任何选项运行将显示带有可用命令和选项的帮助文本。有关环内部工作原理的更多信息,请参阅 环概述

每个磁盘运行对象服务器

Linux 上缺乏真正的异步文件 I/O 使得对象服务器工作进程容易受到行为不端的磁盘的影响。由于任何对象服务器工作进程都可以为任何磁盘提供请求,并且缓慢的 I/O 请求会阻塞 eventlet 集线器,因此单个缓慢的磁盘会损害整个存储节点。这还阻止对象服务器在重负载期间充分利用所有磁盘。

获得完全 I/O 隔离的另一种方法是在存储策略环中为存储节点上的每个磁盘提供不同的端口。然后设置对象服务器配置中的 servers_per_port 选项。注意:虽然此配置设置的目的是为每个磁盘运行一个或多个对象服务器工作进程,但实现只是为环中本地设备的唯一端口运行对象服务器。部署者必须将此选项与适当配置的环结合使用,才能从该功能中受益。

这是一个示例(缩写)旧式环(2 个节点集群,每个节点 2 个磁盘)

Devices:    id  region  zone      ip address  port  replication ip  replication port      name
             0       1     1       1.1.0.1    6200       1.1.0.1                6200      d1
             1       1     1       1.1.0.1    6200       1.1.0.1                6200      d2
             2       1     2       1.1.0.2    6200       1.1.0.2                6200      d3
             3       1     2       1.1.0.2    6200       1.1.0.2                6200      d4

这是为 servers_per_port 设置的相同环

Devices:    id  region  zone      ip address  port  replication ip  replication port      name
             0       1     1       1.1.0.1    6200       1.1.0.1                6200      d1
             1       1     1       1.1.0.1    6201       1.1.0.1                6201      d2
             2       1     2       1.1.0.2    6200       1.1.0.2                6200      d3
             3       1     2       1.1.0.2    6201       1.1.0.2                6201      d4

从正常迁移到 servers_per_port 时,请按以下步骤操作

  1. 将 Swift 代码升级到能够执行 servers_per_port 的版本。

  2. 启用 servers_per_port,其值为大于零。

  3. 使用 SIGHUP 重新启动 swift-object-server 进程。此时,您将拥有 servers_per_portswift-object-server 进程为所有磁盘提供所有请求。这保留了可用性,但您应尽快执行下一步。

  4. 推送新的环,这些环实际上为每个服务器的每个磁盘具有不同的端口。新环中的一个端口应与旧环中使用的端口相同(例如上面的“6200”)。这将涵盖尚未加载新环的现有代理服务器进程。无论存储节点是否已加载环并启动了新端口上的对象服务器进程,它们仍然可以与任何存储节点通信。

如果您不为复制运行单独的对象服务器,则此设置必须可用于对象复制器和对象重构器(即出现在 [DEFAULT] 配置部分中)。

常规服务配置

大多数 Swift 服务分为两类。Swift 的 wsgi 服务器和后台守护进程。

有关 Swift 的 wsgi 服务器使用 paste 部署的更多信息,请参阅 常规服务器配置

服务器和守护进程的配置可以组合在每个类型服务器的同一个文件中,也可以单独表示。如果服务尝试启动时缺少必需的部分,则会发生错误。未被服务使用的部分将被忽略。

考虑对象存储节点的示例。按照惯例,对象服务器、对象更新器、对象复制器、对象审计器和对象重构器的配置存在于单个文件 /etc/swift/object-server.conf

[DEFAULT]
reclaim_age = 604800

[pipeline:main]
pipeline = object-server

[app:object-server]
use = egg:swift#object

[object-replicator]

[object-updater]

[object-auditor]

Swift 服务将配置路径作为第一个参数

$ swift-object-auditor
Usage: swift-object-auditor CONFIG [options]

Error: missing config path argument

如果您省略对象审计器部分,则此文件不能用作启动 swift-object-auditor 守护进程时的配置路径

$ swift-object-auditor /etc/swift/object-server.conf
Unable to find object-auditor config section in /etc/swift/object-server.conf

如果配置路径是目录而不是文件,则目录中的所有扩展名为“.conf”的文件将被组合以生成传递给 Swift 服务的配置对象。通常将其称为“基于目录的配置”。

基于目录的配置利用 ConfigParser 的本机多文件支持。给定目录中的以“.conf”结尾的文件按词法顺序解析。以“.” 开头的文件名将被忽略。不支持文件和目录配置路径的混合 - 如果配置路径是文件,则仅解析该文件。

Swift 服务管理工具 swift-init 采用查找 /etc/swift/{type}-server.conf.d/ 的约定,如果不存在 /etc/swift/{type}-server.conf 文件。

在使用基于目录的配置时,如果不同文件中的相同部分下的相同选项出现多次,则解析的最后一个值被认为会覆盖以前出现的值。您可以通过在配置目录中的文件名前面加上数字值来确保适当的覆盖优先级。

/etc/swift/
    default.base
    object-server.conf.d/
        000_default.conf -> ../default.base
        001_default-override.conf
        010_server.conf
        020_replicator.conf
        030_updater.conf
        040_auditor.conf

您可以使用 swift-config 命令行工具检查生成的组合配置对象

常规服务器配置

Swift 使用 paste.deploy (https://pypi.ac.cn/project/Paste/) 来管理服务器配置。关于配置选项的详细描述,请参阅 配置文档

默认配置选项在 [DEFAULT] 部分中设置,并且在其他任何部分中指定的任何选项都可以被覆盖,但仅通过使用语法 set option_name = value。这是 paste.deploy 的不幸工作方式,我将尝试完全解释它。

首先,这是一个 paste.deploy 配置文件

[DEFAULT]
name1 = globalvalue
name2 = globalvalue
name3 = globalvalue
set name4 = globalvalue

[pipeline:main]
pipeline = myapp

[app:myapp]
use = egg:mypkg#myapp
name2 = localvalue
set name3 = localvalue
set name5 = localvalue
name6 = localvalue

myapp 接收的最终配置是

global {'__file__': '/etc/mypkg/wsgi.conf', 'here': '/etc/mypkg',
        'name1': 'globalvalue',
        'name2': 'globalvalue',
        'name3': 'localvalue',
        'name4': 'globalvalue',
        'name5': 'localvalue',
        'set name4': 'globalvalue'}
local {'name6': 'localvalue'}

因此,name1 获得了全局值,这很好,因为它只在 DEFAULT 部分中。

name2 获得了来自 DEFAULT 的全局值,即使它似乎在 app:myapp 子部分中被覆盖了。这只是 paste.deploy 的不幸工作方式(至少在撰写本文时)。

name3 获得了来自 app:myapp 子部分的本地值,因为它使用了 paste.deploy 的特殊语法 set option_name = value。因此,如果您希望大多数应用程序/过滤器具有默认值,但希望在某个子部分中覆盖它,则可以这样做。

name4DEFAULT 获取了全局值,因为该值仅存在于该部分。但是,由于我们在 DEFAULT 部分使用了 set 语法,尽管我们不应该这样做,请注意我们也获得了一个 set name4 变量。奇怪,但可能不会造成危害。

name5app:myapp 子部分获取了局部值,因为该值仅存在于那里,但请注意它位于全局配置中,而不是局部配置中。这是因为我们使用了 set 语法来设置该值。同样,很奇怪,但不会造成危害,因为 Swift 只是将这两组配置值视为一组。

name6app:myapp 子部分获取了局部值,因为它仅存在于那里,并且由于我们没有使用 set 语法,它仅存在于局部配置中,而不是全局配置中。但是,如上所述,Swift 没有特别区分。

这对于应该如此简单的东西来说,解释得相当详细,但了解 paste.deploy 如何解释配置文件可能很重要。在使用 Swift 配置文件时要记住的主要规则是

注意

如果在 [DEFAULT] 部分中也设置了该选项,则在子部分中使用 set option_name = value 语法。不要养成总是使用 set 语法的习惯,否则你可能会破坏你的非 paste.deploy 配置文件。

按策略配置

可以通过包含每个策略的配置部分来覆盖某些代理服务器配置选项,以用于单个 存储策略。这些选项是

  • sorting_method

  • read_affinity

  • write_affinity

  • write_affinity_node_count

  • write_affinity_handoff_delete_count

每个策略的配置部分名称必须采用以下形式

[proxy-server:policy:<policy index>]

注意

每个策略的配置部分名称应引用策略索引,而不是策略名称。

注意

代理服务器配置部分名称的第一部分必须与代理服务器配置部分的名称匹配。通常是 proxy-server,如上所示,但如果不同,则任何每个策略的配置部分的名称都必须相应地更改。

在每个策略部分中指定的选项的值将覆盖该策略的代理服务器部分中的任何值。否则,这些选项的值将是代理服务器部分中指定的值。

例如,以下部分为索引为 3 的策略提供特定于策略的选项

[proxy-server:policy:3]
sorting_method = affinity
read_affinity = r2=1
write_affinity = r2
write_affinity_node_count = 1 * replicas
write_affinity_handoff_delete_count = 2

注意

建议不要在 [DEFAULT] 部分中包含每个策略的配置选项。如果包含,则适用以下行为。

每个策略的配置部分将继承配置文件 [DEFAULT] 部分中的选项,并且这种继承将优先于从代理服务器配置部分继承选项。

每个策略的配置部分选项将覆盖 [DEFAULT] 部分中的选项。与 常规服务器配置 中 paste-deploy filterapp 部分中描述的行为不同,在每个策略的配置部分中不需要 set 关键字才能覆盖选项。

例如,给定配置文件中的以下设置

[DEFAULT]
sorting_method = affinity
read_affinity = r0=100
write_affinity = r0

[app:proxy-server]
use = egg:swift#proxy
# use of set keyword here overrides [DEFAULT] option
set read_affinity = r1=100
# without set keyword, [DEFAULT] option overrides in a paste-deploy section
write_affinity = r1

[proxy-server:policy:0]
sorting_method = affinity
# set keyword not required here to override [DEFAULT] option
write_affinity = r1

将导致索引为 0 的策略具有以下设置

  • read_affinity = r0=100(从 [DEFAULT] 部分继承)

  • write_affinity = r1(在策略 0 部分中指定)

任何其他策略将具有默认设置

  • read_affinity = r1=100(在 proxy-server 部分中设置)

  • write_affinity = r0(从 [DEFAULT] 部分继承)

代理中间件

Swift 中的许多功能都作为代理服务器管道中的中间件实现。有关更多信息,请参阅 中间件proxy-server.conf-sample 文件。特别是,强烈建议使用某种类型的 身份验证和授权中间件

Memcached 考虑事项

许多服务依赖于 Memcached 来缓存某些类型的查找,例如身份验证令牌以及容器/帐户的存在性。Swift 不会缓存实际的对象数据。Memcached 应该能够在具有可用 RAM 和 CPU 的任何服务器上运行。通常,Memcached 在代理服务器上运行。 proxy-server.conf 中的 memcache_servers 配置选项应包含所有 memcached 服务器。

分片范围列表缓存

当容器被 分片 时,根容器仍然是许多容器请求的主要入口点,因为它提供分片列表。为了减轻根容器的负载,Swift 默认情况下会缓存返回的分片列表。

当根容器的分片数量增长到超过 3k 时,memcache 默认最大大小 1MB 可能会达到。

如果超出配置的 memcache 最大大小,您将看到如下消息

Error setting value in memcached: 127.0.0.1:11211: SERVER_ERROR object too large for cache

当您看到这些消息时,您的根容器正在被猛烈攻击,并且可能向客户端返回 503 响应。将默认 1MB 限制提高到 5MB,例如

/usr/bin/memcached -I 5000000 ...

Memcache 具有 stats sizes 选项,可以指出当前的尺寸使用情况。当达到当前最大值时,可能需要增加。

# telnet <memcache server> 11211
> stats sizes
STAT 160 2
STAT 448 1
STAT 576 1
END

系统时间

时间可能是相对的,但对于 Swift 来说非常重要!Swift 使用时间戳来确定对象的最新版本。集群中每个服务器的系统时间非常重要,必须尽可能同步(对于代理服务器来说更重要,但通常来说,对所有服务器都是一个好主意)。典型的部署使用 NTP 和本地 NTP 服务器来确保系统时间尽可能接近。还应监控这一点,以确保时间不会相差太多。

常规服务调优

大多数服务都支持设置中 workersconcurrency 值。这允许服务有效地利用可用的内核。一个好的起点是将代理和存储服务的并发级别设置为可用内核数的 2 倍。如果多个服务共享同一服务器,则可能需要进行一些实验才能找到最佳平衡。

例如,一位运营商报告在生产 Swift 集群中使用以下设置

  • 代理服务器具有双四核处理器(即 8 个内核);测试表明 16 个工作线程是在饱和 10g 网络时取得良好平衡并提供良好 CPU 利用率。

  • 存储服务器进程都在同一服务器上运行。这些服务器具有双四核处理器,总共 8 个内核。帐户、容器和对象服务器每个都运行 8 个工作线程。大多数后台作业的并发级别为 1,除了复制器,其并发级别为 2。

max_clients 参数可用于调整单个工作线程接受处理的客户端请求数量。处理的请求越少,消耗工作线程 CPU 时间或在操作系统中阻塞的请求对其他请求的影响就越小。处理的请求越多,单个工作线程就越有可能利用网络和磁盘容量。

在具有更多内核和更多内存的系统上,如果可以运行更多工作线程,则提高工作线程数量并降低每个工作线程处理的最大客户端数量可以减轻 CPU 密集型或挂起请求的影响。

nice_priority 参数可用于设置程序调度优先级。 ionice_classionice_priority 参数可用于在支持 I/O 优先级的 I/O 调度程序上设置 I/O 调度类和优先级。截至内核 2.6.17,唯一这样的调度程序是完全公平队列 (CFQ) I/O 调度程序。如果您将所有存储服务器一起运行在同一服务器上,可以通过这些参数来减慢审计器的速度或优先处理对象服务器 I/O(但可能不需要更改代理服务器上的设置)。这是一个新功能,最佳实践仍在制定中。在某些系统上,可能需要以 root 用户身份运行守护程序。有关更多信息,请参阅 setpriority(2) 和 ioprio_set(2)。

上述配置设置应仅作为建议,应进行配置设置测试,以确保最佳利用 CPU、网络连接和磁盘 I/O。

文件系统注意事项

Swift 旨在在很大程度上与文件系统无关——唯一的要求是文件系统必须支持扩展属性 (xattrs)。经过我们使用案例和硬件配置的彻底测试,XFS 是一个全面的最佳选择。如果您决定使用 XFS 以外的文件系统,我们强烈建议进行彻底的测试。

对于具有更新内核的发行版(例如 Ubuntu 12.04 Precise),我们建议使用默认设置(包括默认的 256 字节的 inode 大小)创建文件系统

mkfs.xfs -L D1 /dev/sda1

在过去几年中,XFS 在 inode 分配和使用方面取得了很大的改进。使用默认的 inode 大小不再对性能产生影响。

对于具有较旧内核的发行版(例如 Ubuntu 10.04 Lucid),某些设置会严重影响性能。我们建议在创建文件系统时执行以下操作

mkfs.xfs -i size=1024 -L D1 /dev/sda1

设置 inode 大小很重要,因为 XFS 在 inode 中存储 xattr 数据。如果元数据太大而无法放入 inode,则会创建一个新的范围,这可能会导致性能问题。将 inode 大小增加到 1024 字节可以提供足够的空间来写入默认元数据,并留有一些余地。

建议在以下安装选项中使用 XFS

mount -t xfs -o noatime -L D1 /srv/node/d1

我们不建议在 RAID 上运行 Swift,但如果您正在使用 RAID,确保设置正确的 sunit 和 swidth 设置,以便 XFS 可以最有效地利用 RAID 阵列也很重要。

对于标准的 Swift 安装,所有数据驱动器都直接安装在 /srv/node 下(如上所示,将标签 D1 安装为 /srv/node/d1)。如果您选择将驱动器安装在另一个目录中,请确保将 devices 配置选项设置为所有服务器配置,以指向正确的目录。

/srv/node/ 中的每个驱动器的挂载点应几乎完全由 root 用户拥有(root:root 755)。这是为了防止 rsync 将文件同步到 root 驱动器,如果驱动器被卸载。

Swift 使用系统调用来为写入系统的新对象预留空间。如果您的文件系统不支持 fallocate()posix_fallocate(),请确保在帐户、容器和对象服务器配置中设置 disable_fallocate = true 配置参数。

大多数当前的 Linux 发行版都附带 updatedb 的默认安装。此工具会定期运行并更新 GNU locate 工具使用的文件名数据库。但是,将 Swift 对象和容器数据库文件包含在内很可能是不必要的,并且定期更新会影响性能。要禁用包含这些文件,请将 Swift 存储其数据的路径添加到 /etc/updatedb.conf 中的 PRUNEPATHS 设置

PRUNEPATHS="... /tmp ... /var/spool ... /srv/node"

常规系统调优

在 Ubuntu Server 10.04 上运行 Swift 时,已发现以下更改很有用。

以下设置应在 /etc/sysctl.conf

# disable TIME_WAIT.. wait..
net.ipv4.tcp_tw_recycle=1
net.ipv4.tcp_tw_reuse=1

# disable syn cookies
net.ipv4.tcp_syncookies = 0

# double amount of allowed conntrack
net.netfilter.nf_conntrack_max = 262144

要加载更新的 sysctl 设置,请运行 sudo sysctl -p

关于更改 TIME_WAIT 值的说明。默认情况下,操作系统会保持端口打开 60 秒,以确保可以接收任何剩余的数据包。在高使用量期间,以及创建的连接数量,很容易耗尽端口。由于我们控制网络,因此我们可以更改此设置。如果您不控制网络,或者不期望高负载,则可能不想调整这些值。

日志记录注意事项

Swift 被配置为直接记录到 syslog。每个服务都可以通过 log_facility 选项进行配置,以设置 syslog 日志设施目标。我们建议使用 syslog-ng 将日志路由到服务器上的特定日志文件以及远程日志收集服务器。此外,可以通过 custom_log_handlers 设置使用自定义日志处理器。