2019年1月

我们必须经常更新我们的系统以减少宕机时间和问题。每月给系统打一次补丁,60 天一次或者最多 90 天一次,这是 Linux 管理员的例行任务之一。这是忙碌的工作计划,我们不能在不到一个月内做到这一点,因为它涉及到多种活动和环境。

基本上,基础设施会一同提供测试、开发、 QA 环境(即各个分段和产品)。

最初,我们会在测试环境中部署补丁,相应的团队将监视系统一周,然后他们将给出一份或好或坏的状态的报告。如果成功的话,我们将会在其他环境中继续测试,若正常运行,那么最后我们会给生产服务器打上补丁。

许多组织会对整个系统打上补丁,我的意思是全系统更新,对于典型基础设施这是一种常规修补计划。

某些基础设施中可能只有生产环境,因此,我们不应该做全系统更新,而是应该使用安全修补程序来使系统更加稳定和安全。

由于 Arch Linux 及其衍生的发行版属于滚动更新版本,因此可以认为它们始终是最新的,因为它使用上游软件包的最新版本。

在某些情况下,如果要单独更新安全修补程序,则必须使用 arch-audit 工具来标识和修复安全修补程序。

漏洞是什么?

漏洞是软件程序或硬件组件(固件)中的安全漏洞。这是一个可以让它容易受到攻击的缺陷。

为了缓解这种情况,我们需要相应地修补漏洞,就像应用程序/硬件一样,它可能是代码更改或配置更改或参数更改。

Arch-Audit 工具是什么?

Arch-audit 是一个类似于 Arch Linux 的 pkg-audit 工具。它使用了令人称赞的 Arch 安全小组收集的数据。它不会扫描以发现系统中易受攻击的包(就像 yum –security check-update & yum updateinfo 一样列出可用的软件包),它只需解析 https://security.archlinux.org/ 页面并在终端中显示结果,因此,它将显示准确的数据。(LCTT 译注:此处原作者叙述不清晰。该功能虽然不会像病毒扫描软件一样扫描系统上的文件,但是会读取已安装的软件列表,并据此查询上述网址列出风险报告。)

Arch 安全小组是一群以跟踪 Arch Linux 软件包的安全问题为目的的志愿者。所有问题都在 Arch 安全追踪者的监视下。

该小组以前被称为 Arch CVE 监测小组,Arch 安全小组的使命是为提高 Arch Linux 的安全性做出贡献。

如何在 Arch Linux 上安装 Arch-Audit 工具

Arch-audit 工具已经存在社区的仓库中,所以你可以使用 Pacman 包管理器来安装它。

$ sudo pacman -S arch-audit

运行 arch-audit 工具以查找在基于 Arch 的发行版本上的存在缺陷的包。

$ arch-audit
Package cairo is affected by CVE-2017-7475. Low risk!
Package exiv2 is affected by CVE-2017-11592, CVE-2017-11591, CVE-2017-11553, CVE-2017-17725, CVE-2017-17724, CVE-2017-17723, CVE-2017-17722. Medium risk!
Package libtiff is affected by CVE-2018-18661, CVE-2018-18557, CVE-2017-9935, CVE-2017-11613. High risk!. Update to 4.0.10-1!
Package openssl is affected by CVE-2018-0735, CVE-2018-0734. Low risk!
Package openssl-1.0 is affected by CVE-2018-5407, CVE-2018-0734. Low risk!
Package patch is affected by CVE-2018-6952, CVE-2018-1000156. High risk!. Update to 2.7.6-7!
Package pcre is affected by CVE-2017-11164. Low risk!
Package systemd is affected by CVE-2018-6954, CVE-2018-15688, CVE-2018-15687, CVE-2018-15686. Critical risk!. Update to 239.300-1!
Package unzip is affected by CVE-2018-1000035. Medium risk!
Package webkit2gtk is affected by CVE-2018-4372. Critical risk!. Update to 2.22.4-1!

上述结果显示了系统的脆弱性风险状况,比如:低、中和严重三种情况。

若要仅显示易受攻击的包及其版本,请执行以下操作。

$ arch-audit -q
cairo
exiv2
libtiff>=4.0.10-1
openssl
openssl-1.0
patch>=2.7.6-7
pcre
systemd>=239.300-1
unzip
webkit2gtk>=2.22.4-1

仅显示已修复的包。

$ arch-audit --upgradable --quiet
libtiff>=4.0.10-1
patch>=2.7.6-7
systemd>=239.300-1
webkit2gtk>=2.22.4-1

为了交叉检查上述结果,我将测试在 https://www.archlinux.org/packages/ 列出的一个包以确认漏洞是否仍处于开放状态或已修复。是的,它已经被修复了,并于昨天在社区仓库中发布了更新后的包。

仅打印包名称和其相关的 CVE。

$ arch-audit -uf "%n|%c"
libtiff|CVE-2018-18661,CVE-2018-18557,CVE-2017-9935,CVE-2017-11613
patch|CVE-2018-6952,CVE-2018-1000156
systemd|CVE-2018-6954,CVE-2018-15688,CVE-2018-15687,CVE-2018-15686
webkit2gtk|CVE-2018-4372

via: https://www.2daygeek.com/arch-audit-a-tool-to-check-vulnerable-packages-in-arch-linux/

作者:Prakash Subramanian 选题:lujun9972 译者:Auk7F7 校对:wxy

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

专家回答了敏捷实践如何帮助团队更有效的 6 个常见问题。

“有问题么?”

你可能听过演讲者在演讲结束的时候提出这个问题。这是演讲中最重要的部分 —— 毕竟,你不仅仅是听讲座, 而是参加讨论和社群交流。

最近,我有机会听到我的同伴 Red Hatters 给当地一所大学的一群技术型学生做一个名为 “敏捷实践” 的讲座。讲座中有软件工程师 Tomas Tomecek 和敏捷开发的从业者 Fernando Colleone 、Pavel Najman 合作解释了敏捷开发方法的基础,并展示最佳实践在日常活动中的应用。

知道了学生们参加这个课程是为了了解什么是敏捷实践以及如何将其应用于项目,我想知道学生们的问题会与我作为敏捷从业者在 Red Hat 每天听到的问题相比有什么不同。结果学生的疑问和我的同事们如出一辙。这些问题都直指敏捷实践的核心。

1、完美的团队规模是多大?

学生们想知道一个小团队和一个大团队的规模是多少。这个问题与任何曾经合作过做项目的人都是相关的。根据 Tomas 作为技术领导的经验,12 个人从事的项目被认为是一个大型团队。现实中,团队规模通常与生产力没有直接关系。在有些时候,在一个地方或同一个时区的小团队也许会比一个成员分布在满世界的大团队更具有生产力。最终,该讲座建议理想的团队大小大概是 5 个人(正如 scrum 开发方法的 7,+-2)。

