分类 系统运维 下的文章

当 Linux 系统连接到 SAN(存储区域网络)后,你需要重新扫描 iSCSI 服务以发现新的 LUN。

要做到这一点,你必须向存储团队提供 Linux 主机的 WWN 号和所需的 LUN 大小。

这篇文章将帮助你查找 Linux 主机的 WWN 号

当存储团队将 LUN 与给定的 Linux 主机进行了映射,他们将为你提供新的 LUN 详细信息。

LUN 在存储术语中被称为 LUN 的串行十六进制。

你需要扫描 SCSI 主机来寻找存储团队分配的新 LUN。

这可以通过两种方式进行,扫描每个 scsi 主机设备或运行 rescan-scsi-bus.sh 脚本来检测新磁盘。

扫描后可以在 /dev/disk/by-id 目录下找到它们。

# ll /dev/disk/by-id

total 0
lrwxrwxrwx 1 root root 10 Jul 9 17:52 scsi-60a98000486e542d4f5a2f47694d684b -> ../../sdah
lrwxrwxrwx 1 root root 9 Jul 9 17:52 scsi-60a98000486e542d4f5a2f47694d684c -> ../../sdw
.
.
lrwxrwxrwx 1 root root 10 Jul 9 17:52 scsi-60a98000486e542d4f5a2f47694d684d -> ../../sdjk
lrwxrwxrwx 1 root root 10 Jul 9 17:52 scsi-60a98000486e542d4f5a2f47694d684e -> ../../sdaa
lrwxrwxrwx 1 root root 9 Jul 9 17:52 scsi-60a98000486e542d4f5a2f47694d684f -> ../../sdh

另外,如果你已经用 multipath 配置了它们,那么可以用 multipath 命令找到。

`multipath 主要是配置到 Oracle 数据库服务器中,以提高性能。

# multipath -ll

60a98000486e542d4f5a2f47694d684b dm-37 NETAPP,LUN C-Mode
size=512G features='3 queue_if_no_path pg_init_retries 50' hwhandler='1 alua' wp=rw
|-+- policy='round-robin 0' prio=50 status=active
| |- 1:0:4:18 sdoe 128:416 active ready running
| |- 0:0:4:18 sdpq 131:256 active ready running
| |- 0:0:5:18 sdsr 135:496 active ready running
| `- 1:0:5:18 sdsq 135:480 active ready running
`-+- policy='round-robin 0' prio=10 status=enabled
|- 1:0:1:18 sdfw 131:32 active ready running
|- 1:0:0:18 sdci 69:96 active ready running
|- 0:0:1:18 sdbz 68:208 active ready running
|- 0:0:0:18 sds 65:32 active ready running
|- 1:0:3:18 sdmd 69:336 active ready running
|- 1:0:2:18 sdjj 8:464 active ready running
|- 0:0:3:34 sdjt 65:368 active ready running
`- 0:0:2:34 sdgi 131:224 active ready running

这个过程适用于基于 Red Hat 6.x、7.x 和 8.x(RHEL - Red Hat Enterprise Linux)的系统,如 CentOS 和 Oracle Linux。

方法 1:如何使用 /sys 类文件在 Linux 上扫描新的 LUN 和 SCSI 磁盘

sysfs 文件系统是一个伪文件系统,它为内核数据结构提供了一个接口。

sysfs 下的文件提供了关于设备、内核模块、文件系统和其他内核组件的信息。

sysfs 文件系统通常被挂载在 /sys。通常,它是由系统自动挂载的。

你可以使用 echo 命令来扫描每个 SCSI 主机设备,如下所示:

# echo "- - -" > /sys/class/scsi_host/host[n]/scan

当你运行上面的重新扫描所有的命令时,三个破折号(- – -)指的是通配符选项。这些值如下:

# echo "c t l" > /sys/class/scsi_host/host[n]/scan

这里:

  • c:HBA 上的通道
  • t:SCSI 目标 ID
  • l:LUN ID
  • n:HBA 编号

运行下面的命令来查找系统中所有的主机总线编号:

# ls /sys/class/scsi_host
host0 host1 host2

得到主机总线编号后,运行以下命令来发现新的磁盘:

# echo "- - -" > /sys/class/scsi_host/host0/scan
# echo "- - -" > /sys/class/scsi_host/host1/scan
# echo "- - -" > /sys/class/scsi_host/host2/scan

另外,还可以用 for 循环用一条命令进行扫描。

# for host in ls /sys/class/scsi_host/;do echo "- - -" >/sys/class/scsi_host/${host}/scan; done

你可以使用文章开头提到的 ls 命令来检查它们。

# ls /dev/disk/by-id | grep -i "serial-hex of LUN"

方法 2:如何使用 rescan-scsi-bus.sh 脚本在 Linux 上扫描新的 LUN 和 SCSI 磁盘

确保你已经安装了 sg3_utils 包来使用这个脚本。否则,运行以下命令来安装它。

对于 RHEL/CentOS 6/7 系统,使用 yum 命令安装 sg3_utils

# yum install -y sg3_utils

对于 RHEL/CentOS 8 和 Fedora 系统,使用 dnf 命令安装 sg3\_utils。

# dnf install -y sg3_utils

现在你可以使用 rescan-scsi-bus.sh 脚本重新扫描 LUN。

# ./rescan-scsi-bus.sh

via: https://www.2daygeek.com/scan-detect-luns-scsi-disks-on-redhat-centos-oracle-linux/

作者:Magesh Maruthamuthu 选题:lujun9972 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

如果你的 LVM 不再需要使用某个设备,你可以使用 vgreduce 命令从卷组中删除物理卷。

vgreduce 命令可以通过删除物理卷来缩小卷组的容量。但要确保该物理卷没有被任何逻辑卷使用,请使用 pvdisplay 命令查看。如果物理卷仍在使用,你必须使用 pvmove 命令将数据转移到另一个物理卷。

数据转移后,它就可以从卷组中删除。

最后使用 pvremove 命令删除空物理卷上的 LVM 标签和 LVM 元数据。

将扩展块移动到现有物理卷上

