本文介绍我使用 Terraform 五年之后吸取到的经验。使用 Terraform 五年的经历让我吸取到一些重要经验。无论团队大小、项目性质，有五条要点对于配置合乎逻辑且可用的 Terraform 平台至关重要。1、了解你的目标受众这一点似乎显而易见，但我也见过一些在这方面犯错的案例。当组织和规划 Terraform 的相关代码时，无论是将目录结构标准化还是确定命名规范，考虑目标受众是非常重要的。例如：你的团队是否会使用这些 Terraform 脚本和模块？你是否会向其他团队交接工作？你的团队是否会有新成员加入？你是否正在独自进行项目开发？你是否会半年或一年后仍然使用这些配置，还是会将它安排给别人？这类问题会影响某些决策。理想情况下，无论如何都应该有 远程状态 Remote State 和 状态锁定 State Locking 两种状态。远程状态确保你的笔记本电脑不是你的 Terraform 唯一运行的机器，状态锁定确保同一时刻只有一个人对基础设施进行修改操作。命名规范应该对项目的最终拥有者有意义，而不是只对开发团队有意义。如果项目会转交给其他团队，应该确保他们对命名规范有发言权。如果代码由非技术的利益相关者或内部安全/ GCR 团队负责审查，应该确保他们会检查命名规范。另外，对于资源名称，为了让代码审查人员更仔细地进行检查，你应该使用资源标签，把有关的数据分类/隐私需求（高、中、低）标示出来。2、重用，重用，重用Terraform 注册表 为大多数普通用例提供了现成模块类库。我已经使用过 VPC 模块和安全模块中的大量功能，这些功能只需要提供相关的参数就能使用。使用不同的参数，简单调用这些模块对于处理大部分用例已经足够了。尽可能多地重用这些公共模块，可以避免大量且重复的编码、测试、检查、修复、重构等操作。我也发现，基于使用或变更的频率划分模块和资源大有好处。例如，只使用一次的基础设施手脚架，例如 VPC 相关设置、安全模块、路由表、VPC 端点等，可以放在一起。但是像私有托管域条目、自动伸缩模块、目标模块、负载均衡器等，每次部署时都会变化，所以把这些与一次性的基础设施手脚架分离开来，会令代码检查更方便，调试更快速。3、要明确，而非隐含Terraform 代码中有一些常见的模式，它会导致设计中出现错误的假设。团队可以假设用来写代码的 Terraform 版本永远保持不变，外部模块不会变化，或它们使用的提供者不会变更。当这些外部依赖不可避免地发生变化时，就会导致一些难以发现的问题。无论何处（包括主要的 Terraform 组、提供者组、功能模块组）都要确保定义是明确的。事先定义版本，可以确保依赖库是固定的，因此你可以在讨论、审查、测试后，明明白白地更新依赖关系。4、自动化每一处，包括笔记本电脑、共享虚拟机、CI/CD。在部署的各个阶段使用自动化方法，可以避免可能发生的问题。在你提交代码前，使用 Git 预提交钩子 运行 terraform fmt 和 terraform validate。预提交钩子的作用是确保你的代码满足最低程度的格式和语法正确。把这个预提交文件检入到仓库，对你的团队成员都有好处。项目的第一步就进行质量控制相关的操作，它虽然表面上是小事一桩，但也很重要，能为项目节省大量时间。一切现代化部署工具都有 CI 流程。当你向原始仓库推送代码时，可以使用它来运行 SAST 和单元测试工具。我写过一篇 博客，是关于使用 Checkov 测试 Terraform 代码的安全性和合规性，并为组织特定的惯例创建自定义检查。把这些单元测试工具加入到你的 CI 管道，可以改进代码质量和健壮性。5、写个好的 README.md 文件我们都认为 Terraform 代码是自文档化的。的确如此，但是只有当未来的团队已经了解你的公司的命名规范、开发指南、机密通信、圈内笑话，以及你的仓库内除有效的 Terraform 代码之外其他所有东西，才会如此。维护 README.md 文件是个好习惯，它能节省大量时间，而且团队成员要为自己向 README 文件提交的任何内容负责，这样也就确保团队成员的忠诚度。你的 README 文件至少应该包含在你的工作环境下（Linux、 Windows、Mac 等等）初始化 Terraform 环境的步骤，包括 Terraform 的版本信息。它应当确定需要的依赖库（Checkov、 TerraGrunt 及其他依赖）和其版本，以及团队使用的方便的 Linux 别名（例如有人喜欢将 terraform fmt 简写为 tff）。最重要的是，需要确定分支和 PR 审核策略和流程、命名规范和资源标签的相关标准。README 文件需要通过这样的检验：如果团队有新成员加入，能否告诉他们做什么以及如何正确地完成工作？如果不能，在后续的几个月内，你将面对的是无休止的标准和流程讨论会议。结束语这些就是我使用 Terraform 多年后，认为需要传授给大家的五条有用的建议。也欢迎你分享自己的最佳实践。via: https://opensource.com/article/21/8/terraform-tips作者：Ayush Sharma 选题：lujun9972 译者：cool-summer-021 校对：wxy本文由 LCTT 原创编译，Linux中国 荣誉推出
这篇文章讨论了安装 NumPy，然后创建、读取和排序 NumPy 数组。NumPy（即 Numerical Python）是一个库，它使得在 Python 中对线性数列和矩阵进行统计和集合操作变得容易。我在 Python 数据类型的笔记中介绍过，它比 Python 的列表快几个数量级。NumPy 在数据分析和科学计算中使用得相当频繁。我将介绍安装 NumPy，然后创建、读取和排序 NumPy 数组。NumPy 数组也被称为 ndarray，即 N 维数组的缩写。安装 NumPy使用 pip 安装 NumPy 包非常简单，可以像安装其他软件包一样进行安装：pip install numpy 安装了 NumPy 包后，只需将其导入你的 Python 文件中：import numpy as np 将 numpy 以 np 之名导入是一个标准的惯例，但你可以不使用 np，而是使用你想要的任何其他别名。为什么使用 NumPy? 因为它比 Python 列表要快好几个数量级当涉及到处理大量的数值时，NumPy 比普通的 Python 列表快几个数量级。为了看看它到底有多快，我首先测量在普通 Python 列表上进行 min() 和 max() 操作的时间。我将首先创建一个具有 999,999,999 项的 Python 列表：>>> my_list = range(1, 1000000000) >>> len(my_list) 999999999 现在我将测量在这个列表中找到最小值的时间：>>> start = time.time() >>> min(my_list) 1 >>> print('Time elapsed in milliseconds: ' + str((time.time() - start) * 1000)) Time elapsed in milliseconds: 27007.00879096985 这花了大约 27,007 毫秒，也就是大约 27 秒。这是个很长的时间。现在我试着找出寻找最大值的时间：>>> start = time.time() >>> max(my_list) 999999999 >>> print('Time elapsed in milliseconds: ' + str((time.time() - start) * 1000)) Time elapsed in milliseconds: 28111.071348190308 这花了大约 28,111 毫秒，也就是大约 28 秒。现在我试试用 NumPy 找到最小值和最大值的时间：>>> my_list = np.arange(1, 1000000000) >>> len(my_list) 999999999 >>> start = time.time() >>> my_list.min() 1 >>> print('Time elapsed in milliseconds: ' + str((time.time() - start) * 1000)) Time elapsed in milliseconds: 1151.1778831481934 >>> >>> start = time.time() >>> my_list.max() 999999999 >>> print('Time elapsed in milliseconds: ' + str((time.time() - start) * 1000)) Time elapsed in milliseconds: 1114.8970127105713 找到最小值花了大约 1151 毫秒，找到最大值 1114 毫秒。这大约是 1 秒。正如你所看到的，使用 NumPy 可以将寻找一个大约有 10 亿个值的列表的最小值和最大值的时间 从大约 28 秒减少到 1 秒。这就是 NumPy 的强大之处。使用 Python 列表创建 ndarray有几种方法可以在 NumPy 中创建 ndarray。你可以通过使用元素列表来创建一个 ndarray：>>> my_ndarray = np.array([1, 2, 3, 4, 5]) >>> print(my_ndarray) [1 2 3 4 5] 有了上面的 ndarray 定义，我将检查几件事。首先，上面定义的变量的类型是 numpy.ndarray。这是所有 NumPy ndarray 的类型：>>> type(my_ndarray) <class 'numpy.ndarray'> 这里要注意的另一件事是 “ 形状 shape ”。ndarray 的形状是 ndarray 的每个维度的长度。你可以看到，my_ndarray 的形状是 (5,)。这意味着 my_ndarray 包含一个有 5 个元素的维度（轴）。>>> np.shape(my_ndarray) (5,) 数组中的维数被称为它的 “ 秩 rank ”。所以上面的 ndarray 的秩是 1。我将定义另一个 ndarray my_ndarray2 作为一个多维 ndarray。那么它的形状会是什么呢？请看下面：>>> my_ndarray2 = np.array([(1, 2, 3), (4, 5, 6)]) >>> np.shape(my_ndarray2) (2, 3) 这是一个秩为 2 的 ndarray。另一个要检查的属性是 dtype，也就是数据类型。检查我们的 ndarray 的 dtype 可以得到以下结果：>>> my_ndarray.dtype dtype('int64') int64 意味着我们的 ndarray 是由 64 位整数组成的。NumPy 不能创建混合类型的 ndarray，必须只包含一种类型的元素。如果你定义了一个包含混合元素类型的 ndarray，NumPy 会自动将所有的元素类型转换为可以包含所有元素的最高元素类型。例如，创建一个 int 和 float 的混合序列将创建一个 float64 的 ndarray：>>> my_ndarray2 = np.array([1, 2.0, 3]) >>> print(my_ndarray2) [1. 2. 3.] >>> my_ndarray2.dtype dtype('float64') 另外，将其中一个元素设置为 string 将创建 dtype 等于 <U21 的字符串 ndarray，意味着我们的 ndarray 包含 unicode 字符串：>>> my_ndarray2 = np.array([1, '2', 3]) >>> print(my_ndarray2) ['1' '2' '3'] >>> my_ndarray2.dtype dtype('<U21') size 属性将显示我们的 ndarray 中存在的元素总数：>>> my_ndarray = np.array([1, 2, 3, 4, 5]) >>> my_ndarray.size 5 使用 NumPy 方法创建 ndarray如果你不想直接使用列表来创建 ndarray，还有几种可以用来创建它的 NumPy 方法。你可以使用 np.zeros() 来创建一个填满 0 的 ndarray。它需要一个“形状”作为参数，这是一个包含行数和列数的列表。它还可以接受一个可选的 dtype 参数，这是 ndarray 的数据类型：>>> my_ndarray = np.zeros([2,3], dtype=int) >>> print(my_ndarray) [[0 0 0] [0 0 0]] 你可以使用 np. ones() 来创建一个填满 1 的 ndarray：>>> my_ndarray = np.ones([2,3], dtype=int) >>> print(my_ndarray) [[1 1 1] [1 1 1]] 你可以使用 np.full() 来给 ndarray 填充一个特定的值：>>> my_ndarray = np.full([2,3], 10, dtype=int) >>> print(my_ndarray) [[10 10 10] [10 10 10]] 你可以使用 np.eye() 来创建一个单位矩阵 / ndarray，这是一个沿主对角线都是 1 的正方形矩阵。正方形矩阵是一个行数和列数相同的矩阵：>>> my_ndarray = np.eye(3, dtype=int) >>> print(my_ndarray) [[1 0 0] [0 1 0] [0 0 1]] 你可以使用 np.diag() 来创建一个沿对角线有指定数值的矩阵，而在矩阵的其他部分为 0：>>> my_ndarray = np.diag([10, 20, 30, 40, 50]) >>> print(my_ndarray) [[10 0 0 0 0] [ 0 20 0 0 0] [ 0 0 30 0 0] [ 0 0 0 40 0] [ 0 0 0 0 50]] 你可以使用 np.range() 来创建一个具有特定数值范围的 ndarray。它是通过指定一个整数的开始和结束（不包括）范围以及一个步长来创建的：>>> my_ndarray = np.arange(1, 20, 3) >>> print(my_ndarray) [ 1 4 7 10 13 16 19] 读取 ndarrayndarray 的值可以使用索引、分片或布尔索引来读取。使用索引读取 ndarray 的值在索引中，你可以使用 ndarray 的元素的整数索引来读取数值，就像你读取 Python 列表一样。就像 Python 列表一样，索引从 0 开始。例如，在定义如下的 ndarray 中：>>> my_ndarray = np.arange(1, 20, 3) 第四个值将是 my_ndarray[3]，即 10。最后一个值是 my_ndarray[-1]，即 19：>>> my_ndarray = np.arange(1, 20, 3) >>> print(my_ndarray[0]) 1 >>> print(my_ndarray[3]) 10 >>> print(my_ndarray[-1]) 19 >>> print(my_ndarray[5]) 16 >>> print(my_ndarray[6]) 19 使用分片读取 ndarray你也可以使用分片来读取 ndarray 的块。分片的工作方式是用冒号（:）操作符指定一个开始索引和一个结束索引。然后，Python 将获取该开始和结束索引之间的 ndarray 片断：>>> print(my_ndarray[:]) [ 1 4 7 10 13 16 19] >>> print(my_ndarray[2:4]) [ 7 10] >>> print(my_ndarray[5:6]) [16] >>> print(my_ndarray[6:7]) [19] >>> print(my_ndarray[:-1]) [ 1 4 7 10 13 16] >>> print(my_ndarray[-1:]) [19] 分片创建了一个 ndarray 的引用（或视图）。这意味着，修改分片中的值也会改变原始 ndarray 的值。比如说：>>> my_ndarray[-1:] = 100 >>> print(my_ndarray) [ 1 4 7 10 13 16 100] 对于秩超过 1 的 ndarray 的分片，可以使用 [行开始索引:行结束索引, 列开始索引:列结束索引] 语法：>>> my_ndarray2 = np.array([(1, 2, 3), (4, 5, 6)]) >>> print(my_ndarray2) [[1 2 3] [4 5 6]] >>> print(my_ndarray2[0:2,1:3]) [[2 3] [5 6]] 使用布尔索引读取 ndarray 的方法读取 ndarray 的另一种方法是使用布尔索引。在这种方法中，你在方括号内指定一个过滤条件，然后返回符合该条件的 ndarray 的一个部分。例如，为了获得一个 ndarray 中所有大于 5 的值，你可以指定布尔索引操作 my_ndarray[my_ndarray > 5]。这个操作将返回一个包含所有大于 5 的值的 ndarray：>>> my_ndarray = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) >>> my_ndarray2 = my_ndarray[my_ndarray > 5] >>> print(my_ndarray2) [ 6 7 8 9 10] 例如，为了获得一个 ndarray 中的所有偶数值，你可以使用如下的布尔索引操作：>>> my_ndarray2 = my_ndarray[my_ndarray % 2 == 0] >>> print(my_ndarray2) [ 2 4 6 8 10] 而要得到所有的奇数值，你可以用这个方法：>>> my_ndarray2 = my_ndarray[my_ndarray % 2 == 1] >>> print(my_ndarray2) [1 3 5 7 9] ndarray 的矢量和标量算术NumPy 的 ndarray 允许进行矢量和标量算术操作。在矢量算术中，在两个 ndarray 之间进行一个元素的算术操作。在标量算术中，算术运算是在一个 ndarray 和一个常数标量值之间进行的。如下的两个 ndarray：>>> my_ndarray = np.array([1, 2, 3, 4, 5]) >>> my_ndarray2 = np.array([6, 7, 8, 9, 10]) 如果你将上述两个 ndarray 相加，就会产生一个两个 ndarray 的元素相加的新的 ndarray。例如，产生的 ndarray 的第一个元素将是原始 ndarray 的第一个元素相加的结果，以此类推：>>> print(my_ndarray2 + my_ndarray) [ 7 9 11 13 15] 这里，7 是 1 和 6 的和，这是我相加的 ndarray 中的前两个元素。同样，15 是 5 和10 之和，是最后一个元素。请看以下算术运算：>>> print(my_ndarray2 - my_ndarray) [5 5 5 5 5] >>> >>> print(my_ndarray2 * my_ndarray) [ 6 14 24 36 50] >>> >>> print(my_ndarray2 / my_ndarray) [6. 3.5 2.66666667 2.25 2. ] 在 ndarray 中加一个标量值也有类似的效果，标量值被添加到 ndarray 的所有元素中。这被称为“ 广播 broadcasting ”：>>> print(my_ndarray + 10) [11 12 13 14 15] >>> >>> print(my_ndarray - 10) [-9 -8 -7 -6 -5] >>> >>> print(my_ndarray * 10) [10 20 30 40 50] >>> >>> print(my_ndarray / 10) [0.1 0.2 0.3 0.4 0.5] ndarray 的排序有两种方法可以对 ndarray 进行原地或非原地排序。原地排序会对原始 ndarray 进行排序和修改，而非原地排序会返回排序后的 ndarray，但不会修改原始 ndarray。我将尝试这两个例子：>>> my_ndarray = np.array([3, 1, 2, 5, 4]) >>> my_ndarray.sort() >>> print(my_ndarray) [1 2 3 4 5] 正如你所看到的，sort() 方法对 ndarray 进行原地排序，并修改了原数组。还有一个方法叫 np.sort()，它对数组进行非原地排序：>>> my_ndarray = np.array([3, 1, 2, 5, 4]) >>> print(np.sort(my_ndarray)) [1 2 3 4 5] >>> print(my_ndarray) [3 1 2 5 4] 正如你所看到的，np.sort() 方法返回一个已排序的 ndarray，但没有修改它。总结我已经介绍了很多关于 NumPy 和 ndarray 的内容。我谈到了创建 ndarray，读取它们的不同方法，基本的向量和标量算术，以及排序。NumPy 还有很多东西可以探索，包括像 union() 和 intersection()这样的集合操作，像 min() 和 max() 这样的统计操作，等等。我希望我上面演示的例子是有用的。祝你在探索 NumPy 时愉快。本文最初发表于 作者的个人博客，经授权后改编。via: https://opensource.com/article/21/9/python-numpy作者：Ayush Sharma 选题：lujun9972 译者：wxy 校对：wxy本文由 LCTT 原创编译，Linux中国 荣誉推出
为网页上的缩略图和横幅图片生成经过调整和优化的图片。以前我在处理网页工作时，我对图像敬而远之。处理和优化图像既不精确又费时。后来我发现了一些命令，改变了我的想法。为了创建网页，我使用 Jekyll，所以我在说明中包括了它。然而，这些命令也可以用于其他静态网站生成器。Linux 上的图像命令对我来说有用的命令是 optipng、jpegoptim，当然还有古老的 imagemagick。它们一起使处理图像变得容易管理，甚至可以自动化。下面是我如何使用这些命令实现我的解决方案的概述。我把文章图片放在我的 static/images 文件夹中。在那里，我生成了所有 PNG 和 JPG 图片的两个副本：一个裁剪过的缩略图版本，尺寸为 422×316一个更大的横幅版本，尺寸为 1024×768然后，我把每个副本（缩略图和横幅）放入自己的文件夹，并利用 Jekyll 的自定义变量来确定文件夹路径。下面我将更详细地介绍这些步骤中的每一步。安装要跟上我的解决方案，请确保你已经安装了所有的命令。在 Linux 上，你可以使用软件包管理器安装 optipng、jpegoptim 和 imagemagick。在 Fedora、CentOS、Mageia 和类似系统上：$ sudo dnf install optipng jpegoptim imagemagick 在 Debian、Elementary、Mint 和类似系统上：$ sudo apt install optipng jpegoptim imagemagick 在 macOS 上，使用 MacPorts 或 Homebrew：brew install optipng jpegoptim imagemagick 在 Windows 上，使用 Chocolatey。为缩略图和横幅创建文件夹安装完这些命令后，我在 static/images 下创建了新的文件夹。生成的缩略图放在 img-thumbs，横幅放在 img-normal。$ cd static/images $ mkdir -p img-thumbs img-normal 创建了文件夹后，我把所有的 GIF、SVG、JPG 和 PNG 文件复制到这两个文件夹。我把 GIF 和 SVG 原封不动地用于缩略图和横幅图片。$ cp content/*.gif img-thumbs/; cp content/*.gif img-normal/ $ cp content/*.svg img-thumbs/; cp content/*.svg img-normal/ $ cp content/*.jpg img-thumbs/; cp content/*.jpg img-normal/ $ cp content/*.png img-thumbs/; cp content/*.png img-normal/ 处理缩略图为了调整和优化缩略图的大小，我使用了三个命令。我使用 ImageMagick 的 mogrify 命令来调整 JPG 和 PNG 的大小。因为我希望缩略图是 422×316，所以命令看起来像这样:$ cd img-thumbs $ mogrify -resize 422x316 *.png $ mogrify -format jpg -resize 422x316 *.jpg 现在我用 optipng 优化 PNG，用 jpegoptim 优化 JPG：$ for i in *.png; do optipng -o5 -quiet "$i"; done $ jpegoptim -sq *.jpg 在上述命令中：对于 optipng，-o5 开关设置了优化的级别，0 是最低的。对于jpegoptim，-s 剥离所有图像元数据，-q 设置安静模式。处理横幅我处理横幅图片的方法与处理缩略图的方法基本相同，除了尺寸外，横幅图片的尺寸为 1024×768。$ cd .. $ cd img-normal $ mogrify -resize 1024x768 *.png $ mogrify -format jpg -resize 1024x768 *.jpg $ for i in *.png; do optipng -o5 -quiet "$i"; done $ jpegoptim -sq *.jpg 配置 Jekyll 中的路径img-thumbs 目录现在包含我的缩略图，img-normal 包含横幅。为了更轻松一些，我在Jekyll的 _config.yml 中把它们都设置为自定义变量。content-images-path: /static/images/img-normal/ content-thumbs-images-path: /static/images/img-thumbs/ 使用这些变量很简单。当我想显示缩略图时，我把 content-thumbs-images-path 加到图片上。当我想显示完整的横幅时，我在前面添加 content-images-path。{% if page.banner_img %} {% endif %} 总结我可以对我的优化命令做几个改进。使用 rsync 只复制改变过的文件到 img-thumbs 和 img-normal 是一个明显的改进。这样一来，我就不会一次又一次地重新处理文件。将这些命令添加到 Git 提交前钩子 或 CI 流水线中是另一个有用的步骤。调整和优化图像以减少其大小，对用户和整个网页来说都是一种胜利。也许我减少图片尺寸的下一步将是 webp。更少的字节通过电线传输意味着更低的碳足迹，但这是另一篇文章。目前，用户体验的胜利已经足够好了。本文原载于作者的博客，已获授权转载。via: https://opensource.com/article/21/12/optimize-web-images-linux作者：Ayush Sharma 选题：lujun9972 译者：geekpi 校对：wxy本文由 LCTT 原创编译，Linux中国 荣誉推出
我是如何通过链接每个页面回到其原始源代码来保持我的网站开放的。我是一个开源的超级粉丝。我支持这项事业的一个小方法是从一开始就保持我的个人博客网站开放。我这样做的部分原因是让人们看到每个页面背后的变化历史。还因为当我开始使用 Jekyll 时，我没有找到很多开源的 Jekyll 博客可以学习。我希望保持我的网站开放并公开我的尝试和错误，可以为其他人节省很多时间。Jekyll 的 page.path 变量我实现这一目标的方法之一是将我发布的每一个条目链接到其原始的 Markdown。Jekyll 的变量 中正好有一个需要的工具：page.path。这个变量包含每个页面的原始文件系统路径。官方的描述甚至强调了它的作用是链接回源！在一篇文章的 Markdown 文件中打印 {{page.path }}，可以得到类似这样的结果：_posts/2021-10-10-example.md 假设该文章的源代码存在于这个路径：https://example.com/ayushsharma-in/-/blob/master/_posts/2021-10-10-example.md 如果你在任何文章的 page.path 前加上 https://example.com/ayushsharma-in/-/blob/master/，它就会生成一个返回其源码的链接。在 Jekyll 中，生成这个完整的链接看起来像这样：View source 就是这么简单。Jekyll 和开放 Web现代 Web 是一种错综复杂的多层次技术，但这并不意味着它必须让人摸不清。有了 Jekyll 的变量，你可以确保你的用户可以，了解更多关于你是如何建立你的网站的，如果他们愿意的话。你可以在我的个人博客上看到真实的例子：滚动到底部的查看源码链接。本文改编自 ayush sharma 的笔记，并经许可转载。via: https://opensource.com/article/21/12/reveal-source-code-jinja2-git作者：Ayush Sharma 选题：lujun9972 译者：geekpi 校对：wxy本文由 LCTT 原创编译，Linux中国 荣誉推出
Python 中的 Beautiful Soup 库可以很方便的从网页中提取 HTML 内容。今天我们将讨论如何使用 Beautiful Soup 库从 HTML 页面中提取内容，之后，我们将使用它将其转换为 Python 列表或字典。什么是 Web 刮取，为什么我需要它？答案很简单：并非每个网站都有获取内容的 API。你可能想从你最喜欢的烹饪网站上获取食谱，或者从旅游博客上获取照片。如果没有 API，提取 HTML（或者说 刮取 scraping 可能是获取内容的唯一方法。我将向你展示如何使用 Python 来获取。并非所以网站都喜欢被刮取，有些网站可能会明确禁止。请于网站所有者确认是否同意刮取。Python 如何刮取网站？使用 Python 进行刮取，我们将执行三个基本步骤：使用 requests 库获取 HTML 内容分析 HTML 结构并识别包含我们需要内容的标签使用 Beautiful Soup 提取标签并将数据放入 Python 列表中安装库首先安装我们需要的库。requests 库从网站获取 HTML 内容，Beautiful Soup 解析 HTML 并将其转换为 Python 对象。在 Python3 中安装它们，运行：pip3 install requests beautifulsoup4 提取 HTML在本例中，我将选择刮取网站的 Techhology 部分。如果你跳转到此页面，你会看到带有标题、摘录和发布日期的文章列表。我们的目标是创建一个包含这些信息的文章列表。网站页面的完整 URL 是：https://notes.ayushsharma.in/technology 我们可以使用 requests 从这个页面获取 HTML 内容：#!/usr/bin/python3 import requests url = 'https://notes.ayushsharma.in/technology' data = requests.get(url) print(data.text) 变量 data 将包含页面的 HTML 源代码。从 HTML 中提取内容为了从 data 中提取数据，我们需要确定哪些标签具有我们需要的内容。如果你浏览 HTML，你会发现靠近顶部的这一段： Using variables in Jekyll to define custom content I recently discovered that Jekyll's config.yml can be used to define custom variables for reusing content. I feel like I've been living under a rock all this time. But to err over and over again is human. Aug 2021 这是每篇文章在整个页面中重复的部分。我们可以看到 .card-title 包含文章标题，.card-text 包含摘录，.card-footer > small 包含发布日期。让我们使用 Beautiful Soup 提取这些内容。#!/usr/bin/python3 import requests from bs4 import BeautifulSoup from pprint import pprint url = 'https://notes.ayushsharma.in/technology' data = requests.get(url) my_data = [] html = BeautifulSoup(data.text, 'html.parser') articles = html.select('a.post-card') for article in articles: title = article.select('.card-title')[0].get_text() excerpt = article.select('.card-text')[0].get_text() pub_date = article.select('.card-footer small')[0].get_text() my_data.append({"title": title, "excerpt": excerpt, "pub_date": pub_date}) pprint(my_data) 以上代码提取文章信息并将它们放入 my_data 变量中。我使用了 pprint 来美化输出，但你可以在代码中忽略它。将上面的代码保存在一个名为 fetch.py 的文件中，然后运行它：python3 fetch.py 如果一切顺利，你应该会看到：[{'excerpt': "I recently discovered that Jekyll's config.yml can be used to" "define custom variables for reusing content. I feel like I've" 'been living under a rock all this time. But to err over and over' 'again is human.', 'pub_date': 'Aug 2021', 'title': 'Using variables in Jekyll to define custom content'}, {'excerpt': "In this article, I'll highlight some ideas for Jekyll" 'collections, blog category pages, responsive web-design, and' 'netlify.toml to make static website maintenance a breeze.', 'pub_date': 'Jul 2021', 'title': 'The evolution of ayushsharma.in: Jekyll, Bootstrap, Netlify,' 'static websites, and responsive design.'}, {'excerpt': "These are the top 5 lessons I've learned after 5 years of" 'Terraform-ing.', 'pub_date': 'Jul 2021', 'title': '5 key best practices for sane and usable Terraform setups'}, ... (truncated) 以上是全部内容！在这 22 行代码中，我们用 Python 构建了一个网络刮取器，你可以在 我的示例仓库中找到源代码。总结对于 Python 列表中的网站内容，我们现在可以用它做一些很酷的事情。我们可以将它作为 JSON 返回给另一个应用程序，或者使用自定义样式将其转换为 HTML。随意复制粘贴以上代码并在你最喜欢的网站上进行试验。玩的开心，继续编码吧。本文最初发表在作者个人博客上，经授权改编。via: https://opensource.com/article/21/9/web-scraping-python-beautiful-soup作者：Ayush Sharma 选题：lujun9972 译者：MjSeven 校对：wxy本文由 LCTT 原创编译，Linux中国 荣誉推出
LVM 为你配置存储的方式提供了极大的灵活性。 逻辑卷管理器 Logical Volume Manager （LVM）允许在操作系统和硬件之间建立一个抽象层。通常，你的操作系统会查找磁盘（/dev/sda、/dev/sdb 等）和这些磁盘中的分区（/dev/sda1、/dev/sdb1 等）。LVM 在操作系统和磁盘之间创建了一个虚拟层。LVM 不是一个驱动器持有一定数量的分区，而是创建一个统一的存储池（称为 卷组 Volume Group ），跨越任意数量的物理驱动器（称为 物理卷 Physical Volume ）。使用卷组中可用的存储，LVM 可以为你的操作系统提供类似磁盘和分区的功能。操作系统完全没有意识到它被“欺骗”了。由于 LVM 虚拟地创建卷组和逻辑卷，因此即使在系统运行时，也可以轻松调整它们的大小或移动它们，或者创建新卷。此外，LVM 提供了其它情况下不存在的特性，比如创建逻辑卷的活动快照时无需首先卸载磁盘。LVM 中的卷组是一个命名的虚拟容器，将底层物理磁盘组合在一起。它充当一个池，可以从中创建不同大小的 逻辑卷 Logical Volume 。逻辑卷包含实际的文件系统并且可以跨越多个磁盘，并且不需要物理上连续。特性分区名称通常具有系统名称，例如 /dev/sda1。LVM 具有便于人们理解的名称，例如 home 或者 media。分区的总大小受底层物理磁盘大小的限制。在 LVM 中，卷可以跨越多个磁盘，并且仅受 LVM 中所有物理磁盘总大小的限制。分区通常只有在磁盘未使用且已卸载时才能调整大小、移动或删除。LVM 卷可以在系统运行时进行操作。只能通过分配与分区相邻的可用空间来扩展分区。LVM 卷可以从任何地方占用可用空间。扩展分区涉及移动数据以腾出可用空间，这非常耗时，并且可能会在断电期间导致数据丢失。LVM 卷可以从卷组中的任何地方占用可用空间，甚至可以在另一块磁盘上。因为在 LVM 中创建卷非常容易，所以它鼓励创建不同的卷，例如创建单独的卷来测试功能或尝试不同的操作系统。对于分区，此过程将非常耗时并且容易出错。快照只能在 LVM 中创建。它允许你创建当前逻辑卷的时间点镜像，即使在系统运行时也可以。这非常适合备份。测试设置作为演示，假设你的系统具有以下驱动器配置：NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT xvda 202:0 0 8G 0 disk `-xvda1 202:1 0 8G 0 part / xvdb 202:16 0 1G 0 disk xvdc 202:32 0 1G 0 disk xvdd 202:48 0 2G 0 disk xvde 202:64 0 5G 0 disk xvdf 202:80 0 8G 0 disk 步骤 1. 初始化磁盘以用于 LVM运行 pvcreate /dev/xvdb /dev/xvdc /dev/xvdd /dev/xvde /dev/xvdf。输出应如下：Physical volume "/dev/xvdb" successfully created Physical volume "/dev/xvdc" successfully created Physical volume "/dev/xvdd" successfully created Physical volume "/dev/xvde" successfully created Physical volume "/dev/xvdf" successfully created 使用 pvs 或者 pvdisplay 查看结果："/dev/xvde" is a new physical volume of "5.00 GiB" --- NEW Physical volume --- PV Name /dev/xvde VG Name PV Size 5.00 GiB Allocatable NO PE Size 0 Total PE 0 Free PE 0 Allocated PE 0 PV UUID 728JtI-ffZD-h2dZ-JKnV-8IOf-YKdS-8srJtn "/dev/xvdb" is a new physical volume of "1.00 GiB" --- NEW Physical volume --- PV Name /dev/xvdb VG Name PV Size 1.00 GiB Allocatable NO PE Size 0 Total PE 0 Free PE 0 Allocated PE 0 PV UUID zk1phS-7uXc-PjBP-5Pv9-dtAV-zKe6-8OCRkZ "/dev/xvdd" is a new physical volume of "2.00 GiB" --- NEW Physical volume --- PV Name /dev/xvdd VG Name PV Size 2.00 GiB Allocatable NO PE Size 0 Total PE 0 Free PE 0 Allocated PE 0 PV UUID R0I139-Ipca-KFra-2IZX-o9xJ-IW49-T22fPc "/dev/xvdc" is a new physical volume of "1.00 GiB" --- NEW Physical volume --- PV Name /dev/xvdc VG Name PV Size 1.00 GiB Allocatable NO PE Size 0 Total PE 0 Free PE 0 Allocated PE 0 PV UUID FDzcVS-sq22-2b13-cYRj-dXHf-QLjS-22Meae "/dev/xvdf" is a new physical volume of "8.00 GiB" --- NEW Physical volume --- PV Name /dev/xvdf VG Name PV Size 8.00 GiB Allocatable NO PE Size 0 Total PE 0 Free PE 0 Allocated PE 0 PV UUID TRVSH9-Bo5D-JHHb-g0NX-8IoS-GG6T-YV4d0p 步骤 2. 创建卷组运行 vgcreate myvg /dev/xvdb /dev/xvdc /dev/xvdd /dev/xvde /dev/xvdf。通过 vgs 或者 vgdisplay 查看结果：--- Volume group --- VG Name myvg System ID Format lvm2 Metadata Areas 5 Metadata Sequence No 1 VG Access read/write VG Status resizable MAX LV 0 Cur LV 0 Open LV 0 Max PV 0 Cur PV 5 Act PV 5 VG Size 16.98 GiB PE Size 4.00 MiB Total PE 4347 Alloc PE / Size 0 / 0 Free PE / Size 4347 / 16.98 GiB VG UUID ewrrWp-Tonj-LeFa-4Ogi-BIJJ-vztN-yrepkh 步骤 3: 创建逻辑卷运行以下命令：lvcreate myvg --name media --size 4G lvcreate myvg --name home --size 4G 使用 lvs 或者 lvdisplay 验证结果：--- Logical volume --- LV Path /dev/myvg/media LV Name media VG Name myvg LV UUID LOBga3-pUNX-ZnxM-GliZ-mABH-xsdF-3VBXFT LV Write Access read/write LV Creation host, time ip-10-0-5-236, 2017-02-03 05:29:15 +0000 LV Status available # open 0 LV Size 4.00 GiB Current LE 1024 Segments 1 Allocation inherit Read ahead sectors auto - currently set to 256 Block device 252:0 --- Logical volume --- LV Path /dev/myvg/home LV Name home VG Name myvg LV UUID Hc06sl-vtss-DuS0-jfqj-oNce-qKf6-e5qHhK LV Write Access read/write LV Creation host, time ip-10-0-5-236, 2017-02-03 05:29:40 +0000 LV Status available # open 0 LV Size 4.00 GiB Current LE 1024 Segments 1 Allocation inherit Read ahead sectors auto - currently set to 256 Block device 252:1 步骤 4: 创建文件系统使用以下命令创建文件系统：vgcreate myvg /dev/xvdb /dev/xvdc /dev/xvdd /dev/xvde /dev/xvdf mkfs.ext3 /dev/myvg/media mkfs.ext3 /dev/myvg/home 挂载它：mount /dev/myvg/media /media mount /dev/myvg/home /home 使用 lsblk 命令查看完整配置：NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT xvda 202:0 0 8G 0 disk `-xvda1 202:1 0 8G 0 part / xvdb 202:16 0 1G 0 disk xvdc 202:32 0 1G 0 disk xvdd 202:48 0 2G 0 disk xvde 202:64 0 5G 0 disk `-myvg-media 252:0 0 4G 0 lvm /media xvdf 202:80 0 8G 0 disk `-myvg-home 252:1 0 4G 0 lvm /home 步骤 5: 扩展 LVM添加一块新的 /dev/xvdg 磁盘。要扩展 home 卷，运行以下命令：pvcreate /dev/xvdg vgextend myvg /dev/xvdg lvextend -l 100%FREE /dev/myvg/home resize2fs /dev/myvg/home 运行 df -h，你应该可以看到新的磁盘大小。就是这样！LVM 为你配置存储的方式提供了极大的灵活性。尝试一下，并享受 LVM 的乐趣！本文首发于 作者个人博客，经授权改编。via: https://opensource.com/article/21/9/add-storage-lvm作者：Ayush Sharma 选题：lujun9972 译者：perfiffer 校对：wxy本文由 LCTT 原创编译，Linux中国 荣誉推出
用开源工具将任何网站变成 Linux 桌面应用
使用 Nativefier 和 Electron 从任何网站创建桌面应用。Mastodon 是一个很好的开源、去中心化的社交网络。我每天都在使用 Mastodon，通过它的网页界面使用 Mastodon 可能是最常见的方式（尽管因为开源，它有许多不同的交互方式，包括基于终端的应用和移动应用），但我更喜欢专门的应用窗口。最近，我发现了 Nativefier，现在我可以在我的 Linux 桌面上把 Mastodon 或其他任何网页应用作为桌面应用来使用。Nativefier 将一个 URL 用 Electron 框架包装起来，它将开源的 Chromium 浏览器作为后端，但使用自己的可执行程序运行。Nativefier 采用 MIT 许可证，可用于 Linux、Windows 和 MacOS。安装 NativefierNativefier 需要 Node.js。安装 Nativefier 只需运行：$ sudo npm install -g nativefier 在我的 Ubuntu 桌面上，我必须先升级 NodeJS，所以当你安装 Nativefier 时，一定要检查需要哪个 Node 版本。安装完毕后，你可以检查你的 Nativefier 的版本，以验证它是否已经安装：$ nativefier --version 45.0.4 运行 nativefier --help 列出了应用支持的所有选项。设置我建议你在开始用 Nativefier 创建应用之前，创建一个名为 ~/NativeApps 的新文件夹。这有助于保持你的应用有序。$ mkdir ~/NativeApps cd ~/NativeApps 为 Mastodon 创建一个应用程序我将首先为 mastodon.technology 创建一个应用。使用以下命令：$ nativefier --name Mastodon \ --platform linux --arch x64 \ --width 1024 --height 768 \ --tray --disable-dev-tools \ --single-instance https://mastodon.technology 这个例子中的选项做了以下工作：--name：设置应用的名称为 Mastodon--platform：设置应用程序的平台为 Linux--arch x64：设置架构为 x64--width 1024 --height 768：设置应用启动时的大小--tray：为应用创建一个托盘图标--disable-dev-tools：禁用 Chrome 开发工具--single-instance：只允许应用有一个实例运行这条命令会显示以下输出：Preparing Electron app... Converting icons... Packaging... This will take a few seconds, maybe minutes if the requested Electron isn't cached yet... Packaging app for platform linux x64 using electron v13.4.0 Finalizing build... App built to /home/tux/NativeApps/Mastodon-linux-x64, move to wherever it makes sense for you and run the contained executable file (prefixing with ./ if necessary) Menu/desktop shortcuts are up to you, because Nativefier cannot know where you're going to move the app. Search for "linux .desktop file" for help, or see https://wiki.archlinux.org/index.php/Desktop_entries 输出显示，文件被放置在 /home/tux/NativeApps/Mastodon-linux-x64。当你 cd 进入这个文件夹，你会看到一个名为 Mastodon 的文件。这是启动该应用的主要可执行文件。在你启动它之前，你必须给它适当的权限。$ cd Mastodon-linux-x64 chmod +x Mastodon 现在，执行 ./Mastodon 就可以看到你的 Linux 应用启动了！为我的博客创建一个应用为了好玩，我也要为我的博客创建一个应用。如果没有 Linux 应用，拥有一个技术博客有什么用？命令是：$ nativefier -n ayushsharma \ -p linux -a x64 \ --width 1024 --height 768 \ --tray --disable-dev-tools \ --single-instance https://ayushsharma.in $ cd ayushsharma-linux-x64 chmod +x ayushsharma 为 findmymastodon.com 创建一个应用最后，这是为我的宠物项目 findmymastodon.com 制作的应用。命令是：$ nativefier -n findmymastodon \ -p linux -a x64 \ --width 1024 --height 768 \ --tray --disable-dev-tools \ --single-instance https://findmymastodon.com $ cd findmymastodon-linux-x64 chmod +x findmymastodon 创建 Linux 桌面图标应用已经创建并可以执行了，现在是创建桌面图标的时候了。作为示范，以下是如何为 Mastodon 启动器创建一个桌面图标。首先，下载一个 Mastodon 的图标。将该图标放在其 Nativefier 应用目录下，名为 icon.png。然后创建一个名为 Mastodon.desktop 的文件并输入以下文本：[Desktop Entry] Type=Application Name=Mastodon Path=/home/tux/NativeApps/Mastodon-linux-x64 Exec=/home/tux/NativeApps/Mastodon-linux-x64/Mastodon Icon=/home/tux/NativeApps/Mastodon-linux-x64/icon.png 你可以把 .desktop 文件移到你的 Linux 桌面上，把它作为一个桌面启动器。你也可以把它复制到 ~/.local/share/applications 中，这样它就会出现在你的应用菜单或活动启动器中。总结我喜欢为我经常使用的工具配备专门的应用。我最喜欢的一个 Mastodon 应用特点是，当我登录到 Mastodon 之后，我就不必再次登录了! Nativefier 在底层运行 Chromium。所以它能够像其他浏览器一样记住你的会话。我想特别感谢 Nativefier 团队，他们让 Linux 桌面离完美更近了一步。本文最初发表在 作者的网站 上，并经授权转载。via: https://opensource.com/article/21/11/linux-apps-nativefier作者：Ayush Sharma 选题：lujun9972 译者：geekpi 校对：wxy本文由 LCTT 原创编译，Linux中国 荣誉推出
使用 Python 或 Bash 将动态数据插入 Jekyll 静态网站中，并且避免创建一个 API 后端。静态网站生成器 Jekyll 使用 _config.yml 进行配置。这些配置都是 Jekyll 特有的。但你也可以在这些文件中 用我们自己的内容定义变量，并在整个网站中使用它们。在本文中，我将重点介绍动态创建 Jekyll 配置文件的一些优势。在我的本地笔记本电脑上，我使用以下命令来服务我的 Jekyll 网站进行测试：bundle exec jekyll serve --incremental --config _config.yml 结合多个配置文件在本地测试中，有时需要覆盖配置选项。我的网站的 当前 \_config.yml 有以下设置：# Jekyll Configuration # Site Settings url: "https://notes.ayushsharma.in" website_url: "https://notes.ayushsharma.in/" title: ayush sharma's notes ☕ + ? + ?️ email: ayush@ayushsharma.in images-path: /static/images/ videos-path: /static/videos/ js-path: /static/js/ baseurl: "" # the subpath of your site, e.g. /blog 由于本地的 jekyll serve URL 是 http://localhost:4000，上面定义的 URL 就不能用了。我可以创建一个 _config.yml 的副本 _config-local.yml 并替换所有的值。但还有一个更简单的选择。Jekyll 允许指定多个配置文件，后面的声明覆盖前面的声明。这意味着我可以用以下代码定义一个新的 _config-local.yml：url:"" 然后我可以把上述文件和我的主 _config.yml 结合起来，像这样：bundle exec jekyll serve --incremental --config _config.yml,_config-local.yml 通过合并这两个文件，这个 jekyll serve 的 url 的最终值将是空白。这就把我网站中定义的所有 URL 变成了相对的 URL，并使它们在我的本地笔记本电脑上工作。C结合动态配置文件一个简单的例子，假设你想在你的网站上显示当前日期。它的 bash 命令是：> date '+%A, %d %B %Y' Saturday, 16 October 2021 我知道我也可以 使用 Jekyll 的 \_config.yml 的自定义内容。我将上述日期输出到一个新的 Jekyll 配置文件中。my_date=`date '+%A, %d %B %Y'`; echo 'my_date: "'$my_date'"' > _config-data.yml 现在 _config-data.yml 包含：my_date: "Saturday, 16 October 2021" 我可以把我的新配置文件和其他文件结合起来，在我的网站上使用 my_date 变量。bundle exec jekyll serve --incremental --config _config.yml,_config-local.yml,_config-data.yml 在运行上述命令时，{{ site.my_date }} 输出其配置的值。结论上面的例子很简单，但可能性是无穷的。Bash、Python 和其他编程语言可以动态地生成 Jekyll 配置文件。然后我可以在 build 或 serve 过程中结合这些文件。对于 findmymastodon.com，我使用 Python 来获取 Mastodon 的用户统计数据。然后我把这些写进一个新的 \_config-data.yml 文件（目前是手动）。最后，主页和其他页面从配置文件中显示这些。这样，我就可以利用一个动态的后台，并且仍然保持我所喜欢的所有静态网站的优点。我希望这能为你自己的静态网站激发一些想法。Jamstack 对于静态网站是很好的，但你可以避免为动态内容创建整个 API 后台。为什么不使用一个构建作业来创建带有更新内容的配置文件呢？这可能不适合每一种情况，但少一个 API 意味着更少的基础设施移动部件。我希望这能在你的下一个静态网站项目中给你一些帮助。继续阅读，并祝你编码愉快。这篇文章最初发布在 作者的网站 上，并经授权转载。via: https://opensource.com/article/21/11/jekyll-config-files作者：Ayush Sharma 选题：lujun9972 译者：geekpi 校对：wxy本文由 LCTT 原创编译，Linux中国 荣誉推出
让你的网站根据用户选择的主题进行调整的能力是一个很棒的辅助功能。你可能已经熟悉 媒体查询 media query 了。它们被广泛地用于使网站具有响应性。width 和 height 属性包含视区的尺寸。然后，你可以使用 CSS 在不同的尺寸下呈现不同的布局。prefers-color-scheme 媒体查询 的工作方式与此相同。用户可以将他们的操作系统配置为使用浅色或深色主题。prefers-color-scheme 包含这个值。该值是 light 或 dark ，尽管 W3C 规范指出它可能支持未来的值，如 sepia。我为这两种模式指定不同的 CSS 变量值，让用户的操作系统来决定。prefers-color-scheme 媒体查询prefers-color-scheme 媒体查询的两种变化是:/* Light mode */ @media (prefers-color-scheme: light) { :root { --body-bg: #FFFFFF; --body-color: #000000; } } /* Dark mode */ @media (prefers-color-scheme: dark) { :root { --body-bg: #000000; --body-color: #FFFFFF; } } 在上面的 CSS 中，--body-bg 和 --body-color 是 CSS 变量。正如你所看到的，它们对两种模式都包含不同的值。在浅色主题中，我设置了一个白色背景和黑色文本。在深色主题中，我设置了黑色背景和白色文本。因为规范说 W3C 可能会引入未来的值，所以把这个 CSS 转换为默认值是有意义的。/* Light mode */ :root { --body-bg: #FFFFFF; --body-color: #000000; } /* Dark mode */ @media (prefers-color-scheme: dark) { :root { --body-bg: #000000; --body-color: #FFFFFF; } } 在上面的代码中，我默认定义了一个浅色主题，如果媒体查询是 dark，则将其转换为深色主题。这样一来，以后任何添加到媒体查询的值都会默认设置为浅色主题。使用 CSS 变量现在我为不同的主题设置了不同的值，我需要实际使用它们来设计页面。body { background: var(--body-bg); color: var(--body-color); } var() 语法 是 CSS 使用变量的方式。在上面的代码中，我是说把 background 设置为 --body-bg 的值，把 color 设置为 --body-color 的值。注意，这些变量的值来自媒体查询。这意味着背景和前景的颜色是根据操作系统的设置而改变的！这就是媒体查询的真正能力。提供一个从操作系统到网页的一致的用户体验。如果你进入 findmymastodon.com，并切换你的操作系统的主题，你会看到从一个主题到另一个主题的过渡。CSS 工作组 网站也使用同样的媒体查询。改变你的操作系统主题，网站就会切换主题来进行调整。结论请注意，使用 prefers-color-scheme 与使用普通的编程语言没有什么不同。我定义了一些变量，这些变量的值根据一些逻辑而改变。而这些变量然后被用于进一步的操作。让你的网站根据用户选择的主题进行调整的能力是一个很棒的辅助功能。而且，为了用户的利益，它进一步模糊了桌面和网络之间的界限。最新的浏览器版本 支持 prefers-color-scheme，所以你今天就可以开始实验了。编码愉快。这篇文章最初发表在 作者的网站 上，经许可后重新发表。via: https://opensource.com/article/21/10/dark-themes-websites作者：Ayush Sharma 选题：lujun9972 译者：geekpi 校对：wxy本文由 LCTT 原创编译，Linux中国 荣誉推出
使用此 Linux 命令保持日志文件更新。日志非常适合找出应用程序在做什么或对可能的问题进行故障排除。几乎我们处理的每个应用程序都会生成日志，我们希望我们自己开发的应用程序也生成日志。日志越详细，我们拥有的信息就越多。但放任不管，日志可能会增长到无法管理的大小，反过来，它们可能会成为它们自己的问题。因此，最好将它们进行裁剪，保留我们需要的那些，并将其余的归档。基本功能logrotate 实用程序在管理日志方面非常出色。它可以轮转日志、压缩日志、通过电子邮件发送日志、删除日志、归档日志，并在你需要时开始记录最新的。运行 logrotate 非常简单——只需要运行 logrotate -vs state-file config-file。在上面的命令中，v 选项开启详细模式，s 指定一个状态文件，最后的 config-file 是配置文件，你可以指定需要做什么。实战演练让我们看看在我们的系统上静默运行的 logrotate 配置，它管理我们在 /var/log 目录中找到的大量日志。查看该目录中的当前文件。你是否看到很多 *.[number].gz 文件？这就是 logrotate 正在做的。你可以在 /etc/logrotate.d/rsyslog 下找到此配置文件。我的配置文件如下：/var/log/syslog { rotate 7 daily missingok notifempty delaycompress compress postrotate reload rsyslog > /dev/null 2>&1 || true endscript } /var/log/mail.info /var/log/mail.warn /var/log/mail.err /var/log/mail.log /var/log/daemon.log /var/log/kern.log /var/log/auth.log /var/log/user.log /var/log/lpr.log /var/log/cron.log /var/log/debug /var/log/messages { rotate 4 weekly missingok notifempty compress delaycompress sharedscripts postrotate reload rsyslog > /dev/null 2>&1 || true endscript } 该文件首先定义了轮转 /var/log/syslog 文件的说明，这些说明包含在后面的花括号中。以下是它们的含义：rotate 7: 保留最近 7 次轮转的日志。然后开始删除超出的。daily: 每天轮转日志，与 rotate 7 一起使用，这意味着日志将保留过去 7 天。其它选项是每周、每月、每年。还有一个大小参数，如果日志文件的大小增加超过指定的限制（例如，大小 10k、大小 10M、大小 10G 等），则将轮转日志文件。如果未指定任何内容，日志将在运行 logrotate 时轮转。你甚至可以在 cron 中运行 logrotate 以便在更具体的时间间隔内使用它。missingok: 如果日志文件缺失也没关系。不要惊慌。notifempty: 日志文件为空时不轮转。compress: 开启压缩，使用 nocompress 关闭它。delaycompress: 如果压缩已打开，则将压缩延迟到下一次轮转。这允许至少存在一个轮转但未压缩的文件。如果你希望昨天的日志保持未压缩以便进行故障排除，那么此配置会很有用。如果某些程序在重新启动/重新加载之前可能仍然写入旧文件，这也很有帮助，例如 Apache。postrotate/endscript: 轮转后运行此部分中的脚本。有助于做清理工作。还有一个 prerotate/endscript 用于在轮转开始之前执行操作。你能弄清楚下一节对上面配置中提到的所有文件做了什么吗？第二节中唯一多出的参数是 sharedscripts，它告诉 logrotate 在所有日志轮转完成之前不要运行 postrotate/endscript 中的部分。它可以防止脚本在每一次轮转时执行，只在最后一次轮转完成时执行。看点新的东西我使用下面的配置来处理我系统上的 Nginx 的访问和错误日志。/var/log/nginx/access.log /var/log/nginx/error.log { size 1 missingok notifempty create 544 www-data adm rotate 30 compress delaycompress dateext dateformat -%Y-%m-%d-%s sharedscripts extension .log postrotate service nginx reload endscript } 上面的脚本可以使用如下命令运行：logrotate -vs state-file /tmp/logrotate 第一次运行该命令会给出以下输出：reading config file /tmp/logrotate extension is now .log Handling 1 logs rotating pattern: /var/log/nginx/access.log /var/log/nginx/error.log 1 bytes (30 rotations) empty log files are not rotated, old logs are removed considering log /var/log/nginx/access.log log needs rotating considering log /var/log/nginx/error.log log does not need rotating rotating log /var/log/nginx/access.log, log->rotateCount is 30 Converted ' -%Y-%m-%d-%s' -> '-%Y-%m-%d-%s' dateext suffix '-2021-08-27-1485508250' glob pattern '-[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]' glob finding logs to compress failed glob finding old rotated logs failed renaming /var/log/nginx/access.log to /var/log/nginx/access-2021-08-27-1485508250.log creating new /var/log/nginx/access.log mode = 0544 uid = 33 gid = 4 running postrotate script * Reloading nginx configuration nginx 第二次运行它：reading config file /tmp/logrotate extension is now .log Handling 1 logs rotating pattern: /var/log/nginx/access.log /var/log/nginx/error.log 1 bytes (30 rotations) empty log files are not rotated, old logs are removed considering log /var/log/nginx/access.log log needs rotating considering log /var/log/nginx/error.log log does not need rotating rotating log /var/log/nginx/access.log, log->rotateCount is 30 Converted ' -%Y-%m-%d-%s' -> '-%Y-%m-%d-%s' dateext suffix '-2021-08-27-1485508280' glob pattern '-[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]' compressing log with: /bin/gzip renaming /var/log/nginx/access.log to /var/log/nginx/access-2021-08-27-1485508280.log creating new /var/log/nginx/access.log mode = 0544 uid = 33 gid = 4 running postrotate script * Reloading nginx configuration nginx 第三次运行它：reading config file /tmp/logrotate extension is now .log Handling 1 logs rotating pattern: /var/log/nginx/access.log /var/log/nginx/error.log 1 bytes (30 rotations) empty log files are not rotated, old logs are removed considering log /var/log/nginx/access.log log needs rotating considering log /var/log/nginx/error.log log does not need rotating rotating log /var/log/nginx/access.log, log->rotateCount is 30 Converted ' -%Y-%m-%d-%s' -> '-%Y-%m-%d-%s' dateext suffix '-2021-08-27-1485508316' glob pattern '-[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]' compressing log with: /bin/gzip renaming /var/log/nginx/access.log to /var/log/nginx/access-2021-08-27-1485508316.log creating new /var/log/nginx/access.log mode = 0544 uid = 33 gid = 4 running postrotate script * Reloading nginx configuration nginx 状态文件的内容如下所示：logrotate state -- version 2 "/var/log/nginx/error.log" 2021-08-27-9:0:0 "/var/log/nginx/access.log" 2021-08-27-9:11:56 下载 Linux logrotate 备忘单本文首发于作者个人博客，经授权改编。via: https://opensource.com/article/21/10/linux-logrotate作者：Ayush Sharma 选题：lujun9972 译者：perfiffer 校对：wxy本文由 LCTT 原创编译，Linux中国 荣誉推出