2、团队会面临哪些实际挑战?

演讲者比较了由本地团队组成的项目(团队成员都是一个办公室的,或者相邻近的人)与分散型的团队(位于不同时区)。当项目需要团队成员之间密切合作时,工程师更喜欢本地的团队,因为时间差异造成的延迟可能会破坏软件开发的“流”。同时,分散型团队可以将可能不适用与当地项目但适用于某些开发用例的技能集合在一起。此外,还有各种最佳方法可用于改进分散型团队中的合作方式。

3、整理堆积的工作需要多少时间?

因为这是一个对于新学习敏捷的学生的介绍性质的演讲,演讲者着重把 ScrumKanban 作为介绍敏捷开发的方法。他们使用 Scrum 框架来作为说明软件编写的方法,并且用 Kanban 作为工作规划和沟通的系统。关于需要多少时间来整理项目堆积的工作,演讲者解释说并没有固定的准则,相对的,实践出真知:在开发的早期阶段,当一个崭新的项目 —— 特别如果团队里有新人 —— 每周可能会花费数个小时在整理工作上。随着时间推移和不断地练习,会越来越高效。

4、产品负责人是否是必要的? 他们扮演什么样的角色?

产品负责人会帮助团队更方便的拓展,然而,职位名称并不重要,重要的是你的团队中有人能够传递用户的意愿。在许多团队中,特别是在大型团队中从事单个任务的团队,首席工程师就可以担任产品负责人。

5、建议使用哪些敏捷开发的工具?使用 Scrum 或 Kanban 做敏捷开发的时候必须用特定的软件么?

尽管使用一些专业软件例如 Jira 或 Trello 会很有帮助,特别是在与大量从事大型企业项目的工作者合作时,但它们不是必需的。Scrum 和 Kanban 可以使用像纸卡这样简单的工具完成。关键是在团队中要有一个清晰的信息来源和紧密的交流。推荐两个优秀的 kanban 开源工具 TaigaWekan。更多信息请查看 Trello 的 5 个开源替代品敏捷团队的最好的 7 个开源项目管理工具

6、学生如何在学校项目中使用敏捷开发技术?

演讲者鼓励学生使用 kanban 在项目结束前使用可视化和概述要完成的任务。关键是要创建一个公共板块,这样整个团队就可以看到项目的状态。通过使用 kanban 或者类似的高度可视化的策略,学生不会在项目后期才发现个别成员没有跟上进度。

Scrum 实践比如 sprints 和 daily standups 也是确认每个人都在进步以及项目的各个部分最终会一起发挥作用的绝佳方法。定期检查和信息共享也至关重要。更多关于 Scrum 的信息,访问 什么是 scrum?

牢记 Kanban 和 Scrum 只是敏捷开发中众多框架和工具中的两个而已。它们可能不是应对每一种情况的最佳方法。


via: https://opensource.com/article/18/3/agile-mindset

作者:Dominika Bula 译者:lixinyuxx 校对:wxy

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

Pelican 是那些想要自我托管简单网站或博客的 Python 用户的绝佳选择。

如果你想创建一个自定义网站或博客,有很多选择。许多提供商可以托管你的网站并为你完成大部分工作。(WordPress 是一个非常受欢迎的选项。)但是使用托管方式,你会失去一些灵活性。作为一名软件开发人员,我更喜欢管理我自己的服务器,并在我的网站如何运行方面保持更多的自由。

然而,管理 Web 服务器需要大量的工作。安装它并获得一个简单的应用程序来提供内容是非常容易的。但是,维护安全补丁和更新是非常耗时得。如果你只想提供静态网页,那么拥有一个 Web 服务器和一系列应用程序可能会得不偿失。手动创建 HTML 页面也不是一个好选择。

这是静态网站生成器的用武之地。这些应用程序使用模板来创建所需的静态页面,并将它们与关联的元数据交叉链接。(例如,所有显示的页面都带有公共标签或关键词。)静态网站生成器可以帮助你使用导航区域、页眉和页脚等元素创建一个具有公共外观的网站。

我使用 Pyhton 已经很多年了,所以,当我第一次开始寻找生成静态 HTML 页面的东西时,我想要用 Python 编写的东西。主要原因是我经常想要了解应用程序如何工作的内部细节,而使用一种我已经了解的语言使这一点更容易。(如果这对你不重要或者你不使用 Python,那么还有一些其他很棒的静态网站生成器,它们使用 Ruby、JavaScript 和其它语言。)

我决定试试 Pelican。它是一个用 Python 编写的常用静态网站生成器。它支持 reStructuredText(LCTT 译注:这是一种用于文本数据的文件格式,主要用于 Python 社区的技术文档),并且也支持 Markdown,这需要通过安装必需的包来完成。所有任务都是通过命令行界面(CLI)工具执行的,这使得熟悉命令行的任何人都可以轻松完成。它简单的 quickstart CLI 工具使得创建一个网站非常容易。

在本文中,我将介绍如何安装 Pelican 4,添加一篇文章以及更改默认主题。(注意:我是在 MacOS 上开发的,使用其它 Unix/Linux 实验结果都将相同,但我没有 Windows 主机可以测试。)

安装和配置

第一步是创建一个虚拟环境,在虚拟环境中安装 Pelican。

$ mkdir test-site
$ cd test-site
$ python3 -m venv venv
$ ./venv/bin/pip install --upgrade pip
...
Successfully installed pip-18.1
$ ./venv/bin/pip install pelican
Collecting pelican
...
Successfully installed MarkupSafe-1.1.0 blinker-1.4 docutils-0.14 feedgenerator-1.9 jinja2-2.10 pelican-4.0.1 pygments-2.3.1 python-dateutil-2.7.5 pytz-2018.7 six-1.12.0 unidecode-1.0.23

Pelican 的 quickstart CLI 工具将创建基本布局和一些文件来帮助你开始,运行 pelican-quickstart 命令。为了简单起见,我输入了网站标题作者的名字,并对 URL 前缀和文章分页选择了 “N”。(对于其它选项,我使用了默认值。)稍后在配置文件中更改这些设置非常容易。

$ ./venv/bin/pelicanquickstart
Welcome to pelicanquickstart v4.0.1.

This script will help you create a new Pelican-based website.

Please answer the following questions so this script can generate the files needed by Pelican.