使用 pvs 命令检查是否使用了所需的物理卷(我们计划删除 LVM 中的 /dev/sdc 磁盘)。

# pvs -o+pv_used

PV VG Fmt Attr PSize PFree Used
/dev/sda myvg lvm2 a- 75.00G 14.00G 61.00G
/dev/sdb myvg lvm2 a- 50.00G 45.00G 5.00G
/dev/sdc myvg lvm2 a- 17.15G 12.15G 5.00G

如果使用了,请检查卷组中的其他物理卷是否有足够的空闲 扩展块 extent

如果有的话,你可以在需要删除的设备上运行 pvmove 命令。扩展块将被分配到其他设备上。

# pvmove /dev/sdc

/dev/sdc: Moved: 2.0%
…
/dev/sdc: Moved: 79.2%
…
/dev/sdc: Moved: 100.0%

pvmove 命令完成后。再次使用 pvs 命令检查物理卷是否有空闲。

# pvs -o+pv_used

PV VG Fmt Attr PSize PFree Used
/dev/sda myvg lvm2 a- 75.00G  1.85G 73.15G
/dev/sdb myvg lvm2 a- 50.00G 45.00G 5.00G
/dev/sdc myvg lvm2 a- 17.15G 17.15G 0

如果它是空闲的,使用 vgreduce 命令从卷组中删除物理卷 /dev/sdc

# vgreduce myvg /dev/sdc
Removed "/dev/sdc" from volume group "vg01"

最后,运行 pvremove 命令从 LVM 配置中删除磁盘。现在,磁盘已经完全从 LVM 中移除,可以用于其他用途。

# pvremove /dev/sdc
Labels on physical volume "/dev/sdc" successfully wiped.

移动扩展块到新磁盘

如果你在卷组中的其他物理卷上没有足够的可用扩展。使用以下步骤添加新的物理卷。

向存储组申请新的 LUN。分配完毕后,运行以下命令来在 Linux 中发现新添加的 LUN 或磁盘

# ls /sys/class/scsi_host
host0
# echo "- - -" > /sys/class/scsi_host/host0/scan
# fdisk -l

操作系统中检测到磁盘后,使用 pvcreate 命令创建物理卷。

# pvcreate /dev/sdd
Physical volume "/dev/sdd" successfully created

使用以下命令将新的物理卷 /dev/sdd 添加到现有卷组 vg01 中。

# vgextend vg01 /dev/sdd
Volume group "vg01" successfully extended

现在,使用 pvs 命令查看你添加的新磁盘 /dev/sdd

# pvs -o+pv_used

PV VG Fmt Attr PSize PFree Used
/dev/sda myvg lvm2 a- 75.00G 14.00G 61.00G
/dev/sdb myvg lvm2 a- 50.00G 45.00G 5.00G
/dev/sdc myvg lvm2 a- 17.15G 12.15G 5.00G
/dev/sdd myvg lvm2 a- 60.00G 60.00G 0

使用 pvmove 命令将数据从 /dev/sdc 移动到 /dev/sdd

# pvmove /dev/sdc /dev/sdd

/dev/sdc: Moved: 10.0%
…
/dev/sdc: Moved: 79.7%
…
/dev/sdc: Moved: 100.0%

数据移动到新磁盘后。再次使用 pvs 命令检查物理卷是否空闲。

# pvs -o+pv_used

PV VG Fmt Attr PSize PFree Used
/dev/sda myvg lvm2 a- 75.00G 14.00G 61.00G
/dev/sdb myvg lvm2 a- 50.00G 45.00G 5.00G
/dev/sdc myvg lvm2 a- 17.15G 17.15G 0
/dev/sdd myvg lvm2 a- 60.00G 47.85G 12.15G

如果空闲,使用 vgreduce 命令从卷组中删除物理卷 /dev/sdc

# vgreduce myvg /dev/sdc
Removed "/dev/sdc" from volume group "vg01"

最后,运行 pvremove 命令从 LVM 配置中删除磁盘。现在,磁盘已经完全从 LVM 中移除,可以用于其他用途。

# pvremove /dev/sdc
Labels on physical volume "/dev/sdc" successfully wiped.

via: https://www.2daygeek.com/linux-remove-delete-physical-volume-pv-from-volume-group-vg-in-lvm/

作者:Magesh Maruthamuthu 选题:lujun9972 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

减少/缩小逻辑卷是数据损坏的最高风险。

所以,如果可能的话,尽量避免这种情况,但如果没有其他选择的话,那就继续。

缩减 LVM 之前,建议先做一个备份。

当你在 LVM 中的磁盘空间耗尽时,你可以通过缩小现有的没有使用全部空间的 LVM,而不是增加一个新的物理磁盘,在卷组上腾出一些空闲空间。

需要注意的是: 在 GFS2 或者 XFS 文件系统上不支持缩小。

如果你是逻辑卷管理 (LVM) 的新手,我建议你从我们之前的文章开始学习。

减少逻辑卷涉及以下步骤:

  • 卸载文件系统
  • 检查文件系统是否有任何错误
  • 缩小文件系统的大小
  • 缩小逻辑卷的大小
  • 重新检查文件系统是否存在错误(可选)
  • 挂载文件系统
  • 检查减少后的文件系统大小

比如: 你有一个 100GB 的没有使用全部空间的 LVM,你想把它减少到 80GB,这样 20GB 可以用于其他用途。

# df -h /testlvm1

Filesystem              Size Used Avail Use% Mounted on
/dev/mapper/vg01-lv002  100G 15G  85G   12%  /testlvm1

卸载文件系统

使用 umount 命令卸载文件系统:

# umount /testlvm1

检查文件系统是否有任何错误

使用 e2fsck 命令检查文件系统是否有错误:

# e2fsck -f /dev/mapper/vg01-lv002

e2fsck 1.42.9 (28-Dec-2013)
Pass 1: Checking inodes, blocks, and sizes
Pass 2: Checking directory structure
Pass 3: Checking directory connectivity
Pass 4: Checking reference counts
Pass 5: Checking group summary information
/dev/mapper/vg01-lv002: 13/6553600 files (0.0% non-contiguous), 12231854/26212352 blocks

