Merge remote-tracking branch 'LCTT/master'

This commit is contained in:
Xingyu Wang 2020-07-08 10:53:41 +08:00
commit 4231037efb
8 changed files with 912 additions and 270 deletions

View File

@ -0,0 +1,133 @@
[#]: collector: (lujun9972)
[#]: translator: (chunibyo-wly)
[#]: reviewer: (wxy)
[#]: publisher: (wxy)
[#]: url: (https://linux.cn/article-12392-1.html)
[#]: subject: (One CI/CD pipeline per product to rule them all)
[#]: via: (https://opensource.com/article/19/7/cicd-pipeline-rule-them-all)
[#]: author: (Willy-Peter Schaub https://opensource.com/users/wpschaub/users/bclaster/users/matt-micene/users/barkerd427)
使用一条 CI/CD 流水线管理所有的产品
======
> 统一的持续集成与持续交付的流水线的构想是一种梦想吗?
![](https://img.linux.net.cn/data/attachment/album/202007/07/224752iib7xgh7icr8478f.jpg)
当我加入 WorkSafeBC 负责云端运维和工程流程优化的云端运维团队时,我和大家分享了我的梦想,那就是一个工具化的流水线,每一个产品都可以持续集成和持续交付。
根据 Lukas Klose 的说法,<ruby>[流程][2]<rt>flow</rt></ruby>(在软件工程的语境内)是“软件系统以稳定和可预测的速度创造价值的状态”。我认为这是最大的挑战和机遇之一,特别是在复杂的新兴解决方案领域。我力求通过一种持续、高效和优质的解决方案,提供一种持续交付模式,并且能够构建正确的事物让我们的用户感到满意。想办法把我们的系统分解成更小的碎片,这些碎片本身是有价值的,使团队能够渐进式地交付价值。这需要业务和工程部门改变思维方式。
### 持续集成和持续交付的CI/CD流水线
CI/CD 流水线是一种 DevOps 实践,用于更频繁、一致、可靠的地交付代码变更。它可以帮助敏捷开发团队提高**部署频率**,减少**变更准备时间**、**变更失败率**和关键绩效指标KPI的**平均恢复时间**,从而提高质量并且实现更快的交付。唯一的先决条件就是坚实的开发流程、对质量的心态和对需求从构想到废弃的责任心,以及一个全面的流水线(如下图所示)。
![Prerequisites for a solid development process][3]
它简化了工程流程和产品,以稳定基础架构环境;优化工作流程;并创建一致的、可重复的、自动化的任务。正如 Dave Snowden 的 [Cynefin Sensemaking][4] 模型所说的那样,这样就允许我们将复杂不可解决的任务变成了复杂可解决的任务,降低了维护成本,提高了质量和可靠性。
精简流程的一部分是将 <ruby>[浪费实践类型][5]<rt>wasteful practice types</rt></ruby> Muri过载、Mura变异和 Muda浪费的浪费降低最低。
* **Muri过载**:避免过度工程化,避免与商业价值不相关的功能以及过多的文档。
* **Mura变异**:改善审批和验证流程(比如,安全签批);推动 <ruby>[左移提前][6]<rt>shift-left</rt></ruby> 策略以推行单元测试、安全漏洞扫描与代码质量检查;并改进风险评定。
* **Muda浪费**:避免技术债、错误或前期的详细文档等浪费。
看起来 80 的重点都集中在提供一种可以集成和协作的工程产品上,这些系统可以采用一个创意和计划、开发、测试和监控你的解决方案。然而,一个成功的转型和工程系统是由 5 的产品、15 的过程和 80 的开发人员组成的。
我们可以使用的产品有很多。比如Azure DevOps 为持续集成CI、持续交付CD和可扩展性提供了丰富支持并与 Stryker、SonarQube、WhiteSource、Jenkins 和 Octopus 等开源集成和商用成品COTS软件即服务SaaS进行集成。对于工程师来说关注产品总是一种诱惑但请记住它们只是我们旅程的 5%。
![5% about products, 15% about process, 80% about people][7]
最大的挑战是打破数十年的规则、规定和已经步入舒适区的流程:“*我们一直都是这样做的;为什么需要改变呢?*”
开发和运维人员之间的摩擦导致了各种支离破碎的、重复的、不间断的集成和交付流水线。开发人员希望能访问所有东西,以便持续迭代,让用户使用起来和持续地快速发布。运维人员希望将所有东西锁起来,保护业务、用户和品质。这些矛盾在不经意间导致了很难做到一种自动化的流程,进而导致发布周期晚于预期。
让我们使用最近的一次白板讨论中的片段来探索流水线。
想要支持流水线的变化是一项困难并且花费巨大的工作;版本和可追溯性的不一致使得这个问题变得更加复杂,因此不断精简开发流程和流水线是一项挑战。
![Improving quality and visibility of pipelines][8]
我主张一些原则使得每个产品都能使用通用流水线:
* 使一切可自动化的东西都自动化
* 一次构建
* 保持持续集成和持续交付
* 保持持续精简和改进
* 保持一个构建的定义
* 保持一个发布流水线的定义
* 尽早、频繁地扫描漏洞,并且*尽快失败*
* 尽早、频繁地进行测试,并且*尽快失败*
* 保持已发布版本的可追踪和监控
但是,如果我要打破这些,最重要的原则就是*保持简单*。如果你不能说明流水线化的原因(是什么、为什么)和过程(如何),你或许是不了解自己的软件过程的。我们大多数人想要的不是最好的、超现代的和具有革命意义的流水线 —— 我们仅仅是需要一个功能强大的、有价值的和能促进工程的流水线。首先需要解决的是那 80% —— 文化、人员和他们的心态。请你的 CI/CD 骑士们穿上闪亮的盔甲,在他们的盾牌上贴上 TLA<ruby>两个/三个字母的缩写<rt>two/three-lettered acronym</rt></ruby>)符号,加入到实践和经验工程的力量中来。
### 统一流水线
让我们逐步完成我们的白板会议实践。
![CI build/CD release pipeline][9]
每个应用使用一套构建定义来定义一个 CI/CD 流水线,用来触发*拉取请求的预合并验证*与*持续集成*的构建。生成一个带有调试信息的*发布*的构建,并且将其上传到 [符号服务器][10]。这使开发人员可以在本地和远程生产环境进行调试,而在不用考虑需要加载哪个构建和符号,符号服务器为我们施展了这样的魔法。
![Breaking down the CI build pipeline][11]
在构建过程中进行尽可能多的验证(*左移提前*),这允许开发新特性的团队可以尽快失败,不断的提高整体的产品质量,并在拉取请求中为代码审核人员提供宝贵证据。你喜欢有大量提交的拉取请求吗?还是一个带有少数提交和提供了漏洞检查、测试覆盖率、代码质量检查和 [Stryker][12] 突变残余等支持的拉取请求?就我个人而言,我投后者的票。
![Breaking down the CD release pipeline][13]
不要使用构建转换来生成多个特定环境的构建。通过一个构建实现*发布时转换*、*标记化*和 XML/JSON 的*值替换*。换句话说,*右移滞后*具体环境的配置。
![Shift-right the environment-specific configuration][14]
安全存储发布配置数据,并且根据数据的*信任度*和*敏感度*让开发和运维都能使用。使用开源的密钥管理工具、Azure 密钥保险库、AWS 密钥管理服务或者其他产品,记住你的工具箱中有很多方便的工具。
![Dev-QA-production pipeline][15]
使用*用户组*而不是*用户*,将审批人管理从跨多个流水线的多个阶段移动到简单的组成员。
![Move approver management to simple group membership][16]
创建一条流水线并且对赋予特定的交付阶段的权限,而不是重复流水线让团队进入他们感兴趣的地方。
![Pipeline with access to specific delivery stages][17]
最后,但并非最不重要的是,拥抱拉取请求,以帮助提高对代码仓库的洞察力和透明度,增进整体质量、协作,并将预验证构建发布到选定的环境,比如,开发环境。
这是整个白板更正式的视图。
![The full pipeline][18]
所以,你对 CI/CD 流水线有什么想法和经验?我的通过*一条流水线来管理它们*的这个梦想是空想吗?
--------------------------------------------------------------------------------
via: https://opensource.com/article/19/7/cicd-pipeline-rule-them-all
作者:[Willy-Peter Schaub][a]
选题:[lujun9972][b]
译者:[chunibyo-wly](https://github.com/chunibyo-wly)
校对:[wxy](https://github.com/wxy)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
[a]: https://opensource.com/users/wpschaub/users/bclaster/users/matt-micene/users/barkerd427
[b]: https://github.com/lujun9972
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/LAW-Internet_construction_9401467_520x292_0512_dc.png?itok=RPkPPtDe (An intersection of pipes.)
[2]: https://continuingstudies.sauder.ubc.ca/courses/agile-delivery-methods/ii861
[3]: https://opensource.com/sites/default/files/uploads/devops_pipeline_pipe-2.png (Prerequisites for a solid development process)
[4]: https://en.wikipedia.org/wiki/Cynefin_framework
[5]: https://www.lean.org/lexicon/muda-mura-muri
[6]: https://en.wikipedia.org/wiki/Shift_left_testing
[7]: https://opensource.com/sites/default/files/uploads/devops_pipeline_pipe-3.png (5% about products, 15% about process, 80% about people)
[8]: https://opensource.com/sites/default/files/uploads/devops_pipeline_pipe-4_0.png (Improving quality and visibility of pipelines)
[9]: https://opensource.com/sites/default/files/uploads/devops_pipeline_pipe-5_0.png (CI build/CD release pipeline)
[10]: https://en.wikipedia.org/wiki/Microsoft_Symbol_Server
[11]: https://opensource.com/sites/default/files/uploads/devops_pipeline_pipe-6.png (Breaking down the CI build pipeline)
[12]: https://stryker-mutator.io/
[13]: https://opensource.com/sites/default/files/uploads/devops_pipeline_pipe-7.png (Breaking down the CD release pipeline)
[14]: https://opensource.com/sites/default/files/uploads/devops_pipeline_pipe-8.png (Shift-right the environment-specific configuration)
[15]: https://opensource.com/sites/default/files/uploads/devops_pipeline_pipe-9.png (Dev-QA-production pipeline)
[16]: https://opensource.com/sites/default/files/uploads/devops_pipeline_pipe-10.png (Move approver management to simple group membership)
[17]: https://opensource.com/sites/default/files/uploads/devops_pipeline_pipe-11.png (Pipeline with access to specific delivery stages)
[18]: https://opensource.com/sites/default/files/uploads/devops_pipeline_pipe-12.png (The full pipeline)

View File

@ -1,8 +1,8 @@
[#]: collector: (lujun9972)
[#]: translator: (wxy)
[#]: reviewer: (wxy)
[#]: publisher: ( )
[#]: url: ( )
[#]: publisher: (wxy)
[#]: url: (https://linux.cn/article-12389-1.html)
[#]: subject: (How to stress test your Linux system)
[#]: via: (https://www.networkworld.com/article/3563334/how-to-stress-test-your-linux-system.html)
[#]: author: (Sandra Henry-Stocker https://www.networkworld.com/author/Sandra-Henry_Stocker/)
@ -10,11 +10,11 @@
如何对你的 Linux 系统进行压力测试
======
> 如果你想了解 Linux 服务器在重压之下的运行情况,那么给 Linux 服务器 施加压力是个不错的主意。在这篇文章中,我们将看一些工具,可以帮助你增加服务器压力并衡量结果。
> 如果你想了解 Linux 服务器在重压之下的运行情况,那么给 Linux 服务器施加压力是个不错的主意。在这篇文章中,我们将看一些工具,可以帮助你增加服务器压力并衡量结果。
![](https://images.idgesg.net/images/article/2020/06/stress-test2_linux-penguin-stress-ball_hand-squeezing_by-digitalsoul-getty-images_1136841639-100850120-large.jpg)
为什么你会想给你的 Linux 系统施加压力呢?因为有时你可能想知道当一个系统由于大量运行的进程、繁重的网络流量、过多的内存使用等原因而承受很大的压力时,它的表现如何。这种压力测试可以帮助确保系统已经做好了 “上市” 的准备。
为什么你会想给你的 Linux 系统施加压力呢?因为有时你可能想知道当一个系统由于大量运行的进程、繁重的网络流量、过多的内存使用等原因而承受很大的压力时,它的表现如何。这种压力测试可以帮助确保系统已经做好了 “上市” 的准备。
如果你需要预测应用程序可能需要多长时间才能做出反应,以及哪些(如果有的话)进程可能会在重负载下失败或运行缓慢,那么在前期进行压力测试是一个非常好的主意。

View File

@ -1,130 +0,0 @@
[#]: collector: (lujun9972)
[#]: translator: (geekpi)
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )
[#]: subject: (openSUSE Leap 15.2 Released With Focus on Containers, AI and Encryption)
[#]: via: (https://itsfoss.com/opensuse-leap-15-2-release/)
[#]: author: (Ankush Das https://itsfoss.com/author/ankush/)
openSUSE Leap 15.2 Released With Focus on Containers, AI and Encryption
======
[openSUSE][1] Leap 15.2 has finally landed with some useful changes and improvements.
Also, considering the exciting announcement of [Closing the Leap Gap][2], the release of openSUSE Leap 15.2 brings us one step closer to SLE ([SUSE Linux Enterprise][3]) binaries being integrated to openSUSE Leap 15.3 next.
Lets take a look at what has changed and improved in openSUSE Leap 15.2.
### openSUSE Leap 15.2: Key Changes
![][4]
Overall, openSUSE Leap 15.2 release involves security updates, major new packages, bug fixes, and other improvements.
In their press release, a developer of the project, **Marco Varlese**, mentions:
> “Leap 15.2 represents a huge step forward in the Artificial Intelligence space, “I am super excited that openSUSE end-users can now finally consume Machine Learning / Deep Learning frameworks and applications via our repositories to enjoy a stable and up-to-date ecosystem.”
Even though this hints at what changes it could involve, heres whats new in openSUSE Leap 15.2:
#### Adding Artificial Intelligence (AI) and Machine Learning packages
Unquestionably, Artificial Intelligence (AI) and Machine Learning are some of the most disruptive technologies to learn.
To facilitate that to its end-users, openSUSE Leap 15.2 has added a bunch of important packages for new open source technologies:
* [Tensorflow][5]
* [PyTorch][6]
* [ONNX][7]
* [Grafana][8]
* [Prometheus][9]
#### Introducing a Real-Time Kernel
![][10]
With openSUSE Leap 15.2, a real-time kernel will be introduced to manage the timing of [microprocessors][11] to efficiently handle time-critical events.
The addition of a real-time kernel is a big deal for this real. **Gerald Pfeifer (**chair of the projects board) shared his thoughts with the following statement:
> “The addition of a real time kernel to openSUSE Leap unlocks new possibilities. Think edge computing, embedded devices, data capturing, all of which are seeing immense growth. Historically many of these have been the domain of proprietary approaches; openSUSE now opens the floodgates for developers, researchers and companies that are interested in testing real time capabilities or maybe even in contributing. Another domain open source helps open up!”
#### Inclusion of Container Technologies
With the latest release, you will notice that [Kubernetes][12] is included as an official package. This should make it easy for end-users to automate deployments, scale, and manage containerized applications.
[Helm][13] (the package manager for Kubernetes) also comes baked in. Not just limited to that, you will also find several other additions here and there that makes it easier to secure and deploy containerized applications.
#### Updates to openSUSE Installer
![][14]
openSUSEs installer was already pretty good. But, with the latest Leap 15.2 release, they have added more information, compatibility with right-to-left languages like Arabic, and subtle changes to make it easier to select options right at the time of installation.
#### Improvements to YaST
While [YaST][15] is already a pretty powerful installation and configuration tool, this release adds the ability of creating and managing a Btrfs file-system and enforcing advanced encryption techniques.
Of course, you must be aware of the availability of [openSUSE on Windows Subsystem for Linux][16]. So, with Leap 15.2, YaST compatibility with WSL has improved as per their release notes.
#### Desktop Environment Improvements
![][17]
The desktop environments available have been update to their latest versions that include [KDE Plasma 5.18 LTS][18] and [GNOME 3.34][19].
You will also find an updated [XFCE 4.14][20] desktop available for openSUSE Leap 15.2.
If youre curious to know all the details for the latest release, you may refer to the [official release announcement.][21]
### Download &amp; Availability
As of now, you should be able to find Linode cloud images of Leap 15.2. Eventually, you will notice other cloud hosting services like Amazon Web Services, Azure, and others to offer it as well.
You can also grab the DVD ISO or the network image file from the official website itself.
To upgrade your current installation, Id recommend following the [official instructions][22].
[openSUSE Leap 15.2][23]
Have you tried openSUSE Leap 15.2 yet? Feel free to let me know what you think!
--------------------------------------------------------------------------------
via: https://itsfoss.com/opensuse-leap-15-2-release/
作者:[Ankush Das][a]
选题:[lujun9972][b]
译者:[译者ID](https://github.com/译者ID)
校对:[校对者ID](https://github.com/校对者ID)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
[a]: https://itsfoss.com/author/ankush/
[b]: https://github.com/lujun9972
[1]: https://www.opensuse.org/
[2]: https://www.suse.com/c/sle-15-sp2-schedule-and-closing-the-opensuse-leap-gap/
[3]: https://www.suse.com/
[4]: https://i2.wp.com/itsfoss.com/wp-content/uploads/2020/07/opensuse-leap-15-2-gnome.png?ssl=1
[5]: https://www.tensorflow.org
[6]: https://pytorch.org
[7]: https://onnx.ai
[8]: https://grafana.com
[9]: https://prometheus.io/docs/introduction/overview/
[10]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2020/07/opensuse-leap-15-2-terminal.png?ssl=1
[11]: https://en.wikipedia.org/wiki/Microprocessor
[12]: https://kubernetes.io
[13]: https://helm.sh
[14]: https://i2.wp.com/itsfoss.com/wp-content/uploads/2020/07/opensuse-leap-15-2.png?ssl=1
[15]: https://yast.opensuse.org/
[16]: https://itsfoss.com/opensuse-bash-on-windows/
[17]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2020/07/opensue-leap-15-2-kde.png?ssl=1
[18]: https://itsfoss.com/kde-plasma-5-18-release/
[19]: https://itsfoss.com/gnome-3-34-release/
[20]: https://www.xfce.org/about/news/?post=1565568000
[21]: https://en.opensuse.org/Release_announcement_15.2
[22]: https://en.opensuse.org/SDB:System_upgrade
[23]: https://software.opensuse.org/distributions/leap

View File

@ -1,5 +1,5 @@
[#]: collector: (lujun9972)
[#]: translator: ( )
[#]: translator: (geekpi)
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )

View File

@ -0,0 +1,551 @@
[#]: collector: (lujun9972)
[#]: translator: ( )
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )
[#]: subject: (Use systemd timers instead of cronjobs)
[#]: via: (https://opensource.com/article/20/7/systemd-timers)
[#]: author: (David Both https://opensource.com/users/dboth)
Use systemd timers instead of cronjobs
======
Timers provide finer-grained control of events than cronjobs.
![Team checklist][1]
I am in the process of converting my [cron][2] jobs to systemd timers. I have used timers for a few years, but usually, I learned just enough to perform the task I was working on. While doing research for this [systemd series][3], I learned that systemd timers have some very interesting capabilities.
Like cron jobs, systemd timers can trigger events—shell scripts and programs—at specified time intervals, such as once a day, on a specific day of the month (perhaps only if it is a Monday), or every 15 minutes during business hours from 8am to 6pm. Timers can also do some things that cron jobs cannot. For example, a timer can trigger a script or program to run a specific amount of time after an event such as boot, startup, completion of a previous task, or even the previous completion of the service unit called by the timer.
### System maintenance timers
When Fedora or any systemd-based distribution is installed on a new system, it creates several timers that are part of the system maintenance procedures that happen in the background of any Linux host. These timers trigger events necessary for common maintenance tasks, such as updating system databases, cleaning temporary directories, rotating log files, and more.
As an example, I'll look at some of the timers on my primary workstation by using the `systemctl status *timer` command to list all the timers on my host. The asterisk symbol works the same as it does for file globbing, so this command lists all systemd timer units:
```
[root@testvm1 ~]# systemctl status *timer
● mlocate-updatedb.timer - Updates mlocate database every day
     Loaded: loaded (/usr/lib/systemd/system/mlocate-updatedb.timer; enabled; vendor preset: enabled)
     Active: active (waiting) since Tue 2020-06-02 08:02:33 EDT; 2 days ago
    Trigger: Fri 2020-06-05 00:00:00 EDT; 15h left
   Triggers: ● mlocate-updatedb.service
Jun 02 08:02:33 testvm1.both.org systemd[1]: Started Updates mlocate database every day.
● logrotate.timer - Daily rotation of log files
     Loaded: loaded (/usr/lib/systemd/system/logrotate.timer; enabled; vendor preset: enabled)
     Active: active (waiting) since Tue 2020-06-02 08:02:33 EDT; 2 days ago
    Trigger: Fri 2020-06-05 00:00:00 EDT; 15h left
   Triggers: ● logrotate.service
       Docs: man:logrotate(8)
             man:logrotate.conf(5)
Jun 02 08:02:33 testvm1.both.org systemd[1]: Started Daily rotation of log files.
● sysstat-summary.timer - Generate summary of yesterday's process accounting
     Loaded: loaded (/usr/lib/systemd/system/sysstat-summary.timer; enabled; vendor preset: enabled)
     Active: active (waiting) since Tue 2020-06-02 08:02:33 EDT; 2 days ago
    Trigger: Fri 2020-06-05 00:07:00 EDT; 15h left
   Triggers: ● sysstat-summary.service
Jun 02 08:02:33 testvm1.both.org systemd[1]: Started Generate summary of yesterday's process accounting.
● fstrim.timer - Discard unused blocks once a week
     Loaded: loaded (/usr/lib/systemd/system/fstrim.timer; enabled; vendor preset: enabled)
     Active: active (waiting) since Tue 2020-06-02 08:02:33 EDT; 2 days ago
    Trigger: Mon 2020-06-08 00:00:00 EDT; 3 days left
   Triggers: ● fstrim.service
       Docs: man:fstrim
Jun 02 08:02:33 testvm1.both.org systemd[1]: Started Discard unused blocks once a week.
● sysstat-collect.timer - Run system activity accounting tool every 10 minutes
     Loaded: loaded (/usr/lib/systemd/system/sysstat-collect.timer; enabled; vendor preset: enabled)
     Active: active (waiting) since Tue 2020-06-02 08:02:33 EDT; 2 days ago
    Trigger: Thu 2020-06-04 08:50:00 EDT; 41s left
   Triggers: ● sysstat-collect.service
Jun 02 08:02:33 testvm1.both.org systemd[1]: Started Run system activity accounting tool every 10 minutes.
● dnf-makecache.timer - dnf makecache --timer
     Loaded: loaded (/usr/lib/systemd/system/dnf-makecache.timer; enabled; vendor preset: enabled)
     Active: active (waiting) since Tue 2020-06-02 08:02:33 EDT; 2 days ago
    Trigger: Thu 2020-06-04 08:51:00 EDT; 1min 41s left
   Triggers: ● dnf-makecache.service
Jun 02 08:02:33 testvm1.both.org systemd[1]: Started dnf makecache timer.
● systemd-tmpfiles-clean.timer - Daily Cleanup of Temporary Directories
     Loaded: loaded (/usr/lib/systemd/system/systemd-tmpfiles-clean.timer; static; vendor preset: disabled)
     Active: active (waiting) since Tue 2020-06-02 08:02:33 EDT; 2 days ago
    Trigger: Fri 2020-06-05 08:19:00 EDT; 23h left
   Triggers: ● systemd-tmpfiles-clean.service
       Docs: man:tmpfiles.d(5)
             man:systemd-tmpfiles(8)
Jun 02 08:02:33 testvm1.both.org systemd[1]: Started Daily Cleanup of Temporary Directories.
```
Each timer has at least six lines of information associated with it:
* The first line has the timer's file name and a short description of its purpose.
* The second line displays the timer's status, whether it is loaded, the full path to the timer unit file, and the vendor preset.
* The third line indicates its active status, which includes the date and time the timer became active.
* The fourth line contains the date and time the timer will be triggered next and an approximate time until the trigger occurs.
* The fifth line shows the name of the event or the service that is triggered by the timer.
* Some (but not all) systemd unit files have pointers to the relevant documentation. Three of the timers in my virtual machine's output have pointers to documentation. This is a nice (but optional) bit of data.
* The final line is the journal entry for the most recent instance of the service triggered by the timer.
Depending upon your host, you will probably have a different set of timers.
### Create a timer
Although we can deconstruct one or more of the existing timers to learn how they work, lets create our own [service unit][4] and a timer unit to trigger it. We will use a fairly trivial example in order to keep this simple. After we have finished this, it will be easier to understand how the other timers work and to determine what they are doing.
First, create a simple service that will run something basic, such as the `free` command. For example, you may want to monitor free memory at regular intervals. Create the following `myMonitor.service` unit file in the `/etc/systemd/system` directory. It does not need to be executable:
```
# This service unit is for testing timer units
# By David Both
# Licensed under GPL V2
#
[Unit]
Description=Logs system statistics to the systemd journal
Wants=myMonitor.timer
[Service]
Type=oneshot
ExecStart=/usr/bin/free
[Install]
WantedBy=multi-user.target
```
This is about the simplest service unit you can create. Now lets look at the status and test our service unit to ensure that it works as we expect it to.
```
[root@testvm1 system]# systemctl status myMonitor.service
● myMonitor.service - Logs system statistics to the systemd journal
     Loaded: loaded (/etc/systemd/system/myMonitor.service; disabled; vendor preset: disabled)
     Active: inactive (dead)
[root@testvm1 system]# systemctl start myMonitor.service
[root@testvm1 system]#
```
Where is the output? By default, the standard output (`STDOUT`) from programs run by systemd service units is sent to the systemd journal, which leaves a record you can view now or later—up to a point. (I will look at systemd journaling and retention strategies in a future article in this series.) Look at the journal specifically for your service unit and for today only. The `-S` option, which is the short version of `--since`, allows you to specify the time period that the `journalctl` tool should search for entries. This isn't because you don't care about previous results—in this case, there won't be any—it is to shorten the search time if your host has been running for a long time and has accumulated a large number of entries in the journal:
```
[root@testvm1 system]# journalctl -S today -u myMonitor.service
\-- Logs begin at Mon 2020-06-08 07:47:20 EDT, end at Thu 2020-06-11 09:40:47 EDT. --
Jun 11 09:12:09 testvm1.both.org systemd[1]: Starting Logs system statistics to the systemd journal...
Jun 11 09:12:09 testvm1.both.org free[377966]:               total        used        free      shared  buff/cache   available
Jun 11 09:12:09 testvm1.both.org free[377966]: Mem:       12635740      522868    11032860        8016     1080012    11821508
Jun 11 09:12:09 testvm1.both.org free[377966]: Swap:       8388604           0     8388604
Jun 11 09:12:09 testvm1.both.org systemd[1]: myMonitor.service: Succeeded.
[root@testvm1 system]#
```
A task triggered by a service can be a single program, a series of programs, or a script written in any scripting language. Add another task to the service by adding the following line to the end of the `[Service]` section of the `myMonitor.service` unit file:
```
`ExecStart=/usr/bin/lsblk`
```
Start the service again and check the journal for the results, which should look like this. You should see the results from both commands in the journal:
```
Jun 11 15:42:18 testvm1.both.org systemd[1]: Starting Logs system statistics to the systemd journal...
Jun 11 15:42:18 testvm1.both.org free[379961]:               total        used        free      shared  buff/cache   available
Jun 11 15:42:18 testvm1.both.org free[379961]: Mem:       12635740      531788    11019540        8024     1084412    11812272
Jun 11 15:42:18 testvm1.both.org free[379961]: Swap:       8388604           0     8388604
Jun 11 15:42:18 testvm1.both.org lsblk[379962]: NAME          MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
Jun 11 15:42:18 testvm1.both.org lsblk[379962]: sda             8:0    0  120G  0 disk
Jun 11 15:42:18 testvm1.both.org lsblk[379962]: ├─sda1          8:1    0    4G  0 part /boot
Jun 11 15:42:18 testvm1.both.org lsblk[379962]: └─sda2          8:2    0  116G  0 part
Jun 11 15:42:18 testvm1.both.org lsblk[379962]:   ├─VG01-root 253:0    0    5G  0 lvm  /
Jun 11 15:42:18 testvm1.both.org lsblk[379962]:   ├─VG01-swap 253:1    0    8G  0 lvm  [SWAP]
Jun 11 15:42:18 testvm1.both.org lsblk[379962]:   ├─VG01-usr  253:2    0   30G  0 lvm  /usr
Jun 11 15:42:18 testvm1.both.org lsblk[379962]:   ├─VG01-tmp  253:3    0   10G  0 lvm  /tmp
Jun 11 15:42:18 testvm1.both.org lsblk[379962]:   ├─VG01-var  253:4    0   20G  0 lvm  /var
Jun 11 15:42:18 testvm1.both.org lsblk[379962]:   └─VG01-home 253:5    0   10G  0 lvm  /home
Jun 11 15:42:18 testvm1.both.org lsblk[379962]: sr0            11:0    1 1024M  0 rom
Jun 11 15:42:18 testvm1.both.org systemd[1]: myMonitor.service: Succeeded.
Jun 11 15:42:18 testvm1.both.org systemd[1]: Finished Logs system statistics to the systemd journal.
```
Now that you know your service works as expected, create the timer unit file, `myMonitor.timer` in `/etc/systemd/system`, and add the following:
```
# This timer unit is for testing
# By David Both
# Licensed under GPL V2
#
[Unit]
Description=Logs some system statistics to the systemd journal
Requires=myMonitor.service
[Timer]
Unit=myMonitor.service
OnCalendar=*-*-* *:*:00
[Install]
WantedBy=timers.target
```
The `OnCalendar` time specification in the `myMonitor.timer file`, `*-*-* *:*:00`, should trigger the timer to execute the `myMonitor.service` unit every minute. I will explore `OnCalendar` settings a bit later in this article.
For now, observe any journal entries pertaining to running your service when it is triggered by the timer. You could also follow the timer, but following the service allows you to see the results in near real time. Run `journalctl` with the `-f` (follow) option:
```
[root@testvm1 system]# journalctl -S today -f -u myMonitor.service
\-- Logs begin at Mon 2020-06-08 07:47:20 EDT. --
```
Start but do not enable the timer, and see what happens after it runs for a while:
```
[root@testvm1 ~]# systemctl start myMonitor.service
[root@testvm1 ~]#
```
One result shows up right away, and the next ones come at—sort of—one-minute intervals. Watch the journal for a few minutes and see if you notice the same things I did:
```
[root@testvm1 system]# journalctl -S today -f -u myMonitor.service
\-- Logs begin at Mon 2020-06-08 07:47:20 EDT. --
Jun 13 08:39:18 testvm1.both.org systemd[1]: Starting Logs system statistics to the systemd journal...
Jun 13 08:39:18 testvm1.both.org systemd[1]: myMonitor.service: Succeeded.
Jun 13 08:39:19 testvm1.both.org free[630566]:               total        used        free      shared  buff/cache   available
Jun 13 08:39:19 testvm1.both.org free[630566]: Mem:       12635740      556604    10965516        8036     1113620    11785628
Jun 13 08:39:19 testvm1.both.org free[630566]: Swap:       8388604           0     8388604
Jun 13 08:39:18 testvm1.both.org systemd[1]: Finished Logs system statistics to the systemd journal.
Jun 13 08:39:19 testvm1.both.org lsblk[630567]: NAME          MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
Jun 13 08:39:19 testvm1.both.org lsblk[630567]: sda             8:0    0  120G  0 disk
Jun 13 08:39:19 testvm1.both.org lsblk[630567]: ├─sda1          8:1    0    4G  0 part /boot
Jun 13 08:39:19 testvm1.both.org lsblk[630567]: └─sda2          8:2    0  116G  0 part
Jun 13 08:39:19 testvm1.both.org lsblk[630567]:   ├─VG01-root 253:0    0    5G  0 lvm  /
Jun 13 08:39:19 testvm1.both.org lsblk[630567]:   ├─VG01-swap 253:1    0    8G  0 lvm  [SWAP]
Jun 13 08:39:19 testvm1.both.org lsblk[630567]:   ├─VG01-usr  253:2    0   30G  0 lvm  /usr
Jun 13 08:39:19 testvm1.both.org lsblk[630567]:   ├─VG01-tmp  253:3    0   10G  0 lvm  /tmp
Jun 13 08:39:19 testvm1.both.org lsblk[630567]:   ├─VG01-var  253:4    0   20G  0 lvm  /var
Jun 13 08:39:19 testvm1.both.org lsblk[630567]:   └─VG01-home 253:5    0   10G  0 lvm  /home
Jun 13 08:39:19 testvm1.both.org lsblk[630567]: sr0            11:0    1 1024M  0 rom
Jun 13 08:40:46 testvm1.both.org systemd[1]: Starting Logs system statistics to the systemd journal...
Jun 13 08:40:46 testvm1.both.org free[630572]:               total        used        free      shared  buff/cache   available
Jun 13 08:40:46 testvm1.both.org free[630572]: Mem:       12635740      555228    10966836        8036     1113676    11786996
Jun 13 08:40:46 testvm1.both.org free[630572]: Swap:       8388604           0     8388604
Jun 13 08:40:46 testvm1.both.org lsblk[630574]: NAME          MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
Jun 13 08:40:46 testvm1.both.org lsblk[630574]: sda             8:0    0  120G  0 disk
Jun 13 08:40:46 testvm1.both.org lsblk[630574]: ├─sda1          8:1    0    4G  0 part /boot
Jun 13 08:40:46 testvm1.both.org lsblk[630574]: └─sda2          8:2    0  116G  0 part
Jun 13 08:40:46 testvm1.both.org lsblk[630574]:   ├─VG01-root 253:0    0    5G  0 lvm  /
Jun 13 08:40:46 testvm1.both.org lsblk[630574]:   ├─VG01-swap 253:1    0    8G  0 lvm  [SWAP]
Jun 13 08:40:46 testvm1.both.org lsblk[630574]:   ├─VG01-usr  253:2    0   30G  0 lvm  /usr
Jun 13 08:40:46 testvm1.both.org lsblk[630574]:   ├─VG01-tmp  253:3    0   10G  0 lvm  /tmp
Jun 13 08:40:46 testvm1.both.org lsblk[630574]:   ├─VG01-var  253:4    0   20G  0 lvm  /var
Jun 13 08:40:46 testvm1.both.org lsblk[630574]:   └─VG01-home 253:5    0   10G  0 lvm  /home
Jun 13 08:40:46 testvm1.both.org lsblk[630574]: sr0            11:0    1 1024M  0 rom
Jun 13 08:40:46 testvm1.both.org systemd[1]: myMonitor.service: Succeeded.
Jun 13 08:40:46 testvm1.both.org systemd[1]: Finished Logs system statistics to the systemd journal.
Jun 13 08:41:46 testvm1.both.org systemd[1]: Starting Logs system statistics to the systemd journal...
Jun 13 08:41:46 testvm1.both.org free[630580]:               total        used        free      shared  buff/cache   available
Jun 13 08:41:46 testvm1.both.org free[630580]: Mem:       12635740      553488    10968564        8036     1113688    11788744
Jun 13 08:41:46 testvm1.both.org free[630580]: Swap:       8388604           0     8388604
Jun 13 08:41:47 testvm1.both.org lsblk[630581]: NAME          MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
Jun 13 08:41:47 testvm1.both.org lsblk[630581]: sda             8:0    0  120G  0 disk
Jun 13 08:41:47 testvm1.both.org lsblk[630581]: ├─sda1          8:1    0    4G  0 part /boot
Jun 13 08:41:47 testvm1.both.org lsblk[630581]: └─sda2          8:2    0  116G  0 part
Jun 13 08:41:47 testvm1.both.org lsblk[630581]:   ├─VG01-root 253:0    0    5G  0 lvm  /
Jun 13 08:41:47 testvm1.both.org lsblk[630581]:   ├─VG01-swap 253:1    0    8G  0 lvm  [SWAP]
Jun 13 08:41:47 testvm1.both.org lsblk[630581]:   ├─VG01-usr  253:2    0   30G  0 lvm  /usr
Jun 13 08:41:47 testvm1.both.org lsblk[630581]:   ├─VG01-tmp  253:3    0   10G  0 lvm  /tmp
Jun 13 08:41:47 testvm1.both.org lsblk[630581]:   ├─VG01-var  253:4    0   20G  0 lvm  /var
Jun 13 08:41:47 testvm1.both.org lsblk[630581]:   └─VG01-home 253:5    0   10G  0 lvm  /home
Jun 13 08:41:47 testvm1.both.org lsblk[630581]: sr0            11:0    1 1024M  0 rom
Jun 13 08:41:47 testvm1.both.org systemd[1]: myMonitor.service: Succeeded.
Jun 13 08:41:47 testvm1.both.org systemd[1]: Finished Logs system statistics to the systemd journal.
```
Be sure to check the status of both the timer and the service.
You probably noticed at least two things in the journal. First, you do not need to do anything special to cause the `STDOUT` from the `ExecStart` triggers in the `myMonitor.service` unit to be stored in the journal. That is all part of using systemd for running services. However, it does mean that you might need to be careful about running scripts from a service unit and how much `STDOUT` they generate.
The second thing is that the timer does not trigger exactly on the minute at :00 seconds or even exactly one minute from the previous instance. This is intentional, but it can be overridden if necessary (or if it just offends your sysadmin sensibilities).
The reason for this behavior is to prevent multiple services from triggering at exactly the same time. For example, you can use time specifications such as Weekly, Daily, and more. These shortcuts are all defined to trigger at 00:00:00 hours on the day they are triggered. When multiple timers are specified this way, there is a strong likelihood that they would attempt to start simultaneously.
systemd timers are intentionally designed to trigger somewhat randomly around the specified time to try to prevent simultaneous triggers. They trigger semi-randomly within a time window that starts at the specified trigger time and ends at the specified time plus one minute. This trigger time is maintained at a stable position with respect to all other defined timer units, according to the `systemd.timer` man page. You can see in the journal entries above that the timer triggered immediately when it started and then about 46 or 47 seconds after each minute.
Most of the time, such probabilistic trigger times are fine. When scheduling tasks such as backups to run, so long as they run during off-hours, there will be no problems. A sysadmin can select a deterministic start time, such as 01:05:00 in a typical cron job specification, to not conflict with other tasks, but there is a large range of time values that will accomplish that. A one-minute bit of randomness in a start time is usually irrelevant.
However, for some tasks, exact trigger times are an absolute requirement. For those, you can specify greater trigger time-span accuracy (to within a microsecond) by adding a statement like this to the `Timer` section of the timer unit file:
```
`AccuracySec=1us`
```
Time spans can be used to specify the desired accuracy as well as to define time spans for repeating or one-time events. It recognizes the following units:
* usec, us, µs
* msec, ms
* seconds, second, sec, s
* minutes, minute, min, m
* hours, hour, hr, h
* days, day, d
* weeks, week, w
* months, month, M (defined as 30.44 days)
* years, year, y (defined as 365.25 days)
All the default timers in `/usr/lib/systemd/system` specify a much larger range for accuracy because exact times are not critical. Look at some of the specifications in the system-created timers:
```
[root@testvm1 system]# grep Accur /usr/lib/systemd/system/*timer
/usr/lib/systemd/system/fstrim.timer:AccuracySec=1h
/usr/lib/systemd/system/logrotate.timer:AccuracySec=1h
/usr/lib/systemd/system/logwatch.timer:AccuracySec=12h
/usr/lib/systemd/system/mlocate-updatedb.timer:AccuracySec=24h
/usr/lib/systemd/system/raid-check.timer:AccuracySec=24h
/usr/lib/systemd/system/unbound-anchor.timer:AccuracySec=24h
[root@testvm1 system]#
```
View the complete contents of some of the timer unit files in the `/usr/lib/systemd/system` directory to see how they are constructed.
You do not have to enable the timer in this experiment to activate it at boot time, but the command to do so would be:
```
`[root@testvm1 system]# systemctl enable myMonitor.timer`
```
The unit files you created do not need to be executable. You also did not enable the service unit because it is triggered by the timer. You can still trigger the service unit manually from the command line, should you want to. Try that and observe the journal.
See the man pages for `systemd.timer` and `systemd.time` for more information about timer accuracy, event-time specifications, and trigger events.
### Timer types
systemd timers have other capabilities that are not found in cron, which triggers only on specific, repetitive, real-time dates and times. systemd timers can be configured to trigger based on status changes in other systemd units. For example, a timer might be configured to trigger a specific elapsed time after system boot, after startup, or after a defined service unit activates. These are called monotonic timers. Monotonic refers to a count or sequence that continually increases. These timers are not persistent because they reset after each boot.
Table 1 lists the monotonic timers along with a short definition of each, as well as the `OnCalendar` timer, which is not monotonic and is used to specify future times that may or may not be repetitive. This information is derived from the `systemd.timer` man page with a few minor changes.
Timer | Monotonic | Definition
---|---|---
`OnActiveSec=` | X | This defines a timer relative to the moment the timer is activated.
`OnBootSec=` | X | This defines a timer relative to when the machine boots up.
`OnStartupSec=` | X | This defines a timer relative to when the service manager first starts. For system timer units, this is very similar to `OnBootSec=`, as the system service manager generally starts very early at boot. It's primarily useful when configured in units running in the per-user service manager, as the user service manager generally starts on first login only, not during boot.
`OnUnitActiveSec=` | X | This defines a timer relative to when the timer that is to be activated was last activated.
`OnUnitInactiveSec=` | X | This defines a timer relative to when the timer that is to be activated was last deactivated.
`OnCalendar=` | | This defines real-time (i.e., wall clock) timers with calendar event expressions. See `systemd.time(7)` for more information on the syntax of calendar event expressions. Otherwise, the semantics are similar to `OnActiveSec=` and related settings. This timer is the one most like those used with the cron service.
_Table 1: systemd timer definitions_
The monotonic timers can use the same shortcut names for their time spans as the `AccuracySec` statement mentioned before, but systemd normalizes those names to seconds. For example, you might want to specify a timer that triggers an event one time, five days after the system boots; that might look like: `OnBootSec=5d`. If the host booted at `2020-06-15 09:45:27`, the timer would trigger at `2020-06-20 09:45:27` or within one minute after.
### Calendar event specifications
Calendar event specifications are a key part of triggering timers at desired repetitive times. Start by looking at some specifications used with the `OnCalendar` setting.
systemd and its timers use a different style for time and date specifications than the format used in crontab. It is more flexible than crontab and allows fuzzy dates and times in the manner of the `at` command. It should also be familiar enough that it will be easy to understand.
The basic format for systemd timers using `OnCalendar=` is `DOW YYYY-MM-DD HH:MM:SS`. DOW (day of week) is optional, and other fields can use an asterisk (*) to match any value for that position. All calendar time forms are converted to a normalized form. If the time is not specified, it is assumed to be 00:00:00. If the date is not specified but the time is, the next match might be today or tomorrow, depending upon the current time. Names or numbers can be used for the month and day of the week. Comma-separated lists of each unit can be specified. Unit ranges can be specified with `..` between the beginning and ending values.
There are a couple interesting options for specifying dates. The Tilde (~) can be used to specify the last day of the month or a specified number of days prior to the last day of the month. The “/” can be used to specify a day of the week as a modifier.
Here are some examples of some typical time specifications used in `OnCalendar` statements.
Calendar event specification | Description
---|---
DOW YYYY-MM-DD HH:MM:SS |
*-*-* 00:15:30 | Every day of every month of every year at 15 minutes and 30 seconds after midnight
Weekly | Every Monday at 00:00:00
Mon *-*-* 00:00:00 | Same as weekly
Mon | Same as weekly
Wed 2020-*-* | Every Wednesday in 2020 at 00:00:00
Mon..Fri 2021-*-* | Every weekday in 2021 at 00:00:00
2022-6,7,8-1,15 01:15:00 | The 1st and 15th of June, July, and August of 2022 at 01:15:00am
Mon *-05~03 | The next occurrence of a Monday in May of any year which is also the 3rd day from the end of the month.
Mon..Fri *-08~04 | The 4th day preceding the end of August for any years in which it also falls on a weekday.
*-05~03/2 | The 3rd day from the end of the month of May and then again two days later. Repeats every year. Note that this expression uses the Tilde (~).
*-05-03/2 | The third day of the month of may and then every 2nd day for the rest of May. Repeats every year. Note that this expression uses the dash (-).
_Table 2: Sample `OnCalendar` event specifications_
### Test calendar specifications
systemd provides an excellent tool for validating and examining calendar time event specifications in a timer. The `systemd-analyze calendar` tool parses a calendar time event specification and provides the normalized form as well as other interesting information such as the date and time of the next "elapse," i.e., match, and the approximate amount of time before the trigger time is reached.
First, look at a date in the future without a time (note that the times for `Next elapse` and `UTC` will differ based on your local time zone):
```
[student@studentvm1 ~]$ systemd-analyze calendar 2030-06-17
  Original form: 2030-06-17                
Normalized form: 2030-06-17 00:00:00        
    Next elapse: Mon 2030-06-17 00:00:00 EDT
       (in UTC): Mon 2030-06-17 04:00:00 UTC
       From now: 10 years 0 months left    
[root@testvm1 system]#
```
Now add a time. In this example, the date and time are analyzed separately as non-related entities:
```
[root@testvm1 system]# systemd-analyze calendar 2030-06-17 15:21:16
  Original form: 2030-06-17                
Normalized form: 2030-06-17 00:00:00        
    Next elapse: Mon 2030-06-17 00:00:00 EDT
       (in UTC): Mon 2030-06-17 04:00:00 UTC
       From now: 10 years 0 months left    
  Original form: 15:21:16                  
Normalized form: *-*-* 15:21:16            
    Next elapse: Mon 2020-06-15 15:21:16 EDT
       (in UTC): Mon 2020-06-15 19:21:16 UTC
       From now: 3h 55min left              
[root@testvm1 system]#
```
To analyze the date and time as a single unit, enclose them together in quotes. Be sure to remove the quotes when using them in the `OnCalendar=` event specification in a timer unit or you will get errors:
```
[root@testvm1 system]# systemd-analyze calendar "2030-06-17 15:21:16"
Normalized form: 2030-06-17 15:21:16        
    Next elapse: Mon 2030-06-17 15:21:16 EDT
       (in UTC): Mon 2030-06-17 19:21:16 UTC
       From now: 10 years 0 months left    
[root@testvm1 system]#
```
Now test the entries in Table 2. I like the last one, especially:
```
[root@testvm1 system]# systemd-analyze calendar "2022-6,7,8-1,15 01:15:00"
  Original form: 2022-6,7,8-1,15 01:15:00
Normalized form: 2022-06,07,08-01,15 01:15:00
    Next elapse: Wed 2022-06-01 01:15:00 EDT
       (in UTC): Wed 2022-06-01 05:15:00 UTC
       From now: 1 years 11 months left
[root@testvm1 system]#
```
Lets look at one example in which we list the next five elapses for the timestamp expression.
```
[root@testvm1 ~]# systemd-analyze calendar --iterations=5 "Mon *-05~3"
  Original form: Mon *-05~3                
Normalized form: Mon *-05~03 00:00:00      
    Next elapse: Mon 2023-05-29 00:00:00 EDT
       (in UTC): Mon 2023-05-29 04:00:00 UTC
       From now: 2 years 11 months left    
       Iter. #2: Mon 2028-05-29 00:00:00 EDT
       (in UTC): Mon 2028-05-29 04:00:00 UTC
       From now: 7 years 11 months left    
       Iter. #3: Mon 2034-05-29 00:00:00 EDT
       (in UTC): Mon 2034-05-29 04:00:00 UTC
       From now: 13 years 11 months left    
       Iter. #4: Mon 2045-05-29 00:00:00 EDT
       (in UTC): Mon 2045-05-29 04:00:00 UTC
       From now: 24 years 11 months left    
       Iter. #5: Mon 2051-05-29 00:00:00 EDT
       (in UTC): Mon 2051-05-29 04:00:00 UTC
       From now: 30 years 11 months left    
[root@testvm1 ~]#
```
This should give you enough information to start testing your `OnCalendar` time specifications. The `systemd-analyze` tool can be used for other interesting analyses, which I will begin to explore in the next article in this series.
### Summary
systemd timers can be used to perform the same kinds of tasks as the cron tool but offer more flexibility in terms of the calendar and monotonic time specifications for triggering events.
Even though the service unit you created for this experiment is usually triggered by the timer, you can also use the `systemctl start myMonitor.service` command to trigger it at any time. Multiple maintenance tasks can be scripted in a single timer; these can be Bash scripts or Linux utility programs. You can run the service triggered by the timer to run all the scripts, or you can run individual scripts as needed.
I will explore systemd's use of time and time specifications in much more detail in the next article.
I have not yet seen any indication that `cron` and `at` will be deprecated. I hope that does not happen because `at`, at least, is much easier to use for one-off task scheduling than systemd timers.
### Resources
There is a great deal of information about systemd available on the internet, but much is terse, obtuse, or even misleading. In addition to the resources mentioned in this article, the following webpages offer more detailed and reliable information about systemd startup.
* The Fedora Project has a good, practical [guide to systemd][5]. It has pretty much everything you need to know in order to configure, manage, and maintain a Fedora computer using systemd.
* The Fedora Project also has a good [cheat sheet][6] that cross-references the old SystemV commands to comparable systemd ones.
* For detailed technical information about systemd and the reasons for creating it, check out [Freedesktop.org][7]'s [description of systemd][8].
* [Linux.com][9]'s "More systemd fun" offers more advanced systemd [information and tips][10].
There is also a series of deeply technical articles for Linux sysadmins by Lennart Poettering, the designer and primary developer of systemd. These articles were written between April 2010 and September 2011, but they are just as relevant now as they were then. Much of everything else good that has been written about systemd and its ecosystem is based on these papers.
* [Rethinking PID 1][11]
* [systemd for Administrators, Part I][12]
* [systemd for Administrators, Part II][13]
* [systemd for Administrators, Part III][14]
* [systemd for Administrators, Part IV][15]
* [systemd for Administrators, Part V][16]
* [systemd for Administrators, Part VI][17]
* [systemd for Administrators, Part VII][18]
* [systemd for Administrators, Part VIII][19]
* [systemd for Administrators, Part IX][20]
* [systemd for Administrators, Part X][21]
* [systemd for Administrators, Part XI][22]
--------------------------------------------------------------------------------
via: https://opensource.com/article/20/7/systemd-timers
作者:[David Both][a]
选题:[lujun9972][b]
译者:[译者ID](https://github.com/译者ID)
校对:[校对者ID](https://github.com/校对者ID)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
[a]: https://opensource.com/users/dboth
[b]: https://github.com/lujun9972
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/checklist_todo_clock_time_team.png?itok=1z528Q0y (Team checklist)
[2]: https://opensource.com/article/17/11/how-use-cron-linux
[3]: https://opensource.com/users/dboth
[4]: https://opensource.com/article/20/5/manage-startup-systemd
[5]: https://docs.fedoraproject.org/en-US/quick-docs/understanding-and-administering-systemd/index.html
[6]: https://fedoraproject.org/wiki/SysVinit_to_Systemd_Cheatsheet
[7]: http://Freedesktop.org
[8]: http://www.freedesktop.org/wiki/Software/systemd
[9]: http://Linux.com
[10]: https://www.linux.com/training-tutorials/more-systemd-fun-blame-game-and-stopping-services-prejudice/
[11]: http://0pointer.de/blog/projects/systemd.html
[12]: http://0pointer.de/blog/projects/systemd-for-admins-1.html
[13]: http://0pointer.de/blog/projects/systemd-for-admins-2.html
[14]: http://0pointer.de/blog/projects/systemd-for-admins-3.html
[15]: http://0pointer.de/blog/projects/systemd-for-admins-4.html
[16]: http://0pointer.de/blog/projects/three-levels-of-off.html
[17]: http://0pointer.de/blog/projects/changing-roots
[18]: http://0pointer.de/blog/projects/blame-game.html
[19]: http://0pointer.de/blog/projects/the-new-configuration-files.html
[20]: http://0pointer.de/blog/projects/on-etc-sysinit.html
[21]: http://0pointer.de/blog/projects/instances.html
[22]: http://0pointer.de/blog/projects/inetd.html

View File

@ -0,0 +1,93 @@
[#]: collector: (lujun9972)
[#]: translator: ( )
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )
[#]: subject: (What you need to know about hash functions)
[#]: via: (https://opensource.com/article/20/7/hash-functions)
[#]: author: (Mike Bursell https://opensource.com/users/mikecamel)
What you need to know about hash functions
======
It should be computationally implausible to work backwards from the
output hash to the input.
![City with numbers overlay][1]
There is a tool in the security practitioner's repertoire that's helpful for everyone to understand, regardless of what they do with computers: cryptographic hash functions. That may sound mysterious, technical, and maybe even boring, but I have a concise explanation of what hashes are and why they matter to you.
A cryptographic hash function, such as SHA-256 or MD5, takes as input a set of binary data (typically as bytes) and gives output that is hopefully unique for each set of possible inputs. The length of the output—"the hash"—for any particular hash function is typically the same for any pattern of inputs (for SHA-256, it is 32 bytes or 256 bits—the clue's in the name). The important thing is this: It should be computationally implausible (cryptographers hate the word _impossible_) to work backward from the output hash to the input. This is why they are sometimes referred to as one-way hash functions.
But what are hash functions used for? And why is the property of being unique so important?
### Unique output
The phrase "hopefully unique" when describing the output of a hash function is vital because hash functions are used to render wholly unique output. Hash functions, for example, are used as a way to verify that the copy of a file _you_ downloaded is a byte-for-byte duplicate of the file _I_ downloaded. You'll see this verification process at work when you download a Linux ISO, or software from a Linux repository. Without uniqueness, the technology is rendered useless, at least for the purpose you generally have for it.
Should two inputs yield the same output, the hash is said to have a "collision." In fact, MD5 has become deprecated because it is now trivially possible to find collisions with commercially available hardware and software systems.
Another important property is that a tiny change in a message, even changing a single bit, is expected to generate a noticeable change to the output (this is the "avalanche effect").
### Verifying binary data
The typical use for hash functions is to ensure that when someone hands you a piece of binary data, it is what you expect. All data in the world of computing can be described in binary format, whether it is text, an executable, a video, an image, or a complete database of data, so hashes are broadly applicable, to say the least. Comparing binary data directly is slow and arduous computationally, but hash functions are designed to be very quick. Given two files several megabytes or gigabytes in size, you can produce hashes of them ahead of time and defer the comparisons to when you need them.
It's also generally easier to digitally sign hashes of data rather than large sets of data themselves. This is such an important feature that one of the most common uses of hashes in cryptography is to generate "digital" signatures.
Given the fact that it is easy to produce hashes of data, there's often no need to have both sets of data. Let's say you want to run an executable file on your computer. Before you do, though, you want to check that it really is the file you think it is and that no malicious actor has tampered with it. You can hash that file very quickly and easily, and as long as you have a copy of what the hash should look like, you can be fairly certain you have the file you want.
Here's a simple example:
```
$ shasum -a256 ~/bin/fop
87227baf4e1e78f6499e4905e8640c1f36720ae5f2bd167de325fd0d4ebc791c  /home/bob/bin/fop
```
If I know that the SHA-256 sum of the `fop` executable, as delivered by its vendor (Apache Foundation, in this case) is:
```
`87227baf4e1e78f6499e4905e8640c1f36720ae5f2bd167de325fd0d4ebc791c`
```
then I can be confident that the executable on my drive is indeed the same executable that Apache Foundation distributes on its website. This is where the lack of collisions (or at least the _difficulty in computing collisions_) property of hash functions is so important. If a malicious actor can craft a _replacement_ file that shares the same hash as the real file, then the process of verification is essentially useless.
In fact, there are more technical names for the various properties, and what I've described above mashes three important ones together. More accurately, those technical names are:
1. **Pre-image resistance:** Given a hash, it should be difficult to find the message from which it was created, even if you know the hash function used.
2. **Second pre-image resistance:** Given a message, it should be difficult to find another message that, when hashed, generates the same hash.
3. **Collision resistance:** It should be difficult to find any two messages that generate the same hash.
_Collision resistance_ and _second pre-image resistance_ may sound like the same property, but they're subtly (and significantly) different. Pre-image resistance says that if you _already_ have a message, you will not be able to find another message with a matching hash. Collision resistance makes it hard for you to invent two messages that will generate the same hash and is a much harder property to fulfill in a hash function.
Allow me to return to the scenario of a malicious actor attempting to exchange a file (with a hash, which you can check) with another one. Now, to use cryptographic hashes "in the wild"—out there in the real world, beyond the perfectly secure, bug-free implementations populated by unicorns and overflowing with fat-free doughnuts—there are some important and difficult provisos that need to be met. Very paranoid readers may already have spotted some of them; in particular:
1. You must have assurances that the copy of the hash you have has also not been subject to tampering.
2. You must have assurances that the entity performing the hash performs and reports it correctly.
3. You must have assurances that the entity comparing the two hashes does indeed report the result of that comparison correctly.
Ensuring that you can meet such assurances is not necessarily an easy task. This is one of the reasons Trusted Platform Modules (TPMs) are part of many computing systems. They act as a hardware root of trust with capabilities to provide assurances about the cryptography tools verifying the authenticity of important binary data. TPMs are a useful and important tool for real-world systems, and I plan to write an article about them in the future.
* * *
_This article was originally published on [Alice, Eve, and Bob][2] and is adapted and reprinted with the author's permission._
--------------------------------------------------------------------------------
via: https://opensource.com/article/20/7/hash-functions
作者:[Mike Bursell][a]
选题:[lujun9972][b]
译者:[译者ID](https://github.com/译者ID)
校对:[校对者ID](https://github.com/校对者ID)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
[a]: https://opensource.com/users/mikecamel
[b]: https://github.com/lujun9972
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/osdc_OpenData_CityNumbers.png?itok=lC03ce76 (City with numbers overlay)
[2]: https://aliceevebob.com/2020/06/16/whats-a-hash-function/

View File

@ -0,0 +1,130 @@
[#]: collector: (lujun9972)
[#]: translator: (geekpi)
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )
[#]: subject: (openSUSE Leap 15.2 Released With Focus on Containers, AI and Encryption)
[#]: via: (https://itsfoss.com/opensuse-leap-15-2-release/)
[#]: author: (Ankush Das https://itsfoss.com/author/ankush/)
openSUSE Leap 15.2 发布重点关注容器AI 和加密
======
[openSUSE][1] Leap 15.2 最终带来了一些有用的变化和改进。
同样,考虑到[消除 Leap 差异][2]这篇让人激动的公告openSUSE Leap 15.2 的发布使我们下一步将 SLE[SUSE Linux Enterprise][3])二进制文件集成到 openSUSE Leap 15.3 更加近了一步。
让我们看一下 openSUSE Leap 15.2 中发生了哪些变化和改进。
### openSUSE Leap 15.2:关键变化
![][4]
总体而言openSUSE Leap 15.2 发行版涉及安全更新、主要的新软件包、bug 修复以及其他改进。
该项目的开发人员 **Marco Varlese** 在新闻稿中提到:
>“ Leap 15.2 代表了人工智能领域的巨大进步,我很高兴 openSUSE 用户现在终于可以通过我们的仓库使用机器学习/深度学习框架和应用,以享受稳定和最新的生态系统。”
尽管这暗示了可能涉及一些变化,以下是 openSUSE Leap 15.2 中的新功能:
#### 添加人工智能 AI 和机器学习包
毫无疑问,人工智能 AI 和机器学习是一些最划时代的技术。
为了向用户提供便利openSUSE Leap 15.2 为新的开源技术添加了许多重要的软件包:
* [Tensorflow][5]
* [PyTorch][6]
* [ONNX][7]
* [Grafana][8]
* [Prometheus][9]
#### 引入实时内核
![][10]
在 openSUSE Leap 15.2 中,将引入实时内核来管理[微处理器][11]的时序,以有效处理时间严格的事件。
实时内核的添加对于现实意义重大。 项目委员会主席 **Gerald Pfeifer** 的讲话如下:
>“将实时内核添加到 openSUSE Leap 释放了新的可能性。想想边缘计算、嵌入式设备、数据抓取所有这些都在飞速发展。从历史上看其中很多都是专有领域。现在openSUSE 为有兴趣测试实时功能的开发者、研究人员和公司甚至贡献者将打开了通道。另一个领域开源帮助开放!”
#### 包含容器技术
在最新版本中,你会注意到 [Kubernetes][12] 作为官方包装随附。这让用户可以轻松地自动化部署、扩展和管理容器化的应用。
[Helm][13] Kubernetes 的包管理器)也加入了进来。不仅于此,你还可以在此找到其他一些软件,从而可以更轻松地保护和部署容器化应用。
#### 更新的 openSUSE 安装程序
![][14]
openSUSE 的安装程序已经非常不错。但是,在最新的 Leap 15.2 版本中,它们添加了更多信息,兼容从右至左语言(如阿拉伯语)以及一些小的更改,从而在安装时更容易选择。
#### YaST 改进
尽管 [YaST][15] 已经是一个非常强大的安装和配置工具,但是此发行版增加了创建和管理 Btrfs 文件系统以及实施高级加密技术的能力。
当然,你肯定想到 [OpenSuse 在 WSL][16] 的可用性。因此,根据 Leap 15.2 的发行说明YaST 与 WSL 的兼容性得到了改善。
#### 桌面环境改进
![][17]
可用的桌面环境已更新为最新版本,包括 [KDE Plasma 5.18 LTS][18] 和 [GNOME 3.34][19]。
你还能发现在 openSUSE Leap 15.2 可用 [XFCE 4.14][20] 桌面。
如果你想知道最新版本的所有详细信息,可以参考[官方发布公告][21]。
### 下载和可用性
目前,你可以找到 Linode 云镜像中找到 Leap 15.2。另外,你会注意到其他云托管服务(如 AWS、Azure 和其他服务)也提供了它。
你还可以从官方网站获取 DVD ISO 或网络镜像文件。
要升级你当前的安装,我建议按照[官方说明][22]操作。
[openSUSE Leap 15.2][23]
你尝试过 openSUSE Leap 15.2 了么?请随时让我知道你的想法!
--------------------------------------------------------------------------------
via: https://itsfoss.com/opensuse-leap-15-2-release/
作者:[Ankush Das][a]
选题:[lujun9972][b]
译者:[geekpi](https://github.com/geekpi)
校对:[校对者ID](https://github.com/校对者ID)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
[a]: https://itsfoss.com/author/ankush/
[b]: https://github.com/lujun9972
[1]: https://www.opensuse.org/
[2]: https://www.suse.com/c/sle-15-sp2-schedule-and-closing-the-opensuse-leap-gap/
[3]: https://www.suse.com/
[4]: https://i2.wp.com/itsfoss.com/wp-content/uploads/2020/07/opensuse-leap-15-2-gnome.png?ssl=1
[5]: https://www.tensorflow.org
[6]: https://pytorch.org
[7]: https://onnx.ai
[8]: https://grafana.com
[9]: https://prometheus.io/docs/introduction/overview/
[10]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2020/07/opensuse-leap-15-2-terminal.png?ssl=1
[11]: https://en.wikipedia.org/wiki/Microprocessor
[12]: https://kubernetes.io
[13]: https://helm.sh
[14]: https://i2.wp.com/itsfoss.com/wp-content/uploads/2020/07/opensuse-leap-15-2.png?ssl=1
[15]: https://yast.opensuse.org/
[16]: https://itsfoss.com/opensuse-bash-on-windows/
[17]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2020/07/opensue-leap-15-2-kde.png?ssl=1
[18]: https://itsfoss.com/kde-plasma-5-18-release/
[19]: https://itsfoss.com/gnome-3-34-release/
[20]: https://www.xfce.org/about/news/?post=1565568000
[21]: https://en.opensuse.org/Release_announcement_15.2
[22]: https://en.opensuse.org/SDB:System_upgrade
[23]: https://software.opensuse.org/distributions/leap

View File

@ -1,135 +0,0 @@
[#]: collector: (lujun9972)
[#]: translator: (chunibyo-wly)
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )
[#]: subject: (One CI/CD pipeline per product to rule them all)
[#]: via: (https://opensource.com/article/19/7/cicd-pipeline-rule-them-all)
[#]: author: (Willy-Peter Schaub https://opensource.com/users/wpschaub/users/bclaster/users/matt-micene/users/barkerd427)
使用一条CI/CD流水线管理所有的产品
======
统一的持续集成与持续交付的流水线的构想是一种梦想吗?
![An intersection of pipes.][1]
当我加入 WorkSafeBC 的云端运维团队,我负责的是云端运维和工作流水线优化,我分享了一个可以对任意产品进行持续集成和持续交付的流水线。
根据 Lukas Klose 所说,[flow][2](在软件工程范围内)是“软件系统可以在一种稳定和可预测的状态下创造价值”一种状态。我认为这是我遇到的最大的挑战和机遇之一,特别是在解决紧急问题的复杂领域。我力求通过一种持续,高效和优质的解决方案,提供一种持续交付模式,并且能够构建正确的事物让我们的用户感到满意。
### 持续集成和持续交付的 (CI/CD) 流水线
CI/CD 流水线是一种代码变更的频率更高,一致并且可靠的持续交付 DevOps 实践。它可以帮组敏捷开发团队增加**部署的频率**并且减少**变更所需要的时间****变更失败的频率**,和**故障恢复的时间**等关键绩效指标 (KPIS) ,从而提高质量并且实现更快的交付。唯一的先决条件就是坚实的开发基础,对需求从构想到放弃的责任心,和一个全面的流水线(如下图所示)。
![Prerequisites for a solid development process][3]
它简化了工程流程和产品用以稳定基础架构环境;优化工作流程;创建一致的,可重复的,自动化的任务。正如 Dave Snowden [Cynefin Sensemaking][4] 所说的那样,这样就允许我们将复杂不可解决的任务变成了复杂可解决的任务,降低了维护成本,提高了质量和可靠性。
精简流程的一部分是需要最大程度地减少浪费 [wasteful practice types][5] Muri (过载), Mura (变异), 和 Muda (浪费)
* **Muri:** 避免过度工程化,和与业务价值不相关的功能以及过多的文档
* **Mura:** 改善确认和审批流(比如,安全审核);驱动测试 [shift-left][6],安全漏洞扫描与代码质量检查;并且可以改进风险评定。
* **Muda:** 避免浪费比如技术债务bugs 或者前期详细的文档等。
看起来 80 的重点都集中在提供一种可以持续集成和协作的工程产品,这些系统可以采用构想一个创意,计划,开发,测试和监视您的解决方案。然而,一个成功的转型和工程系统是由 5 的产品15 的过程和 80 的开发人员组成的。
有很多产品供我们使用。比如Azure DevOps 提供了持续集成 (CI) 和持续交付 (CD) 的丰富支持。可扩展,开源集成和商用现成品或技术 (COTS) 像软件即服务 (SaaS) 比如 Stryker, SonarQube, WhiteSource, Jenkins和 Octopus 等。
![5% about products, 15% about process, 80% about people][7]
最大的挑战是打破数十年的规则,规定和已经步入舒适区的流程:“*我们一直都是这样做的;为什么需要改变呢?*”
开发和运维人员之间的摩擦导致了各种支离破碎,重复的,不间断的集成和交付管道。开发人员希望能访问所有东西,以便快速的持续迭代,用户使用和持续发布。运维人员希望将所有东西说起来来保护业务,用户和品质。这些矛盾在不经意间导致了很难做到一种自动化的流程,进而导致发布周期晚于预期。
让我们使用最近的白板讨论中的片段来探索流水线。
想要支持流水线的变化是一项困难并且花费巨大的工作;版本控制和可追溯更使这个问题变得更加复杂,因此不断精简开发流程和流水线是一项挑战。
![Improving quality and visibility of pipelines][8]
我主张一些原则使得每个产品都能使用通用流水线:
* 使一切自动化
* 一次构建
* 保持持续集成和持续交付
* 保持持续精简和改进
* 保持一个定义构建
* 保持一个发布的流水线定义
* 频繁和尽早的扫描漏洞,并且*尽快的失败*
* 频繁和尽早的进行测试,并且*尽快的失败*
* 保持已发布版本的可追踪和监控
但是,如果我要打破这些,最重要的原则就是*保持简单*。如果你不能说明流水线化的原因,你或许是不了解自己的软件过程的。我们大多数想要的不是最好的,超现代的和具有革命意义的流水线——我们仅仅是需要一个功能强大,有价值的和能适配不同工程的流水线。首先需要解决的是那 80% —— 文化,人员和他们的心态。
### 统一流水线
让我们逐步完成我们的白板会议实践。
![CI build/CD release pipeline][9]
每个应用使用一套构建定义来定义一个 CI/CD 流水线,用来触发*合并请求前的验证*与*持续集成*的构建。生成一个带有 debug 信息的*发布*的构建并且将其上传到 [Symbol Server][10]。这允许了开发者们可以在不需要考虑什么被构建或者什么符号需要被加载的情况下在本地和远程生产环境进行 debug符号服务器对我们来说就是这样的魔法。
![Breaking down the CI build pipeline][11]
在构建过程中进行尽可能多的构建——*左移测试*——允许制作新特性的团队可以经常的失败,不断的提高整体的产品质量,并且可以为代码审核员提供每个 pull request 的无价证据。你喜欢有大量提交的 pull request 吗?或者一个带有少数提交和提供了漏洞检查,测试覆盖率,代码质量检查和 [Stryker][12] 突变残余的 pull request
![Breaking down the CD release pipeline][13]
不要通过转变构建去生成多个环境特定的构建。通过一个构建实现*发布时转型**标记化*和 XML/JSON 的*值替换*。换句话说,*左移测试*具体环境的配置。
![Shift-right the environment-specific configuration][14]
安全存储发布的配置数据,并且使他基于*可信任*和*敏感*的数据对开发和运维都能有效。使用开源的密钥管理工具Azure 密钥金库AWS 密钥管理服务,或者其他产品,记住你的工具箱中有很多方便的工具。
![Dev-QA-production pipeline][15]
使用*用户组*而不是*用户*移动权限系统,使用多流水线的多阶段转移到简单的用户组成员资格。
![Move approver management to simple group membership][16]
创建一条流水线并且对赋予特定的交付阶段的权限,而不是重复流水线让团队进入他们感兴趣的地方。
![Pipeline with access to specific delivery stages][17]
最后但并非不重要,包括 pull request 帮助提高代码仓库洞察力和透明度,增进总体质量,协作和发布预验证构建到已筛选的环境;比如,开发环境。
这是整个白板更正式的视图。
![The full pipeline][18]
所以您对CI/CD管道有什么想法和经验是我通过*一条管道来管理它们*的这个梦想吗?
--------------------------------------------------------------------------------
via: https://opensource.com/article/19/7/cicd-pipeline-rule-them-all
作者:[Willy-Peter Schaub][a]
选题:[lujun9972][b]
译者:[译者ID](https://github.com/chunibyo-wly)
校对:[校对者ID](https://github.com/校对者ID)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
[a]: https://opensource.com/users/wpschaub/users/bclaster/users/matt-micene/users/barkerd427
[b]: https://github.com/lujun9972
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/LAW-Internet_construction_9401467_520x292_0512_dc.png?itok=RPkPPtDe (An intersection of pipes.)
[2]: https://continuingstudies.sauder.ubc.ca/courses/agile-delivery-methods/ii861
[3]: https://opensource.com/sites/default/files/uploads/devops_pipeline_pipe-2.png (Prerequisites for a solid development process)
[4]: https://en.wikipedia.org/wiki/Cynefin_framework
[5]: https://www.lean.org/lexicon/muda-mura-muri
[6]: https://en.wikipedia.org/wiki/Shift_left_testing
[7]: https://opensource.com/sites/default/files/uploads/devops_pipeline_pipe-3.png (5% about products, 15% about process, 80% about people)
[8]: https://opensource.com/sites/default/files/uploads/devops_pipeline_pipe-4_0.png (Improving quality and visibility of pipelines)
[9]: https://opensource.com/sites/default/files/uploads/devops_pipeline_pipe-5_0.png (CI build/CD release pipeline)
[10]: https://en.wikipedia.org/wiki/Microsoft_Symbol_Server
[11]: https://opensource.com/sites/default/files/uploads/devops_pipeline_pipe-6.png (Breaking down the CI build pipeline)
[12]: https://stryker-mutator.io/
[13]: https://opensource.com/sites/default/files/uploads/devops_pipeline_pipe-7.png (Breaking down the CD release pipeline)
[14]: https://opensource.com/sites/default/files/uploads/devops_pipeline_pipe-8.png (Shift-right the environment-specific configuration)
[15]: https://opensource.com/sites/default/files/uploads/devops_pipeline_pipe-9.png (Dev-QA-production pipeline)
[16]: https://opensource.com/sites/default/files/uploads/devops_pipeline_pipe-10.png (Move approver management to simple group membership)
[17]: https://opensource.com/sites/default/files/uploads/devops_pipeline_pipe-11.png (Pipeline with access to specific delivery stages)
[18]: https://opensource.com/sites/default/files/uploads/devops_pipeline_pipe-12.png (The full pipeline)