> Where do you want to create your new web site? [.]
> What will be the title of this web site? My Test Blog
> Who will be the author of this web site? Craig
> What will be the default language of this web site? [en]
> Do you want to specify a URL prefix? e.g., https://example.com (Y/n) n
> Do you want to enable article pagination? (Y/n) n
> What is your time zone? [Europe/Paris]
> Do you want to generate a tasks.py/Makefile to automate generation and publishing? (Y/n)
> Do you want to upload your website using FTP? (y/N)
> Do you want to upload your website using SSH? (y/N)
> Do you want to upload your website using Dropbox? (y/N)
> Do you want to upload your website using S3? (y/N)
> Do you want to upload your website using Rackspace Cloud Files? (y/N)
> Do you want to upload your website using GitHub Pages? (y/N)
Done. Your new project is available at /Users/craig/tmp/pelican/test-site

你需要启动的所有文件都准备好了。

quickstart 默认为欧洲/巴黎时区,所以在继续之前更改一下。在你喜欢的文本编辑器中打开 pelicanconf.py 文件,寻找 TIMEZONE 变量。

TIMEZONE = 'Europe/Paris'

将其改为 UTC

TIMEZONE = 'UTC'

要更新公共设置,在 pelicanconf.py 中查找 SOCIAL 变量。

SOCIAL = (('You can add links in your config file', '#'),
          ('Another social link', '#'),)

我将添加一个我的 Twitter 账户链接。

SOCIAL = (('Twitter (#craigs55)', 'https://twitter.com/craigs55'),)

注意末尾的逗号,它很重要。这个逗号将帮助 Python 识别变量实际上是一个集合。确保你没有删除这个逗号。

现在你已经有了网站的基本知识。quickstart 创建了一个包含许多目标的 Makefile。将 devserver 传给 make 命令将在你的计算机上启动一个开发服务器,以便你可以预览所有内容。Makefile 中使用的 CLI 命令假定放在 PATH 搜索路径中,因此你需要首先激活该虚拟环境。

$ source ./venv/bin/activate
$ make devserver
pelican -lr /Users/craig/tmp/pelican/test-site/content o
/Users/craig/tmp/pelican/test-site/output -s /Users/craig/tmp/pelican/test-site/pelicanconf.py

-> Modified: theme, settings. regenerating...
WARNING: No valid files found in content for the active readers:
   | BaseReader (static)
   | HTMLReader (htm, html)
   | RstReader (rst)
Done: Processed 0 articles, 0 drafts, 0 pages, 0 hidden pages and 0 draft pages in 0.18 seconds.

在你最喜欢的浏览器中打开 http://localhost:8000 来查看你的简单测试博客。

你可以在右侧看到 Twitter 链接,左侧有 Pelican、Python 和 Jinja 的一些链接。(Jinja 是 Pelican 可以使用的一种很棒的模板语言。你可以在 Jinja 的文档中了解更多相关信息。)

添加内容

现在你又了一个基本的网站,试着添加一些内容。首先,将名为 welcome.rst 的文件添加到网站的 content 目录中。在你喜欢的文本编辑器中,使用以下文本创建一个文件:

$ pwd
/Users/craig/tmp/pelican/test-site
$ cat content/welcome.rst

Welcome to my blog!
###################

:date: 20181216 08:30
:tags: welcome
:category: Intro
:slug: welcome
:author: Craig
:summary: Welcome document

Welcome to my blog.
This is a short page just to show how to put up a static page.

Pelican 会自动解析元数据行,包括日期、标签等。

编写完文件后,开发服务器应该输出以下内容:

-> Modified: content. regenerating...
Done: Processed 1 article, 0 drafts, 0 pages, 0 hidden pages and 0 draft pages in 0.10 seconds.

在浏览器中刷新你的测试网站来查看更改。

元数据(例如日期和标签)会自动添加到页面中。此外,Pelican 会自动检测到 intro 栏目,并将该部分添加到顶部导航中。

更改主题

使用像 Pelican 这样流行的开源软件的好处之一是,非常多的用户会做出更改并将其贡献给项目。许多都是以主题形式贡献的。

网站的主题会设置颜色、布局选项等。尝试一个新主题非常容易,你可以在 Pelican 主题中预览其中的许多内容。

首先,克隆 GitHub 仓库:

$ cd ..
$ git clone --recursive https://github.com/getpelican/pelicanthemes
Cloning into 'pelicanthemes'...

我喜欢蓝色,那么试试 blueidea

编辑 pelicanconf.py,添加以下行:

THEME = '/Users/craig/tmp/pelican/pelican-themes/blueidea/'

开发服务器将重新生成你的输出。在浏览器中刷新网页来查看新主题。

主题控制布局的方方面面。例如,在默认主题中,你可以看到文章旁边带有元标记的栏目(Intro),但这个栏目并未显示在 blueidea 主题中。

其他考虑因素

本文是对 Pelican 的快速介绍,所以我并没有涉及一些重要的主题。

首先,我对迁移到静态站点犹豫不决的一个原因是它无法对文章评论。幸运的是,有一些第三方服务商将为你提供评论功能。我目前正在关注的是 Disqus

接下来,上面的所有内容都是在我的本地机器上完成的。如果我希望其他人查看我的网站,我将不得不将预先生成的 HTML 文件上传到某个地方。如果你查看 pelican-quickstart 输出,你将看到使用 FTP、 SSH、S3 甚至 GitHub 页面的选项,每个选项都有其优点和缺点。但是,如果我必须选择一个,那么我可能会选择发布到 GitHub 页面。

Pelican 还有许多其他功能,我每天都在学习它。如果你想自托管一个网站或博客,内容简单并且是静态内容,同时你想使用 Python,那么 Pelican 是一个很好的选择。它有一个活跃的用户社区,可以修复 bug,添加特性,而且还会创建新的和有趣的主题。试试看吧!


via: https://opensource.com/article/19/1/getting-started-pelican

作者:Craig Sebenik 选题:lujun9972 译者:MjSeven 校对:wxy

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

在本系列教程中所构建的网络引导服务器有一个很重要的限制,那就是所提供的操作系统镜像是只读的。一些使用场景或许要求终端用户能够修改操作系统镜像。例如,一些教师或许希望学生能够安装和配置一些像 MariaDB 和 Node.js 这样的包来做为他们课程练习的一部分。

可写镜像的另外的好处是,终端用户“私人定制”的操作系统,在下次不同的工作站上使用时能够“跟着”他们。

修改 Bootmenu 应用程序以使用 HTTPS

为 bootmenu 应用程序创建一个自签名的证书:

$ sudo -i
# MY_NAME=$(</etc/hostname)
# MY_TLSD=/opt/bootmenu/tls
# mkdir $MY_TLSD
# openssl req -newkey rsa:2048 -nodes -keyout $MY_TLSD/$MY_NAME.key -x509 -days 3650 -out $MY_TLSD/$MY_NAME.pem