缩小文件系统

下面的命令将把 testlvm1 文件系统从 100GB 缩小到 80GB

文件系统大小调整的常用语法(resize2fs

resize2fs [现有逻辑卷名] [新的文件系统大小]

实际命令如下:

# resize2fs /dev/mapper/vg01-lv002 80G

resize2fs 1.42.9 (28-Dec-2013)
Resizing the filesystem on /dev/mapper/vg01-lv002 to 28321400 (4k) blocks.
The filesystem on /dev/mapper/vg01-lv002 is now 28321400 blocks long.

减少逻辑卷 (LVM) 容量

现在使用 lvreduce 命令缩小逻辑卷(LVM) 的大小。通过下面的命令, /dev/mapper/vg01-lv002 将把逻辑卷 (LVM) 从 100GB 缩小到 80GB。

LVM 缩减 (lvreduce) 的常用语法

lvreduce [新的 LVM 大小] [现有逻辑卷名称]

实际命令如下:

# lvreduce -L 80G /dev/mapper/vg01-lv002

WARNING: Reducing active logical volume to 80.00 GiB
THIS MAY DESTROY YOUR DATA (filesystem etc.)
Do you really want to reduce lv002? [y/n]: y
Reducing logical volume lv002 to 80.00 GiB
Logical volume lv002 successfully resized

可选:检查文件系统是否有错误

缩减 LVM 后再次检查文件系统是否有错误:

# e2fsck -f /dev/mapper/vg01-lv002

e2fsck 1.42.9 (28-Dec-2013)
Pass 1: Checking inodes, blocks, and sizes
Pass 2: Checking directory structure
Pass 3: Checking directory connectivity
Pass 4: Checking reference counts
Pass 5: Checking group summary information
/dev/mapper/vg01-lv002: 13/4853600 files (0.0% non-contiguous), 1023185/2021235 blocks

挂载文件系统并检查缩小后的大小

最后挂载文件系统,并检查缩小后的文件系统大小。

使用 mount 命令挂载逻辑卷

# mount /testlvm1

使用 df 命令检查挂载的卷。

# df -h /testlvm1

Filesystem              Size Used Avail Use% Mounted on
/dev/mapper/vg01-lv002  80G  15G  65G   18%  /testlvm1

via: https://www.2daygeek.com/reduce-shrink-decrease-resize-lvm-logical-volume-in-linux/

作者:Magesh Maruthamuthu 选题:lujun9972 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

通过将日历应用集成到 Ansible 中,确保你的自动化工作流计划不会与其他东西冲突。

“随时”是执行自动化工作流的好时机吗?出于不同的原因,答案可能是否定的。

如果要避免同时进行更改,以最大限度地减少对关键业务流程的影响,并降低意外服务中断的风险,则在你的自动化运行的同时,其他任何人都不应该试图进行更改。

在某些情况下,可能存在一个正在进行的计划维护窗口。或者,可能有大型事件即将来临、一个关键的业务时间、或者假期(你或许不想在星期五晚上进行更改)。

 title=

无论出于什么原因,你都希望将此信息发送到你的自动化平台,以防止在特定时间段内执行周期性或临时任务。用变更管理的行话,我说的是当变更活动不应该发生时,指定封锁窗口。

Ansible 中的日历集成

如何在 Ansible 中实现这个功能?虽然它本身没有日历功能,但 Ansible 的可扩展性将允许它与任何具有 API 的日历应用集成。

目标是这样的:在执行任何自动化或变更活动之前,你要执行一个 pre-task ,它会检查日历中是否已经安排了某些事情(目前或最近),并确认你没有在一个阻塞的时间段中。

想象一下,你有一个名为 calendar 的虚构模块,它可以连接到一个远程日历,比如 Google 日历,以确定你指定的时间是否已经以其他方式被标记为繁忙。你可以写一个类似这样的剧本:

- name: Check if timeslot is taken
  calendar:
    time: "{{ ansible_date_time.iso8601 }}"
  register: output

Ansible 实际会给出 ansible_date_time,将其传递给 calendar 模块,以验证时间的可用性,以便它可以注册响应 (output),用于后续任务。

如果你的日历是这样的:

 title=

那么这个任务的输出就会指明这个时间段被占用的事实 (busy: true):

ok: [localhost] => {
   "output": {
       "busy": true,
       "changed": false,
       "failed": false,
       "msg": "The timeslot 2020-09-02T17:53:43Z is busy: true"
   }
}

阻止任务运行

接下来,Ansible Conditionals 将帮助阻止所有之后任务的执行。一个简单的例子,你可以在下一个任务上使用 when 语句来强制它只有当上一个输出中的 busy 字段不是 true 时,它才会运行:

tasks:
  - shell: echo "Run this only when not busy!"
    when: not output.busy

总结

上一篇文章中,我说过 Ansible 是一个将事物连接在一起的框架,将不同的组成部分相互连接,以协调端到端自动化工作流。

这篇文章探讨了 Ansible 剧本如何与日历应用集成以检查可用性。然而,我只做了一些表面工作!例如,你的任务也可以阻止日历中的一个时间段,这里的发挥空间很大。

在我的下一篇文章中,我将深入 calendar 模块是如何构建的,以及其他编程语言如何与 Ansible 一起使用。如果你和我一样是 Go 的粉丝,请继续关注!


这篇文章最初发表在 Medium 上,名为 Ansible and Google Calendar integration for change management,采用 CC BY-SA 4.0 许可,经许可后转载。


via: https://opensource.com/article/20/10/calendar-ansible

作者:Nicolas Leiva 选题:lujun9972 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

Linux TCP 协议栈具有无数个可以更改其行为的 sysctl 旋钮。 这包括可用于接收或发送操作的内存量、套接字的最大数量、可选的特性和协议扩展。

有很多文章出于各种“性能调优”或“安全性”原因,建议禁用 TCP 扩展,比如时间戳或 选择性确认 Selective ACKnowledgments (SACK)。

本文提供了这些扩展功能的背景,为什么会默认启用,它们之间是如何关联的,以及为什么通常情况下将它们关闭是个坏主意。

TCP 窗口缩放

TCP 可以承受的数据传输速率受到几个因素的限制。其中包括:

  • 往返时间 Round trip time (RTT)。

这是数据包到达目的地并返回回复所花费的时间。越低越好。

  • 所涉及的网络路径的最低链路速度。
  • 丢包频率。
  • 新数据可用于传输的速度。

例如,CPU 需要能够以足够快的速度将数据传递到网络适配器。如果 CPU 需要首先加密数据,则适配器可能必须等待新数据。同样地,如果磁盘存储不能足够快地读取数据,则磁盘存储可能会成为瓶颈。

  • TCP 接收窗口的最大可能大小。

接收窗口决定了 TCP 在必须等待接收方报告接收到该数据之前可以传输多少数据(以字节为单位)。这是由接收方宣布的。接收方将在读取并确认接收到传入数据时不断更新此值。接收窗口的当前值包含在 TCP 报头 中,它是 TCP 发送的每个数据段的一部分。因此,只要发送方接收到来自对等方的确认,它就知道当前的接收窗口。这意味着往返时间(RTT)越长,发送方获得接收窗口更新所需的时间就越长。

TCP 的未确认(正在传输)数据被限制为最多 64KB。在大多数网络场景中,这甚至还不足以维持一个像样的数据速率。让我们看看一些例子。

理论数据速率

在往返时间(RTT)为 100 毫秒的情况下,TCP 每秒最多可以传输 640KB。在延迟为 1 秒的情况下,最大理论数据速率降至只有 64KB/s。

这是因为接收窗口的原因。一旦发送了 64KB 的数据,接收窗口就已经满了。发送方必须等待,直到对等方通知它应用程序已经读取了至少一部分数据。

发送的第一个段会把 TCP 窗口缩减去该段的大小。在接收窗口值的更新信息可用之前,需要往返一次。当更新以 1 秒的延迟到达时,即使链路有足够的可用带宽,也会导致 64KB 的限制。

为了充分利用一个具有几毫秒延迟的快速网络,必须有一个比传统 TCP 支持的窗口更大的窗口。“64KB 限制”是协议规范的产物:TCP 头只为接收窗口大小保留了 16 个位。这允许接收窗口最大为 64KB。在 TCP 协议最初设计时,这个大小并没有被视为一个限制。

不幸的是,想通过仅仅更改 TCP 头来支持更大的最大窗口值是不可能的。如果这样做就意味着 TCP 的所有实现都必须同时更新,否则它们将无法相互理解。为了解决这个问题,我们改变了对接收窗口值的解释。

“窗口缩放选项”允许你改变这个解释,同时保持与现有实现的兼容性。

TCP 选项:向后兼容的协议扩展

TCP 支持可选扩展。这允许使用新特性增强协议,而无需立即更新所有实现。当 TCP 发起方连接到对等方时,它还会发送一个支持的扩展列表。所有扩展都遵循相同的格式:一个唯一的选项号,后跟选项的长度以及选项数据本身。

TCP 响应方检查连接请求中包含的所有选项号。如果它遇到一个不能理解的选项号,则会跳过 该选项号附带的“长度”字节的数据,并检查下一个选项号。响应方忽略了从答复中无法理解的内容。这使发送方和接收方都够理解所支持的公共选项集。

使用窗口缩放时,选项数据总是由单个数字组成。

窗口缩放选项

Window Scale option (WSopt): Kind: 3, Length: 3
    +---------+---------+---------+
    | Kind=3  |Length=3 |shift.cnt|
    +---------+---------+---------+
         1         1         1

窗口缩放 选项告诉对等方,应该使用给定的数字缩放 TCP 标头中的接收窗口值,以获取实际大小。

例如,一个宣告窗口缩放因子为 7 的 TCP 发起方试图指示响应方,任何将来携带接收窗口值为 512 的数据包实际上都会宣告 65536 字节的窗口。增加了 128 倍(2^7)。这将允许最大为 8MB 的 TCP 窗口。

不能理解此选项的 TCP 响应方将会忽略它,为响应连接请求而发送的 TCP 数据包(SYN-ACK)不会包含该窗口缩放选项。在这种情况下,双方只能使用 64k 的窗口大小。幸运的是,默认情况下,几乎每个 TCP 栈都支持并默认启用了此选项,包括 Linux。

响应方包括了它自己所需的缩放因子。两个对等方可以使用不同的因子。宣布缩放因子为 0 也是合法的。这意味着对等方应该如实处理它接收到的接收窗口值,但它允许应答方向上的缩放值,然后接收方可以使用更大的接收窗口。

与 SACK 或 TCP 时间戳不同,窗口缩放选项仅出现在 TCP 连接的前两个数据包中,之后无法更改。也不可能通过查看不包含初始连接三次握手的连接的数据包捕获来确定缩放因子。

支持的最大缩放因子为 14。这将允许 TCP 窗口的大小高达 1GB。

窗口缩放的缺点

在非常特殊的情况下,它可能导致数据损坏。但在你禁用该选项之前,要知道通常情况下是不可能损坏的。还有一种解决方案可以防止这种情况。不幸的是,有些人在没有意识到它与窗口缩放的关系的情况下禁用了该解决方案。首先,让我们看一下需要解决的实际问题。想象以下事件序列:

  1. 发送方发送段:s\_1、s\_2、s\_3、... s\_n。
  2. 接收方看到:s\_1、s\_3、... s\_n,并发送对 s\_1 的确认。
  3. 发送方认为 s\_2 丢失,然后再次发送。它还发送了段 s\_n+1 中包含的新数据。
  4. 接收方然后看到:s\_2、s\_n+1,s\_2:数据包 s\_2 被接收两次。

当发送方过早触发重新传输时,可能会发生这种情况。在正常情况下,即使使用窗口缩放,这种错误的重传也绝不会成为问题。接收方将只丢弃重复项。

从旧数据到新数据

TCP 序列号最多可以为 4GB。如果它变得大于此值,则该序列会回绕到 0,然后再次增加。这本身不是问题,但是如果这种问题发生得足够快,则上述情况可能会造成歧义。

如果在正确的时刻发生回绕,则序列号 s\_2(重新发送的数据包)可能已经大于 s\_n+1。因此,在最后的步骤(4)中,接收方可以将其解释为:s\_2、s\_n+1、s\_n+m,即它可以将 “旧” 数据包 s\_2 视为包含新数据。

通常,这不会发生,因为即使在高带宽链接上,“回绕”也只会每隔几秒钟或几分钟发生一次。原始数据包和不需要的重传的数据包之间的间隔将小得多。

例如,对于 50MB/s 的传输速度,重复项要迟到一分钟以上才会成为问题。序列号的回绕速度没有快到让小的延迟会导致这个问题。

一旦 TCP 达到 “GB/s” 的吞吐率,序列号的回绕速度就会非常快,以至于即使只有几毫秒的延迟也可能会造成 TCP 无法检测出的重复项。通过解决接收窗口太小的问题,TCP 现在可以用于以前无法实现的网络速度,这会产生一个新的,尽管很少见的问题。为了在 RTT 非常低的环境中安全使用 GB/s 的速度,接收方必须能够检测到这些旧的重复项,而不必仅依赖序列号。

TCP 时间戳

最佳截止日期

用最简单的术语来说,TCP 时间戳只是在数据包上添加时间戳,以解决由非常快速的序列号回绕引起的歧义。如果一个段看起来包含新数据,但其时间戳早于上一个在接收窗口内的数据包,则该序列号已被重新回绕,而“新”数据包实际上是一个较旧的重复项。这解决了即使在极端情况下重传的歧义。

但是,该扩展不仅仅是检测旧数据包。TCP 时间戳的另一个主要功能是更精确的往返时间测量(RTTm)。

需要准确的 RTT 估算

当两个对等方都支持时间戳时,每个 TCP 段都携带两个附加数字:时间戳值和回显时间戳。

TCP Timestamp option (TSopt): Kind: 8, Length: 10
+-------+----+----------------+-----------------+
|Kind=8 | 10 |TS Value (TSval)|EchoReply (TSecr)|
+-------+----+----------------+-----------------+
    1      1         4                4

准确的 RTT 估算对于 TCP 性能至关重要。TCP 会自动重新发送未确认的数据。重传由计时器触发:如果超时,则 TCP 会将尚未收到确认的一个或多个数据包视为丢失。然后再发送一次。

但是,“尚未得到确认” 并不意味着该段已丢失。也有可能是接收方到目前为止没有发送确认,或者确认仍在传输中。这就造成了一个两难的困境:TCP 必须等待足够长的时间,才能让这种轻微的延迟变得无关紧要,但它也不能等待太久。

低网络延迟 VS 高网络延迟

在延迟较高的网络中,如果计时器触发过快,TCP 经常会将时间和带宽浪费在不必要的重发上。

然而,在延迟较低的网络中,等待太长时间会导致真正发生数据包丢失时吞吐量降低。因此,在低延迟网络中,计时器应该比高延迟网络中更早到期。所以,TCP 重传超时不能使用固定常量值作为超时。它需要根据其在网络中所经历的延迟来调整该值。

往返时间的测量

TCP 选择基于预期的往返时间(RTT)的重传超时。RTT 事先是未知的。它是通过测量发送的段与 TCP 接收到该段所承载数据的确认之间的增量来估算的。

由于多种因素使其而变得复杂。

  • 出于性能原因,TCP 不会为收到的每个数据包生成新的确认。它等待的时间非常短:如果有更多的数据段到达,则可以通过单个 ACK 数据包确认其接收。这称为 “累积确认” cumulative ACK
  • 往返时间并不恒定。这是有多种因素造成的。例如,客户端可能是一部移动电话,随其移动而切换到不同的基站。也可能是当链路或 CPU 的利用率提高时,数据包交换花费了更长的时间。
  • 必须重新发送的数据包在计算过程中必须被忽略。这是因为发送方无法判断重传数据段的 ACK 是在确认原来的传输数据(毕竟已到达)还是在确认重传数据。

最后一点很重要:当 TCP 忙于从丢失中恢复时,它可能仅接收到重传段的 ACK。这样,它就无法在此恢复阶段测量(更新)RTT。所以,它无法调整重传超时,然后超时将以指数级增长。那是一种非常具体的情况(它假设其他机制,如快速重传或 SACK 不起作用)。但是,使用 TCP 时间戳,即使在这种情况下也会进行 RTT 评估。

如果使用了扩展,则对等方将从 TCP 段的扩展空间中读取时间戳值并将其存储在本地。然后,它将该值作为 “回显时间戳” 放入发回的所有数据段中。

因此,该选项带有两个时间戳:它的发送方自己的时间戳和它从对等方收到的最新时间戳。原始发送方使用 “回显时间戳” 来计算 RTT。它是当前时间戳时钟与 “回显时间戳” 中所反映的值之间的增量。

时间戳的其他用途

TCP 时间戳甚至还有除 PAWS( 防止序列号回绕 Protection Against Wrapped Sequences ) 和 RTT 测量以外的其他用途。例如,可以检测是否不需要重发。如果该确认携带较旧的回显时间戳,则该确认针对的是初始数据包,而不是重新发送的数据包。

TCP 时间戳的另一个更晦涩的用例与 TCP syn cookie 功能有关。

在服务器端建立 TCP 连接

当连接请求到达的速度快于服务器应用程序可以接受新的传入连接的速度时,连接积压最终将达到其极限。这可能是由于系统配置错误或应用程序中的错误引起的。当一个或多个客户端发送连接请求而不对 “SYN ACK” 响应做出反应时,也会发生这种情况。这将用不完整的连接填充连接队列。这些条目需要几秒钟才会超时。这被称为 “同步泛洪攻击” syn flood attack

TCP 时间戳和 TCP Syn Cookie

即使队列已满,某些 TCP 协议栈也允许继续接受新连接。发生这种情况时,Linux 内核将在系统日志中打印一条突出的消息:

端口 P 上可能发生 SYN 泛洪。正在发送 Cookie。检查 SNMP 计数器。

此机制将完全绕过连接队列。通常存储在连接队列中的信息被编码到 SYN/ACK 响应 TCP 序列号中。当 ACK 返回时,可以根据序列号重建队列条目。

序列号只有有限的空间来存储信息。因此,使用 “TCP Syn Cookie” 机制建立的连接不能支持 TCP 选项。

但是,对两个对等点都通用的 TCP 选项可以存储在时间戳中。ACK 数据包在回显时间戳字段中反映了该值,这也允许恢复已达成共识的 TCP 选项。否则,cookie 连接受标准的 64KB 接收窗口限制。

常见误区 —— 时间戳不利于性能

不幸的是,一些指南建议禁用 TCP 时间戳,以减少内核访问时间戳时钟来获取当前时间所需的次数。这是不正确的。如前所述,RTT 估算是 TCP 的必要部分。因此,内核在接收/发送数据包时总是采用微秒级的时间戳。

在包处理步骤的其余部分中,Linux 会重用 RTT 估算所需的时钟时间戳。这还避免了将时间戳添加到传出 TCP 数据包的额外时钟访问。

整个时间戳选项在每个数据包中仅需要 10 个字节的 TCP 选项空间,这不会显著减少可用于数据包有效负载的空间。

常见误区 —— 时间戳是个安全问题

一些安全审计工具和(较旧的)博客文章建议禁用 TCP 时间戳,因为据称它们泄露了系统正常运行时间:这样一来,便可以估算系统/内核的补丁级别。这在过去是正确的:时间戳时钟基于不断增加的值,该值在每次系统引导时都以固定值开始。时间戳值可以估计机器已经运行了多长时间(正常运行时间 uptime)。

从 Linux 4.12 开始,TCP 时间戳不再显示正常运行时间。发送的所有时间戳值都使用对等设备特定的偏移量。时间戳值也每 49 天回绕一次。

换句话说,从地址 “A” 出发,或者终到地址 “A” 的连接看到的时间戳与到远程地址 “B” 的连接看到的时间戳不同。

运行 sysctl net.ipv4.tcp_timeamp=2 以禁用随机化偏移。这使得分析由诸如 wiresharktcpdump 之类的工具记录的数据包跟踪变得更容易 —— 从主机发送的数据包在其 TCP 选项时间戳中都具有相同的时钟基准。因此,对于正常操作,默认设置应保持不变。

选择性确认

如果同一数据窗口中的多个数据包丢失了,TCP 将会出现问题。这是因为 TCP 确认是累积的,但仅适用于按顺序到达的数据包。例如:

  • 发送方发送段 s\_1、s\_2、s\_3、... s\_n
  • 发送方收到 s\_2 的 ACK
  • 这意味着 s\_1 和 s\_2 都已收到,并且发送方不再需要保留这些段。
  • s\_3 是否应该重新发送? s\_4 呢? s\_n?

发送方等待 “重传超时” 或 “重复 ACK” 以使 s\_2 到达。如果发生重传超时或到达了 s\_2 的多个重复 ACK,则发送方再次发送 s\_3。

如果发送方收到对 s\_n 的确认,则 s\_3 是唯一丢失的数据包。这是理想的情况。仅发送单个丢失的数据包。

如果发送方收到的确认段小于 s\_n,例如 s\_4,则意味着丢失了多个数据包。发送方也需要重传下一个数据段。

重传策略

可能只是重复相同的序列:重新发送下一个数据包,直到接收方指示它已处理了直至 s\_n 的所有数据包为止。这种方法的问题在于,它需要一个 RTT,直到发送方知道接下来必须重新发送的数据包为止。尽管这种策略可以避免不必要的重传,但要等到 TCP 重新发送整个数据窗口后,它可能要花几秒钟甚至更长的时间。

另一种方法是一次重新发送几个数据包。当丢失了几个数据包时,此方法可使 TCP 恢复更快。在上面的示例中,TCP 重新发送了 s\_3、s\_4、s\_5、...,但是只能确保已丢失 s\_3。

从延迟的角度来看,这两种策略都不是最佳的。如果只有一个数据包需要重新发送,第一种策略是快速的,但是当多个数据包丢失时,它花费的时间太长。

即使必须重新发送多个数据包,第二个也是快速的,但是以浪费带宽为代价。此外,这样的 TCP 发送方在进行不必要的重传时可能已经发送了新数据。

通过可用信息,TCP 无法知道丢失了哪些数据包。这就是 TCP 选择性确认(SACK)的用武之地了。就像窗口缩放和时间戳一样,它是另一个可选的但非常有用的 TCP 特性。

SACK 选项

   TCP Sack-Permitted Option: Kind: 4, Length 2
   +---------+---------+
   | Kind=4  | Length=2|
   +---------+---------+

支持此扩展的发送方在连接请求中包括 “允许 SACK” 选项。如果两个端点都支持该扩展,则检测到数据流中丢失数据包的对等方可以将此信息通知发送方。

   TCP SACK Option: Kind: 5, Length: Variable
                     +--------+--------+
                     | Kind=5 | Length |
   +--------+--------+--------+--------+
   |      Left Edge of 1st Block       |
   +--------+--------+--------+--------+
   |      Right Edge of 1st Block      |
   +--------+--------+--------+--------+
   |                                   |
   /            . . .                  /
   |                                   |
   +--------+--------+--------+--------+
   |      Left Edge of nth Block       |
   +--------+--------+--------+--------+
   |      Right Edge of nth Block      |
   +--------+--------+--------+--------+

接收方遇到 s\_2 后跟 s\_5 ... s\_n,则在发送对 s\_2 的确认时将包括一个 SACK 块:

                +--------+-------+
                | Kind=5 |   10  |
+--------+------+--------+-------+
| Left edge: s_5                 |
+--------+--------+-------+------+
| Right edge: s_n                |
+--------+-------+-------+-------+

这告诉发送方到 s\_2 的段都是按顺序到达的,但也让发送方知道段 s\_5 至 s\_n 也已收到。然后,发送方可以重新发送那两个数据包(s\_3、s\_4),并继续发送新数据。

神话般的无损网络

从理论上讲,如果连接不会丢包,那么 SACK 就没有任何优势。或者连接具有如此低的延迟,甚至等待一个完整的 RTT 都无关紧要。

在实践中,无损行为几乎是不可能保证的。即使网络及其所有交换机和路由器具有足够的带宽和缓冲区空间,数据包仍然可能丢失:

  • 主机操作系统可能面临内存压力并丢弃数据包。请记住,一台主机可能同时处理数万个数据包流。
  • CPU 可能无法足够快地消耗掉来自网络接口的传入数据包。这会导致网络适配器本身中的数据包丢失。
  • 如果 TCP 时间戳不可用,即使一个非常小的 RTT 的连接也可能在丢失恢复期间暂时停止。

使用 SACK 不会增加 TCP 数据包的大小,除非连接遇到数据包丢失。因此,几乎没有理由禁用此功能。几乎所有的 TCP 协议栈都支持 SACK —— 它通常只在不进行 TCP 批量数据传输的低功耗 IOT 类的设备上才不存在。

当 Linux 系统接受来自此类设备的连接时,TCP 会自动为受影响的连接禁用 SACK。

总结

本文中研究的三个 TCP 扩展都与 TCP 性能有关,最好都保留其默认设置:启用。

TCP 握手可确保仅使用双方都可以理解的扩展,因此,永远不需因为对等方可能不支持而全局禁用该扩展。

关闭这些扩展会导致严重的性能损失,尤其是 TCP 窗口缩放和 SACK。可以禁用 TCP 时间戳而不会立即造成不利影响,但是现在没有令人信服的理由这样做了。启用它们还可以支持 TCP 选项,即使在 SYN cookie 生效时也是如此。


via: https://fedoramagazine.org/tcp-window-scaling-timestamps-and-sack/

作者:Florian Westphal 选题:lujun9972 译者:gxlct008 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

使用 Ansible 剧本自动安装和更新设备上的软件。

Ansible 是系统管理员和开发人员用来保持计算机系统处于最佳状态的一种流行的自动化工具。与可扩展框架一样,Ansible 本身功能有限,它真正的功能体现在许多模块中。在某种程度上,Ansible 模块就是 Linux 系统的命令。它们针对特定问题提供解决方案,而维护计算机时的一项常见任务是使所有计算机的更新和一致。

我曾经使用软件包的文本列表来保持系统或多或少的同步:我会列出笔记本电脑上安装的软件包,然后将其与台式机或另一台服务器之间进行交叉参考,手动弥补差异。当然,在 Linux 机器上安装和维护应用程序是 Ansible 的一项基本功能,这意味着你可以在自己关心的计算机上列出所需的内容。

寻找正确的 Ansible 模块

Ansible 模块的数量非常庞大,如何找到能完成你任务的模块?在 Linux 中,你可以在应用程序菜单或 /usr/bin 中查找要运行的应用程序。使用 Ansible 时,你可以参考 Ansible 模块索引

这个索引按照类别列出。稍加搜索,你就很可能找到所需的模块。对于包管理,Packaging 模块几乎适用于所有带包管理器的系统。

动手写一个 Ansible 剧本

首先,选择本地计算机上的包管理器。例如,如果你打算在运行 Fedora 的笔记本电脑上编写 Ansible 指令(在 Ansible 中称为“ 剧本 playbook ”),那么从 dnf 模块开始。如果你在 Elementary OS 上编写,使用 apt 模块,以此类推。这样你就可以开始进行测试和验证,并可以在以后扩展到其它计算机。

第一步是创建一个代表你的剧本的目录。这不是绝对必要的,但这是一个好习惯。Ansible 只需要一个配置文件就可以运行在 YAML 中,但是如果你以后想要扩展剧本,你就可以通过改变目录和文件的方式来控制 Ansible。现在,只需创建一个名为 install_packages 或类似的目录:

$ mkdir ~/install_packages

你可以根据自己的喜好来命名 Ansible 的剧本,但通常将其命名为 site.yml

$ touch ~/install_packages/site.yml

在你最喜欢的文本编辑器中打开 site.yml,添加以下内容:

---
- hosts: localhost
  tasks:
    - name: install packages
      become: true
      become_user: root
      dnf:
        state: present
        name:
         - tcsh
         - htop

你必须调整使用的模块名称以匹配你使用的发行版。在此示例中,我使用 dnf 是因为我在 Fedora Linux 上编写剧本。

就像 Linux 终端中的命令一样,知道 如何 来调用 Ansible 模块就已经成功了一半。这个示例剧本遵循标准剧本格式:

  • hosts 是一台或多台计算机。在本示例中,目标计算机是 localhost,即你当前正在使用的计算机(而不是你希望 Ansible 连接的远程系统)。
  • tasks 是你要在主机上执行的任务列表。

    • name 是任务的人性化名称。在这种情况下,我使用 install packages,因为这就是该任务正在做的事情。
    • become 允许 Ansible 更改运行此任务的用户。
    • become_user 允许 Ansible 成为 root 用户来运行此任务。这是必须的,因为只有 root 用户才能使用 dnf 安装应用程序。
    • dnf 是模块名称,你可以在 Ansible 网站上的模块索引中找到。

dnf 下的节点是 dnf 模块专用的。这是模块文档的关键所在。就像 Linux 命令的手册页一样,模块文档会告诉你可用的选项和所需的参数。

 title=

安装软件包是一个相对简单的任务,仅需要两个元素。state 选项指示 Ansible 检查系统上是否存在 软件包,而 name 选项列出要查找的软件包。Ansible 会针对机器的 状态 进行调整,因此模块指令始终意味着更改。假如 Ansible 扫描了系统状态,发现剧本里描述的系统(在本例中,tcshhtop 存在)与实际状态存在冲突,那么 Ansible 的任务是进行必要的更改来使系统与剧本匹配。Ansible 可以通过 dnf(或 apt 或者其它任何包管理器)模块进行更改。

每个模块可能都有一组不同的选项,所以在编写剧本时,要经常参考模块文档。除非你对模块非常熟悉,否则这是期望模块完成工作的唯一合理方法。

验证 YAML

剧本是用 YAML 编写的。因为 YAML 遵循严格的语法,所以安装 yamllint 来检查剧本是很有帮助的。更妙的是,有一个专门针对 Ansible 的检查工具称为 ansible-lint,它专门为剧本而生。在继续之前,安装它。

在 Fedora 或 CentOs 上:

$ sudo dnf ins tall yamllint python3-ansible-lint

在 Debian、Elementary 或 Ubuntu 上,同样的:

$ sudo apt install yamllint ansible-lint

使用 ansible-link 来验证你的剧本。如果你无法使用 ansible-lint,你可以使用 yamllint

$ ansible-lint ~/install_packages/site.yml

成功则不返回任何内容,但如果文件中有错误,则必须先修复它们,然后再继续。复制和粘贴过程中的常见错误包括在最后一行的末尾省略换行符、使用制表符而不是空格来缩进。在文本编辑器中修复它们,重新运行 ansible-llint,重复这个过程,直到 ansible-lintyamllint 没有返回为止。

使用 Ansible 安装一个应用

现在你有了一个可验证的有效剧本,你终于可以在本地计算机上运行它了,因为你碰巧知道该剧本定义的任务需要 root 权限,所以在调用 Ansible 时必须使用 --ask-become-pass 选项,因此系统会提示你输入管理员密码。

开始安装:

$ ansible-playbook --ask-become-pass ~/install_packages/site.yml
BECOME password:
PLAY [localhost] ******************************

TASK [Gathering Facts] ******************************
ok: [localhost]

TASK [install packages] ******************************
ok: [localhost]

PLAY RECAP ******************************
localhost: ok=0 changed=2 unreachable=0 failed=0 [...]

这些命令被执行后,目标系统将处于与剧本中描述的相同的状态。

在远程系统上安装应用程序

通过这么多操作来替换一个简单的命令可能会适得其反,但是 Ansible 的优势是它可以在你的所有系统中实现自动化。你可以使用条件语句使 Ansible 在不同的系统上使用特定的模块,但是现在,假定所有计算机都使用相同的包管理器。

要连接到远程系统,你必须在 /etc/ansible/hosts 文件中定义远程系统,该文件与 Ansible 是一起安装的,所以它已经存在了,但它可能是空的,除了一些解释性注释之外。使用 sudo 在你喜欢的文本编辑器中打开它。

你可以通过其 IP 地址或主机名(只要主机名可以解析)定义主机。例如,如果你已经在 /etc/hosts 中定义了 liavara 并可以成功 ping 通,那么你可以在 /etc/ansible/hosts 中将 liavara 设置为主机。或者,如果你正在运行一个域名服务器或 Avahi 服务器并且可以 pingliavara,那么你就可以在 /etc/ansible/hosts 中定义它。否则,你必须使用它的 IP 地址。

你还必须成功地建立与目标主机的安全 shell(SSH)连接。最简单的方法是使用 ssh-copy-id 命令,但是如果你以前从未与主机建立 SSH 连接,阅读我关于如何创建自动 SSH 连接的文章

一旦你在 /etc/ansible/hosts 文件中输入了主机名或 IP 地址后,你就可以在剧本中更改 hosts 定义:

---
- hosts: all
  tasks:
    - name: install packages
      become: true
      become_user: root
      dnf:
        state: present
        name:
         - tcsh
         - htop

再次运行 ansible-playbook

$ ansible-playbook --ask-become-pass ~/install_packages/site.yml

这次,剧本会在你的远程系统上运行。

如果你添加更多主机,则有许多方法可以过滤哪个主机执行哪个任务。例如,你可以创建主机组(服务器的 webserves,台式机的 workstations等)。

适用于混合环境的 Ansible

到目前为止,我们一直假定 Ansible 配置的所有主机都运行相同的操作系统(都是是使用 dnf 命令进行程序包管理的操作系统)。那么,如果你要管理不同发行版的主机,例如 Ubuntu(使用 apt)或 Arch(使用 pacman),或者其它的操作系统时,该怎么办?

只要目标操作系统具有程序包管理器(MacOs 有 HomebrewWindows 有 Chocolatey),Ansible 就能派上用场。

这就是 Ansible 优势最明显的地方。在 shell 脚本中,你必须检查目标主机上有哪些可用的包管理器,即使使用纯 Python,也必须检查操作系统。Ansible 不仅内置了这些功能,而且还具有在剧本中使用命令结果的机制。你可以使用 action 关键字来执行由 Ansible 事实收集子系统提供的变量定义的任务,而不是使用 dnf 模块。

---
- hosts: all
  tasks:
    - name: install packages
      become: true
      become_user: root
      action: >
       {{ ansible_pkg_mgr }} name=htop,transmission state=present update_cache=yes

action 关键字会加载目标插件。在本例中,它使用了 ansible_pkg_mgr 变量,该变量由 Ansible 在初始 收集信息 期间填充。你不需要告诉 Ansible 收集有关其运行操作系统的事实,所以很容易忽略这一点,但是当你运行一个剧本时,你会在默认输出中看到它:

TASK [Gathering Facts] *****************************************
ok: [localhost]

action 插件使用来自这个探针的信息,使用相关的包管理器命令填充 ansible_pkg_mgr,以安装在 name 参数之后列出的程序包。使用 8 行代码,你可以克服在其它脚本选项中很少允许的复杂跨平台难题。

使用 Ansible

现在是 21 世纪,我们都希望我们的计算机设备能够互联并且相对一致。无论你维护的是两台还是 200 台计算机,你都不必一次又一次地执行相同的维护任务。使用 Ansible 来同步生活中的计算机设备,看看 Ansible 还能为你做些什么。


via: https://opensource.com/article/20/9/install-packages-ansible

作者:Seth Kenlon 选题:lujun9972 译者:MjSeven 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出