验证你的证书的值。确保 Subject 行中 CN 的值与你的 iPXE 客户端连接你的网络引导服务器所使用的 DNS 名字是相匹配的:

# openssl x509 -text -noout -in $MY_TLSD/$MY_NAME.pem

接下来,更新 bootmenu 应用程序去监听 HTTPS 端口和新创建的证书及密钥:

# sed -i "s#listen => .*#listen => ['https://$MY_NAME:443?cert=$MY_TLSD/$MY_NAME.pem\&key=$MY_TLSD/$MY_NAME.key\&ciphers=AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA'],#" /opt/bootmenu/bootmenu.conf

注意 iPXE 当前支持的 加密算法是有限制的。

GnuTLS 要求 “CAPDACREAD\_SEARCH” 能力,因此将它添加到 bootmenu 应用程序的 systemd 服务:

# sed -i '/^AmbientCapabilities=/ s/$/ CAP_DAC_READ_SEARCH/' /etc/systemd/system/bootmenu.service
# sed -i 's/Serves iPXE Menus over HTTP/Serves iPXE Menus over HTTPS/' /etc/systemd/system/bootmenu.service
# systemctl daemon-reload

现在,在防火墙中为 bootmenu 服务添加一个例外规则并重启动该服务:

# MY_SUBNET=192.0.2.0
# MY_PREFIX=24
# firewall-cmd --add-rich-rule="rule family='ipv4' source address='$MY_SUBNET/$MY_PREFIX' service name='https' accept"
# firewall-cmd --runtime-to-permanent
# systemctl restart bootmenu.service

使用 wget 去验证是否工作正常:

$ MY_NAME=server-01.example.edu
$ MY_TLSD=/opt/bootmenu/tls
$ wget -q --ca-certificate=$MY_TLSD/$MY_NAME.pem -O - https://$MY_NAME/menu

添加 HTTPS 到 iPXE

更新 init.ipxe 去使用 HTTPS。接着使用选项重新编译 ipxe 引导加载器,以便它包含和信任你为 bootmenu 应用程序创建的自签名证书:

$ echo '#define DOWNLOAD_PROTO_HTTPS' >> $HOME/ipxe/src/config/local/general.h
$ sed -i 's/^chain http:/chain https:/' $HOME/ipxe/init.ipxe
$ cp $MY_TLSD/$MY_NAME.pem $HOME/ipxe
$ cd $HOME/ipxe/src
$ make clean
$ make bin-x86_64-efi/ipxe.efi EMBED=../init.ipxe CERT="../$MY_NAME.pem" TRUST="../$MY_NAME.pem"

你现在可以将启用了 HTTPS 的 iPXE 引导加载器复制到你的客户端上,并测试它能否正常工作:

$ cp $HOME/ipxe/src/bin-x86_64-efi/ipxe.efi $HOME/esp/efi/boot/bootx64.efi

添加用户验证到 Mojolicious 中

为 bootmenu 应用程序创建一个 PAM 服务定义:

# dnf install -y pam_krb5
# echo 'auth required pam_krb5.so' > /etc/pam.d/bootmenu

添加一个库到 bootmenu 应用程序中,它使用 Authen-PAM 的 Perl 模块去执行用户验证:

# dnf install -y perl-Authen-PAM;
# MY_MOJO=/opt/bootmenu
# mkdir $MY_MOJO/lib
# cat << 'END' > $MY_MOJO/lib/PAM.pm
package PAM;

use Authen::PAM;

sub auth {
   my $success = 0;

   my $username = shift;
   my $password = shift;

   my $callback = sub {
      my @res;
      while (@_) {
         my $code = shift;
         my $msg = shift;
         my $ans = "";
   
         $ans = $username if ($code == PAM_PROMPT_ECHO_ON());
         $ans = $password if ($code == PAM_PROMPT_ECHO_OFF());
   
         push @res, (PAM_SUCCESS(), $ans);
      }
      push @res, PAM_SUCCESS();

      return @res;
   };

   my $pamh = new Authen::PAM('bootmenu', $username, $callback);

   {
      last unless ref $pamh;
      last unless $pamh->pam_authenticate() == PAM_SUCCESS;
      $success = 1;
   }

   return $success;
}

return 1;
END

以上的代码是一字不差是从 Authen::PAM::FAQ 的 man 页面中复制来的。

重定义 bootmenu 应用程序,以使它仅当提供了有效的用户名和密码之后返回一个网络引导模板:

# cat << 'END' > $MY_MOJO/bootmenu.pl
#!/usr/bin/env perl

use lib 'lib';

use PAM;
use Mojolicious::Lite;
use Mojolicious::Plugins;
use Mojo::Util ('url_unescape');

plugin 'Config';

get '/menu';
get '/boot' => sub {
   my $c = shift;

   my $instance = $c->param('instance');
   my $username = $c->param('username');
   my $password = $c->param('password');

   my $template = 'menu';

   {
      last unless $instance =~ /^fc[[:digit:]]{2}$/;
      last unless $username =~ /^[[:alnum:]]+$/;
      last unless PAM::auth($username, url_unescape($password));
      $template = $instance;
   }

   return $c->render(template => $template);
};

app->start;
END

bootmenu 应用程序现在查找 lib 命令去找到相应的 WorkingDirectory。但是,默认情况下,对于 systemd 单元它的工作目录设置为服务器的 root 目录。因此,你必须更新 systemd 单元去设置 WorkingDirectory 为 bootmenu 应用程序的根目录:

# sed -i "/^RuntimeDirectory=/ a WorkingDirectory=$MY_MOJO" /etc/systemd/system/bootmenu.service
# systemctl daemon-reload

更新模块去使用重定义后的 bootmenu 应用程序:

# cd $MY_MOJO/templates
# MY_BOOTMENU_SERVER=$(</etc/hostname)
# MY_FEDORA_RELEASES="28 29"
# for i in $MY_FEDORA_RELEASES; do echo '#!ipxe' > fc$i.html.ep; grep "^kernel\|initrd" menu.html.ep | grep "fc$i" >> fc$i.html.ep; echo "boot || chain https://$MY_BOOTMENU_SERVER/menu" >> fc$i.html.ep; sed -i "/^:f$i$/,/^boot /c :f$i\nlogin\nchain https://$MY_BOOTMENU_SERVER/boot?instance=fc$i\&username=\${username}\&password=\${password:uristring} || goto failed" menu.html.ep; done

上面的最后的命令将生成类似下面的三个文件:

menu.html.ep

#!ipxe

set timeout 5000

:menu
menu iPXE Boot Menu
item --key 1 lcl 1. Microsoft Windows 10
item --key 2 f29 2. RedHat Fedora 29
item --key 3 f28 3. RedHat Fedora 28
choose --timeout ${timeout} --default lcl selected || goto shell
set timeout 0
goto ${selected}

:failed
echo boot failed, dropping to shell...
goto shell

:shell
echo type 'exit' to get the back to the menu
set timeout 0
shell
goto menu

:lcl
exit

:f29
login
chain https://server-01.example.edu/boot?instance=fc29&username=${username}&password=${password:uristring} || goto failed

:f28
login
chain https://server-01.example.edu/boot?instance=fc28&username=${username}&password=${password:uristring} || goto failed

fc29.html.ep

#!ipxe
kernel --name kernel.efi ${prefix}/vmlinuz-4.19.5-300.fc29.x86_64 initrd=initrd.img ro ip=dhcp rd.peerdns=0 nameserver=192.0.2.91 nameserver=192.0.2.92 root=/dev/disk/by-path/ip-192.0.2.158:3260-iscsi-iqn.edu.example.server-01:fc29-lun-1 netroot=iscsi:192.0.2.158::::iqn.edu.example.server-01:fc29 console=tty0 console=ttyS0,115200n8 audit=0 selinux=0 quiet
initrd --name initrd.img ${prefix}/initramfs-4.19.5-300.fc29.x86_64.img
boot || chain https://server-01.example.edu/menu

fc28.html.ep

#!ipxe
kernel --name kernel.efi ${prefix}/vmlinuz-4.19.3-200.fc28.x86_64 initrd=initrd.img ro ip=dhcp rd.peerdns=0 nameserver=192.0.2.91 nameserver=192.0.2.92 root=/dev/disk/by-path/ip-192.0.2.158:3260-iscsi-iqn.edu.example.server-01:fc28-lun-1 netroot=iscsi:192.0.2.158::::iqn.edu.example.server-01:fc28 console=tty0 console=ttyS0,115200n8 audit=0 selinux=0 quiet
initrd --name initrd.img ${prefix}/initramfs-4.19.3-200.fc28.x86_64.img
boot || chain https://server-01.example.edu/menu

现在,重启动 bootmenu 应用程序,并验证用户认证是否正常工作:

# systemctl restart bootmenu.service

使得 iSCSI Target 可写

现在,用户验证通过 iPXE 可以正常工作,在用户连接时,你可以按需在只读镜像的上面创建每用户可写的 overlay 叠加层 。使用一个 写时复制 的叠加层与简单地为每个用户复制原始镜像相比有三个好处:

  1. 副本创建非常快。这样就可以按需创建。
  2. 副本并不增加服务器上的磁盘使用。除了原始镜像之外,仅存储用户写入个人镜像的内容。
  3. 由于每个副本的扇区大多都是服务器的存储器上的相同扇区,在随后的用户访问这些操作系统的副本时,它们可能已经加载到内存中,这样就提升了服务器的性能,因为对内存的访问速度要比磁盘 I/O 快得多。

使用写时复制的一个潜在隐患是,一旦叠加层创建后,叠加层之下的镜像就不能再改变。如果它们改变,所有它们之上的叠加层将出错。因此,叠加层必须被删除并用新的、空白的进行替换。即便只是简单地以读写模式加载的镜像,也可能因为某些文件系统更新导致叠加层出错。

由于这个隐患,如果原始镜像被修改将导致叠加层出错,因此运行下列的命令,将原始镜像标记为不可改变:

# chattr +i </path/to/file>

你可以使用 lsattr </path/to/file> 去查看不可改变标志,并可以使用 chattr -i </path/to/file> 取消设置不可改变标志。在设置了不可改变标志之后,即便是 root 用户或以 root 运行的系统进程也不修改或删除这个文件。

停止 tgtd.service 之后,你就可以改变镜像文件:

# systemctl stop tgtd.service

当仍有连接打开的时候,运行这个命令一般需要一分钟或更长的时间。

现在,移除只读的 iSCSI 出口。然后更新模板中的 readonly-root 配置文件,以使镜像不再是只读的:

# MY_FC=fc29
# rm -f /etc/tgt/conf.d/$MY_FC.conf
# TEMP_MNT=$(mktemp -d)
# mount /$MY_FC.img $TEMP_MNT
# sed -i 's/^READONLY=yes$/READONLY=no/' $TEMP_MNT/etc/sysconfig/readonly-root
# sed -i 's/^Storage=volatile$/#Storage=auto/' $TEMP_MNT/etc/systemd/journald.conf
# umount $TEMP_MNT

将 journald 日志从发送到内存修改回缺省值(如果 /var/log/journal 存在的话记录到磁盘),因为一个用户报告说,他的客户端由于应用程序生成了大量的系统日志而产生内存溢出错误,导致它的客户端被卡住。而将日志记录到磁盘的负面影响是客户端产生了额外的写入流量,这将在你的网络引导服务器上可能增加一些没有必要的 I/O。你应该去决定到底使用哪个选择 —— 记录到内存还是记录到硬盘 —— 哪个更合适取决于你的环境。

因为你的模板镜像在以后不能做任何的更改,因此在它上面设置不可更改标志,然后重启动 tgtd.service:

# chattr +i /$MY_FC.img
# systemctl start tgtd.service

现在,更新 bootmenu 应用程序:

# cat << 'END' > $MY_MOJO/bootmenu.pl
#!/usr/bin/env perl

use lib 'lib';

use PAM;
use Mojolicious::Lite;
use Mojolicious::Plugins;
use Mojo::Util ('url_unescape');

plugin 'Config';

get '/menu';
get '/boot' => sub {
   my $c = shift;

   my $instance = $c->param('instance');
   my $username = $c->param('username');
   my $password = $c->param('password');

   my $chapscrt;
   my $template = 'menu';

   {
      last unless $instance =~ /^fc[[:digit:]]{2}$/;
      last unless $username =~ /^[[:alnum:]]+$/;
      last unless PAM::auth($username, url_unescape($password));
      last unless $chapscrt = `sudo scripts/mktgt $instance $username`;
      $template = $instance;
   }

   return $c->render(template => $template, username => $username, chapscrt => $chapscrt);
};

app->start;
END

新版本的 bootmenu 应用程序调用一个定制的 mktgt 脚本,如果成功,它将为每个它自己创建的新的 iSCSI 目标返回一个随机的 CHAP 密码。这个 CHAP 密码可以防止其它用户的 iSCSI 目标以间接方式挂载这个用户的目标。这个应用程序只有在用户密码认证成功之后才返回一个正确的 iSCSI 目标密码。

mktgt 脚本要加 sudo 前缀来运行,因为它需要 root 权限去创建目标。

$username$chapscrt 变量也传递给 render 命令,因此在需要的时候,它们也能够被纳入到模板中返回给用户。

接下来,更新我们的引导模板,以便于它们能够读取用户名和 chapscrt 变量,并传递它们到所属的终端用户。也要更新模板以 rw(读写)模式加载根文件系统:

# cd $MY_MOJO/templates
# sed -i "s/:$MY_FC/:$MY_FC-<%= \$username %>/g" $MY_FC.html.ep
# sed -i "s/ netroot=iscsi:/ netroot=iscsi:<%= \$username %>:<%= \$chapscrt %>@/" $MY_FC.html.ep
# sed -i "s/ ro / rw /" $MY_FC.html.ep

运行上面的命令后,你应该会看到如下的引导模板:

#!ipxe
kernel --name kernel.efi ${prefix}/vmlinuz-4.19.5-300.fc29.x86_64 initrd=initrd.img rw ip=dhcp rd.peerdns=0 nameserver=192.0.2.91 nameserver=192.0.2.92 root=/dev/disk/by-path/ip-192.0.2.158:3260-iscsi-iqn.edu.example.server-01:fc29-<%= $username %>-lun-1 netroot=iscsi:<%= $username %>:<%= $chapscrt %>@192.0.2.158::::iqn.edu.example.server-01:fc29-<%= $username %> console=tty0 console=ttyS0,115200n8 audit=0 selinux=0 quiet
initrd --name initrd.img ${prefix}/initramfs-4.19.5-300.fc29.x86_64.img
boot || chain https://server-01.example.edu/menu

注意:如果在 插入 变量后需要查看引导模板,你可以在 boot 命令之前,在它自己的行中插入 shell 命令。然后在你网络引导你的客户端时,iPXE 将在那里给你提供一个用于交互的 shell,你可以在 shell 中输入 imgstat 去查看传递到内核的参数。如果一切正确,你可以输入 exit 去退出 shell 并继续引导过程。

现在,通过 sudo 允许 bootmenu 用户以 root 权限去运行 mktgt 脚本(仅这个脚本):

# echo "bootmenu ALL = NOPASSWD: $MY_MOJO/scripts/mktgt *" > /etc/sudoers.d/bootmenu

bootmenu 用户不应该写访问 mktgt 脚本或在它的家目录下的任何其它文件。在 /opt/bootmenu 目录下的所有文件的属主应该是 root,并且不应该被其它任何 root 以外的用户可写。

sudo 在使用 systemd 的 DynamicUser 选项下不能正常工作,因此创建一个普通用户帐户,并设置 systemd 服务以那个用户运行:

# useradd -r -c 'iPXE Boot Menu Service' -d /opt/bootmenu -s /sbin/nologin bootmenu
# sed -i 's/^DynamicUser=true$/User=bootmenu/' /etc/systemd/system/bootmenu.service
# systemctl daemon-reload

最后,为写时复制覆盖创建一个目录,并创建管理 iSCSI 目标的 mktgt 脚本和它们的覆盖支持存储:

# mkdir /$MY_FC.cow
# mkdir $MY_MOJO/scripts
# cat << 'END' > $MY_MOJO/scripts/mktgt
#!/usr/bin/env perl

# if another instance of this script is running, wait for it to finish
"$ENV{FLOCKER}" eq 'MKTGT' or exec "env FLOCKER=MKTGT flock /tmp $0 @ARGV";

# use "RETURN" to print to STDOUT; everything else goes to STDERR by default
open(RETURN, '>&', STDOUT);
open(STDOUT, '>&', STDERR);

my $instance = shift or die "instance not provided";
my $username = shift or die "username not provided";

my $img = "/$instance.img";
my $dir = "/$instance.cow";
my $top = "$dir/$username";

-f "$img" or die "'$img' is not a file"; 
-d "$dir" or die "'$dir' is not a directory";

my $base;
die unless $base = `losetup --show --read-only --nooverlap --find $img`;
chomp $base;

my $size;
die unless $size = `blockdev --getsz $base`;
chomp $size;

# create the per-user sparse file if it does not exist
if (! -e "$top") {
   die unless system("dd if=/dev/zero of=$top status=none bs=512 count=0 seek=$size") == 0;
}

# create the copy-on-write overlay if it does not exist
my $cow="$instance-$username";
my $dev="/dev/mapper/$cow";
if (! -e "$dev") {
   my $over;
   die unless $over = `losetup --show --nooverlap --find $top`;
   chomp $over;
   die unless system("echo 0 $size snapshot $base $over p 8 | dmsetup create $cow") == 0;
}

my $tgtadm = '/usr/sbin/tgtadm --lld iscsi';

# get textual representations of the iscsi targets
my $text = `$tgtadm --op show --mode target`;
my @targets = $text =~ /(?:^T.*\n)(?:^ .*\n)*/mg;

# convert the textual representations into a hash table
my $targets = {};
foreach (@targets) {
   my $tgt;
   my $sid;

   foreach (split /\n/) {
      /^Target (\d+)(?{ $tgt = $targets->{$^N} = [] })/;
      /I_T nexus: (\d+)(?{ $sid = $^N })/;
      /Connection: (\d+)(?{ push @{$tgt}, [ $sid, $^N ] })/;
   }
}

my $hostname;
die unless $hostname = `hostname`;
chomp $hostname;

my $target = 'iqn.' . join('.', reverse split('\.', $hostname)) . ":$cow";

# find the target id corresponding to the provided target name and
# close any existing connections to it
my $tid = 0;
foreach (@targets) {
   next unless /^Target (\d+)(?{ $tid = $^N }): $target$/m;
   foreach (@{$targets->{$tid}}) {
      die unless system("$tgtadm --op delete --mode conn --tid $tid --sid $_->[0] --cid $_->[1]") == 0;
   }
}

# create a new target if an existing one was not found
if ($tid == 0) {
   # find an available target id
   my @ids = (0, sort keys %{$targets});
   $tid = 1; while ($ids[$tid]==$tid) { $tid++ }

   # create the target
   die unless -e "$dev";
   die unless system("$tgtadm --op new --mode target --tid $tid --targetname $target") == 0;
   die unless system("$tgtadm --op new --mode logicalunit --tid $tid --lun 1 --backing-store $dev") == 0;
   die unless system("$tgtadm --op bind --mode target --tid $tid --initiator-address ALL") == 0;
}

# (re)set the provided target's chap password
my $password = join('', map(chr(int(rand(26))+65), 1..8));
my $accounts = `$tgtadm --op show --mode account`;
if ($accounts =~ / $username$/m) {
   die unless system("$tgtadm --op delete --mode account --user $username") == 0;
}
die unless system("$tgtadm --op new --mode account --user $username --password $password") == 0;
die unless system("$tgtadm --op bind --mode account --tid $tid --user $username") == 0;

# return the new password to the iscsi target on stdout
print RETURN $password;
END
# chmod +x $MY_MOJO/scripts/mktgt

上面的脚本将做以下五件事情:

  1. 创建 /<instance>.cow/<username> 稀疏文件(如果不存在的话)。
  2. 创建 /dev/mapper/<instance>-<username> 设备节点作为 iSCSI 目标的写时复制支持存储(如果不存在的话)。
  3. 创建 iqn.<reverse-hostname>:<instance>-<username> iSCSI 目标(如果不存在的话)。或者,如果已存在了,它将关闭任何已存在的连接,因为在任何时刻,镜像只能以只读模式从一个地方打开。
  4. 它在 iqn.<reverse-hostname>:<instance>-<username> iSCSI 目标上(重新)设置 chap 密码为一个新的随机值。
  5. (如果前面的所有任务都成功的话)它在 标准输出 上显示新的 chap 密码。

你应该可以在命令行上通过使用有效的测试参数来运行它,以测试 mktgt 脚本能否正常工作。例如:

# echo `$MY_MOJO/scripts/mktgt fc29 jsmith`

当你从命令行上运行时,mktgt 脚本应该会输出 iSCSI 目标的一个随意的八字符随机密码(如果成功的话)或者是出错位置的行号(如果失败的话)。

有时候,你可能需要在不停止整个服务的情况下删除一个 iSCSI 目标。例如,一个用户可能无意中损坏了他的个人镜像,在那种情况下,你可能需要按步骤撤销上面的 mktgt 脚本所做的事情,以便于他下次登入时他将得到一个原始镜像。

下面是用于撤销的 rmtgt 脚本,它以相反的顺序做了上面 mktgt 脚本所做的事情:

# mkdir $HOME/bin
# cat << 'END' > $HOME/bin/rmtgt
#!/usr/bin/env perl

@ARGV >= 2 or die "usage: $0 <instance> <username> [+d|+f]\n";

my $instance = shift;
my $username = shift;

my $rmd = ($ARGV[0] eq '+d'); #remove device node if +d flag is set
my $rmf = ($ARGV[0] eq '+f'); #remove sparse file if +f flag is set
my $cow = "$instance-$username";

my $hostname;
die unless $hostname = `hostname`;
chomp $hostname;

my $tgtadm = '/usr/sbin/tgtadm';
my $target = 'iqn.' . join('.', reverse split('\.', $hostname)) . ":$cow";

my $text = `$tgtadm --op show --mode target`;
my @targets = $text =~ /(?:^T.*\n)(?:^ .*\n)*/mg;

my $targets = {};
foreach (@targets) {
   my $tgt;
   my $sid;

   foreach (split /\n/) {
      /^Target (\d+)(?{ $tgt = $targets->{$^N} = [] })/;
      /I_T nexus: (\d+)(?{ $sid = $^N })/;
      /Connection: (\d+)(?{ push @{$tgt}, [ $sid, $^N ] })/;
   }
}

my $tid = 0;
foreach (@targets) {
   next unless /^Target (\d+)(?{ $tid = $^N }): $target$/m;
   foreach (@{$targets->{$tid}}) {
      die unless system("$tgtadm --op delete --mode conn --tid $tid --sid $_->[0] --cid $_->[1]") == 0;
   }
   die unless system("$tgtadm --op delete --mode target --tid $tid") == 0;
   print "target $tid deleted\n";
   sleep 1;
}

my $dev = "/dev/mapper/$cow";
if ($rmd or ($rmf and -e $dev)) {
   die unless system("dmsetup remove $cow") == 0;
   print "device node $dev deleted\n";
}

if ($rmf) {
   my $sf = "/$instance.cow/$username";
   die "sparse file $sf not found" unless -e "$sf";
   die unless system("rm -f $sf") == 0;
   die unless not -e "$sf";
   print "sparse file $sf deleted\n";
}
END
# chmod +x $HOME/bin/rmtgt

例如,使用上面的脚本去完全删除 fc29-jsmith 目标,包含它的支持存储设备节点和稀疏文件,可以按下列方式运行命令:

# rmtgt fc29 jsmith +f

一旦你验证 mktgt 脚本工作正常,你可以重启动 bootmenu 服务。下次有人从网络引导时,他们应该能够接收到一个他们可以写入的、可”私人定制“的网络引导镜像的副本:

# systemctl restart bootmenu.service

现在,就像下面的截屏示范的那样,用户应该可以修改根文件系统了:


via: https://fedoramagazine.org/how-to-build-a-netboot-server-part-4/

作者:Gregory Bartholomew 选题:lujun9972 译者:qhwdw 校对:wxy

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

这也许是一个不太受欢迎的观点,但大多数主流公司最好不要再使用 k8s 了。

你知道那个古老的“以程序员技能写 Hello world ”笑话吗?—— 从一个新手程序员的 printf("hello, world\n") 语句开始,最后结束于高级软件架构工程师令人费解的 Java OOP 模式设计。使用 k8s 就有点像这样。

  • 新手系统管理员:

./binary

  • 有经验的系统管理员:

在 EC2 上的 ./binary

  • DevOp:

在 EC2 上自部署的 CI 管道运行 ./binary

  • 高级云编排工程师:

在 EC2 上通过 k8s 编排的自部署 CI 管道运行 ./binary

¯\\_(ツ)\_/¯

这不意味着 Kubernetes 或者任何这样的东西本身都是坏的,就像 Java 或者 OOP 设计本身并不是坏的一样,但是,在很多情况下,它们被严重地误用,就像在一个 hello world 的程序中可怕地误用 Java 面向对象设计模式一样。对大多数公司而言,系统运维从根本上来说并不十分复杂,此时在这上面应用 k8s 起效甚微。

复杂性本质上来说创造了工作,我十分怀疑使用 k8s 对大多数使用者来说是省时的这一说法。这就好像花一天时间来写一个脚本,用来自动完成一些你一个月进行一次,每次只花 10 分钟完成的工作。这不是一个好的时间投资(特别是你可能会在未来由于扩展或调试这个脚本而进一步投入的更多时间)。

你的部署大概应该需要自动化 – 以免你 最终像 Knightmare 那样 —— 但 k8s 通常可以被一个简单的 shell 脚本所替代。

在我们公司,系统运维团队用了很多时间来设置 k8s 。他们还不得不用了很大一部分时间来更新到新一点的版本(1.6 ➙ 1.8)。结果是如果没有真正深入理解 k8s ,有些东西就没人会真的明白,甚至连深入理解 k8s 这一点也很难(那些 YAML 文件,哦呦!)

在我能自己调试和修复部署问题之前 —— 现在这更难了,我理解基本概念,但在真正调试实际问题的时候,它们并不是那么有用。我不经常用 k8s 足以证明这点。


k8s 真的很难这点并不是什么新看法,这也是为什么现在会有这么多 “k8s 简单用”的解决方案。在 k8s 上再添一层来“让它更简单”的方法让我觉得,呃,不明智。复杂性并没有消失,你只是把它藏起来了。

以前我说过很多次:在确定一样东西是否“简单”时,我最关心的不是写东西的时候有多简单,而是当失败的时候调试起来有多容易。包装 k8s 并不会让调试更加简单,恰恰相反,它让事情更加困难了。


Blaise Pascal 有一句名言:

几乎所有的痛苦都来自于我们不善于在房间里独处。

k8s —— 略微拓展一下,Docker —— 似乎就是这样的例子。许多人似乎迷失在当下的兴奋中,觉得 “k8s 就是这么回事!”,就像有些人迷失在 Java OOP 刚出来时的兴奋中一样,所以一切都必须从“旧”方法转为“新”方法,即使“旧”方法依然可行。

有时候 IT 产业挺蠢的。

或者用 一条推特 来总结:

  • 2014 - 我们必须采用 #微服务 来解决独石应用的所有问题
  • 2016 - 我们必须采用 #docker 来解决微服务的所有问题
  • 2018 - 我们必须采用 #kubernetes 来解决 docker 的所有问题

你可以通过 [email protected] 给我发邮件或者 创建 GitHub issue 来给我反馈或提出问题等。


via: https://arp242.net/weblog/dont-need-k8s.html

作者:Martin Tournoij 选题:lujun9972 译者:beamrolling 校对:wxy

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

从发现软件故障到解决它们,这里讲述是开发团队如何压制软件 bug。

1947 年,发现了第一个计算机 bug —— 被困在计算机继电器中的飞蛾。

要是所有的 bug 都能如此简单地发现就好了。随着软件变得越来越复杂,测试和调试的过程也变得更加复杂。如今,软件 bug 的生命周期可能会很长,尽管正确的技术和业务流程可能会有所帮助。对于开源软件,开发人员使用严格的工单服务和协作来查找和解决 bug。

确认计算机 bug

在测试过程中,发现的 bug 会报告给开发团队。质量保证测试人员尽可能详细地描述 bug ,报告他们的系统状态、他们正在进行的过程以及 bug 是如何表现出来的。

尽管如此,一些 bug 从未得到确认;它们可能会在测试中报告,但永远无法在可控环境中重现。在这种情况下,它们可能得不到解决,而是被关闭。

有些计算机 bug 可能很难确认,因为使用的平台种类繁多,用户行为也非常多。有些 bug 只是间歇性地或在非常特殊的情况下发生的,而另一些 bug 可能会出现在随机的情况下。

许多人使用开源软件并与之交互,许多 bug 和问题可能是不可重复的,或者可能没有得到充分的描述。不过,由于每个用户和开发人员也都扮演质量保证测试人员的角色,至少在一定程度上,bug 还是很有可能会发现的。

确认 bug 后,修复工作就开始了。

分配要修复的 bug

已确认的 bug 被分配给负责解决的开发人员或开发团队。在此阶段,需要重现 bug,发现问题,并修复相关代码。如果 bug 的优先级较低,开发人员可以将此 bug 分类为稍后要修复的问题,也可以在该 bug 具有高优先级的情况下直接指派某人修复。无论哪种方式,都会在开发过程中打开一个工单,并且 bug 将成为已知的问题。

在开源解决方案中,开发人员可以进行选择他们想要解决的 bug,要么选择他们最熟悉的程序区域,要么从优先级最高的的开始。综合解决方案,如 GitHub 使得多个开发人员能够轻松地着手解决,而不会干扰彼此的工作。

当将 bug 设置为需要修复时,bug 报告者还可以为该 bug 选择优先级。主要的 bug 可能具有较高的优先级,而仅与外观相关的 bug 可能具有较低的级别。优先级确定开发团队解决这些问题的方式和时间。无论哪种方式,所有的 bug 都需要先解决,然后才能认为产品已完成。在这方面,适当的回溯到优先级高的需求也会很有帮助。

解决 bug

一旦修复了 bug ,通常会将其作为已解决的 bug 发送回质量保证测试人员。然后,质量保证测试人员再次将产品置于其工作中,以重现 bug。如果无法重现 bug ,质量保证测验人员将假定它已得到适当解决。

在开源情况下,任何更改都会被分发,通常是作为正在测试的暂定版本。此测试版本分发给用户,用户再次履行质量保证测试人员的职责并测试产品。

如果 bug 再次出现,问题将被发送回开发团队。在此阶段,该 bug 将重新触发,开发团队有责任重复解决该 bug 的循环。这种情况可能会发生多次,尤其是在 bug 不可预知或间歇性发生的情况下。众所周知,间歇性的 bug 很难解决。

如果该 bug 不再出现,则该问题将被标记为已解决。在某些情况下,最初的 bug 得到了解决,但由于所做的更改,会出现其他 bug。发生这种情况时,可能需要新的 bug 报告,然后重新开始该过程。

关闭 bug

在处理、识别和解决 bug 后,该 bug 将被关闭,开发人员可以转到软件开发和测试的其他阶段。如果始终找不到 bug ,或者开发人员无法重现 bug ,则该 bug 也将被关闭 —— 无论哪种方式,都将开始开发和测试的下一阶段。

在测试版本中对解决方案所做的任何更改都将滚动到产品的下一个版本中。如果 bug 是严重的,则在下一个版本发布之前,可能会为当前用户提供修补程序或修补程序。这在安全问题中很常见。

软件 bug 可能很难找到,但通过遵循过程,开发人员可以使开发更快、更容易、更一致。质量保证是这一过程的重要组成部分,因为质量保证测试人员必须发现和识别 bug ,并帮助开发人员重现这些 bug 。在 bug 不再发生之前,无法关闭和解决 bug。

开源的解决方案分散了质量保证测试、开发和缓解的负担,这往往导致 bug 被更快、更全面地发现和缓解。但是,由于开源技术的性质,此过程的速度和准确性通常取决于解决方案的受欢迎程度及其维护和开发团队的敬业精神。

Rich Butkevic 是一个 PMP 项目经理认证,,敏捷开发框架认证(certified scrum master) 并且 维护 Project Zendo,这是供项目管理专业人员去发现、简化和改进其项目成果策略的网站。可以在 Richbutkevic.com 或者使用 LinkedIn 与 Rich 联系。


via: https://opensource.com/article/18/6/life-cycle-software-bug

作者:Rich Butkevic 选题:lujun9972 译者:lixinyuxx 校对:wxy

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